diff options
-rw-r--r-- | ChangeLog | 44 | ||||
-rw-r--r-- | Makefile.in | 6 | ||||
-rw-r--r-- | README.openssh2 | 15 | ||||
-rw-r--r-- | auth-rsa.c | 3 | ||||
-rw-r--r-- | auth.c | 686 | ||||
-rw-r--r-- | auth.h | 7 | ||||
-rw-r--r-- | auth1.c | 512 | ||||
-rw-r--r-- | auth2.c | 459 | ||||
-rw-r--r-- | authfile.c | 206 | ||||
-rw-r--r-- | authfile.h | 36 | ||||
-rw-r--r-- | compat.c | 3 | ||||
-rw-r--r-- | dsa.c | 78 | ||||
-rw-r--r-- | dsa.h | 12 | ||||
-rw-r--r-- | hostfile.c | 23 | ||||
-rw-r--r-- | key.c | 107 | ||||
-rw-r--r-- | key.h | 3 | ||||
-rw-r--r-- | radix.c | 96 | ||||
-rw-r--r-- | readconf.c | 45 | ||||
-rw-r--r-- | readconf.h | 6 | ||||
-rw-r--r-- | servconf.c | 4 | ||||
-rw-r--r-- | serverloop.c | 2 | ||||
-rw-r--r-- | session.c | 3 | ||||
-rw-r--r-- | ssh-add.c | 37 | ||||
-rw-r--r-- | ssh-keygen.1 | 8 | ||||
-rw-r--r-- | ssh-keygen.c | 350 | ||||
-rw-r--r-- | ssh.c | 65 | ||||
-rw-r--r-- | ssh.h | 37 | ||||
-rw-r--r-- | sshconnect.c | 1356 | ||||
-rw-r--r-- | sshconnect.h | 16 | ||||
-rw-r--r-- | sshconnect1.c | 1020 | ||||
-rw-r--r-- | sshconnect2.c | 449 | ||||
-rw-r--r-- | sshd.c | 221 | ||||
-rw-r--r-- | uuencode.c | 120 | ||||
-rw-r--r-- | uuencode.h | 6 | ||||
-rw-r--r-- | version.h | 2 |
35 files changed, 3544 insertions, 2499 deletions
@@ -1,3 +1,47 @@ | |||
1 | 20000429 | ||
2 | - Merge big update to OpenSSH-2.0 from OpenBSD CVS | ||
3 | [README.openssh2] | ||
4 | - interop w/ F-secure windows client | ||
5 | - sync documentation | ||
6 | - ssh_host_dsa_key not ssh_dsa_key | ||
7 | [auth-rsa.c] | ||
8 | - missing fclose | ||
9 | [auth.c authfile.c compat.c dsa.c dsa.h hostfile.c key.c key.h radix.c] | ||
10 | [readconf.c readconf.h ssh-add.c ssh-keygen.c ssh.c ssh.h sshconnect.c] | ||
11 | [sshd.c uuencode.c uuencode.h authfile.h] | ||
12 | - add DSA pubkey auth and other SSH2 fixes. use ssh-keygen -[xX] | ||
13 | for trading keys with the real and the original SSH, directly from the | ||
14 | people who invented the SSH protocol. | ||
15 | [auth.c auth.h authfile.c sshconnect.c auth1.c auth2.c sshconnect.h] | ||
16 | [sshconnect1.c sshconnect2.c] | ||
17 | - split auth/sshconnect in one file per protocol version | ||
18 | [sshconnect2.c] | ||
19 | - remove debug | ||
20 | [uuencode.c] | ||
21 | - add trailing = | ||
22 | [version.h] | ||
23 | - OpenSSH-2.0 | ||
24 | [ssh-keygen.1 ssh-keygen.c] | ||
25 | - add -R flag: exit code indicates if RSA is alive | ||
26 | [sshd.c] | ||
27 | - remove unused | ||
28 | silent if -Q is specified | ||
29 | [ssh.h] | ||
30 | - host key becomes /etc/ssh_host_dsa_key | ||
31 | [readconf.c servconf.c ] | ||
32 | - ssh/sshd default to proto 1 and 2 | ||
33 | [uuencode.c] | ||
34 | - remove debug | ||
35 | [auth2.c ssh-keygen.c sshconnect2.c sshd.c] | ||
36 | - xfree DSA blobs | ||
37 | [auth2.c serverloop.c session.c] | ||
38 | - cleanup logging for sshd/2, respect PasswordAuth no | ||
39 | [sshconnect2.c] | ||
40 | - less debug, respect .ssh/config | ||
41 | [README.openssh2 channels.c channels.h] | ||
42 | - clientloop.c session.c ssh.c | ||
43 | - support for x11-fwding, client+server | ||
44 | |||
1 | 20000421 | 45 | 20000421 |
2 | - Merge fix from OpenBSD CVS | 46 | - Merge fix from OpenBSD CVS |
3 | [ssh-agent.c] | 47 | [ssh-agent.c] |
diff --git a/Makefile.in b/Makefile.in index 196cc5786..d5e3fde6c 100644 --- a/Makefile.in +++ b/Makefile.in | |||
@@ -31,11 +31,11 @@ LDFLAGS=-L. @LDFLAGS@ | |||
31 | 31 | ||
32 | TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) | 32 | TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS) |
33 | 33 | ||
34 | LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o | 34 | LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o uuencode.o xmalloc.o |
35 | 35 | ||
36 | SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o | 36 | SSHOBJS= ssh.o sshconnect.o sshconnect1.o sshconnect2.o log-client.o readconf.o clientloop.o |
37 | 37 | ||
38 | SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o auth.o | 38 | SSHDOBJS= sshd.o auth.o auth1.o auth2.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o |
39 | 39 | ||
40 | TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 | 40 | TROFFMAN = scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8 |
41 | CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 | 41 | CATMAN = scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0 |
diff --git a/README.openssh2 b/README.openssh2 index bdf78bf58..fca3173ae 100644 --- a/README.openssh2 +++ b/README.openssh2 | |||
@@ -1,13 +1,16 @@ | |||
1 | $Id: README.openssh2,v 1.3 2000/04/12 07:45:43 markus Exp $ | 1 | $Id: README.openssh2,v 1.6 2000/04/27 13:42:58 provos Exp $ |
2 | 2 | ||
3 | howto: | 3 | howto: |
4 | 1) generate server key: | 4 | 1) generate server key: |
5 | $ umask 077 | 5 | $ ssh-keygen -d -f /etc/ssh_host_dsa_key -N '' |
6 | $ openssl dsaparam 1024 -out dsa1024.pem | ||
7 | $ openssl gendsa -out /etc/ssh_dsa_key dsa1024.pem -rand /dev/arandom | ||
8 | 2) enable ssh2: | 6 | 2) enable ssh2: |
9 | server: add 'Protocol 2,1' to /etc/sshd_config | 7 | server: add 'Protocol 2,1' to /etc/sshd_config |
10 | client: ssh -o 'Protocol 2,1', or add to .ssh/config | 8 | client: ssh -o 'Protocol 2,1', or add to .ssh/config |
9 | 3) interop w/ ssh.com dsa-keys: | ||
10 | ssh-keygen -f /key/from/ssh.com -X >> ~/.ssh/authorized_keys2 | ||
11 | and vice versa | ||
12 | ssh-keygen -f /privatekey/from/openssh -x > ~/.ssh2/mykey.pub | ||
13 | echo Key mykey.pub >> ~/.ssh2/authorization | ||
11 | 14 | ||
12 | works: | 15 | works: |
13 | secsh-transport: works w/o rekey | 16 | secsh-transport: works w/o rekey |
@@ -22,7 +25,7 @@ works: | |||
22 | key database in ~/.ssh/known_hosts with bits == 0 hack | 25 | key database in ~/.ssh/known_hosts with bits == 0 hack |
23 | dss: signature works, keygen w/ openssl | 26 | dss: signature works, keygen w/ openssl |
24 | client interops w/ sshd2, lshd | 27 | client interops w/ sshd2, lshd |
25 | server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT | 28 | server interops w/ ssh2, lsh, ssh.com's Windows client, SecureCRT, F-Secure SSH Client 4.0 |
26 | server supports multiple concurrent sessions (e.g. with SSH.com Windows client) | 29 | server supports multiple concurrent sessions (e.g. with SSH.com Windows client) |
27 | todo: | 30 | todo: |
28 | re-keying | 31 | re-keying |
@@ -38,4 +41,4 @@ todo: | |||
38 | sftp | 41 | sftp |
39 | 42 | ||
40 | -markus | 43 | -markus |
41 | $Date: 2000/04/12 07:45:43 $ | 44 | $Date: 2000/04/27 13:42:58 $ |
diff --git a/auth-rsa.c b/auth-rsa.c index aa8be2bb0..c61eab29a 100644 --- a/auth-rsa.c +++ b/auth-rsa.c | |||
@@ -16,7 +16,7 @@ | |||
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include "includes.h" | 18 | #include "includes.h" |
19 | RCSID("$Id: auth-rsa.c,v 1.17 2000/04/16 02:31:49 damien Exp $"); | 19 | RCSID("$Id: auth-rsa.c,v 1.18 2000/04/29 13:57:09 damien Exp $"); |
20 | 20 | ||
21 | #include "rsa.h" | 21 | #include "rsa.h" |
22 | #include "packet.h" | 22 | #include "packet.h" |
@@ -185,6 +185,7 @@ auth_rsa(struct passwd *pw, BIGNUM *client_n) | |||
185 | } | 185 | } |
186 | } | 186 | } |
187 | if (fail) { | 187 | if (fail) { |
188 | fclose(f); | ||
188 | log(buf); | 189 | log(buf); |
189 | packet_send_debug(buf); | 190 | packet_send_debug(buf); |
190 | restore_uid(); | 191 | restore_uid(); |
@@ -5,7 +5,7 @@ | |||
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include "includes.h" | 7 | #include "includes.h" |
8 | RCSID("$OpenBSD: auth.c,v 1.4 2000/04/14 10:30:29 markus Exp $"); | 8 | RCSID("$OpenBSD: auth.c,v 1.6 2000/04/26 21:28:31 markus Exp $"); |
9 | 9 | ||
10 | #include "xmalloc.h" | 10 | #include "xmalloc.h" |
11 | #include "rsa.h" | 11 | #include "rsa.h" |
@@ -40,7 +40,7 @@ extern char *forced_command; | |||
40 | * If the user's shell is not executable, false will be returned. | 40 | * If the user's shell is not executable, false will be returned. |
41 | * Otherwise true is returned. | 41 | * Otherwise true is returned. |
42 | */ | 42 | */ |
43 | static int | 43 | int |
44 | allowed_user(struct passwd * pw) | 44 | allowed_user(struct passwd * pw) |
45 | { | 45 | { |
46 | struct stat st; | 46 | struct stat st; |
@@ -118,685 +118,3 @@ allowed_user(struct passwd * pw) | |||
118 | /* We found no reason not to let this user try to log on... */ | 118 | /* We found no reason not to let this user try to log on... */ |
119 | return 1; | 119 | return 1; |
120 | } | 120 | } |
121 | |||
122 | /* | ||
123 | * convert ssh auth msg type into description | ||
124 | */ | ||
125 | char * | ||
126 | get_authname(int type) | ||
127 | { | ||
128 | static char buf[1024]; | ||
129 | switch (type) { | ||
130 | case SSH_CMSG_AUTH_PASSWORD: | ||
131 | return "password"; | ||
132 | case SSH_CMSG_AUTH_RSA: | ||
133 | return "rsa"; | ||
134 | case SSH_CMSG_AUTH_RHOSTS_RSA: | ||
135 | return "rhosts-rsa"; | ||
136 | case SSH_CMSG_AUTH_RHOSTS: | ||
137 | return "rhosts"; | ||
138 | #ifdef KRB4 | ||
139 | case SSH_CMSG_AUTH_KERBEROS: | ||
140 | return "kerberos"; | ||
141 | #endif | ||
142 | #ifdef SKEY | ||
143 | case SSH_CMSG_AUTH_TIS_RESPONSE: | ||
144 | return "s/key"; | ||
145 | #endif | ||
146 | } | ||
147 | snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); | ||
148 | return buf; | ||
149 | } | ||
150 | |||
151 | #define AUTH_FAIL_MAX 6 | ||
152 | #define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) | ||
153 | #define AUTH_FAIL_MSG "Too many authentication failures for %.100s" | ||
154 | |||
155 | /* | ||
156 | * The user does not exist or access is denied, | ||
157 | * but fake indication that authentication is needed. | ||
158 | */ | ||
159 | void | ||
160 | do_fake_authloop1(char *user) | ||
161 | { | ||
162 | int attempt = 0; | ||
163 | |||
164 | log("Faking authloop for illegal user %.200s from %.200s port %d", | ||
165 | user, | ||
166 | get_remote_ipaddr(), | ||
167 | get_remote_port()); | ||
168 | |||
169 | #ifdef WITH_AIXAUTHENTICATE | ||
170 | if (strncmp(get_authname(type),"password", | ||
171 | strlen(get_authname(type))) == 0) | ||
172 | loginfailed(pw->pw_name,get_canonical_hostname(),"ssh"); | ||
173 | #endif /* WITH_AIXAUTHENTICATE */ | ||
174 | |||
175 | /* Indicate that authentication is needed. */ | ||
176 | packet_start(SSH_SMSG_FAILURE); | ||
177 | packet_send(); | ||
178 | packet_write_wait(); | ||
179 | |||
180 | /* | ||
181 | * Keep reading packets, and always respond with a failure. This is | ||
182 | * to avoid disclosing whether such a user really exists. | ||
183 | */ | ||
184 | for (attempt = 1;; attempt++) { | ||
185 | /* Read a packet. This will not return if the client disconnects. */ | ||
186 | int plen; | ||
187 | #ifndef SKEY | ||
188 | (void)packet_read(&plen); | ||
189 | #else /* SKEY */ | ||
190 | int type = packet_read(&plen); | ||
191 | unsigned int dlen; | ||
192 | char *password, *skeyinfo; | ||
193 | /* Try to send a fake s/key challenge. */ | ||
194 | if (options.skey_authentication == 1 && | ||
195 | (skeyinfo = skey_fake_keyinfo(user)) != NULL) { | ||
196 | password = NULL; | ||
197 | if (type == SSH_CMSG_AUTH_TIS) { | ||
198 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
199 | packet_put_string(skeyinfo, strlen(skeyinfo)); | ||
200 | packet_send(); | ||
201 | packet_write_wait(); | ||
202 | continue; | ||
203 | } else if (type == SSH_CMSG_AUTH_PASSWORD && | ||
204 | options.password_authentication && | ||
205 | (password = packet_get_string(&dlen)) != NULL && | ||
206 | dlen == 5 && | ||
207 | strncasecmp(password, "s/key", 5) == 0 ) { | ||
208 | packet_send_debug(skeyinfo); | ||
209 | } | ||
210 | if (password != NULL) | ||
211 | xfree(password); | ||
212 | } | ||
213 | #endif | ||
214 | if (attempt > AUTH_FAIL_MAX) | ||
215 | packet_disconnect(AUTH_FAIL_MSG, user); | ||
216 | |||
217 | /* | ||
218 | * Send failure. This should be indistinguishable from a | ||
219 | * failed authentication. | ||
220 | */ | ||
221 | packet_start(SSH_SMSG_FAILURE); | ||
222 | packet_send(); | ||
223 | packet_write_wait(); | ||
224 | } | ||
225 | /* NOTREACHED */ | ||
226 | abort(); | ||
227 | } | ||
228 | |||
229 | /* | ||
230 | * read packets and try to authenticate local user *pw. | ||
231 | * return if authentication is successfull | ||
232 | */ | ||
233 | void | ||
234 | do_authloop(struct passwd * pw) | ||
235 | { | ||
236 | int attempt = 0; | ||
237 | unsigned int bits; | ||
238 | RSA *client_host_key; | ||
239 | BIGNUM *n; | ||
240 | char *client_user = NULL, *password = NULL; | ||
241 | char user[1024]; | ||
242 | unsigned int dlen; | ||
243 | int plen, nlen, elen; | ||
244 | unsigned int ulen; | ||
245 | int type = 0; | ||
246 | void (*authlog) (const char *fmt,...) = verbose; | ||
247 | |||
248 | /* Indicate that authentication is needed. */ | ||
249 | packet_start(SSH_SMSG_FAILURE); | ||
250 | packet_send(); | ||
251 | packet_write_wait(); | ||
252 | |||
253 | for (attempt = 1;; attempt++) { | ||
254 | int authenticated = 0; | ||
255 | strlcpy(user, "", sizeof user); | ||
256 | |||
257 | /* Get a packet from the client. */ | ||
258 | type = packet_read(&plen); | ||
259 | |||
260 | /* Process the packet. */ | ||
261 | switch (type) { | ||
262 | #ifdef AFS | ||
263 | case SSH_CMSG_HAVE_KERBEROS_TGT: | ||
264 | if (!options.kerberos_tgt_passing) { | ||
265 | /* packet_get_all(); */ | ||
266 | verbose("Kerberos tgt passing disabled."); | ||
267 | break; | ||
268 | } else { | ||
269 | /* Accept Kerberos tgt. */ | ||
270 | char *tgt = packet_get_string(&dlen); | ||
271 | packet_integrity_check(plen, 4 + dlen, type); | ||
272 | if (!auth_kerberos_tgt(pw, tgt)) | ||
273 | verbose("Kerberos tgt REFUSED for %s", pw->pw_name); | ||
274 | xfree(tgt); | ||
275 | } | ||
276 | continue; | ||
277 | |||
278 | case SSH_CMSG_HAVE_AFS_TOKEN: | ||
279 | if (!options.afs_token_passing || !k_hasafs()) { | ||
280 | /* packet_get_all(); */ | ||
281 | verbose("AFS token passing disabled."); | ||
282 | break; | ||
283 | } else { | ||
284 | /* Accept AFS token. */ | ||
285 | char *token_string = packet_get_string(&dlen); | ||
286 | packet_integrity_check(plen, 4 + dlen, type); | ||
287 | if (!auth_afs_token(pw, token_string)) | ||
288 | verbose("AFS token REFUSED for %s", pw->pw_name); | ||
289 | xfree(token_string); | ||
290 | } | ||
291 | continue; | ||
292 | #endif /* AFS */ | ||
293 | #ifdef KRB4 | ||
294 | case SSH_CMSG_AUTH_KERBEROS: | ||
295 | if (!options.kerberos_authentication) { | ||
296 | /* packet_get_all(); */ | ||
297 | verbose("Kerberos authentication disabled."); | ||
298 | break; | ||
299 | } else { | ||
300 | /* Try Kerberos v4 authentication. */ | ||
301 | KTEXT_ST auth; | ||
302 | char *tkt_user = NULL; | ||
303 | char *kdata = packet_get_string((unsigned int *) &auth.length); | ||
304 | packet_integrity_check(plen, 4 + auth.length, type); | ||
305 | |||
306 | if (auth.length < MAX_KTXT_LEN) | ||
307 | memcpy(auth.dat, kdata, auth.length); | ||
308 | xfree(kdata); | ||
309 | |||
310 | authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); | ||
311 | |||
312 | if (authenticated) { | ||
313 | snprintf(user, sizeof user, " tktuser %s", tkt_user); | ||
314 | xfree(tkt_user); | ||
315 | } | ||
316 | } | ||
317 | break; | ||
318 | #endif /* KRB4 */ | ||
319 | |||
320 | case SSH_CMSG_AUTH_RHOSTS: | ||
321 | if (!options.rhosts_authentication) { | ||
322 | verbose("Rhosts authentication disabled."); | ||
323 | break; | ||
324 | } | ||
325 | /* | ||
326 | * Get client user name. Note that we just have to | ||
327 | * trust the client; this is one reason why rhosts | ||
328 | * authentication is insecure. (Another is | ||
329 | * IP-spoofing on a local network.) | ||
330 | */ | ||
331 | client_user = packet_get_string(&ulen); | ||
332 | packet_integrity_check(plen, 4 + ulen, type); | ||
333 | |||
334 | /* Try to authenticate using /etc/hosts.equiv and | ||
335 | .rhosts. */ | ||
336 | authenticated = auth_rhosts(pw, client_user); | ||
337 | |||
338 | snprintf(user, sizeof user, " ruser %s", client_user); | ||
339 | break; | ||
340 | |||
341 | case SSH_CMSG_AUTH_RHOSTS_RSA: | ||
342 | if (!options.rhosts_rsa_authentication) { | ||
343 | verbose("Rhosts with RSA authentication disabled."); | ||
344 | break; | ||
345 | } | ||
346 | /* | ||
347 | * Get client user name. Note that we just have to | ||
348 | * trust the client; root on the client machine can | ||
349 | * claim to be any user. | ||
350 | */ | ||
351 | client_user = packet_get_string(&ulen); | ||
352 | |||
353 | /* Get the client host key. */ | ||
354 | client_host_key = RSA_new(); | ||
355 | if (client_host_key == NULL) | ||
356 | fatal("RSA_new failed"); | ||
357 | client_host_key->e = BN_new(); | ||
358 | client_host_key->n = BN_new(); | ||
359 | if (client_host_key->e == NULL || client_host_key->n == NULL) | ||
360 | fatal("BN_new failed"); | ||
361 | bits = packet_get_int(); | ||
362 | packet_get_bignum(client_host_key->e, &elen); | ||
363 | packet_get_bignum(client_host_key->n, &nlen); | ||
364 | |||
365 | if (bits != BN_num_bits(client_host_key->n)) | ||
366 | error("Warning: keysize mismatch for client_host_key: " | ||
367 | "actual %d, announced %d", BN_num_bits(client_host_key->n), bits); | ||
368 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); | ||
369 | |||
370 | authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); | ||
371 | RSA_free(client_host_key); | ||
372 | |||
373 | snprintf(user, sizeof user, " ruser %s", client_user); | ||
374 | break; | ||
375 | |||
376 | case SSH_CMSG_AUTH_RSA: | ||
377 | if (!options.rsa_authentication) { | ||
378 | verbose("RSA authentication disabled."); | ||
379 | break; | ||
380 | } | ||
381 | /* RSA authentication requested. */ | ||
382 | n = BN_new(); | ||
383 | packet_get_bignum(n, &nlen); | ||
384 | packet_integrity_check(plen, nlen, type); | ||
385 | authenticated = auth_rsa(pw, n); | ||
386 | BN_clear_free(n); | ||
387 | break; | ||
388 | |||
389 | case SSH_CMSG_AUTH_PASSWORD: | ||
390 | if (!options.password_authentication) { | ||
391 | verbose("Password authentication disabled."); | ||
392 | break; | ||
393 | } | ||
394 | /* | ||
395 | * Read user password. It is in plain text, but was | ||
396 | * transmitted over the encrypted channel so it is | ||
397 | * not visible to an outside observer. | ||
398 | */ | ||
399 | password = packet_get_string(&dlen); | ||
400 | packet_integrity_check(plen, 4 + dlen, type); | ||
401 | |||
402 | #ifdef USE_PAM | ||
403 | /* Do PAM auth with password */ | ||
404 | authenticated = auth_pam_password(pw, password); | ||
405 | #else /* USE_PAM */ | ||
406 | /* Try authentication with the password. */ | ||
407 | authenticated = auth_password(pw, password); | ||
408 | #endif /* USE_PAM */ | ||
409 | memset(password, 0, strlen(password)); | ||
410 | xfree(password); | ||
411 | break; | ||
412 | |||
413 | #ifdef SKEY | ||
414 | case SSH_CMSG_AUTH_TIS: | ||
415 | debug("rcvd SSH_CMSG_AUTH_TIS"); | ||
416 | if (options.skey_authentication == 1) { | ||
417 | char *skeyinfo = skey_keyinfo(pw->pw_name); | ||
418 | if (skeyinfo == NULL) { | ||
419 | debug("generating fake skeyinfo for %.100s.", pw->pw_name); | ||
420 | skeyinfo = skey_fake_keyinfo(pw->pw_name); | ||
421 | } | ||
422 | if (skeyinfo != NULL) { | ||
423 | /* we send our s/key- in tis-challenge messages */ | ||
424 | debug("sending challenge '%s'", skeyinfo); | ||
425 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
426 | packet_put_string(skeyinfo, strlen(skeyinfo)); | ||
427 | packet_send(); | ||
428 | packet_write_wait(); | ||
429 | continue; | ||
430 | } | ||
431 | } | ||
432 | break; | ||
433 | case SSH_CMSG_AUTH_TIS_RESPONSE: | ||
434 | debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); | ||
435 | if (options.skey_authentication == 1) { | ||
436 | char *response = packet_get_string(&dlen); | ||
437 | debug("skey response == '%s'", response); | ||
438 | packet_integrity_check(plen, 4 + dlen, type); | ||
439 | authenticated = (skey_haskey(pw->pw_name) == 0 && | ||
440 | skey_passcheck(pw->pw_name, response) != -1); | ||
441 | xfree(response); | ||
442 | } | ||
443 | break; | ||
444 | #else | ||
445 | case SSH_CMSG_AUTH_TIS: | ||
446 | /* TIS Authentication is unsupported */ | ||
447 | log("TIS authentication unsupported."); | ||
448 | break; | ||
449 | #endif | ||
450 | |||
451 | default: | ||
452 | /* | ||
453 | * Any unknown messages will be ignored (and failure | ||
454 | * returned) during authentication. | ||
455 | */ | ||
456 | log("Unknown message during authentication: type %d", type); | ||
457 | break; | ||
458 | } | ||
459 | |||
460 | /* | ||
461 | * Check if the user is logging in as root and root logins | ||
462 | * are disallowed. | ||
463 | * Note that root login is allowed for forced commands. | ||
464 | */ | ||
465 | if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) { | ||
466 | if (forced_command) { | ||
467 | log("Root login accepted for forced command."); | ||
468 | } else { | ||
469 | authenticated = 0; | ||
470 | log("ROOT LOGIN REFUSED FROM %.200s", | ||
471 | get_canonical_hostname()); | ||
472 | } | ||
473 | } | ||
474 | |||
475 | /* Raise logging level */ | ||
476 | if (authenticated || | ||
477 | attempt == AUTH_FAIL_LOG || | ||
478 | type == SSH_CMSG_AUTH_PASSWORD) | ||
479 | authlog = log; | ||
480 | |||
481 | authlog("%s %s for %.200s from %.200s port %d%s", | ||
482 | authenticated ? "Accepted" : "Failed", | ||
483 | get_authname(type), | ||
484 | pw->pw_uid == 0 ? "ROOT" : pw->pw_name, | ||
485 | get_remote_ipaddr(), | ||
486 | get_remote_port(), | ||
487 | user); | ||
488 | |||
489 | #ifdef USE_PAM | ||
490 | if (authenticated) { | ||
491 | if (!do_pam_account(pw->pw_name, client_user)) { | ||
492 | if (client_user != NULL) { | ||
493 | xfree(client_user); | ||
494 | client_user = NULL; | ||
495 | } | ||
496 | do_fake_authloop1(pw->pw_name); | ||
497 | } | ||
498 | return; | ||
499 | } | ||
500 | #else /* USE_PAM */ | ||
501 | if (authenticated) { | ||
502 | return; | ||
503 | } | ||
504 | #endif /* USE_PAM */ | ||
505 | |||
506 | if (client_user != NULL) { | ||
507 | xfree(client_user); | ||
508 | client_user = NULL; | ||
509 | } | ||
510 | |||
511 | if (attempt > AUTH_FAIL_MAX) | ||
512 | packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); | ||
513 | |||
514 | /* Send a message indicating that the authentication attempt failed. */ | ||
515 | packet_start(SSH_SMSG_FAILURE); | ||
516 | packet_send(); | ||
517 | packet_write_wait(); | ||
518 | } | ||
519 | } | ||
520 | |||
521 | /* | ||
522 | * Performs authentication of an incoming connection. Session key has already | ||
523 | * been exchanged and encryption is enabled. | ||
524 | */ | ||
525 | void | ||
526 | do_authentication() | ||
527 | { | ||
528 | struct passwd *pw, pwcopy; | ||
529 | int plen; | ||
530 | unsigned int ulen; | ||
531 | char *user; | ||
532 | #ifdef WITH_AIXAUTHENTICATE | ||
533 | char *loginmsg; | ||
534 | #endif /* WITH_AIXAUTHENTICATE */ | ||
535 | |||
536 | /* Get the name of the user that we wish to log in as. */ | ||
537 | packet_read_expect(&plen, SSH_CMSG_USER); | ||
538 | |||
539 | /* Get the user name. */ | ||
540 | user = packet_get_string(&ulen); | ||
541 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); | ||
542 | |||
543 | setproctitle("%s", user); | ||
544 | |||
545 | #ifdef AFS | ||
546 | /* If machine has AFS, set process authentication group. */ | ||
547 | if (k_hasafs()) { | ||
548 | k_setpag(); | ||
549 | k_unlog(); | ||
550 | } | ||
551 | #endif /* AFS */ | ||
552 | |||
553 | /* Verify that the user is a valid user. */ | ||
554 | pw = getpwnam(user); | ||
555 | if (!pw || !allowed_user(pw)) | ||
556 | do_fake_authloop1(user); | ||
557 | xfree(user); | ||
558 | |||
559 | /* Take a copy of the returned structure. */ | ||
560 | memset(&pwcopy, 0, sizeof(pwcopy)); | ||
561 | pwcopy.pw_name = xstrdup(pw->pw_name); | ||
562 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); | ||
563 | pwcopy.pw_uid = pw->pw_uid; | ||
564 | pwcopy.pw_gid = pw->pw_gid; | ||
565 | pwcopy.pw_dir = xstrdup(pw->pw_dir); | ||
566 | pwcopy.pw_shell = xstrdup(pw->pw_shell); | ||
567 | pw = &pwcopy; | ||
568 | |||
569 | #ifdef USE_PAM | ||
570 | start_pam(pw); | ||
571 | #endif | ||
572 | |||
573 | /* | ||
574 | * If we are not running as root, the user must have the same uid as | ||
575 | * the server. | ||
576 | */ | ||
577 | if (getuid() != 0 && pw->pw_uid != getuid()) | ||
578 | packet_disconnect("Cannot change user when server not running as root."); | ||
579 | |||
580 | debug("Attempting authentication for %.100s.", pw->pw_name); | ||
581 | |||
582 | /* If the user has no password, accept authentication immediately. */ | ||
583 | if (options.password_authentication && | ||
584 | #ifdef KRB4 | ||
585 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && | ||
586 | #endif /* KRB4 */ | ||
587 | #ifdef USE_PAM | ||
588 | auth_pam_password(pw, "")) { | ||
589 | #else /* USE_PAM */ | ||
590 | auth_password(pw, "")) { | ||
591 | #endif /* USE_PAM */ | ||
592 | /* Authentication with empty password succeeded. */ | ||
593 | log("Login for user %s from %.100s, accepted without authentication.", | ||
594 | pw->pw_name, get_remote_ipaddr()); | ||
595 | } else { | ||
596 | /* Loop until the user has been authenticated or the | ||
597 | connection is closed, do_authloop() returns only if | ||
598 | authentication is successfull */ | ||
599 | do_authloop(pw); | ||
600 | } | ||
601 | |||
602 | /* The user has been authenticated and accepted. */ | ||
603 | #ifdef WITH_AIXAUTHENTICATE | ||
604 | loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg); | ||
605 | #endif /* WITH_AIXAUTHENTICATE */ | ||
606 | packet_start(SSH_SMSG_SUCCESS); | ||
607 | packet_send(); | ||
608 | packet_write_wait(); | ||
609 | |||
610 | /* Perform session preparation. */ | ||
611 | do_authenticated(pw); | ||
612 | } | ||
613 | |||
614 | |||
615 | void input_service_request(int type, int plen); | ||
616 | void input_userauth_request(int type, int plen); | ||
617 | void ssh2_pty_cleanup(void); | ||
618 | |||
619 | typedef struct Authctxt Authctxt; | ||
620 | struct Authctxt { | ||
621 | char *user; | ||
622 | char *service; | ||
623 | struct passwd pw; | ||
624 | int valid; | ||
625 | }; | ||
626 | static Authctxt *authctxt = NULL; | ||
627 | static int userauth_success = 0; | ||
628 | |||
629 | struct passwd* | ||
630 | auth_get_user(void) | ||
631 | { | ||
632 | return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; | ||
633 | } | ||
634 | struct passwd* | ||
635 | auth_set_user(char *u, char *s) | ||
636 | { | ||
637 | struct passwd *pw, *copy; | ||
638 | |||
639 | if (authctxt == NULL) { | ||
640 | authctxt = xmalloc(sizeof(*authctxt)); | ||
641 | authctxt->valid = 0; | ||
642 | authctxt->user = xstrdup(u); | ||
643 | authctxt->service = xstrdup(s); | ||
644 | setproctitle("%s", u); | ||
645 | pw = getpwnam(u); | ||
646 | if (!pw || !allowed_user(pw)) { | ||
647 | log("auth_set_user: bad user %s", u); | ||
648 | return NULL; | ||
649 | } | ||
650 | #ifdef USE_PAM | ||
651 | start_pam(pw); | ||
652 | #endif | ||
653 | copy = &authctxt->pw; | ||
654 | memset(copy, 0, sizeof(*copy)); | ||
655 | copy->pw_name = xstrdup(pw->pw_name); | ||
656 | copy->pw_passwd = xstrdup(pw->pw_passwd); | ||
657 | copy->pw_uid = pw->pw_uid; | ||
658 | copy->pw_gid = pw->pw_gid; | ||
659 | copy->pw_dir = xstrdup(pw->pw_dir); | ||
660 | copy->pw_shell = xstrdup(pw->pw_shell); | ||
661 | authctxt->valid = 1; | ||
662 | } else { | ||
663 | if (strcmp(u, authctxt->user) != 0 || | ||
664 | strcmp(s, authctxt->service) != 0) { | ||
665 | log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", | ||
666 | u, s, authctxt->user, authctxt->service); | ||
667 | return NULL; | ||
668 | } | ||
669 | } | ||
670 | return auth_get_user(); | ||
671 | } | ||
672 | |||
673 | static void | ||
674 | protocol_error(int type, int plen) | ||
675 | { | ||
676 | log("auth: protocol error: type %d plen %d", type, plen); | ||
677 | packet_start(SSH2_MSG_UNIMPLEMENTED); | ||
678 | packet_put_int(0); | ||
679 | packet_send(); | ||
680 | packet_write_wait(); | ||
681 | } | ||
682 | void | ||
683 | input_service_request(int type, int plen) | ||
684 | { | ||
685 | unsigned int len; | ||
686 | int accept = 0; | ||
687 | char *service = packet_get_string(&len); | ||
688 | packet_done(); | ||
689 | |||
690 | if (strcmp(service, "ssh-userauth") == 0) { | ||
691 | if (!userauth_success) { | ||
692 | accept = 1; | ||
693 | /* now we can handle user-auth requests */ | ||
694 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); | ||
695 | } | ||
696 | } | ||
697 | /* XXX all other service requests are denied */ | ||
698 | |||
699 | if (accept) { | ||
700 | packet_start(SSH2_MSG_SERVICE_ACCEPT); | ||
701 | packet_put_cstring(service); | ||
702 | packet_send(); | ||
703 | packet_write_wait(); | ||
704 | } else { | ||
705 | debug("bad service request %s", service); | ||
706 | packet_disconnect("bad service request %s", service); | ||
707 | } | ||
708 | xfree(service); | ||
709 | } | ||
710 | void | ||
711 | input_userauth_request(int type, int plen) | ||
712 | { | ||
713 | static int try = 0; | ||
714 | unsigned int len; | ||
715 | int c, authenticated = 0; | ||
716 | char *user, *service, *method; | ||
717 | struct passwd *pw; | ||
718 | |||
719 | if (++try == AUTH_FAIL_MAX) | ||
720 | packet_disconnect("too many failed userauth_requests"); | ||
721 | |||
722 | user = packet_get_string(&len); | ||
723 | service = packet_get_string(&len); | ||
724 | method = packet_get_string(&len); | ||
725 | debug("userauth-request for user %s service %s method %s", user, service, method); | ||
726 | |||
727 | /* XXX we only allow the ssh-connection service */ | ||
728 | pw = auth_set_user(user, service); | ||
729 | if (pw && strcmp(service, "ssh-connection")==0) { | ||
730 | if (strcmp(method, "none") == 0 && try == 1) { | ||
731 | packet_done(); | ||
732 | #ifdef USE_PAM | ||
733 | /* Do PAM auth with password */ | ||
734 | authenticated = auth_pam_password(pw, ""); | ||
735 | #else /* USE_PAM */ | ||
736 | /* Try authentication with the password. */ | ||
737 | authenticated = auth_password(pw, ""); | ||
738 | #endif /* USE_PAM */ | ||
739 | } else if (strcmp(method, "password") == 0) { | ||
740 | char *password; | ||
741 | c = packet_get_char(); | ||
742 | if (c) | ||
743 | debug("password change not supported"); | ||
744 | password = packet_get_string(&len); | ||
745 | packet_done(); | ||
746 | #ifdef USE_PAM | ||
747 | /* Do PAM auth with password */ | ||
748 | authenticated = auth_pam_password(pw, password); | ||
749 | #else /* USE_PAM */ | ||
750 | /* Try authentication with the password. */ | ||
751 | authenticated = auth_password(pw, password); | ||
752 | #endif /* USE_PAM */ | ||
753 | memset(password, 0, len); | ||
754 | xfree(password); | ||
755 | } else if (strcmp(method, "publickey") == 0) { | ||
756 | /* XXX TODO */ | ||
757 | char *pkalg, *pkblob, *sig; | ||
758 | int have_sig = packet_get_char(); | ||
759 | pkalg = packet_get_string(&len); | ||
760 | pkblob = packet_get_string(&len); | ||
761 | if (have_sig) { | ||
762 | sig = packet_get_string(&len); | ||
763 | /* test for correct signature */ | ||
764 | packet_done(); | ||
765 | xfree(sig); | ||
766 | } else { | ||
767 | packet_done(); | ||
768 | /* test whether pkalg/pkblob are acceptable */ | ||
769 | } | ||
770 | xfree(pkalg); | ||
771 | xfree(pkblob); | ||
772 | } | ||
773 | } | ||
774 | /* XXX check if other auth methods are needed */ | ||
775 | if (authenticated) { | ||
776 | /* turn off userauth */ | ||
777 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); | ||
778 | packet_start(SSH2_MSG_USERAUTH_SUCCESS); | ||
779 | packet_send(); | ||
780 | packet_write_wait(); | ||
781 | log("userauth success for %s", user); | ||
782 | /* now we can break out */ | ||
783 | userauth_success = 1; | ||
784 | } else { | ||
785 | packet_start(SSH2_MSG_USERAUTH_FAILURE); | ||
786 | packet_put_cstring("password"); | ||
787 | packet_put_char(0); /* partial success */ | ||
788 | packet_send(); | ||
789 | packet_write_wait(); | ||
790 | } | ||
791 | xfree(service); | ||
792 | xfree(user); | ||
793 | xfree(method); | ||
794 | } | ||
795 | void | ||
796 | do_authentication2() | ||
797 | { | ||
798 | dispatch_init(&protocol_error); | ||
799 | dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); | ||
800 | dispatch_run(DISPATCH_BLOCK, &userauth_success); | ||
801 | do_authenticated2(); | ||
802 | } | ||
@@ -7,4 +7,11 @@ void do_authentication2(void); | |||
7 | struct passwd * | 7 | struct passwd * |
8 | auth_get_user(void); | 8 | auth_get_user(void); |
9 | 9 | ||
10 | int allowed_user(struct passwd * pw);; | ||
11 | |||
12 | #define AUTH_FAIL_MAX 6 | ||
13 | #define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) | ||
14 | #define AUTH_FAIL_MSG "Too many authentication failures for %.100s" | ||
15 | |||
10 | #endif | 16 | #endif |
17 | |||
diff --git a/auth1.c b/auth1.c new file mode 100644 index 000000000..ae5f1cd84 --- /dev/null +++ b/auth1.c | |||
@@ -0,0 +1,512 @@ | |||
1 | /* | ||
2 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
3 | * All rights reserved | ||
4 | */ | ||
5 | |||
6 | #include "includes.h" | ||
7 | RCSID("$OpenBSD: auth1.c,v 1.1 2000/04/26 21:28:32 markus Exp $"); | ||
8 | |||
9 | #include "xmalloc.h" | ||
10 | #include "rsa.h" | ||
11 | #include "ssh.h" | ||
12 | #include "packet.h" | ||
13 | #include "buffer.h" | ||
14 | #include "cipher.h" | ||
15 | #include "mpaux.h" | ||
16 | #include "servconf.h" | ||
17 | #include "compat.h" | ||
18 | #include "auth.h" | ||
19 | #include "session.h" | ||
20 | |||
21 | /* import */ | ||
22 | extern ServerOptions options; | ||
23 | extern char *forced_command; | ||
24 | |||
25 | /* | ||
26 | * convert ssh auth msg type into description | ||
27 | */ | ||
28 | char * | ||
29 | get_authname(int type) | ||
30 | { | ||
31 | static char buf[1024]; | ||
32 | switch (type) { | ||
33 | case SSH_CMSG_AUTH_PASSWORD: | ||
34 | return "password"; | ||
35 | case SSH_CMSG_AUTH_RSA: | ||
36 | return "rsa"; | ||
37 | case SSH_CMSG_AUTH_RHOSTS_RSA: | ||
38 | return "rhosts-rsa"; | ||
39 | case SSH_CMSG_AUTH_RHOSTS: | ||
40 | return "rhosts"; | ||
41 | #ifdef KRB4 | ||
42 | case SSH_CMSG_AUTH_KERBEROS: | ||
43 | return "kerberos"; | ||
44 | #endif | ||
45 | #ifdef SKEY | ||
46 | case SSH_CMSG_AUTH_TIS_RESPONSE: | ||
47 | return "s/key"; | ||
48 | #endif | ||
49 | } | ||
50 | snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); | ||
51 | return buf; | ||
52 | } | ||
53 | |||
54 | /* | ||
55 | * The user does not exist or access is denied, | ||
56 | * but fake indication that authentication is needed. | ||
57 | */ | ||
58 | void | ||
59 | do_fake_authloop1(char *user) | ||
60 | { | ||
61 | int attempt = 0; | ||
62 | |||
63 | log("Faking authloop for illegal user %.200s from %.200s port %d", | ||
64 | user, | ||
65 | get_remote_ipaddr(), | ||
66 | get_remote_port()); | ||
67 | |||
68 | #ifdef WITH_AIXAUTHENTICATE | ||
69 | if (strncmp(get_authname(type),"password", | ||
70 | strlen(get_authname(type))) == 0) | ||
71 | loginfailed(pw->pw_name,get_canonical_hostname(),"ssh"); | ||
72 | #endif /* WITH_AIXAUTHENTICATE */ | ||
73 | |||
74 | /* Indicate that authentication is needed. */ | ||
75 | packet_start(SSH_SMSG_FAILURE); | ||
76 | packet_send(); | ||
77 | packet_write_wait(); | ||
78 | |||
79 | /* | ||
80 | * Keep reading packets, and always respond with a failure. This is | ||
81 | * to avoid disclosing whether such a user really exists. | ||
82 | */ | ||
83 | for (attempt = 1;; attempt++) { | ||
84 | /* Read a packet. This will not return if the client disconnects. */ | ||
85 | int plen; | ||
86 | #ifndef SKEY | ||
87 | (void)packet_read(&plen); | ||
88 | #else /* SKEY */ | ||
89 | int type = packet_read(&plen); | ||
90 | unsigned int dlen; | ||
91 | char *password, *skeyinfo; | ||
92 | password = NULL; | ||
93 | /* Try to send a fake s/key challenge. */ | ||
94 | if (options.skey_authentication == 1 && | ||
95 | (skeyinfo = skey_fake_keyinfo(user)) != NULL) { | ||
96 | if (type == SSH_CMSG_AUTH_TIS) { | ||
97 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
98 | packet_put_string(skeyinfo, strlen(skeyinfo)); | ||
99 | packet_send(); | ||
100 | packet_write_wait(); | ||
101 | continue; | ||
102 | } else if (type == SSH_CMSG_AUTH_PASSWORD && | ||
103 | options.password_authentication && | ||
104 | (password = packet_get_string(&dlen)) != NULL && | ||
105 | dlen == 5 && | ||
106 | strncasecmp(password, "s/key", 5) == 0 ) { | ||
107 | packet_send_debug(skeyinfo); | ||
108 | } | ||
109 | } | ||
110 | if (password != NULL) | ||
111 | xfree(password); | ||
112 | #endif | ||
113 | if (attempt > AUTH_FAIL_MAX) | ||
114 | packet_disconnect(AUTH_FAIL_MSG, user); | ||
115 | |||
116 | /* | ||
117 | * Send failure. This should be indistinguishable from a | ||
118 | * failed authentication. | ||
119 | */ | ||
120 | packet_start(SSH_SMSG_FAILURE); | ||
121 | packet_send(); | ||
122 | packet_write_wait(); | ||
123 | } | ||
124 | /* NOTREACHED */ | ||
125 | abort(); | ||
126 | } | ||
127 | |||
128 | /* | ||
129 | * read packets and try to authenticate local user *pw. | ||
130 | * return if authentication is successfull | ||
131 | */ | ||
132 | void | ||
133 | do_authloop(struct passwd * pw) | ||
134 | { | ||
135 | int attempt = 0; | ||
136 | unsigned int bits; | ||
137 | RSA *client_host_key; | ||
138 | BIGNUM *n; | ||
139 | char *client_user = NULL, *password = NULL; | ||
140 | char user[1024]; | ||
141 | unsigned int dlen; | ||
142 | int plen, nlen, elen; | ||
143 | unsigned int ulen; | ||
144 | int type = 0; | ||
145 | void (*authlog) (const char *fmt,...) = verbose; | ||
146 | |||
147 | /* Indicate that authentication is needed. */ | ||
148 | packet_start(SSH_SMSG_FAILURE); | ||
149 | packet_send(); | ||
150 | packet_write_wait(); | ||
151 | |||
152 | for (attempt = 1;; attempt++) { | ||
153 | int authenticated = 0; | ||
154 | strlcpy(user, "", sizeof user); | ||
155 | |||
156 | /* Get a packet from the client. */ | ||
157 | type = packet_read(&plen); | ||
158 | |||
159 | /* Process the packet. */ | ||
160 | switch (type) { | ||
161 | #ifdef AFS | ||
162 | case SSH_CMSG_HAVE_KERBEROS_TGT: | ||
163 | if (!options.kerberos_tgt_passing) { | ||
164 | /* packet_get_all(); */ | ||
165 | verbose("Kerberos tgt passing disabled."); | ||
166 | break; | ||
167 | } else { | ||
168 | /* Accept Kerberos tgt. */ | ||
169 | char *tgt = packet_get_string(&dlen); | ||
170 | packet_integrity_check(plen, 4 + dlen, type); | ||
171 | if (!auth_kerberos_tgt(pw, tgt)) | ||
172 | verbose("Kerberos tgt REFUSED for %s", pw->pw_name); | ||
173 | xfree(tgt); | ||
174 | } | ||
175 | continue; | ||
176 | |||
177 | case SSH_CMSG_HAVE_AFS_TOKEN: | ||
178 | if (!options.afs_token_passing || !k_hasafs()) { | ||
179 | /* packet_get_all(); */ | ||
180 | verbose("AFS token passing disabled."); | ||
181 | break; | ||
182 | } else { | ||
183 | /* Accept AFS token. */ | ||
184 | char *token_string = packet_get_string(&dlen); | ||
185 | packet_integrity_check(plen, 4 + dlen, type); | ||
186 | if (!auth_afs_token(pw, token_string)) | ||
187 | verbose("AFS token REFUSED for %s", pw->pw_name); | ||
188 | xfree(token_string); | ||
189 | } | ||
190 | continue; | ||
191 | #endif /* AFS */ | ||
192 | #ifdef KRB4 | ||
193 | case SSH_CMSG_AUTH_KERBEROS: | ||
194 | if (!options.kerberos_authentication) { | ||
195 | /* packet_get_all(); */ | ||
196 | verbose("Kerberos authentication disabled."); | ||
197 | break; | ||
198 | } else { | ||
199 | /* Try Kerberos v4 authentication. */ | ||
200 | KTEXT_ST auth; | ||
201 | char *tkt_user = NULL; | ||
202 | char *kdata = packet_get_string((unsigned int *) &auth.length); | ||
203 | packet_integrity_check(plen, 4 + auth.length, type); | ||
204 | |||
205 | if (auth.length < MAX_KTXT_LEN) | ||
206 | memcpy(auth.dat, kdata, auth.length); | ||
207 | xfree(kdata); | ||
208 | |||
209 | authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); | ||
210 | |||
211 | if (authenticated) { | ||
212 | snprintf(user, sizeof user, " tktuser %s", tkt_user); | ||
213 | xfree(tkt_user); | ||
214 | } | ||
215 | } | ||
216 | break; | ||
217 | #endif /* KRB4 */ | ||
218 | |||
219 | case SSH_CMSG_AUTH_RHOSTS: | ||
220 | if (!options.rhosts_authentication) { | ||
221 | verbose("Rhosts authentication disabled."); | ||
222 | break; | ||
223 | } | ||
224 | /* | ||
225 | * Get client user name. Note that we just have to | ||
226 | * trust the client; this is one reason why rhosts | ||
227 | * authentication is insecure. (Another is | ||
228 | * IP-spoofing on a local network.) | ||
229 | */ | ||
230 | client_user = packet_get_string(&ulen); | ||
231 | packet_integrity_check(plen, 4 + ulen, type); | ||
232 | |||
233 | /* Try to authenticate using /etc/hosts.equiv and | ||
234 | .rhosts. */ | ||
235 | authenticated = auth_rhosts(pw, client_user); | ||
236 | |||
237 | snprintf(user, sizeof user, " ruser %s", client_user); | ||
238 | break; | ||
239 | |||
240 | case SSH_CMSG_AUTH_RHOSTS_RSA: | ||
241 | if (!options.rhosts_rsa_authentication) { | ||
242 | verbose("Rhosts with RSA authentication disabled."); | ||
243 | break; | ||
244 | } | ||
245 | /* | ||
246 | * Get client user name. Note that we just have to | ||
247 | * trust the client; root on the client machine can | ||
248 | * claim to be any user. | ||
249 | */ | ||
250 | client_user = packet_get_string(&ulen); | ||
251 | |||
252 | /* Get the client host key. */ | ||
253 | client_host_key = RSA_new(); | ||
254 | if (client_host_key == NULL) | ||
255 | fatal("RSA_new failed"); | ||
256 | client_host_key->e = BN_new(); | ||
257 | client_host_key->n = BN_new(); | ||
258 | if (client_host_key->e == NULL || client_host_key->n == NULL) | ||
259 | fatal("BN_new failed"); | ||
260 | bits = packet_get_int(); | ||
261 | packet_get_bignum(client_host_key->e, &elen); | ||
262 | packet_get_bignum(client_host_key->n, &nlen); | ||
263 | |||
264 | if (bits != BN_num_bits(client_host_key->n)) | ||
265 | error("Warning: keysize mismatch for client_host_key: " | ||
266 | "actual %d, announced %d", BN_num_bits(client_host_key->n), bits); | ||
267 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); | ||
268 | |||
269 | authenticated = auth_rhosts_rsa(pw, client_user, client_host_key); | ||
270 | RSA_free(client_host_key); | ||
271 | |||
272 | snprintf(user, sizeof user, " ruser %s", client_user); | ||
273 | break; | ||
274 | |||
275 | case SSH_CMSG_AUTH_RSA: | ||
276 | if (!options.rsa_authentication) { | ||
277 | verbose("RSA authentication disabled."); | ||
278 | break; | ||
279 | } | ||
280 | /* RSA authentication requested. */ | ||
281 | n = BN_new(); | ||
282 | packet_get_bignum(n, &nlen); | ||
283 | packet_integrity_check(plen, nlen, type); | ||
284 | authenticated = auth_rsa(pw, n); | ||
285 | BN_clear_free(n); | ||
286 | break; | ||
287 | |||
288 | case SSH_CMSG_AUTH_PASSWORD: | ||
289 | if (!options.password_authentication) { | ||
290 | verbose("Password authentication disabled."); | ||
291 | break; | ||
292 | } | ||
293 | /* | ||
294 | * Read user password. It is in plain text, but was | ||
295 | * transmitted over the encrypted channel so it is | ||
296 | * not visible to an outside observer. | ||
297 | */ | ||
298 | password = packet_get_string(&dlen); | ||
299 | packet_integrity_check(plen, 4 + dlen, type); | ||
300 | |||
301 | #ifdef USE_PAM | ||
302 | /* Do PAM auth with password */ | ||
303 | authenticated = auth_pam_password(pw, password); | ||
304 | #else /* USE_PAM */ | ||
305 | /* Try authentication with the password. */ | ||
306 | authenticated = auth_password(pw, password); | ||
307 | #endif /* USE_PAM */ | ||
308 | |||
309 | memset(password, 0, strlen(password)); | ||
310 | xfree(password); | ||
311 | break; | ||
312 | |||
313 | #ifdef SKEY | ||
314 | case SSH_CMSG_AUTH_TIS: | ||
315 | debug("rcvd SSH_CMSG_AUTH_TIS"); | ||
316 | if (options.skey_authentication == 1) { | ||
317 | char *skeyinfo = skey_keyinfo(pw->pw_name); | ||
318 | if (skeyinfo == NULL) { | ||
319 | debug("generating fake skeyinfo for %.100s.", pw->pw_name); | ||
320 | skeyinfo = skey_fake_keyinfo(pw->pw_name); | ||
321 | } | ||
322 | if (skeyinfo != NULL) { | ||
323 | /* we send our s/key- in tis-challenge messages */ | ||
324 | debug("sending challenge '%s'", skeyinfo); | ||
325 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); | ||
326 | packet_put_string(skeyinfo, strlen(skeyinfo)); | ||
327 | packet_send(); | ||
328 | packet_write_wait(); | ||
329 | continue; | ||
330 | } | ||
331 | } | ||
332 | break; | ||
333 | case SSH_CMSG_AUTH_TIS_RESPONSE: | ||
334 | debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); | ||
335 | if (options.skey_authentication == 1) { | ||
336 | char *response = packet_get_string(&dlen); | ||
337 | debug("skey response == '%s'", response); | ||
338 | packet_integrity_check(plen, 4 + dlen, type); | ||
339 | authenticated = (skey_haskey(pw->pw_name) == 0 && | ||
340 | skey_passcheck(pw->pw_name, response) != -1); | ||
341 | xfree(response); | ||
342 | } | ||
343 | break; | ||
344 | #else | ||
345 | case SSH_CMSG_AUTH_TIS: | ||
346 | /* TIS Authentication is unsupported */ | ||
347 | log("TIS authentication unsupported."); | ||
348 | break; | ||
349 | #endif | ||
350 | |||
351 | default: | ||
352 | /* | ||
353 | * Any unknown messages will be ignored (and failure | ||
354 | * returned) during authentication. | ||
355 | */ | ||
356 | log("Unknown message during authentication: type %d", type); | ||
357 | break; | ||
358 | } | ||
359 | |||
360 | /* | ||
361 | * Check if the user is logging in as root and root logins | ||
362 | * are disallowed. | ||
363 | * Note that root login is allowed for forced commands. | ||
364 | */ | ||
365 | if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) { | ||
366 | if (forced_command) { | ||
367 | log("Root login accepted for forced command."); | ||
368 | } else { | ||
369 | authenticated = 0; | ||
370 | log("ROOT LOGIN REFUSED FROM %.200s", | ||
371 | get_canonical_hostname()); | ||
372 | } | ||
373 | } | ||
374 | |||
375 | /* Raise logging level */ | ||
376 | if (authenticated || | ||
377 | attempt == AUTH_FAIL_LOG || | ||
378 | type == SSH_CMSG_AUTH_PASSWORD) | ||
379 | authlog = log; | ||
380 | |||
381 | authlog("%s %s for %.200s from %.200s port %d%s", | ||
382 | authenticated ? "Accepted" : "Failed", | ||
383 | get_authname(type), | ||
384 | pw->pw_uid == 0 ? "ROOT" : pw->pw_name, | ||
385 | get_remote_ipaddr(), | ||
386 | get_remote_port(), | ||
387 | user); | ||
388 | |||
389 | #ifdef USE_PAM | ||
390 | if (authenticated) { | ||
391 | if (!do_pam_account(pw->pw_name, client_user)) { | ||
392 | if (client_user != NULL) { | ||
393 | xfree(client_user); | ||
394 | client_user = NULL; | ||
395 | } | ||
396 | do_fake_authloop1(pw->pw_name); | ||
397 | } | ||
398 | return; | ||
399 | } | ||
400 | #else /* USE_PAM */ | ||
401 | if (authenticated) { | ||
402 | return; | ||
403 | } | ||
404 | #endif /* USE_PAM */ | ||
405 | |||
406 | if (client_user != NULL) { | ||
407 | xfree(client_user); | ||
408 | client_user = NULL; | ||
409 | } | ||
410 | |||
411 | if (attempt > AUTH_FAIL_MAX) | ||
412 | packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); | ||
413 | |||
414 | /* Send a message indicating that the authentication attempt failed. */ | ||
415 | packet_start(SSH_SMSG_FAILURE); | ||
416 | packet_send(); | ||
417 | packet_write_wait(); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * Performs authentication of an incoming connection. Session key has already | ||
423 | * been exchanged and encryption is enabled. | ||
424 | */ | ||
425 | void | ||
426 | do_authentication() | ||
427 | { | ||
428 | struct passwd *pw, pwcopy; | ||
429 | int plen; | ||
430 | unsigned int ulen; | ||
431 | char *user; | ||
432 | #ifdef WITH_AIXAUTHENTICATE | ||
433 | char *loginmsg; | ||
434 | #endif /* WITH_AIXAUTHENTICATE */ | ||
435 | |||
436 | /* Get the name of the user that we wish to log in as. */ | ||
437 | packet_read_expect(&plen, SSH_CMSG_USER); | ||
438 | |||
439 | /* Get the user name. */ | ||
440 | user = packet_get_string(&ulen); | ||
441 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); | ||
442 | |||
443 | setproctitle("%s", user); | ||
444 | |||
445 | #ifdef AFS | ||
446 | /* If machine has AFS, set process authentication group. */ | ||
447 | if (k_hasafs()) { | ||
448 | k_setpag(); | ||
449 | k_unlog(); | ||
450 | } | ||
451 | #endif /* AFS */ | ||
452 | |||
453 | /* Verify that the user is a valid user. */ | ||
454 | pw = getpwnam(user); | ||
455 | if (!pw || !allowed_user(pw)) | ||
456 | do_fake_authloop1(user); | ||
457 | xfree(user); | ||
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 | #ifdef USE_PAM | ||
470 | start_pam(pw); | ||
471 | #endif | ||
472 | |||
473 | /* | ||
474 | * If we are not running as root, the user must have the same uid as | ||
475 | * the server. | ||
476 | */ | ||
477 | if (getuid() != 0 && pw->pw_uid != getuid()) | ||
478 | packet_disconnect("Cannot change user when server not running as root."); | ||
479 | |||
480 | debug("Attempting authentication for %.100s.", pw->pw_name); | ||
481 | |||
482 | /* If the user has no password, accept authentication immediately. */ | ||
483 | if (options.password_authentication && | ||
484 | #ifdef KRB4 | ||
485 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && | ||
486 | #endif /* KRB4 */ | ||
487 | #ifdef USE_PAM | ||
488 | auth_pam_password(pw, "")) { | ||
489 | #else /* USE_PAM */ | ||
490 | auth_password(pw, "")) { | ||
491 | #endif /* USE_PAM */ | ||
492 | /* Authentication with empty password succeeded. */ | ||
493 | log("Login for user %s from %.100s, accepted without authentication.", | ||
494 | pw->pw_name, get_remote_ipaddr()); | ||
495 | } else { | ||
496 | /* Loop until the user has been authenticated or the | ||
497 | connection is closed, do_authloop() returns only if | ||
498 | authentication is successfull */ | ||
499 | do_authloop(pw); | ||
500 | } | ||
501 | |||
502 | /* The user has been authenticated and accepted. */ | ||
503 | #ifdef WITH_AIXAUTHENTICATE | ||
504 | loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg); | ||
505 | #endif /* WITH_AIXAUTHENTICATE */ | ||
506 | packet_start(SSH_SMSG_SUCCESS); | ||
507 | packet_send(); | ||
508 | packet_write_wait(); | ||
509 | |||
510 | /* Perform session preparation. */ | ||
511 | do_authenticated(pw); | ||
512 | } | ||
diff --git a/auth2.c b/auth2.c new file mode 100644 index 000000000..9937ed678 --- /dev/null +++ b/auth2.c | |||
@@ -0,0 +1,459 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. 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 BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | #include "includes.h" | ||
30 | RCSID("$OpenBSD: auth2.c,v 1.3 2000/04/27 15:23:02 markus Exp $"); | ||
31 | |||
32 | #include <openssl/dsa.h> | ||
33 | #include <openssl/rsa.h> | ||
34 | #include <openssl/evp.h> | ||
35 | |||
36 | #include "xmalloc.h" | ||
37 | #include "rsa.h" | ||
38 | #include "ssh.h" | ||
39 | #include "pty.h" | ||
40 | #include "packet.h" | ||
41 | #include "buffer.h" | ||
42 | #include "cipher.h" | ||
43 | #include "servconf.h" | ||
44 | #include "compat.h" | ||
45 | #include "channels.h" | ||
46 | #include "bufaux.h" | ||
47 | #include "ssh2.h" | ||
48 | #include "auth.h" | ||
49 | #include "session.h" | ||
50 | #include "dispatch.h" | ||
51 | #include "auth.h" | ||
52 | #include "key.h" | ||
53 | #include "kex.h" | ||
54 | |||
55 | #include "dsa.h" | ||
56 | #include "uidswap.h" | ||
57 | |||
58 | /* import */ | ||
59 | extern ServerOptions options; | ||
60 | extern unsigned char *session_id2; | ||
61 | extern int session_id2_len; | ||
62 | |||
63 | /* protocol */ | ||
64 | |||
65 | void input_service_request(int type, int plen); | ||
66 | void input_userauth_request(int type, int plen); | ||
67 | void protocol_error(int type, int plen); | ||
68 | |||
69 | /* auth */ | ||
70 | int ssh2_auth_none(struct passwd *pw); | ||
71 | int ssh2_auth_password(struct passwd *pw); | ||
72 | int ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen); | ||
73 | |||
74 | /* helper */ | ||
75 | struct passwd* auth_set_user(char *u, char *s); | ||
76 | int user_dsa_key_allowed(struct passwd *pw, Key *key); | ||
77 | |||
78 | typedef struct Authctxt Authctxt; | ||
79 | struct Authctxt { | ||
80 | char *user; | ||
81 | char *service; | ||
82 | struct passwd pw; | ||
83 | int valid; | ||
84 | }; | ||
85 | static Authctxt *authctxt = NULL; | ||
86 | static int userauth_success = 0; | ||
87 | |||
88 | /* | ||
89 | * loop until userauth_success == TRUE | ||
90 | */ | ||
91 | |||
92 | void | ||
93 | do_authentication2() | ||
94 | { | ||
95 | dispatch_init(&protocol_error); | ||
96 | dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); | ||
97 | dispatch_run(DISPATCH_BLOCK, &userauth_success); | ||
98 | do_authenticated2(); | ||
99 | } | ||
100 | |||
101 | void | ||
102 | protocol_error(int type, int plen) | ||
103 | { | ||
104 | log("auth: protocol error: type %d plen %d", type, plen); | ||
105 | packet_start(SSH2_MSG_UNIMPLEMENTED); | ||
106 | packet_put_int(0); | ||
107 | packet_send(); | ||
108 | packet_write_wait(); | ||
109 | } | ||
110 | |||
111 | void | ||
112 | input_service_request(int type, int plen) | ||
113 | { | ||
114 | unsigned int len; | ||
115 | int accept = 0; | ||
116 | char *service = packet_get_string(&len); | ||
117 | packet_done(); | ||
118 | |||
119 | if (strcmp(service, "ssh-userauth") == 0) { | ||
120 | if (!userauth_success) { | ||
121 | accept = 1; | ||
122 | /* now we can handle user-auth requests */ | ||
123 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); | ||
124 | } | ||
125 | } | ||
126 | /* XXX all other service requests are denied */ | ||
127 | |||
128 | if (accept) { | ||
129 | packet_start(SSH2_MSG_SERVICE_ACCEPT); | ||
130 | packet_put_cstring(service); | ||
131 | packet_send(); | ||
132 | packet_write_wait(); | ||
133 | } else { | ||
134 | debug("bad service request %s", service); | ||
135 | packet_disconnect("bad service request %s", service); | ||
136 | } | ||
137 | xfree(service); | ||
138 | } | ||
139 | |||
140 | void | ||
141 | input_userauth_request(int type, int plen) | ||
142 | { | ||
143 | static void (*authlog) (const char *fmt,...) = verbose; | ||
144 | static int attempt = 0; | ||
145 | unsigned int len, rlen; | ||
146 | int authenticated = 0; | ||
147 | char *raw, *user, *service, *method, *authmsg = NULL; | ||
148 | struct passwd *pw; | ||
149 | |||
150 | if (++attempt == AUTH_FAIL_MAX) | ||
151 | packet_disconnect("too many failed userauth_requests"); | ||
152 | |||
153 | raw = packet_get_raw(&rlen); | ||
154 | if (plen != rlen) | ||
155 | fatal("plen != rlen"); | ||
156 | user = packet_get_string(&len); | ||
157 | service = packet_get_string(&len); | ||
158 | method = packet_get_string(&len); | ||
159 | debug("userauth-request for user %s service %s method %s", user, service, method); | ||
160 | |||
161 | /* XXX we only allow the ssh-connection service */ | ||
162 | pw = auth_set_user(user, service); | ||
163 | if (pw && strcmp(service, "ssh-connection")==0) { | ||
164 | if (strcmp(method, "none") == 0) { | ||
165 | authenticated = ssh2_auth_none(pw); | ||
166 | } else if (strcmp(method, "password") == 0) { | ||
167 | authenticated = ssh2_auth_password(pw); | ||
168 | } else if (strcmp(method, "publickey") == 0) { | ||
169 | authenticated = ssh2_auth_pubkey(pw, raw, rlen); | ||
170 | } | ||
171 | } | ||
172 | if (authenticated && pw && pw->pw_uid == 0 && !options.permit_root_login) { | ||
173 | authenticated = 0; | ||
174 | log("ROOT LOGIN REFUSED FROM %.200s", | ||
175 | get_canonical_hostname()); | ||
176 | } | ||
177 | |||
178 | #ifdef USE_PAM | ||
179 | if (authenticated && !do_pam_account(pw->pw_name, NULL)) | ||
180 | authenticated = 0; | ||
181 | #endif /* USE_PAM */ | ||
182 | |||
183 | /* XXX todo: check if multiple auth methods are needed */ | ||
184 | if (authenticated == 1) { | ||
185 | authmsg = "Accepted"; | ||
186 | /* turn off userauth */ | ||
187 | dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &protocol_error); | ||
188 | packet_start(SSH2_MSG_USERAUTH_SUCCESS); | ||
189 | packet_send(); | ||
190 | packet_write_wait(); | ||
191 | /* now we can break out */ | ||
192 | userauth_success = 1; | ||
193 | } else if (authenticated == 0) { | ||
194 | authmsg = "Failed"; | ||
195 | packet_start(SSH2_MSG_USERAUTH_FAILURE); | ||
196 | packet_put_cstring("publickey,password"); /* XXX dynamic */ | ||
197 | packet_put_char(0); /* XXX partial success, unused */ | ||
198 | packet_send(); | ||
199 | packet_write_wait(); | ||
200 | } else { | ||
201 | authmsg = "Postponed"; | ||
202 | } | ||
203 | /* Raise logging level */ | ||
204 | if (authenticated == 1|| | ||
205 | attempt == AUTH_FAIL_LOG || | ||
206 | strcmp(method, "password") == 0) | ||
207 | authlog = log; | ||
208 | |||
209 | authlog("%s %s for %.200s from %.200s port %d ssh2", | ||
210 | authmsg, | ||
211 | method, | ||
212 | pw && pw->pw_uid == 0 ? "ROOT" : user, | ||
213 | get_remote_ipaddr(), | ||
214 | get_remote_port()); | ||
215 | |||
216 | xfree(service); | ||
217 | xfree(user); | ||
218 | xfree(method); | ||
219 | } | ||
220 | |||
221 | int | ||
222 | ssh2_auth_none(struct passwd *pw) | ||
223 | { | ||
224 | packet_done(); | ||
225 | #ifdef USE_PAM | ||
226 | return auth_pam_password(pw, ""); | ||
227 | #else /* USE_PAM */ | ||
228 | return auth_password(pw, ""); | ||
229 | #endif /* USE_PAM */ | ||
230 | } | ||
231 | int | ||
232 | ssh2_auth_password(struct passwd *pw) | ||
233 | { | ||
234 | char *password; | ||
235 | int authenticated = 0; | ||
236 | int change; | ||
237 | unsigned int len; | ||
238 | change = packet_get_char(); | ||
239 | if (change) | ||
240 | log("password change not supported"); | ||
241 | password = packet_get_string(&len); | ||
242 | packet_done(); | ||
243 | if (options.password_authentication && | ||
244 | #ifdef USE_PAM | ||
245 | auth_pam_password(pw, password) == 1) | ||
246 | #else /* USE_PAM */ | ||
247 | auth_password(pw, password) == 1) | ||
248 | #endif /* USE_PAM */ | ||
249 | authenticated = 1; | ||
250 | memset(password, 0, len); | ||
251 | xfree(password); | ||
252 | return authenticated; | ||
253 | } | ||
254 | int | ||
255 | ssh2_auth_pubkey(struct passwd *pw, unsigned char *raw, unsigned int rlen) | ||
256 | { | ||
257 | Buffer b; | ||
258 | Key *key; | ||
259 | char *pkalg, *pkblob, *sig; | ||
260 | unsigned int alen, blen, slen; | ||
261 | int have_sig; | ||
262 | int authenticated = 0; | ||
263 | |||
264 | if (options.rsa_authentication == 0) { | ||
265 | debug("pubkey auth disabled"); | ||
266 | return 0; | ||
267 | } | ||
268 | have_sig = packet_get_char(); | ||
269 | pkalg = packet_get_string(&alen); | ||
270 | if (strcmp(pkalg, KEX_DSS) != 0) { | ||
271 | xfree(pkalg); | ||
272 | log("bad pkalg %s", pkalg); /*XXX*/ | ||
273 | return 0; | ||
274 | } | ||
275 | pkblob = packet_get_string(&blen); | ||
276 | key = dsa_key_from_blob(pkblob, blen); | ||
277 | if (key != NULL) { | ||
278 | if (have_sig) { | ||
279 | sig = packet_get_string(&slen); | ||
280 | packet_done(); | ||
281 | buffer_init(&b); | ||
282 | buffer_append(&b, session_id2, session_id2_len); | ||
283 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | ||
284 | if (slen + 4 > rlen) | ||
285 | fatal("bad rlen/slen"); | ||
286 | buffer_append(&b, raw, rlen - slen - 4); | ||
287 | #ifdef DEBUG_DSS | ||
288 | buffer_dump(&b); | ||
289 | #endif | ||
290 | /* test for correct signature */ | ||
291 | if (user_dsa_key_allowed(pw, key) && | ||
292 | dsa_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) | ||
293 | authenticated = 1; | ||
294 | buffer_clear(&b); | ||
295 | xfree(sig); | ||
296 | } else { | ||
297 | packet_done(); | ||
298 | debug("test key..."); | ||
299 | /* test whether pkalg/pkblob are acceptable */ | ||
300 | /* XXX fake reply and always send PK_OK ? */ | ||
301 | if (user_dsa_key_allowed(pw, key)) { | ||
302 | packet_start(SSH2_MSG_USERAUTH_PK_OK); | ||
303 | packet_put_string(pkalg, alen); | ||
304 | packet_put_string(pkblob, blen); | ||
305 | packet_send(); | ||
306 | packet_write_wait(); | ||
307 | authenticated = -1; | ||
308 | } | ||
309 | } | ||
310 | key_free(key); | ||
311 | } | ||
312 | xfree(pkalg); | ||
313 | xfree(pkblob); | ||
314 | return authenticated; | ||
315 | } | ||
316 | |||
317 | /* set and get current user */ | ||
318 | |||
319 | struct passwd* | ||
320 | auth_get_user(void) | ||
321 | { | ||
322 | return (authctxt != NULL && authctxt->valid) ? &authctxt->pw : NULL; | ||
323 | } | ||
324 | |||
325 | struct passwd* | ||
326 | auth_set_user(char *u, char *s) | ||
327 | { | ||
328 | struct passwd *pw, *copy; | ||
329 | |||
330 | if (authctxt == NULL) { | ||
331 | authctxt = xmalloc(sizeof(*authctxt)); | ||
332 | authctxt->valid = 0; | ||
333 | authctxt->user = xstrdup(u); | ||
334 | authctxt->service = xstrdup(s); | ||
335 | setproctitle("%s", u); | ||
336 | pw = getpwnam(u); | ||
337 | if (!pw || !allowed_user(pw)) { | ||
338 | log("auth_set_user: illegal user %s", u); | ||
339 | return NULL; | ||
340 | } | ||
341 | #ifdef USE_PAM | ||
342 | start_pam(pw); | ||
343 | #endif | ||
344 | copy = &authctxt->pw; | ||
345 | memset(copy, 0, sizeof(*copy)); | ||
346 | copy->pw_name = xstrdup(pw->pw_name); | ||
347 | copy->pw_passwd = xstrdup(pw->pw_passwd); | ||
348 | copy->pw_uid = pw->pw_uid; | ||
349 | copy->pw_gid = pw->pw_gid; | ||
350 | copy->pw_dir = xstrdup(pw->pw_dir); | ||
351 | copy->pw_shell = xstrdup(pw->pw_shell); | ||
352 | authctxt->valid = 1; | ||
353 | } else { | ||
354 | if (strcmp(u, authctxt->user) != 0 || | ||
355 | strcmp(s, authctxt->service) != 0) { | ||
356 | log("auth_set_user: missmatch: (%s,%s)!=(%s,%s)", | ||
357 | u, s, authctxt->user, authctxt->service); | ||
358 | return NULL; | ||
359 | } | ||
360 | } | ||
361 | return auth_get_user(); | ||
362 | } | ||
363 | |||
364 | /* return 1 if user allows given key */ | ||
365 | int | ||
366 | user_dsa_key_allowed(struct passwd *pw, Key *key) | ||
367 | { | ||
368 | char line[8192], file[1024]; | ||
369 | int found_key = 0; | ||
370 | unsigned int bits = -1; | ||
371 | FILE *f; | ||
372 | unsigned long linenum = 0; | ||
373 | struct stat st; | ||
374 | Key *found; | ||
375 | |||
376 | /* Temporarily use the user's uid. */ | ||
377 | temporarily_use_uid(pw->pw_uid); | ||
378 | |||
379 | /* The authorized keys. */ | ||
380 | snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, | ||
381 | SSH_USER_PERMITTED_KEYS2); | ||
382 | |||
383 | /* Fail quietly if file does not exist */ | ||
384 | if (stat(file, &st) < 0) { | ||
385 | /* Restore the privileged uid. */ | ||
386 | restore_uid(); | ||
387 | return 0; | ||
388 | } | ||
389 | /* Open the file containing the authorized keys. */ | ||
390 | f = fopen(file, "r"); | ||
391 | if (!f) { | ||
392 | /* Restore the privileged uid. */ | ||
393 | restore_uid(); | ||
394 | return 0; | ||
395 | } | ||
396 | if (options.strict_modes) { | ||
397 | int fail = 0; | ||
398 | char buf[1024]; | ||
399 | /* Check open file in order to avoid open/stat races */ | ||
400 | if (fstat(fileno(f), &st) < 0 || | ||
401 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
402 | (st.st_mode & 022) != 0) { | ||
403 | snprintf(buf, sizeof buf, "DSA authentication refused for %.100s: " | ||
404 | "bad ownership or modes for '%s'.", pw->pw_name, file); | ||
405 | fail = 1; | ||
406 | } else { | ||
407 | /* Check path to SSH_USER_PERMITTED_KEYS */ | ||
408 | int i; | ||
409 | static const char *check[] = { | ||
410 | "", SSH_USER_DIR, NULL | ||
411 | }; | ||
412 | for (i = 0; check[i]; i++) { | ||
413 | snprintf(line, sizeof line, "%.500s/%.100s", | ||
414 | pw->pw_dir, check[i]); | ||
415 | if (stat(line, &st) < 0 || | ||
416 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
417 | (st.st_mode & 022) != 0) { | ||
418 | snprintf(buf, sizeof buf, | ||
419 | "DSA authentication refused for %.100s: " | ||
420 | "bad ownership or modes for '%s'.", | ||
421 | pw->pw_name, line); | ||
422 | fail = 1; | ||
423 | break; | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | if (fail) { | ||
428 | log(buf); | ||
429 | fclose(f); | ||
430 | restore_uid(); | ||
431 | return 0; | ||
432 | } | ||
433 | } | ||
434 | found_key = 0; | ||
435 | found = key_new(KEY_DSA); | ||
436 | |||
437 | while (fgets(line, sizeof(line), f)) { | ||
438 | char *cp; | ||
439 | linenum++; | ||
440 | /* Skip leading whitespace, empty and comment lines. */ | ||
441 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
442 | ; | ||
443 | if (!*cp || *cp == '\n' || *cp == '#') | ||
444 | continue; | ||
445 | bits = key_read(found, &cp); | ||
446 | if (bits == 0) | ||
447 | continue; | ||
448 | if (key_equal(found, key)) { | ||
449 | found_key = 1; | ||
450 | debug("matching key found: file %s, line %ld", | ||
451 | file, linenum); | ||
452 | break; | ||
453 | } | ||
454 | } | ||
455 | restore_uid(); | ||
456 | fclose(f); | ||
457 | key_free(found); | ||
458 | return found_key; | ||
459 | } | ||
diff --git a/authfile.c b/authfile.c index e17c60381..f93c9d470 100644 --- a/authfile.c +++ b/authfile.c | |||
@@ -15,14 +15,20 @@ | |||
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: authfile.c,v 1.11 2000/04/16 02:31:49 damien Exp $"); | 18 | RCSID("$Id: authfile.c,v 1.12 2000/04/29 13:57:10 damien Exp $"); |
19 | 19 | ||
20 | #include <openssl/bn.h> | 20 | #include <openssl/bn.h> |
21 | #include <openssl/dsa.h> | ||
22 | #include <openssl/rsa.h> | ||
23 | #include <openssl/pem.h> | ||
24 | #include <openssl/evp.h> | ||
25 | |||
21 | #include "xmalloc.h" | 26 | #include "xmalloc.h" |
22 | #include "buffer.h" | 27 | #include "buffer.h" |
23 | #include "bufaux.h" | 28 | #include "bufaux.h" |
24 | #include "cipher.h" | 29 | #include "cipher.h" |
25 | #include "ssh.h" | 30 | #include "ssh.h" |
31 | #include "key.h" | ||
26 | 32 | ||
27 | /* Version identification string for identity files. */ | 33 | /* Version identification string for identity files. */ |
28 | #define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" | 34 | #define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n" |
@@ -35,8 +41,8 @@ RCSID("$Id: authfile.c,v 1.11 2000/04/16 02:31:49 damien Exp $"); | |||
35 | */ | 41 | */ |
36 | 42 | ||
37 | int | 43 | int |
38 | save_private_key(const char *filename, const char *passphrase, | 44 | save_private_key_rsa(const char *filename, const char *passphrase, |
39 | RSA *key, const char *comment) | 45 | RSA *key, const char *comment) |
40 | { | 46 | { |
41 | Buffer buffer, encrypted; | 47 | Buffer buffer, encrypted; |
42 | char buf[100], *cp; | 48 | char buf[100], *cp; |
@@ -128,6 +134,63 @@ save_private_key(const char *filename, const char *passphrase, | |||
128 | return 1; | 134 | return 1; |
129 | } | 135 | } |
130 | 136 | ||
137 | /* save DSA key in OpenSSL PEM format */ | ||
138 | |||
139 | int | ||
140 | save_private_key_dsa(const char *filename, const char *passphrase, | ||
141 | DSA *dsa, const char *comment) | ||
142 | { | ||
143 | FILE *fp; | ||
144 | int fd; | ||
145 | int success = 1; | ||
146 | int len = strlen(passphrase); | ||
147 | |||
148 | if (len > 0 && len <= 4) { | ||
149 | error("passphrase too short: %d bytes", len); | ||
150 | errno = 0; | ||
151 | return 0; | ||
152 | } | ||
153 | fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); | ||
154 | if (fd < 0) { | ||
155 | debug("open %s failed", filename); | ||
156 | return 0; | ||
157 | } | ||
158 | fp = fdopen(fd, "w"); | ||
159 | if (fp == NULL ) { | ||
160 | debug("fdopen %s failed", filename); | ||
161 | close(fd); | ||
162 | return 0; | ||
163 | } | ||
164 | if (len > 0) { | ||
165 | if (!PEM_write_DSAPrivateKey(fp, dsa, EVP_des_ede3_cbc(), | ||
166 | (char *)passphrase, strlen(passphrase), NULL, NULL)) | ||
167 | success = 0; | ||
168 | } else { | ||
169 | if (!PEM_write_DSAPrivateKey(fp, dsa, NULL, | ||
170 | NULL, 0, NULL, NULL)) | ||
171 | success = 0; | ||
172 | } | ||
173 | fclose(fp); | ||
174 | return success; | ||
175 | } | ||
176 | |||
177 | int | ||
178 | save_private_key(const char *filename, const char *passphrase, Key *key, | ||
179 | const char *comment) | ||
180 | { | ||
181 | switch (key->type) { | ||
182 | case KEY_RSA: | ||
183 | return save_private_key_rsa(filename, passphrase, key->rsa, comment); | ||
184 | break; | ||
185 | case KEY_DSA: | ||
186 | return save_private_key_dsa(filename, passphrase, key->dsa, comment); | ||
187 | break; | ||
188 | default: | ||
189 | break; | ||
190 | } | ||
191 | return 0; | ||
192 | } | ||
193 | |||
131 | /* | 194 | /* |
132 | * Loads the public part of the key file. Returns 0 if an error was | 195 | * Loads the public part of the key file. Returns 0 if an error was |
133 | * encountered (the file does not exist or is not readable), and non-zero | 196 | * encountered (the file does not exist or is not readable), and non-zero |
@@ -135,8 +198,7 @@ save_private_key(const char *filename, const char *passphrase, | |||
135 | */ | 198 | */ |
136 | 199 | ||
137 | int | 200 | int |
138 | load_public_key(const char *filename, RSA * pub, | 201 | load_public_key_rsa(const char *filename, RSA * pub, char **comment_return) |
139 | char **comment_return) | ||
140 | { | 202 | { |
141 | int fd, i; | 203 | int fd, i; |
142 | off_t len; | 204 | off_t len; |
@@ -154,7 +216,7 @@ load_public_key(const char *filename, RSA * pub, | |||
154 | 216 | ||
155 | if (read(fd, cp, (size_t) len) != (size_t) len) { | 217 | if (read(fd, cp, (size_t) len) != (size_t) len) { |
156 | debug("Read from key file %.200s failed: %.100s", filename, | 218 | debug("Read from key file %.200s failed: %.100s", filename, |
157 | strerror(errno)); | 219 | strerror(errno)); |
158 | buffer_free(&buffer); | 220 | buffer_free(&buffer); |
159 | close(fd); | 221 | close(fd); |
160 | return 0; | 222 | return 0; |
@@ -183,9 +245,13 @@ load_public_key(const char *filename, RSA * pub, | |||
183 | 245 | ||
184 | /* Read the public key from the buffer. */ | 246 | /* Read the public key from the buffer. */ |
185 | buffer_get_int(&buffer); | 247 | buffer_get_int(&buffer); |
186 | pub->n = BN_new(); | 248 | /* XXX alloc */ |
249 | if (pub->n == NULL) | ||
250 | pub->n = BN_new(); | ||
187 | buffer_get_bignum(&buffer, pub->n); | 251 | buffer_get_bignum(&buffer, pub->n); |
188 | pub->e = BN_new(); | 252 | /* XXX alloc */ |
253 | if (pub->e == NULL) | ||
254 | pub->e = BN_new(); | ||
189 | buffer_get_bignum(&buffer, pub->e); | 255 | buffer_get_bignum(&buffer, pub->e); |
190 | if (comment_return) | 256 | if (comment_return) |
191 | *comment_return = buffer_get_string(&buffer, NULL); | 257 | *comment_return = buffer_get_string(&buffer, NULL); |
@@ -196,6 +262,20 @@ load_public_key(const char *filename, RSA * pub, | |||
196 | return 1; | 262 | return 1; |
197 | } | 263 | } |
198 | 264 | ||
265 | int | ||
266 | load_public_key(const char *filename, Key * key, char **comment_return) | ||
267 | { | ||
268 | switch (key->type) { | ||
269 | case KEY_RSA: | ||
270 | return load_public_key_rsa(filename, key->rsa, comment_return); | ||
271 | break; | ||
272 | case KEY_DSA: | ||
273 | default: | ||
274 | break; | ||
275 | } | ||
276 | return 0; | ||
277 | } | ||
278 | |||
199 | /* | 279 | /* |
200 | * Loads the private key from the file. Returns 0 if an error is encountered | 280 | * Loads the private key from the file. Returns 0 if an error is encountered |
201 | * (file does not exist or is not readable, or passphrase is bad). This | 281 | * (file does not exist or is not readable, or passphrase is bad). This |
@@ -204,35 +284,17 @@ load_public_key(const char *filename, RSA * pub, | |||
204 | */ | 284 | */ |
205 | 285 | ||
206 | int | 286 | int |
207 | load_private_key(const char *filename, const char *passphrase, | 287 | load_private_key_rsa(int fd, const char *filename, |
208 | RSA * prv, char **comment_return) | 288 | const char *passphrase, RSA * prv, char **comment_return) |
209 | { | 289 | { |
210 | int fd, i, check1, check2, cipher_type; | 290 | int i, check1, check2, cipher_type; |
211 | off_t len; | 291 | off_t len; |
212 | Buffer buffer, decrypted; | 292 | Buffer buffer, decrypted; |
213 | char *cp; | 293 | char *cp; |
214 | CipherContext cipher; | 294 | CipherContext cipher; |
215 | BN_CTX *ctx; | 295 | BN_CTX *ctx; |
216 | BIGNUM *aux; | 296 | BIGNUM *aux; |
217 | struct stat st; | ||
218 | |||
219 | fd = open(filename, O_RDONLY); | ||
220 | if (fd < 0) | ||
221 | return 0; | ||
222 | 297 | ||
223 | /* check owner and modes */ | ||
224 | if (fstat(fd, &st) < 0 || | ||
225 | (st.st_uid != 0 && getuid() != 0 && st.st_uid != getuid()) || | ||
226 | (st.st_mode & 077) != 0) { | ||
227 | close(fd); | ||
228 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
229 | error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); | ||
230 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
231 | error("Bad ownership or mode(0%3.3o) for '%s'.", | ||
232 | st.st_mode & 0777, filename); | ||
233 | error("It is recommended that your private key files are NOT accessible by others."); | ||
234 | return 0; | ||
235 | } | ||
236 | len = lseek(fd, (off_t) 0, SEEK_END); | 298 | len = lseek(fd, (off_t) 0, SEEK_END); |
237 | lseek(fd, (off_t) 0, SEEK_SET); | 299 | lseek(fd, (off_t) 0, SEEK_SET); |
238 | 300 | ||
@@ -309,7 +371,9 @@ load_private_key(const char *filename, const char *passphrase, | |||
309 | buffer_free(&decrypted); | 371 | buffer_free(&decrypted); |
310 | fail: | 372 | fail: |
311 | BN_clear_free(prv->n); | 373 | BN_clear_free(prv->n); |
374 | prv->n = NULL; | ||
312 | BN_clear_free(prv->e); | 375 | BN_clear_free(prv->e); |
376 | prv->e = NULL; | ||
313 | if (comment_return) | 377 | if (comment_return) |
314 | xfree(*comment_return); | 378 | xfree(*comment_return); |
315 | return 0; | 379 | return 0; |
@@ -343,3 +407,87 @@ fail: | |||
343 | 407 | ||
344 | return 1; | 408 | return 1; |
345 | } | 409 | } |
410 | |||
411 | int | ||
412 | load_private_key_dsa(int fd, const char *passphrase, Key *k, char **comment_return) | ||
413 | { | ||
414 | DSA *dsa; | ||
415 | BIO *in; | ||
416 | FILE *fp; | ||
417 | |||
418 | in = BIO_new(BIO_s_file()); | ||
419 | if (in == NULL) { | ||
420 | error("BIO_new failed"); | ||
421 | return 0; | ||
422 | } | ||
423 | fp = fdopen(fd, "r"); | ||
424 | if (fp == NULL) { | ||
425 | error("fdopen failed"); | ||
426 | return 0; | ||
427 | } | ||
428 | BIO_set_fp(in, fp, BIO_NOCLOSE); | ||
429 | dsa = PEM_read_bio_DSAPrivateKey(in, NULL, NULL, (char *)passphrase); | ||
430 | if (dsa == NULL) { | ||
431 | debug("PEM_read_bio_DSAPrivateKey failed"); | ||
432 | } else { | ||
433 | /* replace k->dsa with loaded key */ | ||
434 | DSA_free(k->dsa); | ||
435 | k->dsa = dsa; | ||
436 | } | ||
437 | BIO_free(in); | ||
438 | fclose(fp); | ||
439 | if (comment_return) | ||
440 | *comment_return = xstrdup("dsa w/o comment"); | ||
441 | debug("read DSA private key done"); | ||
442 | #ifdef DEBUG_DSS | ||
443 | DSA_print_fp(stderr, dsa, 8); | ||
444 | #endif | ||
445 | return dsa != NULL ? 1 : 0; | ||
446 | } | ||
447 | |||
448 | int | ||
449 | load_private_key(const char *filename, const char *passphrase, Key *key, | ||
450 | char **comment_return) | ||
451 | { | ||
452 | int fd; | ||
453 | int ret = 0; | ||
454 | struct stat st; | ||
455 | |||
456 | fd = open(filename, O_RDONLY); | ||
457 | if (fd < 0) | ||
458 | return 0; | ||
459 | |||
460 | /* check owner and modes */ | ||
461 | if (fstat(fd, &st) < 0 || | ||
462 | (st.st_uid != 0 && st.st_uid != getuid()) || | ||
463 | (st.st_mode & 077) != 0) { | ||
464 | close(fd); | ||
465 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
466 | error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); | ||
467 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
468 | error("Bad ownership or mode(0%3.3o) for '%s'.", | ||
469 | st.st_mode & 0777, filename); | ||
470 | error("It is recommended that your private key files are NOT accessible by others."); | ||
471 | return 0; | ||
472 | } | ||
473 | switch (key->type) { | ||
474 | case KEY_RSA: | ||
475 | if (key->rsa->e != NULL) { | ||
476 | BN_clear_free(key->rsa->e); | ||
477 | key->rsa->e = NULL; | ||
478 | } | ||
479 | if (key->rsa->n != NULL) { | ||
480 | BN_clear_free(key->rsa->n); | ||
481 | key->rsa->n = NULL; | ||
482 | } | ||
483 | ret = load_private_key_rsa(fd, filename, passphrase, | ||
484 | key->rsa, comment_return); | ||
485 | break; | ||
486 | case KEY_DSA: | ||
487 | ret = load_private_key_dsa(fd, passphrase, key, comment_return); | ||
488 | default: | ||
489 | break; | ||
490 | } | ||
491 | close(fd); | ||
492 | return ret; | ||
493 | } | ||
diff --git a/authfile.h b/authfile.h new file mode 100644 index 000000000..afec27d54 --- /dev/null +++ b/authfile.h | |||
@@ -0,0 +1,36 @@ | |||
1 | #ifndef AUTHFILE_H | ||
2 | #define AUTHFILE_H | ||
3 | |||
4 | /* | ||
5 | * Saves the authentication (private) key in a file, encrypting it with | ||
6 | * passphrase. | ||
7 | * For RSA keys: The identification of the file (lowest 64 bits of n) | ||
8 | * will precede the key to provide identification of the key without | ||
9 | * needing a passphrase. | ||
10 | */ | ||
11 | int | ||
12 | save_private_key(const char *filename, const char *passphrase, | ||
13 | Key * private_key, const char *comment); | ||
14 | |||
15 | /* | ||
16 | * Loads the public part of the key file (public key and comment). Returns 0 | ||
17 | * if an error occurred; zero if the public key was successfully read. The | ||
18 | * comment of the key is returned in comment_return if it is non-NULL; the | ||
19 | * caller must free the value with xfree. | ||
20 | */ | ||
21 | int | ||
22 | load_public_key(const char *filename, Key * pub, | ||
23 | char **comment_return); | ||
24 | |||
25 | /* | ||
26 | * Loads the private key from the file. Returns 0 if an error is encountered | ||
27 | * (file does not exist or is not readable, or passphrase is bad). This | ||
28 | * initializes the private key. The comment of the key is returned in | ||
29 | * comment_return if it is non-NULL; the caller must free the value with | ||
30 | * xfree. | ||
31 | */ | ||
32 | int | ||
33 | load_private_key(const char *filename, const char *passphrase, | ||
34 | Key * private_key, char **comment_return); | ||
35 | |||
36 | #endif | ||
@@ -28,7 +28,7 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include "includes.h" | 30 | #include "includes.h" |
31 | RCSID("$Id: compat.c,v 1.8 2000/04/16 01:18:42 damien Exp $"); | 31 | RCSID("$Id: compat.c,v 1.9 2000/04/29 13:57:10 damien Exp $"); |
32 | 32 | ||
33 | #include "ssh.h" | 33 | #include "ssh.h" |
34 | #include "packet.h" | 34 | #include "packet.h" |
@@ -44,7 +44,6 @@ enable_compat20(void) | |||
44 | { | 44 | { |
45 | verbose("Enabling compatibility mode for protocol 2.0"); | 45 | verbose("Enabling compatibility mode for protocol 2.0"); |
46 | compat20 = 1; | 46 | compat20 = 1; |
47 | packet_set_ssh2_format(); | ||
48 | } | 47 | } |
49 | void | 48 | void |
50 | enable_compat13(void) | 49 | enable_compat13(void) |
@@ -28,7 +28,7 @@ | |||
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include "includes.h" | 30 | #include "includes.h" |
31 | RCSID("$Id: dsa.c,v 1.4 2000/04/14 10:30:31 markus Exp $"); | 31 | RCSID("$Id: dsa.c,v 1.5 2000/04/26 20:56:29 markus Exp $"); |
32 | 32 | ||
33 | #include "ssh.h" | 33 | #include "ssh.h" |
34 | #include "xmalloc.h" | 34 | #include "xmalloc.h" |
@@ -47,13 +47,14 @@ RCSID("$Id: dsa.c,v 1.4 2000/04/14 10:30:31 markus Exp $"); | |||
47 | #include <openssl/hmac.h> | 47 | #include <openssl/hmac.h> |
48 | #include "kex.h" | 48 | #include "kex.h" |
49 | #include "key.h" | 49 | #include "key.h" |
50 | #include "uuencode.h" | ||
50 | 51 | ||
51 | #define INTBLOB_LEN 20 | 52 | #define INTBLOB_LEN 20 |
52 | #define SIGBLOB_LEN (2*INTBLOB_LEN) | 53 | #define SIGBLOB_LEN (2*INTBLOB_LEN) |
53 | 54 | ||
54 | Key * | 55 | Key * |
55 | dsa_serverkey_from_blob( | 56 | dsa_key_from_blob( |
56 | char *serverhostkey, int serverhostkeylen) | 57 | char *blob, int blen) |
57 | { | 58 | { |
58 | Buffer b; | 59 | Buffer b; |
59 | char *ktype; | 60 | char *ktype; |
@@ -61,14 +62,17 @@ dsa_serverkey_from_blob( | |||
61 | DSA *dsa; | 62 | DSA *dsa; |
62 | Key *key; | 63 | Key *key; |
63 | 64 | ||
65 | #ifdef DEBUG_DSS | ||
66 | dump_base64(blob, blen); | ||
67 | #endif | ||
64 | /* fetch & parse DSA/DSS pubkey */ | 68 | /* fetch & parse DSA/DSS pubkey */ |
65 | key = key_new(KEY_DSA); | 69 | key = key_new(KEY_DSA); |
66 | dsa = key->dsa; | 70 | dsa = key->dsa; |
67 | buffer_init(&b); | 71 | buffer_init(&b); |
68 | buffer_append(&b, serverhostkey, serverhostkeylen); | 72 | buffer_append(&b, blob, blen); |
69 | ktype = buffer_get_string(&b, NULL); | 73 | ktype = buffer_get_string(&b, NULL); |
70 | if (strcmp(KEX_DSS, ktype) != 0) { | 74 | if (strcmp(KEX_DSS, ktype) != 0) { |
71 | error("dsa_serverkey_from_blob: cannot handle type %s", ktype); | 75 | error("dsa_key_from_blob: cannot handle type %s", ktype); |
72 | key_free(key); | 76 | key_free(key); |
73 | return NULL; | 77 | return NULL; |
74 | } | 78 | } |
@@ -78,7 +82,7 @@ dsa_serverkey_from_blob( | |||
78 | buffer_get_bignum2(&b, dsa->pub_key); | 82 | buffer_get_bignum2(&b, dsa->pub_key); |
79 | rlen = buffer_len(&b); | 83 | rlen = buffer_len(&b); |
80 | if(rlen != 0) | 84 | if(rlen != 0) |
81 | error("dsa_serverkey_from_blob: remaining bytes in serverhostkey %d", rlen); | 85 | error("dsa_key_from_blob: remaining bytes in key blob %d", rlen); |
82 | buffer_free(&b); | 86 | buffer_free(&b); |
83 | 87 | ||
84 | debug("keytype %s", ktype); | 88 | debug("keytype %s", ktype); |
@@ -87,37 +91,8 @@ dsa_serverkey_from_blob( | |||
87 | #endif | 91 | #endif |
88 | return key; | 92 | return key; |
89 | } | 93 | } |
90 | DSA * | ||
91 | dsa_load_private(char *filename) | ||
92 | { | ||
93 | DSA *dsa; | ||
94 | BIO *in; | ||
95 | |||
96 | in = BIO_new(BIO_s_file()); | ||
97 | if (in == NULL) | ||
98 | fatal("BIO_new failed"); | ||
99 | if (BIO_read_filename(in, filename) <= 0) | ||
100 | fatal("BIO_read failed %s: %s", filename, strerror(errno)); | ||
101 | fprintf(stderr, "read DSA private key\n"); | ||
102 | dsa = PEM_read_bio_DSAPrivateKey(in,NULL,NULL,NULL); | ||
103 | if (dsa == NULL) | ||
104 | fatal("PEM_read_bio_DSAPrivateKey failed %s", filename); | ||
105 | BIO_free(in); | ||
106 | return dsa; | ||
107 | } | ||
108 | Key * | ||
109 | dsa_get_serverkey(char *filename) | ||
110 | { | ||
111 | Key *k = key_new(KEY_EMPTY); | ||
112 | k->type = KEY_DSA; | ||
113 | k->dsa = dsa_load_private(filename); | ||
114 | #ifdef DEBUG_DSS | ||
115 | DSA_print_fp(stderr, dsa, 8); | ||
116 | #endif | ||
117 | return k; | ||
118 | } | ||
119 | int | 94 | int |
120 | dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp) | 95 | dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp) |
121 | { | 96 | { |
122 | Buffer b; | 97 | Buffer b; |
123 | int len; | 98 | int len; |
@@ -146,7 +121,7 @@ int | |||
146 | dsa_sign( | 121 | dsa_sign( |
147 | Key *key, | 122 | Key *key, |
148 | unsigned char **sigp, int *lenp, | 123 | unsigned char **sigp, int *lenp, |
149 | unsigned char *hash, int hlen) | 124 | unsigned char *data, int datalen) |
150 | { | 125 | { |
151 | unsigned char *digest; | 126 | unsigned char *digest; |
152 | unsigned char *ret; | 127 | unsigned char *ret; |
@@ -165,10 +140,13 @@ dsa_sign( | |||
165 | } | 140 | } |
166 | digest = xmalloc(evp_md->md_size); | 141 | digest = xmalloc(evp_md->md_size); |
167 | EVP_DigestInit(&md, evp_md); | 142 | EVP_DigestInit(&md, evp_md); |
168 | EVP_DigestUpdate(&md, hash, hlen); | 143 | EVP_DigestUpdate(&md, data, datalen); |
169 | EVP_DigestFinal(&md, digest, NULL); | 144 | EVP_DigestFinal(&md, digest, NULL); |
170 | 145 | ||
171 | sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); | 146 | sig = DSA_do_sign(digest, evp_md->md_size, key->dsa); |
147 | if (sig == NULL) { | ||
148 | fatal("dsa_sign: cannot sign"); | ||
149 | } | ||
172 | 150 | ||
173 | rlen = BN_num_bytes(sig->r); | 151 | rlen = BN_num_bytes(sig->r); |
174 | slen = BN_num_bytes(sig->s); | 152 | slen = BN_num_bytes(sig->s); |
@@ -212,7 +190,7 @@ int | |||
212 | dsa_verify( | 190 | dsa_verify( |
213 | Key *key, | 191 | Key *key, |
214 | unsigned char *signature, int signaturelen, | 192 | unsigned char *signature, int signaturelen, |
215 | unsigned char *hash, int hlen) | 193 | unsigned char *data, int datalen) |
216 | { | 194 | { |
217 | Buffer b; | 195 | Buffer b; |
218 | unsigned char *digest; | 196 | unsigned char *digest; |
@@ -269,10 +247,10 @@ dsa_verify( | |||
269 | xfree(sigblob); | 247 | xfree(sigblob); |
270 | } | 248 | } |
271 | 249 | ||
272 | /* sha1 the signed data (== session_id == hash) */ | 250 | /* sha1 the data */ |
273 | digest = xmalloc(evp_md->md_size); | 251 | digest = xmalloc(evp_md->md_size); |
274 | EVP_DigestInit(&md, evp_md); | 252 | EVP_DigestInit(&md, evp_md); |
275 | EVP_DigestUpdate(&md, hash, hlen); | 253 | EVP_DigestUpdate(&md, data, datalen); |
276 | EVP_DigestFinal(&md, digest, NULL); | 254 | EVP_DigestFinal(&md, digest, NULL); |
277 | 255 | ||
278 | ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); | 256 | ret = DSA_do_verify(digest, evp_md->md_size, sig, key->dsa); |
@@ -296,3 +274,21 @@ dsa_verify( | |||
296 | debug("dsa_verify: signature %s", txt); | 274 | debug("dsa_verify: signature %s", txt); |
297 | return ret; | 275 | return ret; |
298 | } | 276 | } |
277 | |||
278 | Key * | ||
279 | dsa_generate_key(unsigned int bits) | ||
280 | { | ||
281 | DSA *dsa = DSA_generate_parameters(bits, NULL, 0, NULL, NULL, NULL, NULL); | ||
282 | Key *k; | ||
283 | if (dsa == NULL) { | ||
284 | fatal("DSA_generate_parameters failed"); | ||
285 | } | ||
286 | if (!DSA_generate_key(dsa)) { | ||
287 | fatal("DSA_generate_keys failed"); | ||
288 | } | ||
289 | |||
290 | k = key_new(KEY_EMPTY); | ||
291 | k->type = KEY_DSA; | ||
292 | k->dsa = dsa; | ||
293 | return k; | ||
294 | } | ||
@@ -1,20 +1,22 @@ | |||
1 | #ifndef DSA_H | 1 | #ifndef DSA_H |
2 | #define DSA_H | 2 | #define DSA_H |
3 | 3 | ||
4 | Key *dsa_serverkey_from_blob(char *serverhostkey, int serverhostkeylen); | 4 | Key *dsa_key_from_blob(char *blob, int blen); |
5 | Key *dsa_get_serverkey(char *filename); | 5 | int dsa_make_key_blob(Key *key, unsigned char **blobp, unsigned int *lenp); |
6 | int dsa_make_serverkey_blob(Key *key, unsigned char **blobp, unsigned int *lenp); | ||
7 | 6 | ||
8 | int | 7 | int |
9 | dsa_sign( | 8 | dsa_sign( |
10 | Key *key, | 9 | Key *key, |
11 | unsigned char **sigp, int *lenp, | 10 | unsigned char **sigp, int *lenp, |
12 | unsigned char *hash, int hlen); | 11 | unsigned char *data, int datalen); |
13 | 12 | ||
14 | int | 13 | int |
15 | dsa_verify( | 14 | dsa_verify( |
16 | Key *key, | 15 | Key *key, |
17 | unsigned char *signature, int signaturelen, | 16 | unsigned char *signature, int signaturelen, |
18 | unsigned char *hash, int hlen); | 17 | unsigned char *data, int datalen); |
18 | |||
19 | Key * | ||
20 | dsa_generate_key(unsigned int bits); | ||
19 | 21 | ||
20 | #endif | 22 | #endif |
diff --git a/hostfile.c b/hostfile.c index 29efe5656..e1c2429bd 100644 --- a/hostfile.c +++ b/hostfile.c | |||
@@ -14,7 +14,7 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$OpenBSD: hostfile.c,v 1.16 2000/04/14 10:30:31 markus Exp $"); | 17 | RCSID("$OpenBSD: hostfile.c,v 1.17 2000/04/26 20:56:29 markus Exp $"); |
18 | 18 | ||
19 | #include "packet.h" | 19 | #include "packet.h" |
20 | #include "match.h" | 20 | #include "match.h" |
@@ -39,13 +39,8 @@ hostfile_read_key(char **cpp, unsigned int *bitsp, Key *ret) | |||
39 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) | 39 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) |
40 | ; | 40 | ; |
41 | 41 | ||
42 | /* Get number of bits. */ | 42 | bits = key_read(ret, &cp); |
43 | if (*cp < '0' || *cp > '9') | 43 | if (bits == 0) |
44 | return 0; /* Bad bit count... */ | ||
45 | for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) | ||
46 | bits = 10 * bits + *cp - '0'; | ||
47 | |||
48 | if (!key_read(ret, bits, &cp)) | ||
49 | return 0; | 44 | return 0; |
50 | 45 | ||
51 | /* Skip trailing whitespace. */ | 46 | /* Skip trailing whitespace. */ |
@@ -182,24 +177,18 @@ add_host_to_hostfile(const char *filename, const char *host, Key *key) | |||
182 | { | 177 | { |
183 | FILE *f; | 178 | FILE *f; |
184 | int success = 0; | 179 | int success = 0; |
185 | |||
186 | if (key == NULL) | 180 | if (key == NULL) |
187 | return 1; | 181 | return 1; /* XXX ? */ |
188 | |||
189 | /* Open the file for appending. */ | ||
190 | f = fopen(filename, "a"); | 182 | f = fopen(filename, "a"); |
191 | if (!f) | 183 | if (!f) |
192 | return 0; | 184 | return 0; |
193 | |||
194 | fprintf(f, "%s ", host); | 185 | fprintf(f, "%s ", host); |
195 | if (key_write(key, f)) { | 186 | if (key_write(key, f)) { |
196 | fprintf(f, "\n"); | ||
197 | success = 1; | 187 | success = 1; |
198 | } else { | 188 | } else { |
199 | error("add_host_to_hostfile: saving key failed"); | 189 | error("add_host_to_hostfile: saving key in %s failed", filename); |
200 | } | 190 | } |
201 | 191 | fprintf(f, "\n"); | |
202 | /* Close the file. */ | ||
203 | fclose(f); | 192 | fclose(f); |
204 | return success; | 193 | return success; |
205 | } | 194 | } |
@@ -38,6 +38,10 @@ | |||
38 | #include <openssl/evp.h> | 38 | #include <openssl/evp.h> |
39 | #include "xmalloc.h" | 39 | #include "xmalloc.h" |
40 | #include "key.h" | 40 | #include "key.h" |
41 | #include "dsa.h" | ||
42 | #include "uuencode.h" | ||
43 | |||
44 | #define SSH_DSS "ssh-dss" | ||
41 | 45 | ||
42 | Key * | 46 | Key * |
43 | key_new(int type) | 47 | key_new(int type) |
@@ -47,6 +51,8 @@ key_new(int type) | |||
47 | DSA *dsa; | 51 | DSA *dsa; |
48 | k = xmalloc(sizeof(*k)); | 52 | k = xmalloc(sizeof(*k)); |
49 | k->type = type; | 53 | k->type = type; |
54 | k->dsa = NULL; | ||
55 | k->rsa = NULL; | ||
50 | switch (k->type) { | 56 | switch (k->type) { |
51 | case KEY_RSA: | 57 | case KEY_RSA: |
52 | rsa = RSA_new(); | 58 | rsa = RSA_new(); |
@@ -63,8 +69,6 @@ key_new(int type) | |||
63 | k->dsa = dsa; | 69 | k->dsa = dsa; |
64 | break; | 70 | break; |
65 | case KEY_EMPTY: | 71 | case KEY_EMPTY: |
66 | k->dsa = NULL; | ||
67 | k->rsa = NULL; | ||
68 | break; | 72 | break; |
69 | default: | 73 | default: |
70 | fatal("key_new: bad key type %d", k->type); | 74 | fatal("key_new: bad key type %d", k->type); |
@@ -111,7 +115,7 @@ key_equal(Key *a, Key *b) | |||
111 | BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; | 115 | BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; |
112 | break; | 116 | break; |
113 | default: | 117 | default: |
114 | fatal("key_free: bad key type %d", a->type); | 118 | fatal("key_equal: bad key type %d", a->type); |
115 | break; | 119 | break; |
116 | } | 120 | } |
117 | return 0; | 121 | return 0; |
@@ -127,46 +131,37 @@ char * | |||
127 | key_fingerprint(Key *k) | 131 | key_fingerprint(Key *k) |
128 | { | 132 | { |
129 | static char retval[80]; | 133 | static char retval[80]; |
130 | unsigned char *buf = NULL; | 134 | unsigned char *blob = NULL; |
131 | int len = 0; | 135 | int len = 0; |
132 | int nlen, elen, plen, qlen, glen, publen; | 136 | int nlen, elen; |
133 | 137 | ||
134 | switch (k->type) { | 138 | switch (k->type) { |
135 | case KEY_RSA: | 139 | case KEY_RSA: |
136 | nlen = BN_num_bytes(k->rsa->n); | 140 | nlen = BN_num_bytes(k->rsa->n); |
137 | elen = BN_num_bytes(k->rsa->e); | 141 | elen = BN_num_bytes(k->rsa->e); |
138 | len = nlen + elen; | 142 | len = nlen + elen; |
139 | buf = xmalloc(len); | 143 | blob = xmalloc(len); |
140 | BN_bn2bin(k->rsa->n, buf); | 144 | BN_bn2bin(k->rsa->n, blob); |
141 | BN_bn2bin(k->rsa->e, buf + nlen); | 145 | BN_bn2bin(k->rsa->e, blob + nlen); |
142 | break; | 146 | break; |
143 | case KEY_DSA: | 147 | case KEY_DSA: |
144 | plen = BN_num_bytes(k->dsa->p); | 148 | dsa_make_key_blob(k, &blob, &len); |
145 | qlen = BN_num_bytes(k->dsa->q); | ||
146 | glen = BN_num_bytes(k->dsa->g); | ||
147 | publen = BN_num_bytes(k->dsa->pub_key); | ||
148 | len = qlen + qlen + glen + publen; | ||
149 | buf = xmalloc(len); | ||
150 | BN_bn2bin(k->dsa->p, buf); | ||
151 | BN_bn2bin(k->dsa->q, buf + plen); | ||
152 | BN_bn2bin(k->dsa->g, buf + plen + qlen); | ||
153 | BN_bn2bin(k->dsa->pub_key , buf + plen + qlen + glen); | ||
154 | break; | 149 | break; |
155 | default: | 150 | default: |
156 | fatal("key_fingerprint: bad key type %d", k->type); | 151 | fatal("key_fingerprint: bad key type %d", k->type); |
157 | break; | 152 | break; |
158 | } | 153 | } |
159 | if (buf != NULL) { | 154 | if (blob != NULL) { |
160 | unsigned char d[16]; | 155 | unsigned char d[16]; |
161 | EVP_MD_CTX md; | 156 | EVP_MD_CTX md; |
162 | EVP_DigestInit(&md, EVP_md5()); | 157 | EVP_DigestInit(&md, EVP_md5()); |
163 | EVP_DigestUpdate(&md, buf, len); | 158 | EVP_DigestUpdate(&md, blob, len); |
164 | EVP_DigestFinal(&md, d, NULL); | 159 | EVP_DigestFinal(&md, d, NULL); |
165 | snprintf(retval, sizeof(retval), FPRINT, | 160 | snprintf(retval, sizeof(retval), FPRINT, |
166 | d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], | 161 | d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], |
167 | d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); | 162 | d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); |
168 | memset(buf, 0, len); | 163 | memset(blob, 0, len); |
169 | xfree(buf); | 164 | xfree(blob); |
170 | } | 165 | } |
171 | return retval; | 166 | return retval; |
172 | } | 167 | } |
@@ -226,13 +221,27 @@ write_bignum(FILE *f, BIGNUM *num) | |||
226 | free(buf); | 221 | free(buf); |
227 | return 1; | 222 | return 1; |
228 | } | 223 | } |
229 | int | 224 | unsigned int |
230 | key_read(Key *ret, unsigned int bits, char **cpp) | 225 | key_read(Key *ret, char **cpp) |
231 | { | 226 | { |
227 | Key *k; | ||
228 | unsigned int bits = 0; | ||
229 | char *cp; | ||
230 | int len, n; | ||
231 | unsigned char *blob; | ||
232 | |||
233 | cp = *cpp; | ||
234 | |||
232 | switch(ret->type) { | 235 | switch(ret->type) { |
233 | case KEY_RSA: | 236 | case KEY_RSA: |
237 | /* Get number of bits. */ | ||
238 | if (*cp < '0' || *cp > '9') | ||
239 | return 0; /* Bad bit count... */ | ||
240 | for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) | ||
241 | bits = 10 * bits + *cp - '0'; | ||
234 | if (bits == 0) | 242 | if (bits == 0) |
235 | return 0; | 243 | return 0; |
244 | *cpp = cp; | ||
236 | /* Get public exponent, public modulus. */ | 245 | /* Get public exponent, public modulus. */ |
237 | if (!read_bignum(cpp, ret->rsa->e)) | 246 | if (!read_bignum(cpp, ret->rsa->e)) |
238 | return 0; | 247 | return 0; |
@@ -240,22 +249,32 @@ key_read(Key *ret, unsigned int bits, char **cpp) | |||
240 | return 0; | 249 | return 0; |
241 | break; | 250 | break; |
242 | case KEY_DSA: | 251 | case KEY_DSA: |
243 | if (bits != 0) | 252 | if (strncmp(cp, SSH_DSS " ", 7) != 0) |
244 | return 0; | ||
245 | if (!read_bignum(cpp, ret->dsa->p)) | ||
246 | return 0; | 253 | return 0; |
247 | if (!read_bignum(cpp, ret->dsa->q)) | 254 | cp += 7; |
248 | return 0; | 255 | len = 2*strlen(cp); |
249 | if (!read_bignum(cpp, ret->dsa->g)) | 256 | blob = xmalloc(len); |
250 | return 0; | 257 | n = uudecode(cp, blob, len); |
251 | if (!read_bignum(cpp, ret->dsa->pub_key)) | 258 | k = dsa_key_from_blob(blob, n); |
259 | if (k == NULL) | ||
260 | return 0; | ||
261 | xfree(blob); | ||
262 | if (ret->dsa != NULL) | ||
263 | DSA_free(ret->dsa); | ||
264 | ret->dsa = k->dsa; | ||
265 | k->dsa = NULL; | ||
266 | key_free(k); | ||
267 | bits = BN_num_bits(ret->dsa->p); | ||
268 | cp = strchr(cp, '='); | ||
269 | if (cp == NULL) | ||
252 | return 0; | 270 | return 0; |
271 | *cpp = cp + 1; | ||
253 | break; | 272 | break; |
254 | default: | 273 | default: |
255 | fatal("bad key type: %d", ret->type); | 274 | fatal("key_read: bad key type: %d", ret->type); |
256 | break; | 275 | break; |
257 | } | 276 | } |
258 | return 1; | 277 | return bits; |
259 | } | 278 | } |
260 | int | 279 | int |
261 | key_write(Key *key, FILE *f) | 280 | key_write(Key *key, FILE *f) |
@@ -274,17 +293,15 @@ key_write(Key *key, FILE *f) | |||
274 | error("key_write: failed for RSA key"); | 293 | error("key_write: failed for RSA key"); |
275 | } | 294 | } |
276 | } else if (key->type == KEY_DSA && key->dsa != NULL) { | 295 | } else if (key->type == KEY_DSA && key->dsa != NULL) { |
277 | /* bits == 0 means DSA key */ | 296 | int len, n; |
278 | bits = 0; | 297 | unsigned char *blob, *uu; |
279 | fprintf(f, "%u", bits); | 298 | dsa_make_key_blob(key, &blob, &len); |
280 | if (write_bignum(f, key->dsa->p) && | 299 | uu = xmalloc(2*len); |
281 | write_bignum(f, key->dsa->q) && | 300 | n = uuencode(blob, len, uu); |
282 | write_bignum(f, key->dsa->g) && | 301 | fprintf(f, "%s %s", SSH_DSS, uu); |
283 | write_bignum(f, key->dsa->pub_key)) { | 302 | xfree(blob); |
284 | success = 1; | 303 | xfree(uu); |
285 | } else { | 304 | success = 1; |
286 | error("key_write: failed for DSA key"); | ||
287 | } | ||
288 | } | 305 | } |
289 | return success; | 306 | return success; |
290 | } | 307 | } |
@@ -18,6 +18,7 @@ void key_free(Key *k); | |||
18 | int key_equal(Key *a, Key *b); | 18 | int key_equal(Key *a, Key *b); |
19 | char *key_fingerprint(Key *k); | 19 | char *key_fingerprint(Key *k); |
20 | int key_write(Key *key, FILE *f); | 20 | int key_write(Key *key, FILE *f); |
21 | int key_read(Key *key, unsigned int bits, char **cpp); | 21 | unsigned int |
22 | key_read(Key *key, char **cpp); | ||
22 | 23 | ||
23 | #endif | 24 | #endif |
@@ -1,109 +1,15 @@ | |||
1 | /* | 1 | /* |
2 | * radix.c | 2 | * radix.c |
3 | * | 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> | 4 | * Dug Song <dugsong@UMICH.EDU> |
9 | */ | 5 | */ |
10 | 6 | ||
11 | #include "includes.h" | 7 | #include "includes.h" |
8 | #include "uuencode.h" | ||
12 | 9 | ||
13 | #ifdef AFS | 10 | #ifdef AFS |
14 | #include <krb.h> | 11 | #include <krb.h> |
15 | 12 | ||
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 | ||
27 | uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) | ||
28 | { | ||
29 | /* ENC is the basic 1 character encoding function to make a char printing */ | ||
30 | #define ENC(c) six2pr[c] | ||
31 | |||
32 | register char *outptr = bufcoded; | ||
33 | unsigned int i; | ||
34 | |||
35 | for (i = 0; i < nbytes; i += 3) { | ||
36 | *(outptr++) = ENC(*bufin >> 2); /* c1 */ | ||
37 | *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ | ||
38 | *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ | ||
39 | *(outptr++) = ENC(bufin[2] & 077); /* c4 */ | ||
40 | bufin += 3; | ||
41 | } | ||
42 | if (i == nbytes + 1) { | ||
43 | outptr[-1] = '='; | ||
44 | } else if (i == nbytes + 2) { | ||
45 | outptr[-1] = '='; | ||
46 | outptr[-2] = '='; | ||
47 | } | ||
48 | *outptr = '\0'; | ||
49 | return (outptr - bufcoded); | ||
50 | } | ||
51 | |||
52 | int | ||
53 | uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) | ||
54 | { | ||
55 | /* single character decode */ | ||
56 | #define DEC(c) pr2six[(unsigned char)c] | ||
57 | #define MAXVAL 63 | ||
58 | |||
59 | static int first = 1; | ||
60 | int nbytesdecoded, j; | ||
61 | const char *bufin = bufcoded; | ||
62 | register unsigned char *bufout = bufplain; | ||
63 | register int nprbytes; | ||
64 | |||
65 | /* If this is the first call, initialize the mapping table. */ | ||
66 | if (first) { | ||
67 | first = 0; | ||
68 | for (j = 0; j < 256; j++) | ||
69 | pr2six[j] = MAXVAL + 1; | ||
70 | for (j = 0; j < 64; j++) | ||
71 | pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; | ||
72 | } | ||
73 | /* Strip leading whitespace. */ | ||
74 | while (*bufcoded == ' ' || *bufcoded == '\t') | ||
75 | bufcoded++; | ||
76 | |||
77 | /* | ||
78 | * Figure out how many characters are in the input buffer. If this | ||
79 | * would decode into more bytes than would fit into the output | ||
80 | * buffer, adjust the number of input bytes downwards. | ||
81 | */ | ||
82 | bufin = bufcoded; | ||
83 | while (DEC(*(bufin++)) <= MAXVAL); | ||
84 | nprbytes = bufin - bufcoded - 1; | ||
85 | nbytesdecoded = ((nprbytes + 3) / 4) * 3; | ||
86 | if (nbytesdecoded > outbufsize) | ||
87 | nprbytes = (outbufsize * 4) / 3; | ||
88 | |||
89 | bufin = bufcoded; | ||
90 | |||
91 | while (nprbytes > 0) { | ||
92 | *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); | ||
93 | *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); | ||
94 | *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); | ||
95 | bufin += 4; | ||
96 | nprbytes -= 4; | ||
97 | } | ||
98 | if (nprbytes & 03) { | ||
99 | if (DEC(bufin[-2]) > MAXVAL) | ||
100 | nbytesdecoded -= 2; | ||
101 | else | ||
102 | nbytesdecoded -= 1; | ||
103 | } | ||
104 | return (nbytesdecoded); | ||
105 | } | ||
106 | |||
107 | typedef unsigned char my_u_char; | 13 | typedef unsigned char my_u_char; |
108 | typedef unsigned int my_u_int32_t; | 14 | typedef unsigned int my_u_int32_t; |
109 | typedef unsigned short my_u_short; | 15 | typedef unsigned short my_u_short; |
diff --git a/readconf.c b/readconf.c index 3b75290f3..529f8039b 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -14,7 +14,7 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: readconf.c,v 1.11 2000/04/16 01:18:44 damien Exp $"); | 17 | RCSID("$Id: readconf.c,v 1.12 2000/04/29 13:57:11 damien Exp $"); |
18 | 18 | ||
19 | #include "ssh.h" | 19 | #include "ssh.h" |
20 | #include "cipher.h" | 20 | #include "cipher.h" |
@@ -104,7 +104,8 @@ typedef enum { | |||
104 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, | 104 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, |
105 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, | 105 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, |
106 | oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, | 106 | oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, |
107 | oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol | 107 | oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oIdentityFile2, |
108 | oGlobalKnownHostsFile2, oUserKnownHostsFile2 | ||
108 | } OpCodes; | 109 | } OpCodes; |
109 | 110 | ||
110 | /* Textual representations of the tokens. */ | 111 | /* Textual representations of the tokens. */ |
@@ -131,6 +132,7 @@ static struct { | |||
131 | { "fallbacktorsh", oFallBackToRsh }, | 132 | { "fallbacktorsh", oFallBackToRsh }, |
132 | { "usersh", oUseRsh }, | 133 | { "usersh", oUseRsh }, |
133 | { "identityfile", oIdentityFile }, | 134 | { "identityfile", oIdentityFile }, |
135 | { "identityfile2", oIdentityFile2 }, | ||
134 | { "hostname", oHostName }, | 136 | { "hostname", oHostName }, |
135 | { "proxycommand", oProxyCommand }, | 137 | { "proxycommand", oProxyCommand }, |
136 | { "port", oPort }, | 138 | { "port", oPort }, |
@@ -145,6 +147,8 @@ static struct { | |||
145 | { "rhostsrsaauthentication", oRhostsRSAAuthentication }, | 147 | { "rhostsrsaauthentication", oRhostsRSAAuthentication }, |
146 | { "globalknownhostsfile", oGlobalKnownHostsFile }, | 148 | { "globalknownhostsfile", oGlobalKnownHostsFile }, |
147 | { "userknownhostsfile", oUserKnownHostsFile }, | 149 | { "userknownhostsfile", oUserKnownHostsFile }, |
150 | { "globalknownhostsfile2", oGlobalKnownHostsFile2 }, | ||
151 | { "userknownhostsfile2", oUserKnownHostsFile2 }, | ||
148 | { "connectionattempts", oConnectionAttempts }, | 152 | { "connectionattempts", oConnectionAttempts }, |
149 | { "batchmode", oBatchMode }, | 153 | { "batchmode", oBatchMode }, |
150 | { "checkhostip", oCheckHostIP }, | 154 | { "checkhostip", oCheckHostIP }, |
@@ -368,14 +372,22 @@ parse_flag: | |||
368 | goto parse_int; | 372 | goto parse_int; |
369 | 373 | ||
370 | case oIdentityFile: | 374 | case oIdentityFile: |
375 | case oIdentityFile2: | ||
371 | cp = strtok(NULL, WHITESPACE); | 376 | cp = strtok(NULL, WHITESPACE); |
372 | if (!cp) | 377 | if (!cp) |
373 | fatal("%.200s line %d: Missing argument.", filename, linenum); | 378 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
374 | if (*activep) { | 379 | if (*activep) { |
375 | if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) | 380 | intptr = (opcode == oIdentityFile) ? |
381 | &options->num_identity_files : | ||
382 | &options->num_identity_files2; | ||
383 | if (*intptr >= SSH_MAX_IDENTITY_FILES) | ||
376 | fatal("%.200s line %d: Too many identity files specified (max %d).", | 384 | fatal("%.200s line %d: Too many identity files specified (max %d).", |
377 | filename, linenum, SSH_MAX_IDENTITY_FILES); | 385 | filename, linenum, SSH_MAX_IDENTITY_FILES); |
378 | options->identity_files[options->num_identity_files++] = xstrdup(cp); | 386 | charptr = (opcode == oIdentityFile) ? |
387 | &options->identity_files[*intptr] : | ||
388 | &options->identity_files2[*intptr]; | ||
389 | *charptr = xstrdup(cp); | ||
390 | *intptr = *intptr + 1; | ||
379 | } | 391 | } |
380 | break; | 392 | break; |
381 | 393 | ||
@@ -397,6 +409,14 @@ parse_string: | |||
397 | charptr = &options->user_hostfile; | 409 | charptr = &options->user_hostfile; |
398 | goto parse_string; | 410 | goto parse_string; |
399 | 411 | ||
412 | case oGlobalKnownHostsFile2: | ||
413 | charptr = &options->system_hostfile2; | ||
414 | goto parse_string; | ||
415 | |||
416 | case oUserKnownHostsFile2: | ||
417 | charptr = &options->user_hostfile2; | ||
418 | goto parse_string; | ||
419 | |||
400 | case oHostName: | 420 | case oHostName: |
401 | charptr = &options->hostname; | 421 | charptr = &options->hostname; |
402 | goto parse_string; | 422 | goto parse_string; |
@@ -642,12 +662,15 @@ initialize_options(Options * options) | |||
642 | options->ciphers = NULL; | 662 | options->ciphers = NULL; |
643 | options->protocol = SSH_PROTO_UNKNOWN; | 663 | options->protocol = SSH_PROTO_UNKNOWN; |
644 | options->num_identity_files = 0; | 664 | options->num_identity_files = 0; |
665 | options->num_identity_files2 = 0; | ||
645 | options->hostname = NULL; | 666 | options->hostname = NULL; |
646 | options->proxy_command = NULL; | 667 | options->proxy_command = NULL; |
647 | options->user = NULL; | 668 | options->user = NULL; |
648 | options->escape_char = -1; | 669 | options->escape_char = -1; |
649 | options->system_hostfile = NULL; | 670 | options->system_hostfile = NULL; |
650 | options->user_hostfile = NULL; | 671 | options->user_hostfile = NULL; |
672 | options->system_hostfile2 = NULL; | ||
673 | options->user_hostfile2 = NULL; | ||
651 | options->num_local_forwards = 0; | 674 | options->num_local_forwards = 0; |
652 | options->num_remote_forwards = 0; | 675 | options->num_remote_forwards = 0; |
653 | options->log_level = (LogLevel) - 1; | 676 | options->log_level = (LogLevel) - 1; |
@@ -715,19 +738,31 @@ fill_default_options(Options * options) | |||
715 | if (options->cipher == -1) | 738 | if (options->cipher == -1) |
716 | options->cipher = SSH_CIPHER_NOT_SET; | 739 | options->cipher = SSH_CIPHER_NOT_SET; |
717 | if (options->protocol == SSH_PROTO_UNKNOWN) | 740 | if (options->protocol == SSH_PROTO_UNKNOWN) |
718 | options->protocol = SSH_PROTO_1; | 741 | options->protocol = SSH_PROTO_1|SSH_PROTO_2|SSH_PROTO_1_PREFERRED; |
719 | if (options->num_identity_files == 0) { | 742 | if (options->num_identity_files == 0) { |
720 | options->identity_files[0] = | 743 | options->identity_files[0] = |
721 | xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); | 744 | xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); |
722 | sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); | 745 | sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); |
723 | options->num_identity_files = 1; | 746 | options->num_identity_files = 1; |
724 | } | 747 | } |
748 | #if 0 | ||
749 | if (options->num_identity_files2 == 0) { | ||
750 | options->identity_files2[0] = | ||
751 | xmalloc(2 + strlen(SSH2_CLIENT_IDENTITY) + 1); | ||
752 | sprintf(options->identity_files2[0], "~/%.100s", SSH2_CLIENT_IDENTITY); | ||
753 | options->num_identity_files2 = 1; | ||
754 | } | ||
755 | #endif | ||
725 | if (options->escape_char == -1) | 756 | if (options->escape_char == -1) |
726 | options->escape_char = '~'; | 757 | options->escape_char = '~'; |
727 | if (options->system_hostfile == NULL) | 758 | if (options->system_hostfile == NULL) |
728 | options->system_hostfile = SSH_SYSTEM_HOSTFILE; | 759 | options->system_hostfile = SSH_SYSTEM_HOSTFILE; |
729 | if (options->user_hostfile == NULL) | 760 | if (options->user_hostfile == NULL) |
730 | options->user_hostfile = SSH_USER_HOSTFILE; | 761 | options->user_hostfile = SSH_USER_HOSTFILE; |
762 | if (options->system_hostfile2 == NULL) | ||
763 | options->system_hostfile2 = SSH_SYSTEM_HOSTFILE2; | ||
764 | if (options->user_hostfile2 == NULL) | ||
765 | options->user_hostfile2 = SSH_USER_HOSTFILE2; | ||
731 | if (options->log_level == (LogLevel) - 1) | 766 | if (options->log_level == (LogLevel) - 1) |
732 | options->log_level = SYSLOG_LEVEL_INFO; | 767 | options->log_level = SYSLOG_LEVEL_INFO; |
733 | /* options->proxy_command should not be set by default */ | 768 | /* options->proxy_command should not be set by default */ |
diff --git a/readconf.h b/readconf.h index 0582a8f2e..bbef923ab 100644 --- a/readconf.h +++ b/readconf.h | |||
@@ -13,7 +13,7 @@ | |||
13 | * | 13 | * |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: readconf.h,v 1.8 2000/04/16 01:18:44 damien Exp $"); */ | 16 | /* RCSID("$Id: readconf.h,v 1.9 2000/04/29 13:57:11 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef READCONF_H | 18 | #ifndef READCONF_H |
19 | #define READCONF_H | 19 | #define READCONF_H |
@@ -73,9 +73,13 @@ typedef struct { | |||
73 | 73 | ||
74 | char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ | 74 | char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ |
75 | char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ | 75 | char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ |
76 | char *system_hostfile2; | ||
77 | char *user_hostfile2; | ||
76 | 78 | ||
77 | int num_identity_files; /* Number of files for RSA identities. */ | 79 | int num_identity_files; /* Number of files for RSA identities. */ |
80 | int num_identity_files2; /* DSA identities. */ | ||
78 | char *identity_files[SSH_MAX_IDENTITY_FILES]; | 81 | char *identity_files[SSH_MAX_IDENTITY_FILES]; |
82 | char *identity_files2[SSH_MAX_IDENTITY_FILES]; | ||
79 | 83 | ||
80 | /* Local TCP/IP forward requests. */ | 84 | /* Local TCP/IP forward requests. */ |
81 | int num_local_forwards; | 85 | int num_local_forwards; |
diff --git a/servconf.c b/servconf.c index fe72d2757..298fefbe2 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -12,7 +12,7 @@ | |||
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include "includes.h" | 14 | #include "includes.h" |
15 | RCSID("$Id: servconf.c,v 1.12 2000/04/16 01:18:45 damien Exp $"); | 15 | RCSID("$Id: servconf.c,v 1.13 2000/04/29 13:57:11 damien Exp $"); |
16 | 16 | ||
17 | #include "ssh.h" | 17 | #include "ssh.h" |
18 | #include "servconf.h" | 18 | #include "servconf.h" |
@@ -143,7 +143,7 @@ fill_default_server_options(ServerOptions *options) | |||
143 | if (options->use_login == -1) | 143 | if (options->use_login == -1) |
144 | options->use_login = 0; | 144 | options->use_login = 0; |
145 | if (options->protocol == SSH_PROTO_UNKNOWN) | 145 | if (options->protocol == SSH_PROTO_UNKNOWN) |
146 | options->protocol = SSH_PROTO_1; | 146 | options->protocol = SSH_PROTO_1|SSH_PROTO_2; |
147 | } | 147 | } |
148 | 148 | ||
149 | #define WHITESPACE " \t\r\n" | 149 | #define WHITESPACE " \t\r\n" |
diff --git a/serverloop.c b/serverloop.c index 1a76b8da8..1e031873c 100644 --- a/serverloop.c +++ b/serverloop.c | |||
@@ -733,7 +733,7 @@ server_input_channel_open(int type, int plen) | |||
733 | rwindow = packet_get_int(); | 733 | rwindow = packet_get_int(); |
734 | rmaxpack = packet_get_int(); | 734 | rmaxpack = packet_get_int(); |
735 | 735 | ||
736 | log("channel_input_open: ctype %s rchan %d win %d max %d", | 736 | debug("channel_input_open: ctype %s rchan %d win %d max %d", |
737 | ctype, rchan, rwindow, rmaxpack); | 737 | ctype, rchan, rwindow, rmaxpack); |
738 | 738 | ||
739 | if (strcmp(ctype, "session") == 0) { | 739 | if (strcmp(ctype, "session") == 0) { |
@@ -8,7 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include "includes.h" | 10 | #include "includes.h" |
11 | RCSID("$OpenBSD: session.c,v 1.5 2000/04/19 09:24:39 markus Exp $"); | 11 | RCSID("$OpenBSD: session.c,v 1.6 2000/04/27 15:23:02 markus Exp $"); |
12 | 12 | ||
13 | #include "xmalloc.h" | 13 | #include "xmalloc.h" |
14 | #include "ssh.h" | 14 | #include "ssh.h" |
@@ -1474,6 +1474,5 @@ do_authenticated2(void) | |||
1474 | * authentication. | 1474 | * authentication. |
1475 | */ | 1475 | */ |
1476 | alarm(0); | 1476 | alarm(0); |
1477 | log("do_authenticated2"); | ||
1478 | server_loop2(); | 1477 | server_loop2(); |
1479 | } | 1478 | } |
@@ -7,13 +7,18 @@ | |||
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "includes.h" | 9 | #include "includes.h" |
10 | RCSID("$Id: ssh-add.c,v 1.16 1999/12/06 00:47:29 damien Exp $"); | 10 | RCSID("$Id: ssh-add.c,v 1.17 2000/04/29 13:57:12 damien Exp $"); |
11 | |||
12 | #include <openssl/rsa.h> | ||
13 | #include <openssl/dsa.h> | ||
11 | 14 | ||
12 | #include "rsa.h" | 15 | #include "rsa.h" |
13 | #include "ssh.h" | 16 | #include "ssh.h" |
14 | #include "xmalloc.h" | 17 | #include "xmalloc.h" |
15 | #include "authfd.h" | 18 | #include "authfd.h" |
16 | #include "fingerprint.h" | 19 | #include "fingerprint.h" |
20 | #include "key.h" | ||
21 | #include "authfile.h" | ||
17 | 22 | ||
18 | #ifdef HAVE___PROGNAME | 23 | #ifdef HAVE___PROGNAME |
19 | extern char *__progname; | 24 | extern char *__progname; |
@@ -24,19 +29,19 @@ const char *__progname = "ssh-add"; | |||
24 | void | 29 | void |
25 | delete_file(AuthenticationConnection *ac, const char *filename) | 30 | delete_file(AuthenticationConnection *ac, const char *filename) |
26 | { | 31 | { |
27 | RSA *key; | 32 | Key *public; |
28 | char *comment; | 33 | char *comment; |
29 | 34 | ||
30 | key = RSA_new(); | 35 | public = key_new(KEY_RSA); |
31 | if (!load_public_key(filename, key, &comment)) { | 36 | if (!load_public_key(filename, public, &comment)) { |
32 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | 37 | printf("Bad key file %s: %s\n", filename, strerror(errno)); |
33 | return; | 38 | return; |
34 | } | 39 | } |
35 | if (ssh_remove_identity(ac, key)) | 40 | if (ssh_remove_identity(ac, public->rsa)) |
36 | fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); | 41 | fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); |
37 | else | 42 | else |
38 | fprintf(stderr, "Could not remove identity: %s\n", filename); | 43 | fprintf(stderr, "Could not remove identity: %s\n", filename); |
39 | RSA_free(key); | 44 | key_free(public); |
40 | xfree(comment); | 45 | xfree(comment); |
41 | } | 46 | } |
42 | 47 | ||
@@ -91,20 +96,19 @@ ssh_askpass(char *askpass, char *msg) | |||
91 | void | 96 | void |
92 | add_file(AuthenticationConnection *ac, const char *filename) | 97 | add_file(AuthenticationConnection *ac, const char *filename) |
93 | { | 98 | { |
94 | RSA *key; | 99 | Key *public; |
95 | RSA *public_key; | 100 | Key *private; |
96 | char *saved_comment, *comment, *askpass = NULL; | 101 | char *saved_comment, *comment, *askpass = NULL; |
97 | char buf[1024], msg[1024]; | 102 | char buf[1024], msg[1024]; |
98 | int success; | 103 | int success; |
99 | int interactive = isatty(STDIN_FILENO); | 104 | int interactive = isatty(STDIN_FILENO); |
100 | 105 | ||
101 | key = RSA_new(); | 106 | public = key_new(KEY_RSA); |
102 | public_key = RSA_new(); | 107 | if (!load_public_key(filename, public, &saved_comment)) { |
103 | if (!load_public_key(filename, public_key, &saved_comment)) { | ||
104 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | 108 | printf("Bad key file %s: %s\n", filename, strerror(errno)); |
105 | return; | 109 | return; |
106 | } | 110 | } |
107 | RSA_free(public_key); | 111 | key_free(public); |
108 | 112 | ||
109 | if (!interactive && getenv("DISPLAY")) { | 113 | if (!interactive && getenv("DISPLAY")) { |
110 | if (getenv(SSH_ASKPASS_ENV)) | 114 | if (getenv(SSH_ASKPASS_ENV)) |
@@ -114,7 +118,8 @@ add_file(AuthenticationConnection *ac, const char *filename) | |||
114 | } | 118 | } |
115 | 119 | ||
116 | /* At first, try empty passphrase */ | 120 | /* At first, try empty passphrase */ |
117 | success = load_private_key(filename, "", key, &comment); | 121 | private = key_new(KEY_RSA); |
122 | success = load_private_key(filename, "", private, &comment); | ||
118 | if (!success) { | 123 | if (!success) { |
119 | printf("Need passphrase for %.200s\n", filename); | 124 | printf("Need passphrase for %.200s\n", filename); |
120 | if (!interactive && askpass == NULL) { | 125 | if (!interactive && askpass == NULL) { |
@@ -135,7 +140,7 @@ add_file(AuthenticationConnection *ac, const char *filename) | |||
135 | xfree(saved_comment); | 140 | xfree(saved_comment); |
136 | return; | 141 | return; |
137 | } | 142 | } |
138 | success = load_private_key(filename, pass, key, &comment); | 143 | success = load_private_key(filename, pass, private, &comment); |
139 | memset(pass, 0, strlen(pass)); | 144 | memset(pass, 0, strlen(pass)); |
140 | xfree(pass); | 145 | xfree(pass); |
141 | if (success) | 146 | if (success) |
@@ -145,11 +150,11 @@ add_file(AuthenticationConnection *ac, const char *filename) | |||
145 | } | 150 | } |
146 | xfree(saved_comment); | 151 | xfree(saved_comment); |
147 | 152 | ||
148 | if (ssh_add_identity(ac, key, comment)) | 153 | if (ssh_add_identity(ac, private->rsa, comment)) |
149 | fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | 154 | fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); |
150 | else | 155 | else |
151 | fprintf(stderr, "Could not add identity: %s\n", filename); | 156 | fprintf(stderr, "Could not add identity: %s\n", filename); |
152 | RSA_free(key); | 157 | key_free(private); |
153 | xfree(comment); | 158 | xfree(comment); |
154 | } | 159 | } |
155 | 160 | ||
diff --git a/ssh-keygen.1 b/ssh-keygen.1 index b809a1a5e..486de0421 100644 --- a/ssh-keygen.1 +++ b/ssh-keygen.1 | |||
@@ -9,7 +9,7 @@ | |||
9 | .\" | 9 | .\" |
10 | .\" Created: Sat Apr 22 23:55:14 1995 ylo | 10 | .\" Created: Sat Apr 22 23:55:14 1995 ylo |
11 | .\" | 11 | .\" |
12 | .\" $Id: ssh-keygen.1,v 1.12 2000/04/20 13:27:27 damien Exp $ | 12 | .\" $Id: ssh-keygen.1,v 1.13 2000/04/29 13:57:12 damien Exp $ |
13 | .\" | 13 | .\" |
14 | .Dd September 25, 1999 | 14 | .Dd September 25, 1999 |
15 | .Dt SSH-KEYGEN 1 | 15 | .Dt SSH-KEYGEN 1 |
@@ -37,6 +37,8 @@ | |||
37 | .Nm ssh-keygen | 37 | .Nm ssh-keygen |
38 | .Fl l | 38 | .Fl l |
39 | .Op Fl f Ar keyfile | 39 | .Op Fl f Ar keyfile |
40 | .Nm ssh-keygen | ||
41 | .Fl R | ||
40 | .Sh DESCRIPTION | 42 | .Sh DESCRIPTION |
41 | .Nm | 43 | .Nm |
42 | generates and manages authentication keys for | 44 | generates and manages authentication keys for |
@@ -112,6 +114,10 @@ Provides the new comment. | |||
112 | Provides the new passphrase. | 114 | Provides the new passphrase. |
113 | .It Fl P Ar passphrase | 115 | .It Fl P Ar passphrase |
114 | Provides the (old) passphrase. | 116 | Provides the (old) passphrase. |
117 | .It Fl R | ||
118 | If RSA support is functional, immediately exits with code 0. If RSA | ||
119 | support is not functional, exits with code 1. This flag will be | ||
120 | removed once the RSA patent expires. | ||
115 | .El | 121 | .El |
116 | .Sh FILES | 122 | .Sh FILES |
117 | .Bl -tag -width Ds | 123 | .Bl -tag -width Ds |
diff --git a/ssh-keygen.c b/ssh-keygen.c index f2484a4b1..0155949fd 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -7,20 +7,23 @@ | |||
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include "includes.h" | 9 | #include "includes.h" |
10 | RCSID("$Id: ssh-keygen.c,v 1.13 2000/04/16 01:18:46 damien Exp $"); | 10 | RCSID("$Id: ssh-keygen.c,v 1.14 2000/04/29 13:57:12 damien Exp $"); |
11 | |||
12 | #include <openssl/evp.h> | ||
13 | #include <openssl/pem.h> | ||
14 | #include <openssl/rsa.h> | ||
15 | #include <openssl/dsa.h> | ||
11 | 16 | ||
12 | #include "rsa.h" | ||
13 | #include "ssh.h" | 17 | #include "ssh.h" |
14 | #include "xmalloc.h" | 18 | #include "xmalloc.h" |
15 | #include "fingerprint.h" | 19 | #include "fingerprint.h" |
20 | #include "key.h" | ||
21 | #include "rsa.h" | ||
22 | #include "dsa.h" | ||
23 | #include "authfile.h" | ||
24 | #include "uuencode.h" | ||
16 | 25 | ||
17 | /* Generated private key. */ | 26 | /* Number of bits in the RSA/DSA key. This value can be changed on the command line. */ |
18 | RSA *private_key; | ||
19 | |||
20 | /* Generated public key. */ | ||
21 | RSA *public_key; | ||
22 | |||
23 | /* Number of bits in the RSA key. This value can be changed on the command line. */ | ||
24 | int bits = 1024; | 27 | int bits = 1024; |
25 | 28 | ||
26 | /* | 29 | /* |
@@ -53,6 +56,12 @@ char *identity_new_passphrase = NULL; | |||
53 | /* This is set to the new comment if given on the command line. */ | 56 | /* This is set to the new comment if given on the command line. */ |
54 | char *identity_comment = NULL; | 57 | char *identity_comment = NULL; |
55 | 58 | ||
59 | /* Dump public key file in format used by real and the original SSH 2 */ | ||
60 | int convert_to_ssh2 = 0; | ||
61 | int convert_from_ssh2 = 0; | ||
62 | int print_public = 0; | ||
63 | int dsa_mode = 0; | ||
64 | |||
56 | /* argv0 */ | 65 | /* argv0 */ |
57 | #ifdef HAVE___PROGNAME | 66 | #ifdef HAVE___PROGNAME |
58 | extern char *__progname; | 67 | extern char *__progname; |
@@ -60,6 +69,8 @@ extern char *__progname; | |||
60 | const char *__progname = "ssh-keygen"; | 69 | const char *__progname = "ssh-keygen"; |
61 | #endif /* HAVE___PROGNAME */ | 70 | #endif /* HAVE___PROGNAME */ |
62 | 71 | ||
72 | char hostname[MAXHOSTNAMELEN]; | ||
73 | |||
63 | void | 74 | void |
64 | ask_filename(struct passwd *pw, const char *prompt) | 75 | ask_filename(struct passwd *pw, const char *prompt) |
65 | { | 76 | { |
@@ -77,12 +88,140 @@ ask_filename(struct passwd *pw, const char *prompt) | |||
77 | have_identity = 1; | 88 | have_identity = 1; |
78 | } | 89 | } |
79 | 90 | ||
91 | int | ||
92 | try_load_key(char *filename, Key *k) | ||
93 | { | ||
94 | int success = 1; | ||
95 | if (!load_private_key(filename, "", k, NULL)) { | ||
96 | char *pass = read_passphrase("Enter passphrase: ", 1); | ||
97 | if (!load_private_key(filename, pass, k, NULL)) { | ||
98 | success = 0; | ||
99 | } | ||
100 | memset(pass, 0, strlen(pass)); | ||
101 | xfree(pass); | ||
102 | } | ||
103 | return success; | ||
104 | } | ||
105 | |||
106 | #define SSH_COM_MAGIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" | ||
107 | #define SSH_COM_MAGIC_END "---- END SSH2 PUBLIC KEY ----" | ||
108 | |||
109 | void | ||
110 | do_convert_to_ssh2(struct passwd *pw) | ||
111 | { | ||
112 | Key *k; | ||
113 | int len; | ||
114 | unsigned char *blob; | ||
115 | struct stat st; | ||
116 | |||
117 | if (!have_identity) | ||
118 | ask_filename(pw, "Enter file in which the key is"); | ||
119 | if (stat(identity_file, &st) < 0) { | ||
120 | perror(identity_file); | ||
121 | exit(1); | ||
122 | } | ||
123 | k = key_new(KEY_DSA); | ||
124 | if (!try_load_key(identity_file, k)) { | ||
125 | fprintf(stderr, "load failed\n"); | ||
126 | exit(1); | ||
127 | } | ||
128 | dsa_make_key_blob(k, &blob, &len); | ||
129 | fprintf(stdout, SSH_COM_MAGIC_BEGIN "\n"); | ||
130 | fprintf(stdout, | ||
131 | "Comment: \"%d-bit DSA, converted from openssh by %s@%s\"\n", | ||
132 | BN_num_bits(k->dsa->p), | ||
133 | pw->pw_name, hostname); | ||
134 | dump_base64(stdout, blob, len); | ||
135 | fprintf(stdout, SSH_COM_MAGIC_END "\n"); | ||
136 | key_free(k); | ||
137 | xfree(blob); | ||
138 | exit(0); | ||
139 | } | ||
140 | |||
141 | void | ||
142 | do_convert_from_ssh2(struct passwd *pw) | ||
143 | { | ||
144 | Key *k; | ||
145 | int blen; | ||
146 | char line[1024], *p; | ||
147 | char blob[8096]; | ||
148 | char encoded[8096]; | ||
149 | struct stat st; | ||
150 | FILE *fp; | ||
151 | |||
152 | if (!have_identity) | ||
153 | ask_filename(pw, "Enter file in which the key is"); | ||
154 | if (stat(identity_file, &st) < 0) { | ||
155 | perror(identity_file); | ||
156 | exit(1); | ||
157 | } | ||
158 | fp = fopen(identity_file, "r"); | ||
159 | if (fp == NULL) { | ||
160 | perror(identity_file); | ||
161 | exit(1); | ||
162 | } | ||
163 | encoded[0] = '\0'; | ||
164 | while (fgets(line, sizeof(line), fp)) { | ||
165 | if (strncmp(line, "----", 4) == 0 || | ||
166 | strstr(line, ": ") != NULL) { | ||
167 | fprintf(stderr, "ignore: %s", line); | ||
168 | continue; | ||
169 | } | ||
170 | if (!(p = strchr(line, '\n'))) { | ||
171 | fprintf(stderr, "input line too long.\n"); | ||
172 | exit(1); | ||
173 | } | ||
174 | *p = '\0'; | ||
175 | strlcat(encoded, line, sizeof(encoded)); | ||
176 | } | ||
177 | blen = uudecode(encoded, (unsigned char *)blob, sizeof(blob)); | ||
178 | if (blen < 0) { | ||
179 | fprintf(stderr, "uudecode failed.\n"); | ||
180 | exit(1); | ||
181 | } | ||
182 | k = dsa_key_from_blob(blob, blen); | ||
183 | if (!key_write(k, stdout)) | ||
184 | fprintf(stderr, "key_write failed"); | ||
185 | key_free(k); | ||
186 | fprintf(stdout, "\n"); | ||
187 | fclose(fp); | ||
188 | exit(0); | ||
189 | } | ||
190 | |||
191 | void | ||
192 | do_print_public(struct passwd *pw) | ||
193 | { | ||
194 | Key *k; | ||
195 | int len; | ||
196 | unsigned char *blob; | ||
197 | struct stat st; | ||
198 | |||
199 | if (!have_identity) | ||
200 | ask_filename(pw, "Enter file in which the key is"); | ||
201 | if (stat(identity_file, &st) < 0) { | ||
202 | perror(identity_file); | ||
203 | exit(1); | ||
204 | } | ||
205 | k = key_new(KEY_DSA); | ||
206 | if (!try_load_key(identity_file, k)) { | ||
207 | fprintf(stderr, "load failed\n"); | ||
208 | exit(1); | ||
209 | } | ||
210 | dsa_make_key_blob(k, &blob, &len); | ||
211 | if (!key_write(k, stdout)) | ||
212 | fprintf(stderr, "key_write failed"); | ||
213 | key_free(k); | ||
214 | xfree(blob); | ||
215 | fprintf(stdout, "\n"); | ||
216 | exit(0); | ||
217 | } | ||
218 | |||
80 | void | 219 | void |
81 | do_fingerprint(struct passwd *pw) | 220 | do_fingerprint(struct passwd *pw) |
82 | { | 221 | { |
83 | FILE *f; | 222 | FILE *f; |
84 | BIGNUM *e, *n; | 223 | BIGNUM *e, *n; |
85 | RSA *public_key; | 224 | Key *public; |
86 | char *comment = NULL, *cp, *ep, line[16*1024]; | 225 | char *comment = NULL, *cp, *ep, line[16*1024]; |
87 | int i, skip = 0, num = 1, invalid = 1; | 226 | int i, skip = 0, num = 1, invalid = 1; |
88 | unsigned int ignore; | 227 | unsigned int ignore; |
@@ -94,17 +233,16 @@ do_fingerprint(struct passwd *pw) | |||
94 | perror(identity_file); | 233 | perror(identity_file); |
95 | exit(1); | 234 | exit(1); |
96 | } | 235 | } |
97 | 236 | public = key_new(KEY_RSA); | |
98 | public_key = RSA_new(); | 237 | if (load_public_key(identity_file, public, &comment)) { |
99 | if (load_public_key(identity_file, public_key, &comment)) { | 238 | printf("%d %s %s\n", BN_num_bits(public->rsa->n), |
100 | printf("%d %s %s\n", BN_num_bits(public_key->n), | 239 | key_fingerprint(public), comment); |
101 | fingerprint(public_key->e, public_key->n), | 240 | key_free(public); |
102 | comment); | ||
103 | RSA_free(public_key); | ||
104 | exit(0); | 241 | exit(0); |
105 | } | 242 | } |
106 | RSA_free(public_key); | 243 | key_free(public); |
107 | 244 | ||
245 | /* XXX */ | ||
108 | f = fopen(identity_file, "r"); | 246 | f = fopen(identity_file, "r"); |
109 | if (f != NULL) { | 247 | if (f != NULL) { |
110 | n = BN_new(); | 248 | n = BN_new(); |
@@ -172,7 +310,9 @@ do_change_passphrase(struct passwd *pw) | |||
172 | char *comment; | 310 | char *comment; |
173 | char *old_passphrase, *passphrase1, *passphrase2; | 311 | char *old_passphrase, *passphrase1, *passphrase2; |
174 | struct stat st; | 312 | struct stat st; |
175 | RSA *private_key; | 313 | Key *private; |
314 | Key *public; | ||
315 | int type = dsa_mode ? KEY_DSA : KEY_RSA; | ||
176 | 316 | ||
177 | if (!have_identity) | 317 | if (!have_identity) |
178 | ask_filename(pw, "Enter file in which the key is"); | 318 | ask_filename(pw, "Enter file in which the key is"); |
@@ -180,22 +320,26 @@ do_change_passphrase(struct passwd *pw) | |||
180 | perror(identity_file); | 320 | perror(identity_file); |
181 | exit(1); | 321 | exit(1); |
182 | } | 322 | } |
183 | public_key = RSA_new(); | 323 | |
184 | if (!load_public_key(identity_file, public_key, NULL)) { | 324 | if (type == KEY_RSA) { |
185 | printf("%s is not a valid key file.\n", identity_file); | 325 | /* XXX this works currently only for RSA */ |
186 | exit(1); | 326 | public = key_new(type); |
327 | if (!load_public_key(identity_file, public, NULL)) { | ||
328 | printf("%s is not a valid key file.\n", identity_file); | ||
329 | exit(1); | ||
330 | } | ||
331 | /* Clear the public key since we are just about to load the whole file. */ | ||
332 | key_free(public); | ||
187 | } | 333 | } |
188 | /* Clear the public key since we are just about to load the whole file. */ | ||
189 | RSA_free(public_key); | ||
190 | 334 | ||
191 | /* Try to load the file with empty passphrase. */ | 335 | /* Try to load the file with empty passphrase. */ |
192 | private_key = RSA_new(); | 336 | private = key_new(type); |
193 | if (!load_private_key(identity_file, "", private_key, &comment)) { | 337 | if (!load_private_key(identity_file, "", private, &comment)) { |
194 | if (identity_passphrase) | 338 | if (identity_passphrase) |
195 | old_passphrase = xstrdup(identity_passphrase); | 339 | old_passphrase = xstrdup(identity_passphrase); |
196 | else | 340 | else |
197 | old_passphrase = read_passphrase("Enter old passphrase: ", 1); | 341 | old_passphrase = read_passphrase("Enter old passphrase: ", 1); |
198 | if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { | 342 | if (!load_private_key(identity_file, old_passphrase, private, &comment)) { |
199 | memset(old_passphrase, 0, strlen(old_passphrase)); | 343 | memset(old_passphrase, 0, strlen(old_passphrase)); |
200 | xfree(old_passphrase); | 344 | xfree(old_passphrase); |
201 | printf("Bad passphrase.\n"); | 345 | printf("Bad passphrase.\n"); |
@@ -230,19 +374,19 @@ do_change_passphrase(struct passwd *pw) | |||
230 | } | 374 | } |
231 | 375 | ||
232 | /* Save the file using the new passphrase. */ | 376 | /* Save the file using the new passphrase. */ |
233 | if (!save_private_key(identity_file, passphrase1, private_key, comment)) { | 377 | if (!save_private_key(identity_file, passphrase1, private, comment)) { |
234 | printf("Saving the key failed: %s: %s.\n", | 378 | printf("Saving the key failed: %s: %s.\n", |
235 | identity_file, strerror(errno)); | 379 | identity_file, strerror(errno)); |
236 | memset(passphrase1, 0, strlen(passphrase1)); | 380 | memset(passphrase1, 0, strlen(passphrase1)); |
237 | xfree(passphrase1); | 381 | xfree(passphrase1); |
238 | RSA_free(private_key); | 382 | key_free(private); |
239 | xfree(comment); | 383 | xfree(comment); |
240 | exit(1); | 384 | exit(1); |
241 | } | 385 | } |
242 | /* Destroy the passphrase and the copy of the key in memory. */ | 386 | /* Destroy the passphrase and the copy of the key in memory. */ |
243 | memset(passphrase1, 0, strlen(passphrase1)); | 387 | memset(passphrase1, 0, strlen(passphrase1)); |
244 | xfree(passphrase1); | 388 | xfree(passphrase1); |
245 | RSA_free(private_key); /* Destroys contents */ | 389 | key_free(private); /* Destroys contents */ |
246 | xfree(comment); | 390 | xfree(comment); |
247 | 391 | ||
248 | printf("Your identification has been saved with the new passphrase.\n"); | 392 | printf("Your identification has been saved with the new passphrase.\n"); |
@@ -256,11 +400,11 @@ void | |||
256 | do_change_comment(struct passwd *pw) | 400 | do_change_comment(struct passwd *pw) |
257 | { | 401 | { |
258 | char new_comment[1024], *comment; | 402 | char new_comment[1024], *comment; |
259 | RSA *private_key; | 403 | Key *private; |
404 | Key *public; | ||
260 | char *passphrase; | 405 | char *passphrase; |
261 | struct stat st; | 406 | struct stat st; |
262 | FILE *f; | 407 | FILE *f; |
263 | char *tmpbuf; | ||
264 | 408 | ||
265 | if (!have_identity) | 409 | if (!have_identity) |
266 | ask_filename(pw, "Enter file in which the key is"); | 410 | ask_filename(pw, "Enter file in which the key is"); |
@@ -272,14 +416,14 @@ do_change_comment(struct passwd *pw) | |||
272 | * Try to load the public key from the file the verify that it is | 416 | * Try to load the public key from the file the verify that it is |
273 | * readable and of the proper format. | 417 | * readable and of the proper format. |
274 | */ | 418 | */ |
275 | public_key = RSA_new(); | 419 | public = key_new(KEY_RSA); |
276 | if (!load_public_key(identity_file, public_key, NULL)) { | 420 | if (!load_public_key(identity_file, public, NULL)) { |
277 | printf("%s is not a valid key file.\n", identity_file); | 421 | printf("%s is not a valid key file.\n", identity_file); |
278 | exit(1); | 422 | exit(1); |
279 | } | 423 | } |
280 | private_key = RSA_new(); | ||
281 | 424 | ||
282 | if (load_private_key(identity_file, "", private_key, &comment)) | 425 | private = key_new(KEY_RSA); |
426 | if (load_private_key(identity_file, "", private, &comment)) | ||
283 | passphrase = xstrdup(""); | 427 | passphrase = xstrdup(""); |
284 | else { | 428 | else { |
285 | if (identity_passphrase) | 429 | if (identity_passphrase) |
@@ -289,7 +433,7 @@ do_change_comment(struct passwd *pw) | |||
289 | else | 433 | else |
290 | passphrase = read_passphrase("Enter passphrase: ", 1); | 434 | passphrase = read_passphrase("Enter passphrase: ", 1); |
291 | /* Try to load using the passphrase. */ | 435 | /* Try to load using the passphrase. */ |
292 | if (!load_private_key(identity_file, passphrase, private_key, &comment)) { | 436 | if (!load_private_key(identity_file, passphrase, private, &comment)) { |
293 | memset(passphrase, 0, strlen(passphrase)); | 437 | memset(passphrase, 0, strlen(passphrase)); |
294 | xfree(passphrase); | 438 | xfree(passphrase); |
295 | printf("Bad passphrase.\n"); | 439 | printf("Bad passphrase.\n"); |
@@ -305,7 +449,7 @@ do_change_comment(struct passwd *pw) | |||
305 | fflush(stdout); | 449 | fflush(stdout); |
306 | if (!fgets(new_comment, sizeof(new_comment), stdin)) { | 450 | if (!fgets(new_comment, sizeof(new_comment), stdin)) { |
307 | memset(passphrase, 0, strlen(passphrase)); | 451 | memset(passphrase, 0, strlen(passphrase)); |
308 | RSA_free(private_key); | 452 | key_free(private); |
309 | exit(1); | 453 | exit(1); |
310 | } | 454 | } |
311 | if (strchr(new_comment, '\n')) | 455 | if (strchr(new_comment, '\n')) |
@@ -313,18 +457,18 @@ do_change_comment(struct passwd *pw) | |||
313 | } | 457 | } |
314 | 458 | ||
315 | /* Save the file using the new passphrase. */ | 459 | /* Save the file using the new passphrase. */ |
316 | if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { | 460 | if (!save_private_key(identity_file, passphrase, private, new_comment)) { |
317 | printf("Saving the key failed: %s: %s.\n", | 461 | printf("Saving the key failed: %s: %s.\n", |
318 | identity_file, strerror(errno)); | 462 | identity_file, strerror(errno)); |
319 | memset(passphrase, 0, strlen(passphrase)); | 463 | memset(passphrase, 0, strlen(passphrase)); |
320 | xfree(passphrase); | 464 | xfree(passphrase); |
321 | RSA_free(private_key); | 465 | key_free(private); |
322 | xfree(comment); | 466 | xfree(comment); |
323 | exit(1); | 467 | exit(1); |
324 | } | 468 | } |
325 | memset(passphrase, 0, strlen(passphrase)); | 469 | memset(passphrase, 0, strlen(passphrase)); |
326 | xfree(passphrase); | 470 | xfree(passphrase); |
327 | RSA_free(private_key); | 471 | key_free(private); |
328 | 472 | ||
329 | strlcat(identity_file, ".pub", sizeof(identity_file)); | 473 | strlcat(identity_file, ".pub", sizeof(identity_file)); |
330 | f = fopen(identity_file, "w"); | 474 | f = fopen(identity_file, "w"); |
@@ -332,13 +476,10 @@ do_change_comment(struct passwd *pw) | |||
332 | printf("Could not save your public key in %s\n", identity_file); | 476 | printf("Could not save your public key in %s\n", identity_file); |
333 | exit(1); | 477 | exit(1); |
334 | } | 478 | } |
335 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | 479 | if (!key_write(public, f)) |
336 | tmpbuf = BN_bn2dec(public_key->e); | 480 | fprintf(stderr, "write key failed"); |
337 | fprintf(f, "%s ", tmpbuf); | 481 | key_free(public); |
338 | free(tmpbuf); | 482 | fprintf(f, " %s\n", new_comment); |
339 | tmpbuf = BN_bn2dec(public_key->n); | ||
340 | fprintf(f, "%s %s\n", tmpbuf, new_comment); | ||
341 | free(tmpbuf); | ||
342 | fclose(f); | 483 | fclose(f); |
343 | 484 | ||
344 | xfree(comment); | 485 | xfree(comment); |
@@ -351,7 +492,7 @@ void | |||
351 | usage(void) | 492 | usage(void) |
352 | { | 493 | { |
353 | printf("ssh-keygen version %s\n", SSH_VERSION); | 494 | printf("ssh-keygen version %s\n", SSH_VERSION); |
354 | printf("Usage: %s [-b bits] [-p] [-c] [-l] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); | 495 | printf("Usage: %s [-b bits] [-p] [-c] [-l] [-x] [-X] [-y] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); |
355 | exit(1); | 496 | exit(1); |
356 | } | 497 | } |
357 | 498 | ||
@@ -363,29 +504,28 @@ main(int ac, char **av) | |||
363 | { | 504 | { |
364 | char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; | 505 | char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; |
365 | struct passwd *pw; | 506 | struct passwd *pw; |
366 | char *tmpbuf; | ||
367 | int opt; | 507 | int opt; |
368 | struct stat st; | 508 | struct stat st; |
369 | FILE *f; | 509 | FILE *f; |
370 | char hostname[MAXHOSTNAMELEN]; | 510 | Key *private; |
511 | Key *public; | ||
371 | extern int optind; | 512 | extern int optind; |
372 | extern char *optarg; | 513 | extern char *optarg; |
373 | 514 | ||
374 | /* check if RSA support exists */ | 515 | OpenSSL_add_all_algorithms(); |
375 | if (rsa_alive() == 0) { | 516 | |
376 | fprintf(stderr, | ||
377 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
378 | __progname); | ||
379 | exit(1); | ||
380 | } | ||
381 | /* we need this for the home * directory. */ | 517 | /* we need this for the home * directory. */ |
382 | pw = getpwuid(getuid()); | 518 | pw = getpwuid(getuid()); |
383 | if (!pw) { | 519 | if (!pw) { |
384 | printf("You don't exist, go away!\n"); | 520 | printf("You don't exist, go away!\n"); |
385 | exit(1); | 521 | exit(1); |
386 | } | 522 | } |
523 | if (gethostname(hostname, sizeof(hostname)) < 0) { | ||
524 | perror("gethostname"); | ||
525 | exit(1); | ||
526 | } | ||
387 | 527 | ||
388 | while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { | 528 | while ((opt = getopt(ac, av, "dqpclRxXyb:f:P:N:C:")) != EOF) { |
389 | switch (opt) { | 529 | switch (opt) { |
390 | case 'b': | 530 | case 'b': |
391 | bits = atoi(optarg); | 531 | bits = atoi(optarg); |
@@ -428,6 +568,29 @@ main(int ac, char **av) | |||
428 | quiet = 1; | 568 | quiet = 1; |
429 | break; | 569 | break; |
430 | 570 | ||
571 | case 'R': | ||
572 | if (rsa_alive() == 0) | ||
573 | exit(1); | ||
574 | else | ||
575 | exit(0); | ||
576 | break; | ||
577 | |||
578 | case 'x': | ||
579 | convert_to_ssh2 = 1; | ||
580 | break; | ||
581 | |||
582 | case 'X': | ||
583 | convert_from_ssh2 = 1; | ||
584 | break; | ||
585 | |||
586 | case 'y': | ||
587 | print_public = 1; | ||
588 | break; | ||
589 | |||
590 | case 'd': | ||
591 | dsa_mode = 1; | ||
592 | break; | ||
593 | |||
431 | case '?': | 594 | case '?': |
432 | default: | 595 | default: |
433 | usage(); | 596 | usage(); |
@@ -441,22 +604,44 @@ main(int ac, char **av) | |||
441 | printf("Can only have one of -p and -c.\n"); | 604 | printf("Can only have one of -p and -c.\n"); |
442 | usage(); | 605 | usage(); |
443 | } | 606 | } |
607 | /* check if RSA support is needed and exists */ | ||
608 | if (dsa_mode == 0 && rsa_alive() == 0) { | ||
609 | fprintf(stderr, | ||
610 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
611 | __progname); | ||
612 | exit(1); | ||
613 | } | ||
444 | if (print_fingerprint) | 614 | if (print_fingerprint) |
445 | do_fingerprint(pw); | 615 | do_fingerprint(pw); |
446 | if (change_passphrase) | 616 | if (change_passphrase) |
447 | do_change_passphrase(pw); | 617 | do_change_passphrase(pw); |
448 | if (change_comment) | 618 | if (change_comment) |
449 | do_change_comment(pw); | 619 | do_change_comment(pw); |
620 | if (convert_to_ssh2) | ||
621 | do_convert_to_ssh2(pw); | ||
622 | if (convert_from_ssh2) | ||
623 | do_convert_from_ssh2(pw); | ||
624 | if (print_public) | ||
625 | do_print_public(pw); | ||
450 | 626 | ||
451 | arc4random_stir(); | 627 | arc4random_stir(); |
452 | 628 | ||
453 | if (quiet) | 629 | if (dsa_mode != 0) { |
454 | rsa_set_verbose(0); | 630 | if (!quiet) |
455 | 631 | printf("Generating DSA parameter and key.\n"); | |
456 | /* Generate the rsa key pair. */ | 632 | public = private = dsa_generate_key(bits); |
457 | private_key = RSA_new(); | 633 | if (private == NULL) { |
458 | public_key = RSA_new(); | 634 | fprintf(stderr, "dsa_generate_keys failed"); |
459 | rsa_generate_key(private_key, public_key, bits); | 635 | exit(1); |
636 | } | ||
637 | } else { | ||
638 | if (quiet) | ||
639 | rsa_set_verbose(0); | ||
640 | /* Generate the rsa key pair. */ | ||
641 | public = key_new(KEY_RSA); | ||
642 | private = key_new(KEY_RSA); | ||
643 | rsa_generate_key(private->rsa, public->rsa, bits); | ||
644 | } | ||
460 | 645 | ||
461 | if (!have_identity) | 646 | if (!have_identity) |
462 | ask_filename(pw, "Enter file in which to save the key"); | 647 | ask_filename(pw, "Enter file in which to save the key"); |
@@ -509,17 +694,13 @@ passphrase_again: | |||
509 | strlcpy(comment, identity_comment, sizeof(comment)); | 694 | strlcpy(comment, identity_comment, sizeof(comment)); |
510 | } else { | 695 | } else { |
511 | /* Create default commend field for the passphrase. */ | 696 | /* Create default commend field for the passphrase. */ |
512 | if (gethostname(hostname, sizeof(hostname)) < 0) { | ||
513 | perror("gethostname"); | ||
514 | exit(1); | ||
515 | } | ||
516 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); | 697 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); |
517 | } | 698 | } |
518 | 699 | ||
519 | /* Save the key with the given passphrase and comment. */ | 700 | /* Save the key with the given passphrase and comment. */ |
520 | if (!save_private_key(identity_file, passphrase1, private_key, comment)) { | 701 | if (!save_private_key(identity_file, passphrase1, private, comment)) { |
521 | printf("Saving the key failed: %s: %s.\n", | 702 | printf("Saving the key failed: %s: %s.\n", |
522 | identity_file, strerror(errno)); | 703 | identity_file, strerror(errno)); |
523 | memset(passphrase1, 0, strlen(passphrase1)); | 704 | memset(passphrase1, 0, strlen(passphrase1)); |
524 | xfree(passphrase1); | 705 | xfree(passphrase1); |
525 | exit(1); | 706 | exit(1); |
@@ -529,7 +710,9 @@ passphrase_again: | |||
529 | xfree(passphrase1); | 710 | xfree(passphrase1); |
530 | 711 | ||
531 | /* Clear the private key and the random number generator. */ | 712 | /* Clear the private key and the random number generator. */ |
532 | RSA_free(private_key); | 713 | if (private != public) { |
714 | key_free(private); | ||
715 | } | ||
533 | arc4random_stir(); | 716 | arc4random_stir(); |
534 | 717 | ||
535 | if (!quiet) | 718 | if (!quiet) |
@@ -541,21 +724,18 @@ passphrase_again: | |||
541 | printf("Could not save your public key in %s\n", identity_file); | 724 | printf("Could not save your public key in %s\n", identity_file); |
542 | exit(1); | 725 | exit(1); |
543 | } | 726 | } |
544 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | 727 | if (!key_write(public, f)) |
545 | tmpbuf = BN_bn2dec(public_key->e); | 728 | fprintf(stderr, "write key failed"); |
546 | fprintf(f, "%s ", tmpbuf); | 729 | fprintf(f, " %s\n", comment); |
547 | free(tmpbuf); | ||
548 | tmpbuf = BN_bn2dec(public_key->n); | ||
549 | fprintf(f, "%s %s\n", tmpbuf, comment); | ||
550 | free(tmpbuf); | ||
551 | fclose(f); | 730 | fclose(f); |
552 | 731 | ||
553 | if (!quiet) { | 732 | if (!quiet) { |
554 | printf("Your public key has been saved in %s.\n", identity_file); | 733 | printf("Your public key has been saved in %s.\n", |
734 | identity_file); | ||
555 | printf("The key fingerprint is:\n"); | 735 | printf("The key fingerprint is:\n"); |
556 | printf("%d %s %s\n", BN_num_bits(public_key->n), | 736 | printf("%s %s\n", key_fingerprint(public), comment); |
557 | fingerprint(public_key->e, public_key->n), | ||
558 | comment); | ||
559 | } | 737 | } |
738 | |||
739 | key_free(public); | ||
560 | exit(0); | 740 | exit(0); |
561 | } | 741 | } |
@@ -11,7 +11,11 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include "includes.h" | 13 | #include "includes.h" |
14 | RCSID("$Id: ssh.c,v 1.26 2000/04/16 01:18:46 damien Exp $"); | 14 | RCSID("$Id: ssh.c,v 1.27 2000/04/29 13:57:12 damien Exp $"); |
15 | |||
16 | #include <openssl/evp.h> | ||
17 | #include <openssl/dsa.h> | ||
18 | #include <openssl/rsa.h> | ||
15 | 19 | ||
16 | #include "xmalloc.h" | 20 | #include "xmalloc.h" |
17 | #include "ssh.h" | 21 | #include "ssh.h" |
@@ -24,6 +28,8 @@ RCSID("$Id: ssh.c,v 1.26 2000/04/16 01:18:46 damien Exp $"); | |||
24 | #include "ssh2.h" | 28 | #include "ssh2.h" |
25 | #include "compat.h" | 29 | #include "compat.h" |
26 | #include "channels.h" | 30 | #include "channels.h" |
31 | #include "key.h" | ||
32 | #include "authfile.h" | ||
27 | 33 | ||
28 | #ifdef HAVE___PROGNAME | 34 | #ifdef HAVE___PROGNAME |
29 | extern char *__progname; | 35 | extern char *__progname; |
@@ -358,10 +364,16 @@ main(int ac, char **av) | |||
358 | } | 364 | } |
359 | break; | 365 | break; |
360 | case 'c': | 366 | case 'c': |
361 | options.cipher = cipher_number(optarg); | 367 | if (ciphers_valid(optarg)) { |
362 | if (options.cipher == -1) { | 368 | /* SSH2 only */ |
363 | fprintf(stderr, "Unknown cipher type '%s'\n", optarg); | 369 | options.ciphers = xstrdup(optarg); |
364 | exit(1); | 370 | } else { |
371 | /* SSH1 only */ | ||
372 | options.cipher = cipher_number(optarg); | ||
373 | if (options.cipher == -1) { | ||
374 | fprintf(stderr, "Unknown cipher type '%s'\n", optarg); | ||
375 | exit(1); | ||
376 | } | ||
365 | } | 377 | } |
366 | break; | 378 | break; |
367 | case 'p': | 379 | case 'p': |
@@ -417,16 +429,11 @@ main(int ac, char **av) | |||
417 | if (!host) | 429 | if (!host) |
418 | usage(); | 430 | usage(); |
419 | 431 | ||
420 | /* check if RSA support exists */ | ||
421 | if (rsa_alive() == 0) { | ||
422 | fprintf(stderr, | ||
423 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
424 | __progname); | ||
425 | exit(1); | ||
426 | } | ||
427 | /* Initialize the command to execute on remote host. */ | 432 | /* Initialize the command to execute on remote host. */ |
428 | buffer_init(&command); | 433 | buffer_init(&command); |
429 | 434 | ||
435 | OpenSSL_add_all_algorithms(); | ||
436 | |||
430 | /* | 437 | /* |
431 | * Save the command to execute on the remote host in a buffer. There | 438 | * Save the command to execute on the remote host in a buffer. There |
432 | * is no limit on the length of the command, except by the maximum | 439 | * is no limit on the length of the command, except by the maximum |
@@ -496,6 +503,20 @@ main(int ac, char **av) | |||
496 | /* reinit */ | 503 | /* reinit */ |
497 | log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); | 504 | log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); |
498 | 505 | ||
506 | /* check if RSA support exists */ | ||
507 | if ((options.protocol & SSH_PROTO_1) && | ||
508 | rsa_alive() == 0) { | ||
509 | log("%s: no RSA support in libssl and libcrypto. See ssl(8).", | ||
510 | __progname); | ||
511 | log("Disabling protocol version 1"); | ||
512 | options.protocol &= ~ (SSH_PROTO_1|SSH_PROTO_1_PREFERRED); | ||
513 | } | ||
514 | if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { | ||
515 | fprintf(stderr, "%s: No protocol version available.\n", | ||
516 | __progname); | ||
517 | exit(1); | ||
518 | } | ||
519 | |||
499 | if (options.user == NULL) | 520 | if (options.user == NULL) |
500 | options.user = xstrdup(pw->pw_name); | 521 | options.user = xstrdup(pw->pw_name); |
501 | 522 | ||
@@ -562,9 +583,12 @@ main(int ac, char **av) | |||
562 | * authentication. This must be done before releasing extra | 583 | * authentication. This must be done before releasing extra |
563 | * privileges, because the file is only readable by root. | 584 | * privileges, because the file is only readable by root. |
564 | */ | 585 | */ |
565 | if (ok) { | 586 | if (ok && (options.protocol & SSH_PROTO_1)) { |
587 | Key k; | ||
566 | host_private_key = RSA_new(); | 588 | host_private_key = RSA_new(); |
567 | if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) | 589 | k.type = KEY_RSA; |
590 | k.rsa = host_private_key; | ||
591 | if (load_private_key(HOST_KEY_FILE, "", &k, NULL)) | ||
568 | host_private_key_loaded = 1; | 592 | host_private_key_loaded = 1; |
569 | } | 593 | } |
570 | /* | 594 | /* |
@@ -610,15 +634,22 @@ main(int ac, char **av) | |||
610 | exit(1); | 634 | exit(1); |
611 | } | 635 | } |
612 | /* Expand ~ in options.identity_files. */ | 636 | /* Expand ~ in options.identity_files. */ |
637 | /* XXX mem-leaks */ | ||
613 | for (i = 0; i < options.num_identity_files; i++) | 638 | for (i = 0; i < options.num_identity_files; i++) |
614 | options.identity_files[i] = | 639 | options.identity_files[i] = |
615 | tilde_expand_filename(options.identity_files[i], original_real_uid); | 640 | tilde_expand_filename(options.identity_files[i], original_real_uid); |
616 | 641 | for (i = 0; i < options.num_identity_files2; i++) | |
642 | options.identity_files2[i] = | ||
643 | tilde_expand_filename(options.identity_files2[i], original_real_uid); | ||
617 | /* Expand ~ in known host file names. */ | 644 | /* Expand ~ in known host file names. */ |
618 | options.system_hostfile = tilde_expand_filename(options.system_hostfile, | 645 | options.system_hostfile = tilde_expand_filename(options.system_hostfile, |
619 | original_real_uid); | 646 | original_real_uid); |
620 | options.user_hostfile = tilde_expand_filename(options.user_hostfile, | 647 | options.user_hostfile = tilde_expand_filename(options.user_hostfile, |
621 | original_real_uid); | 648 | original_real_uid); |
649 | options.system_hostfile2 = tilde_expand_filename(options.system_hostfile2, | ||
650 | original_real_uid); | ||
651 | options.user_hostfile2 = tilde_expand_filename(options.user_hostfile2, | ||
652 | original_real_uid); | ||
622 | 653 | ||
623 | /* Log into the remote system. This never returns if the login fails. */ | 654 | /* Log into the remote system. This never returns if the login fails. */ |
624 | ssh_login(host_private_key_loaded, host_private_key, | 655 | ssh_login(host_private_key_loaded, host_private_key, |
@@ -13,7 +13,7 @@ | |||
13 | * | 13 | * |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: ssh.h,v 1.34 2000/04/20 13:12:59 damien Exp $"); */ | 16 | /* RCSID("$Id: ssh.h,v 1.35 2000/04/29 13:57:12 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef SSH_H | 18 | #ifndef SSH_H |
19 | #define SSH_H | 19 | #define SSH_H |
@@ -88,6 +88,7 @@ | |||
88 | * world-readable. | 88 | * world-readable. |
89 | */ | 89 | */ |
90 | #define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" | 90 | #define SSH_SYSTEM_HOSTFILE ETCDIR "/ssh_known_hosts" |
91 | #define SSH_SYSTEM_HOSTFILE2 ETCDIR "/ssh_known_hosts2" | ||
91 | 92 | ||
92 | /* | 93 | /* |
93 | * Of these, ssh_host_key must be readable only by root, whereas ssh_config | 94 | * Of these, ssh_host_key must be readable only by root, whereas ssh_config |
@@ -96,7 +97,7 @@ | |||
96 | #define HOST_KEY_FILE ETCDIR "/ssh_host_key" | 97 | #define HOST_KEY_FILE ETCDIR "/ssh_host_key" |
97 | #define SERVER_CONFIG_FILE ETCDIR "/sshd_config" | 98 | #define SERVER_CONFIG_FILE ETCDIR "/sshd_config" |
98 | #define HOST_CONFIG_FILE ETCDIR "/ssh_config" | 99 | #define HOST_CONFIG_FILE ETCDIR "/ssh_config" |
99 | #define DSA_KEY_FILE ETCDIR "/ssh_dsa_key" | 100 | #define DSA_KEY_FILE ETCDIR "/ssh_host_dsa_key" |
100 | 101 | ||
101 | #ifndef SSH_PROGRAM | 102 | #ifndef SSH_PROGRAM |
102 | #define SSH_PROGRAM "/usr/bin/ssh" | 103 | #define SSH_PROGRAM "/usr/bin/ssh" |
@@ -128,6 +129,7 @@ | |||
128 | * contain anything particularly secret. | 129 | * contain anything particularly secret. |
129 | */ | 130 | */ |
130 | #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" | 131 | #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" |
132 | #define SSH_USER_HOSTFILE2 "~/.ssh/known_hosts2" | ||
131 | 133 | ||
132 | /* | 134 | /* |
133 | * Name of the default file containing client-side authentication key. This | 135 | * Name of the default file containing client-side authentication key. This |
@@ -152,6 +154,7 @@ | |||
152 | * running as root.) | 154 | * running as root.) |
153 | */ | 155 | */ |
154 | #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" | 156 | #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" |
157 | #define SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" | ||
155 | 158 | ||
156 | /* | 159 | /* |
157 | * Per-user and system-wide ssh "rc" files. These files are executed with | 160 | * Per-user and system-wide ssh "rc" files. These files are executed with |
@@ -407,36 +410,6 @@ int auth_rsa_challenge_dialog(RSA *pk); | |||
407 | */ | 410 | */ |
408 | char *read_passphrase(const char *prompt, int from_stdin); | 411 | char *read_passphrase(const char *prompt, int from_stdin); |
409 | 412 | ||
410 | /* | ||
411 | * Saves the authentication (private) key in a file, encrypting it with | ||
412 | * passphrase. The identification of the file (lowest 64 bits of n) will | ||
413 | * precede the key to provide identification of the key without needing a | ||
414 | * passphrase. | ||
415 | */ | ||
416 | int | ||
417 | save_private_key(const char *filename, const char *passphrase, | ||
418 | RSA * private_key, const char *comment); | ||
419 | |||
420 | /* | ||
421 | * Loads the public part of the key file (public key and comment). Returns 0 | ||
422 | * if an error occurred; zero if the public key was successfully read. The | ||
423 | * comment of the key is returned in comment_return if it is non-NULL; the | ||
424 | * caller must free the value with xfree. | ||
425 | */ | ||
426 | int | ||
427 | load_public_key(const char *filename, RSA * pub, | ||
428 | char **comment_return); | ||
429 | |||
430 | /* | ||
431 | * Loads the private key from the file. Returns 0 if an error is encountered | ||
432 | * (file does not exist or is not readable, or passphrase is bad). This | ||
433 | * initializes the private key. The comment of the key is returned in | ||
434 | * comment_return if it is non-NULL; the caller must free the value with | ||
435 | * xfree. | ||
436 | */ | ||
437 | int | ||
438 | load_private_key(const char *filename, const char *passphrase, | ||
439 | RSA * private_key, char **comment_return); | ||
440 | 413 | ||
441 | /*------------ Definitions for logging. -----------------------*/ | 414 | /*------------ Definitions for logging. -----------------------*/ |
442 | 415 | ||
diff --git a/sshconnect.c b/sshconnect.c index f58289e7b..5554c0643 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -5,48 +5,29 @@ | |||
5 | * Created: Sat Mar 18 22:15:47 1995 ylo | 5 | * Created: Sat Mar 18 22:15:47 1995 ylo |
6 | * Code to connect to a remote host, and to perform the client side of the | 6 | * Code to connect to a remote host, and to perform the client side of the |
7 | * login (authentication) dialog. | 7 | * login (authentication) dialog. |
8 | * | ||
9 | * SSH2 support added by Markus Friedl. | ||
10 | */ | 8 | */ |
11 | 9 | ||
12 | #include "includes.h" | 10 | #include "includes.h" |
13 | RCSID("$OpenBSD: sshconnect.c,v 1.69 2000/04/19 07:05:50 deraadt Exp $"); | 11 | RCSID("$OpenBSD: sshconnect.c,v 1.71 2000/04/26 21:28:33 markus Exp $"); |
14 | 12 | ||
15 | #include <openssl/bn.h> | 13 | #include <openssl/bn.h> |
14 | #include <openssl/dsa.h> | ||
15 | #include <openssl/rsa.h> | ||
16 | |||
16 | #include "xmalloc.h" | 17 | #include "xmalloc.h" |
17 | #include "rsa.h" | 18 | #include "rsa.h" |
18 | #include "ssh.h" | 19 | #include "ssh.h" |
19 | #include "buffer.h" | 20 | #include "buffer.h" |
20 | #include "packet.h" | 21 | #include "packet.h" |
21 | #include "authfd.h" | ||
22 | #include "cipher.h" | ||
23 | #include "mpaux.h" | ||
24 | #include "uidswap.h" | 22 | #include "uidswap.h" |
25 | #include "compat.h" | 23 | #include "compat.h" |
26 | #include "readconf.h" | 24 | #include "readconf.h" |
27 | |||
28 | #include "bufaux.h" | ||
29 | #include <openssl/rsa.h> | ||
30 | #include <openssl/dsa.h> | ||
31 | |||
32 | #include "ssh2.h" | ||
33 | #include <openssl/md5.h> | ||
34 | #include <openssl/dh.h> | ||
35 | #include <openssl/hmac.h> | ||
36 | #include "kex.h" | ||
37 | #include "myproposal.h" | ||
38 | #include "key.h" | 25 | #include "key.h" |
39 | #include "dsa.h" | 26 | #include "sshconnect.h" |
40 | #include "hostfile.h" | 27 | #include "hostfile.h" |
41 | 28 | ||
42 | /* Session id for the current session. */ | 29 | char *client_version_string = NULL; |
43 | unsigned char session_id[16]; | 30 | char *server_version_string = NULL; |
44 | |||
45 | /* authentications supported by server */ | ||
46 | unsigned int supported_authentications; | ||
47 | |||
48 | static char *client_version_string = NULL; | ||
49 | static char *server_version_string = NULL; | ||
50 | 31 | ||
51 | extern Options options; | 32 | extern Options options; |
52 | extern char *__progname; | 33 | extern char *__progname; |
@@ -316,653 +297,6 @@ ssh_connect(const char *host, struct sockaddr_storage * hostaddr, | |||
316 | return 1; | 297 | return 1; |
317 | } | 298 | } |
318 | 299 | ||
319 | /* | ||
320 | * Checks if the user has an authentication agent, and if so, tries to | ||
321 | * authenticate using the agent. | ||
322 | */ | ||
323 | int | ||
324 | try_agent_authentication() | ||
325 | { | ||
326 | int status, type; | ||
327 | char *comment; | ||
328 | AuthenticationConnection *auth; | ||
329 | unsigned char response[16]; | ||
330 | unsigned int i; | ||
331 | BIGNUM *e, *n, *challenge; | ||
332 | |||
333 | /* Get connection to the agent. */ | ||
334 | auth = ssh_get_authentication_connection(); | ||
335 | if (!auth) | ||
336 | return 0; | ||
337 | |||
338 | e = BN_new(); | ||
339 | n = BN_new(); | ||
340 | challenge = BN_new(); | ||
341 | |||
342 | /* Loop through identities served by the agent. */ | ||
343 | for (status = ssh_get_first_identity(auth, e, n, &comment); | ||
344 | status; | ||
345 | status = ssh_get_next_identity(auth, e, n, &comment)) { | ||
346 | int plen, clen; | ||
347 | |||
348 | /* Try this identity. */ | ||
349 | debug("Trying RSA authentication via agent with '%.100s'", comment); | ||
350 | xfree(comment); | ||
351 | |||
352 | /* Tell the server that we are willing to authenticate using this key. */ | ||
353 | packet_start(SSH_CMSG_AUTH_RSA); | ||
354 | packet_put_bignum(n); | ||
355 | packet_send(); | ||
356 | packet_write_wait(); | ||
357 | |||
358 | /* Wait for server's response. */ | ||
359 | type = packet_read(&plen); | ||
360 | |||
361 | /* The server sends failure if it doesn\'t like our key or | ||
362 | does not support RSA authentication. */ | ||
363 | if (type == SSH_SMSG_FAILURE) { | ||
364 | debug("Server refused our key."); | ||
365 | continue; | ||
366 | } | ||
367 | /* Otherwise it should have sent a challenge. */ | ||
368 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
369 | packet_disconnect("Protocol error during RSA authentication: %d", | ||
370 | type); | ||
371 | |||
372 | packet_get_bignum(challenge, &clen); | ||
373 | |||
374 | packet_integrity_check(plen, clen, type); | ||
375 | |||
376 | debug("Received RSA challenge from server."); | ||
377 | |||
378 | /* Ask the agent to decrypt the challenge. */ | ||
379 | if (!ssh_decrypt_challenge(auth, e, n, challenge, | ||
380 | session_id, 1, response)) { | ||
381 | /* The agent failed to authenticate this identifier although it | ||
382 | advertised it supports this. Just return a wrong value. */ | ||
383 | log("Authentication agent failed to decrypt challenge."); | ||
384 | memset(response, 0, sizeof(response)); | ||
385 | } | ||
386 | debug("Sending response to RSA challenge."); | ||
387 | |||
388 | /* Send the decrypted challenge back to the server. */ | ||
389 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
390 | for (i = 0; i < 16; i++) | ||
391 | packet_put_char(response[i]); | ||
392 | packet_send(); | ||
393 | packet_write_wait(); | ||
394 | |||
395 | /* Wait for response from the server. */ | ||
396 | type = packet_read(&plen); | ||
397 | |||
398 | /* The server returns success if it accepted the authentication. */ | ||
399 | if (type == SSH_SMSG_SUCCESS) { | ||
400 | debug("RSA authentication accepted by server."); | ||
401 | BN_clear_free(e); | ||
402 | BN_clear_free(n); | ||
403 | BN_clear_free(challenge); | ||
404 | return 1; | ||
405 | } | ||
406 | /* Otherwise it should return failure. */ | ||
407 | if (type != SSH_SMSG_FAILURE) | ||
408 | packet_disconnect("Protocol error waiting RSA auth response: %d", | ||
409 | type); | ||
410 | } | ||
411 | |||
412 | BN_clear_free(e); | ||
413 | BN_clear_free(n); | ||
414 | BN_clear_free(challenge); | ||
415 | |||
416 | debug("RSA authentication using agent refused."); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * Computes the proper response to a RSA challenge, and sends the response to | ||
422 | * the server. | ||
423 | */ | ||
424 | void | ||
425 | respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) | ||
426 | { | ||
427 | unsigned char buf[32], response[16]; | ||
428 | MD5_CTX md; | ||
429 | int i, len; | ||
430 | |||
431 | /* Decrypt the challenge using the private key. */ | ||
432 | rsa_private_decrypt(challenge, challenge, prv); | ||
433 | |||
434 | /* Compute the response. */ | ||
435 | /* The response is MD5 of decrypted challenge plus session id. */ | ||
436 | len = BN_num_bytes(challenge); | ||
437 | if (len <= 0 || len > sizeof(buf)) | ||
438 | packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", | ||
439 | len); | ||
440 | |||
441 | memset(buf, 0, sizeof(buf)); | ||
442 | BN_bn2bin(challenge, buf + sizeof(buf) - len); | ||
443 | MD5_Init(&md); | ||
444 | MD5_Update(&md, buf, 32); | ||
445 | MD5_Update(&md, session_id, 16); | ||
446 | MD5_Final(response, &md); | ||
447 | |||
448 | debug("Sending response to host key RSA challenge."); | ||
449 | |||
450 | /* Send the response back to the server. */ | ||
451 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
452 | for (i = 0; i < 16; i++) | ||
453 | packet_put_char(response[i]); | ||
454 | packet_send(); | ||
455 | packet_write_wait(); | ||
456 | |||
457 | memset(buf, 0, sizeof(buf)); | ||
458 | memset(response, 0, sizeof(response)); | ||
459 | memset(&md, 0, sizeof(md)); | ||
460 | } | ||
461 | |||
462 | /* | ||
463 | * Checks if the user has authentication file, and if so, tries to authenticate | ||
464 | * the user using it. | ||
465 | */ | ||
466 | int | ||
467 | try_rsa_authentication(const char *authfile) | ||
468 | { | ||
469 | BIGNUM *challenge; | ||
470 | RSA *private_key; | ||
471 | RSA *public_key; | ||
472 | char *passphrase, *comment; | ||
473 | int type, i; | ||
474 | int plen, clen; | ||
475 | |||
476 | /* Try to load identification for the authentication key. */ | ||
477 | public_key = RSA_new(); | ||
478 | if (!load_public_key(authfile, public_key, &comment)) { | ||
479 | RSA_free(public_key); | ||
480 | /* Could not load it. Fail. */ | ||
481 | return 0; | ||
482 | } | ||
483 | debug("Trying RSA authentication with key '%.100s'", comment); | ||
484 | |||
485 | /* Tell the server that we are willing to authenticate using this key. */ | ||
486 | packet_start(SSH_CMSG_AUTH_RSA); | ||
487 | packet_put_bignum(public_key->n); | ||
488 | packet_send(); | ||
489 | packet_write_wait(); | ||
490 | |||
491 | /* We no longer need the public key. */ | ||
492 | RSA_free(public_key); | ||
493 | |||
494 | /* Wait for server's response. */ | ||
495 | type = packet_read(&plen); | ||
496 | |||
497 | /* | ||
498 | * The server responds with failure if it doesn\'t like our key or | ||
499 | * doesn\'t support RSA authentication. | ||
500 | */ | ||
501 | if (type == SSH_SMSG_FAILURE) { | ||
502 | debug("Server refused our key."); | ||
503 | xfree(comment); | ||
504 | return 0; | ||
505 | } | ||
506 | /* Otherwise, the server should respond with a challenge. */ | ||
507 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
508 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
509 | |||
510 | /* Get the challenge from the packet. */ | ||
511 | challenge = BN_new(); | ||
512 | packet_get_bignum(challenge, &clen); | ||
513 | |||
514 | packet_integrity_check(plen, clen, type); | ||
515 | |||
516 | debug("Received RSA challenge from server."); | ||
517 | |||
518 | private_key = RSA_new(); | ||
519 | /* | ||
520 | * Load the private key. Try first with empty passphrase; if it | ||
521 | * fails, ask for a passphrase. | ||
522 | */ | ||
523 | if (!load_private_key(authfile, "", private_key, NULL)) { | ||
524 | char buf[300]; | ||
525 | snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", | ||
526 | comment); | ||
527 | if (!options.batch_mode) | ||
528 | passphrase = read_passphrase(buf, 0); | ||
529 | else { | ||
530 | debug("Will not query passphrase for %.100s in batch mode.", | ||
531 | comment); | ||
532 | passphrase = xstrdup(""); | ||
533 | } | ||
534 | |||
535 | /* Load the authentication file using the pasphrase. */ | ||
536 | if (!load_private_key(authfile, passphrase, private_key, NULL)) { | ||
537 | memset(passphrase, 0, strlen(passphrase)); | ||
538 | xfree(passphrase); | ||
539 | error("Bad passphrase."); | ||
540 | |||
541 | /* Send a dummy response packet to avoid protocol error. */ | ||
542 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
543 | for (i = 0; i < 16; i++) | ||
544 | packet_put_char(0); | ||
545 | packet_send(); | ||
546 | packet_write_wait(); | ||
547 | |||
548 | /* Expect the server to reject it... */ | ||
549 | packet_read_expect(&plen, SSH_SMSG_FAILURE); | ||
550 | xfree(comment); | ||
551 | return 0; | ||
552 | } | ||
553 | /* Destroy the passphrase. */ | ||
554 | memset(passphrase, 0, strlen(passphrase)); | ||
555 | xfree(passphrase); | ||
556 | } | ||
557 | /* We no longer need the comment. */ | ||
558 | xfree(comment); | ||
559 | |||
560 | /* Compute and send a response to the challenge. */ | ||
561 | respond_to_rsa_challenge(challenge, private_key); | ||
562 | |||
563 | /* Destroy the private key. */ | ||
564 | RSA_free(private_key); | ||
565 | |||
566 | /* We no longer need the challenge. */ | ||
567 | BN_clear_free(challenge); | ||
568 | |||
569 | /* Wait for response from the server. */ | ||
570 | type = packet_read(&plen); | ||
571 | if (type == SSH_SMSG_SUCCESS) { | ||
572 | debug("RSA authentication accepted by server."); | ||
573 | return 1; | ||
574 | } | ||
575 | if (type != SSH_SMSG_FAILURE) | ||
576 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
577 | debug("RSA authentication refused."); | ||
578 | return 0; | ||
579 | } | ||
580 | |||
581 | /* | ||
582 | * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv | ||
583 | * authentication and RSA host authentication. | ||
584 | */ | ||
585 | int | ||
586 | try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) | ||
587 | { | ||
588 | int type; | ||
589 | BIGNUM *challenge; | ||
590 | int plen, clen; | ||
591 | |||
592 | debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); | ||
593 | |||
594 | /* Tell the server that we are willing to authenticate using this key. */ | ||
595 | packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); | ||
596 | packet_put_string(local_user, strlen(local_user)); | ||
597 | packet_put_int(BN_num_bits(host_key->n)); | ||
598 | packet_put_bignum(host_key->e); | ||
599 | packet_put_bignum(host_key->n); | ||
600 | packet_send(); | ||
601 | packet_write_wait(); | ||
602 | |||
603 | /* Wait for server's response. */ | ||
604 | type = packet_read(&plen); | ||
605 | |||
606 | /* The server responds with failure if it doesn't admit our | ||
607 | .rhosts authentication or doesn't know our host key. */ | ||
608 | if (type == SSH_SMSG_FAILURE) { | ||
609 | debug("Server refused our rhosts authentication or host key."); | ||
610 | return 0; | ||
611 | } | ||
612 | /* Otherwise, the server should respond with a challenge. */ | ||
613 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
614 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
615 | |||
616 | /* Get the challenge from the packet. */ | ||
617 | challenge = BN_new(); | ||
618 | packet_get_bignum(challenge, &clen); | ||
619 | |||
620 | packet_integrity_check(plen, clen, type); | ||
621 | |||
622 | debug("Received RSA challenge for host key from server."); | ||
623 | |||
624 | /* Compute a response to the challenge. */ | ||
625 | respond_to_rsa_challenge(challenge, host_key); | ||
626 | |||
627 | /* We no longer need the challenge. */ | ||
628 | BN_clear_free(challenge); | ||
629 | |||
630 | /* Wait for response from the server. */ | ||
631 | type = packet_read(&plen); | ||
632 | if (type == SSH_SMSG_SUCCESS) { | ||
633 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); | ||
634 | return 1; | ||
635 | } | ||
636 | if (type != SSH_SMSG_FAILURE) | ||
637 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
638 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); | ||
639 | return 0; | ||
640 | } | ||
641 | |||
642 | #ifdef KRB4 | ||
643 | int | ||
644 | try_kerberos_authentication() | ||
645 | { | ||
646 | KTEXT_ST auth; /* Kerberos data */ | ||
647 | char *reply; | ||
648 | char inst[INST_SZ]; | ||
649 | char *realm; | ||
650 | CREDENTIALS cred; | ||
651 | int r, type, plen; | ||
652 | socklen_t slen; | ||
653 | Key_schedule schedule; | ||
654 | u_long checksum, cksum; | ||
655 | MSG_DAT msg_data; | ||
656 | struct sockaddr_in local, foreign; | ||
657 | struct stat st; | ||
658 | |||
659 | /* Don't do anything if we don't have any tickets. */ | ||
660 | if (stat(tkt_string(), &st) < 0) | ||
661 | return 0; | ||
662 | |||
663 | strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); | ||
664 | |||
665 | realm = (char *) krb_realmofhost(get_canonical_hostname()); | ||
666 | if (!realm) { | ||
667 | debug("Kerberos V4: no realm for %s", get_canonical_hostname()); | ||
668 | return 0; | ||
669 | } | ||
670 | /* This can really be anything. */ | ||
671 | checksum = (u_long) getpid(); | ||
672 | |||
673 | r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); | ||
674 | if (r != KSUCCESS) { | ||
675 | debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); | ||
676 | return 0; | ||
677 | } | ||
678 | /* Get session key to decrypt the server's reply with. */ | ||
679 | r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); | ||
680 | if (r != KSUCCESS) { | ||
681 | debug("get_cred failed: %s", krb_err_txt[r]); | ||
682 | return 0; | ||
683 | } | ||
684 | des_key_sched((des_cblock *) cred.session, schedule); | ||
685 | |||
686 | /* Send authentication info to server. */ | ||
687 | packet_start(SSH_CMSG_AUTH_KERBEROS); | ||
688 | packet_put_string((char *) auth.dat, auth.length); | ||
689 | packet_send(); | ||
690 | packet_write_wait(); | ||
691 | |||
692 | /* Zero the buffer. */ | ||
693 | (void) memset(auth.dat, 0, MAX_KTXT_LEN); | ||
694 | |||
695 | slen = sizeof(local); | ||
696 | memset(&local, 0, sizeof(local)); | ||
697 | if (getsockname(packet_get_connection_in(), | ||
698 | (struct sockaddr *) & local, &slen) < 0) | ||
699 | debug("getsockname failed: %s", strerror(errno)); | ||
700 | |||
701 | slen = sizeof(foreign); | ||
702 | memset(&foreign, 0, sizeof(foreign)); | ||
703 | if (getpeername(packet_get_connection_in(), | ||
704 | (struct sockaddr *) & foreign, &slen) < 0) { | ||
705 | debug("getpeername failed: %s", strerror(errno)); | ||
706 | fatal_cleanup(); | ||
707 | } | ||
708 | /* Get server reply. */ | ||
709 | type = packet_read(&plen); | ||
710 | switch (type) { | ||
711 | case SSH_SMSG_FAILURE: | ||
712 | /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ | ||
713 | debug("Kerberos V4 authentication failed."); | ||
714 | return 0; | ||
715 | break; | ||
716 | |||
717 | case SSH_SMSG_AUTH_KERBEROS_RESPONSE: | ||
718 | /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ | ||
719 | debug("Kerberos V4 authentication accepted."); | ||
720 | |||
721 | /* Get server's response. */ | ||
722 | reply = packet_get_string((unsigned int *) &auth.length); | ||
723 | memcpy(auth.dat, reply, auth.length); | ||
724 | xfree(reply); | ||
725 | |||
726 | packet_integrity_check(plen, 4 + auth.length, type); | ||
727 | |||
728 | /* | ||
729 | * If his response isn't properly encrypted with the session | ||
730 | * key, and the decrypted checksum fails to match, he's | ||
731 | * bogus. Bail out. | ||
732 | */ | ||
733 | r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, | ||
734 | &foreign, &local, &msg_data); | ||
735 | if (r != KSUCCESS) { | ||
736 | debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); | ||
737 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
738 | } | ||
739 | /* Fetch the (incremented) checksum that we supplied in the request. */ | ||
740 | (void) memcpy((char *) &cksum, (char *) msg_data.app_data, sizeof(cksum)); | ||
741 | cksum = ntohl(cksum); | ||
742 | |||
743 | /* If it matches, we're golden. */ | ||
744 | if (cksum == checksum + 1) { | ||
745 | debug("Kerberos V4 challenge successful."); | ||
746 | return 1; | ||
747 | } else | ||
748 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
749 | break; | ||
750 | |||
751 | default: | ||
752 | packet_disconnect("Protocol error on Kerberos V4 response: %d", type); | ||
753 | } | ||
754 | return 0; | ||
755 | } | ||
756 | |||
757 | #endif /* KRB4 */ | ||
758 | |||
759 | #ifdef AFS | ||
760 | int | ||
761 | send_kerberos_tgt() | ||
762 | { | ||
763 | CREDENTIALS *creds; | ||
764 | char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; | ||
765 | int r, type, plen; | ||
766 | char buffer[8192]; | ||
767 | struct stat st; | ||
768 | |||
769 | /* Don't do anything if we don't have any tickets. */ | ||
770 | if (stat(tkt_string(), &st) < 0) | ||
771 | return 0; | ||
772 | |||
773 | creds = xmalloc(sizeof(*creds)); | ||
774 | |||
775 | if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { | ||
776 | debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]); | ||
777 | return 0; | ||
778 | } | ||
779 | if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { | ||
780 | debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); | ||
781 | return 0; | ||
782 | } | ||
783 | if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { | ||
784 | debug("Kerberos V4 ticket expired: %s", TKT_FILE); | ||
785 | return 0; | ||
786 | } | ||
787 | creds_to_radix(creds, (unsigned char *)buffer); | ||
788 | xfree(creds); | ||
789 | |||
790 | packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); | ||
791 | packet_put_string(buffer, strlen(buffer)); | ||
792 | packet_send(); | ||
793 | packet_write_wait(); | ||
794 | |||
795 | type = packet_read(&plen); | ||
796 | |||
797 | if (type == SSH_SMSG_FAILURE) | ||
798 | debug("Kerberos TGT for realm %s rejected.", prealm); | ||
799 | else if (type != SSH_SMSG_SUCCESS) | ||
800 | packet_disconnect("Protocol error on Kerberos TGT response: %d", type); | ||
801 | |||
802 | return 1; | ||
803 | } | ||
804 | |||
805 | void | ||
806 | send_afs_tokens(void) | ||
807 | { | ||
808 | CREDENTIALS creds; | ||
809 | struct ViceIoctl parms; | ||
810 | struct ClearToken ct; | ||
811 | int i, type, len, plen; | ||
812 | char buf[2048], *p, *server_cell; | ||
813 | char buffer[8192]; | ||
814 | |||
815 | /* Move over ktc_GetToken, here's something leaner. */ | ||
816 | for (i = 0; i < 100; i++) { /* just in case */ | ||
817 | parms.in = (char *) &i; | ||
818 | parms.in_size = sizeof(i); | ||
819 | parms.out = buf; | ||
820 | parms.out_size = sizeof(buf); | ||
821 | if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) | ||
822 | break; | ||
823 | p = buf; | ||
824 | |||
825 | /* Get secret token. */ | ||
826 | memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); | ||
827 | if (creds.ticket_st.length > MAX_KTXT_LEN) | ||
828 | break; | ||
829 | p += sizeof(unsigned int); | ||
830 | memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); | ||
831 | p += creds.ticket_st.length; | ||
832 | |||
833 | /* Get clear token. */ | ||
834 | memcpy(&len, p, sizeof(len)); | ||
835 | if (len != sizeof(struct ClearToken)) | ||
836 | break; | ||
837 | p += sizeof(len); | ||
838 | memcpy(&ct, p, len); | ||
839 | p += len; | ||
840 | p += sizeof(len); /* primary flag */ | ||
841 | server_cell = p; | ||
842 | |||
843 | /* Flesh out our credentials. */ | ||
844 | strlcpy(creds.service, "afs", sizeof creds.service); | ||
845 | creds.instance[0] = '\0'; | ||
846 | strlcpy(creds.realm, server_cell, REALM_SZ); | ||
847 | memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); | ||
848 | creds.issue_date = ct.BeginTimestamp; | ||
849 | creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); | ||
850 | creds.kvno = ct.AuthHandle; | ||
851 | snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); | ||
852 | creds.pinst[0] = '\0'; | ||
853 | |||
854 | /* Encode token, ship it off. */ | ||
855 | if (!creds_to_radix(&creds, (unsigned char*) buffer)) | ||
856 | break; | ||
857 | packet_start(SSH_CMSG_HAVE_AFS_TOKEN); | ||
858 | packet_put_string(buffer, strlen(buffer)); | ||
859 | packet_send(); | ||
860 | packet_write_wait(); | ||
861 | |||
862 | /* Roger, Roger. Clearance, Clarence. What's your vector, | ||
863 | Victor? */ | ||
864 | type = packet_read(&plen); | ||
865 | |||
866 | if (type == SSH_SMSG_FAILURE) | ||
867 | debug("AFS token for cell %s rejected.", server_cell); | ||
868 | else if (type != SSH_SMSG_SUCCESS) | ||
869 | packet_disconnect("Protocol error on AFS token response: %d", type); | ||
870 | } | ||
871 | } | ||
872 | |||
873 | #endif /* AFS */ | ||
874 | |||
875 | /* | ||
876 | * Tries to authenticate with any string-based challenge/response system. | ||
877 | * Note that the client code is not tied to s/key or TIS. | ||
878 | */ | ||
879 | int | ||
880 | try_skey_authentication() | ||
881 | { | ||
882 | int type, i; | ||
883 | int payload_len; | ||
884 | unsigned int clen; | ||
885 | char *challenge, *response; | ||
886 | |||
887 | debug("Doing skey authentication."); | ||
888 | |||
889 | /* request a challenge */ | ||
890 | packet_start(SSH_CMSG_AUTH_TIS); | ||
891 | packet_send(); | ||
892 | packet_write_wait(); | ||
893 | |||
894 | type = packet_read(&payload_len); | ||
895 | if (type != SSH_SMSG_FAILURE && | ||
896 | type != SSH_SMSG_AUTH_TIS_CHALLENGE) { | ||
897 | packet_disconnect("Protocol error: got %d in response " | ||
898 | "to skey-auth", type); | ||
899 | } | ||
900 | if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { | ||
901 | debug("No challenge for skey authentication."); | ||
902 | return 0; | ||
903 | } | ||
904 | challenge = packet_get_string(&clen); | ||
905 | packet_integrity_check(payload_len, (4 + clen), type); | ||
906 | if (options.cipher == SSH_CIPHER_NONE) | ||
907 | log("WARNING: Encryption is disabled! " | ||
908 | "Reponse will be transmitted in clear text."); | ||
909 | fprintf(stderr, "%s\n", challenge); | ||
910 | xfree(challenge); | ||
911 | fflush(stderr); | ||
912 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
913 | if (i != 0) | ||
914 | error("Permission denied, please try again."); | ||
915 | response = read_passphrase("Response: ", 0); | ||
916 | packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); | ||
917 | packet_put_string(response, strlen(response)); | ||
918 | memset(response, 0, strlen(response)); | ||
919 | xfree(response); | ||
920 | packet_send(); | ||
921 | packet_write_wait(); | ||
922 | type = packet_read(&payload_len); | ||
923 | if (type == SSH_SMSG_SUCCESS) | ||
924 | return 1; | ||
925 | if (type != SSH_SMSG_FAILURE) | ||
926 | packet_disconnect("Protocol error: got %d in response " | ||
927 | "to skey-auth-reponse", type); | ||
928 | } | ||
929 | /* failure */ | ||
930 | return 0; | ||
931 | } | ||
932 | |||
933 | /* | ||
934 | * Tries to authenticate with plain passwd authentication. | ||
935 | */ | ||
936 | int | ||
937 | try_password_authentication(char *prompt) | ||
938 | { | ||
939 | int type, i, payload_len; | ||
940 | char *password; | ||
941 | |||
942 | debug("Doing password authentication."); | ||
943 | if (options.cipher == SSH_CIPHER_NONE) | ||
944 | log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); | ||
945 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
946 | if (i != 0) | ||
947 | error("Permission denied, please try again."); | ||
948 | password = read_passphrase(prompt, 0); | ||
949 | packet_start(SSH_CMSG_AUTH_PASSWORD); | ||
950 | packet_put_string(password, strlen(password)); | ||
951 | memset(password, 0, strlen(password)); | ||
952 | xfree(password); | ||
953 | packet_send(); | ||
954 | packet_write_wait(); | ||
955 | |||
956 | type = packet_read(&payload_len); | ||
957 | if (type == SSH_SMSG_SUCCESS) | ||
958 | return 1; | ||
959 | if (type != SSH_SMSG_FAILURE) | ||
960 | packet_disconnect("Protocol error: got %d in response to passwd auth", type); | ||
961 | } | ||
962 | /* failure */ | ||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | char * | 300 | char * |
967 | chop(char *s) | 301 | chop(char *s) |
968 | { | 302 | { |
@@ -1060,7 +394,8 @@ ssh_exchange_identification() | |||
1060 | fatal("Protocol major versions differ: %d vs. %d", | 394 | fatal("Protocol major versions differ: %d vs. %d", |
1061 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 395 | (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
1062 | remote_major); | 396 | remote_major); |
1063 | 397 | if (compat20) | |
398 | packet_set_ssh2_format(); | ||
1064 | /* Send our own protocol version identification. */ | 399 | /* Send our own protocol version identification. */ |
1065 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | 400 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", |
1066 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, | 401 | compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, |
@@ -1122,7 +457,8 @@ read_yes_or_no(const char *prompt, int defval) | |||
1122 | */ | 457 | */ |
1123 | 458 | ||
1124 | void | 459 | void |
1125 | check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | 460 | check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, |
461 | const char *user_hostfile, const char *system_hostfile) | ||
1126 | { | 462 | { |
1127 | Key *file_key; | 463 | Key *file_key; |
1128 | char *ip = NULL; | 464 | char *ip = NULL; |
@@ -1141,6 +477,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1141 | * essentially disables host authentication for localhost; however, | 477 | * essentially disables host authentication for localhost; however, |
1142 | * this is probably not a real problem. | 478 | * this is probably not a real problem. |
1143 | */ | 479 | */ |
480 | /** hostaddr == 0! */ | ||
1144 | switch (hostaddr->sa_family) { | 481 | switch (hostaddr->sa_family) { |
1145 | case AF_INET: | 482 | case AF_INET: |
1146 | local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | 483 | local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; |
@@ -1184,19 +521,19 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1184 | * Check if the host key is present in the user\'s list of known | 521 | * Check if the host key is present in the user\'s list of known |
1185 | * hosts or in the systemwide list. | 522 | * hosts or in the systemwide list. |
1186 | */ | 523 | */ |
1187 | host_status = check_host_in_hostfile(options.user_hostfile, host, host_key, file_key); | 524 | host_status = check_host_in_hostfile(user_hostfile, host, host_key, file_key); |
1188 | if (host_status == HOST_NEW) | 525 | if (host_status == HOST_NEW) |
1189 | host_status = check_host_in_hostfile(options.system_hostfile, host, host_key, file_key); | 526 | host_status = check_host_in_hostfile(system_hostfile, host, host_key, file_key); |
1190 | /* | 527 | /* |
1191 | * Also perform check for the ip address, skip the check if we are | 528 | * Also perform check for the ip address, skip the check if we are |
1192 | * localhost or the hostname was an ip address to begin with | 529 | * localhost or the hostname was an ip address to begin with |
1193 | */ | 530 | */ |
1194 | if (options.check_host_ip && !local && strcmp(host, ip)) { | 531 | if (options.check_host_ip && !local && strcmp(host, ip)) { |
1195 | Key *ip_key = key_new(host_key->type); | 532 | Key *ip_key = key_new(host_key->type); |
1196 | ip_status = check_host_in_hostfile(options.user_hostfile, ip, host_key, ip_key); | 533 | ip_status = check_host_in_hostfile(user_hostfile, ip, host_key, ip_key); |
1197 | 534 | ||
1198 | if (ip_status == HOST_NEW) | 535 | if (ip_status == HOST_NEW) |
1199 | ip_status = check_host_in_hostfile(options.system_hostfile, ip, host_key, ip_key); | 536 | ip_status = check_host_in_hostfile(system_hostfile, ip, host_key, ip_key); |
1200 | if (host_status == HOST_CHANGED && | 537 | if (host_status == HOST_CHANGED && |
1201 | (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) | 538 | (ip_status != HOST_CHANGED || !key_equal(ip_key, file_key))) |
1202 | host_ip_differ = 1; | 539 | host_ip_differ = 1; |
@@ -1213,9 +550,9 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1213 | debug("Host '%.200s' is known and matches the host key.", host); | 550 | debug("Host '%.200s' is known and matches the host key.", host); |
1214 | if (options.check_host_ip) { | 551 | if (options.check_host_ip) { |
1215 | if (ip_status == HOST_NEW) { | 552 | if (ip_status == HOST_NEW) { |
1216 | if (!add_host_to_hostfile(options.user_hostfile, ip, host_key)) | 553 | if (!add_host_to_hostfile(user_hostfile, ip, host_key)) |
1217 | log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", | 554 | log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", |
1218 | ip, options.user_hostfile); | 555 | ip, user_hostfile); |
1219 | else | 556 | else |
1220 | log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", | 557 | log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", |
1221 | ip); | 558 | ip); |
@@ -1249,9 +586,9 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1249 | hostp = host; | 586 | hostp = host; |
1250 | 587 | ||
1251 | /* If not in strict mode, add the key automatically to the local known_hosts file. */ | 588 | /* If not in strict mode, add the key automatically to the local known_hosts file. */ |
1252 | if (!add_host_to_hostfile(options.user_hostfile, hostp, host_key)) | 589 | if (!add_host_to_hostfile(user_hostfile, hostp, host_key)) |
1253 | log("Failed to add the host to the list of known hosts (%.500s).", | 590 | log("Failed to add the host to the list of known hosts (%.500s).", |
1254 | options.user_hostfile); | 591 | user_hostfile); |
1255 | else | 592 | else |
1256 | log("Warning: Permanently added '%.200s' to the list of known hosts.", | 593 | log("Warning: Permanently added '%.200s' to the list of known hosts.", |
1257 | hostp); | 594 | hostp); |
@@ -1283,7 +620,7 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1283 | error("It is also possible that the host key has just been changed."); | 620 | error("It is also possible that the host key has just been changed."); |
1284 | error("Please contact your system administrator."); | 621 | error("Please contact your system administrator."); |
1285 | error("Add correct host key in %.100s to get rid of this message.", | 622 | error("Add correct host key in %.100s to get rid of this message.", |
1286 | options.user_hostfile); | 623 | user_hostfile); |
1287 | 624 | ||
1288 | /* | 625 | /* |
1289 | * If strict host key checking is in use, the user will have | 626 | * If strict host key checking is in use, the user will have |
@@ -1317,260 +654,22 @@ check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) | |||
1317 | if (options.check_host_ip) | 654 | if (options.check_host_ip) |
1318 | xfree(ip); | 655 | xfree(ip); |
1319 | } | 656 | } |
1320 | void | ||
1321 | check_rsa_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key) | ||
1322 | { | ||
1323 | Key k; | ||
1324 | k.type = KEY_RSA; | ||
1325 | k.rsa = host_key; | ||
1326 | check_host_key(host, hostaddr, &k); | ||
1327 | } | ||
1328 | 657 | ||
1329 | /* | 658 | /* |
1330 | * SSH2 key exchange | 659 | * Starts a dialog with the server, and authenticates the current user on the |
1331 | */ | 660 | * server. This does not need any extra privileges. The basic connection |
1332 | void | 661 | * to the server must already have been established before this is called. |
1333 | ssh_kex2(char *host, struct sockaddr *hostaddr) | 662 | * If login fails, this function prints an error and never returns. |
1334 | { | 663 | * This function does not require super-user privileges. |
1335 | Kex *kex; | ||
1336 | char *cprop[PROPOSAL_MAX]; | ||
1337 | char *sprop[PROPOSAL_MAX]; | ||
1338 | Buffer *client_kexinit; | ||
1339 | Buffer *server_kexinit; | ||
1340 | int payload_len, dlen; | ||
1341 | unsigned int klen, kout; | ||
1342 | char *ptr; | ||
1343 | char *signature = NULL; | ||
1344 | unsigned int slen; | ||
1345 | char *server_host_key_blob = NULL; | ||
1346 | Key *server_host_key; | ||
1347 | unsigned int sbloblen; | ||
1348 | DH *dh; | ||
1349 | BIGNUM *dh_server_pub = 0; | ||
1350 | BIGNUM *shared_secret = 0; | ||
1351 | int i; | ||
1352 | unsigned char *kbuf; | ||
1353 | unsigned char *hash; | ||
1354 | |||
1355 | /* KEXINIT */ | ||
1356 | |||
1357 | debug("Sending KEX init."); | ||
1358 | if (options.ciphers != NULL) { | ||
1359 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
1360 | myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | ||
1361 | } else if ( | ||
1362 | options.cipher == SSH_CIPHER_ARCFOUR || | ||
1363 | options.cipher == SSH_CIPHER_3DES_CBC || | ||
1364 | options.cipher == SSH_CIPHER_CAST128_CBC || | ||
1365 | options.cipher == SSH_CIPHER_BLOWFISH_CBC) { | ||
1366 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
1367 | myproposal[PROPOSAL_ENC_ALGS_STOC] = cipher_name(options.cipher); | ||
1368 | } | ||
1369 | if (options.compression) { | ||
1370 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; | ||
1371 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; | ||
1372 | } else { | ||
1373 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; | ||
1374 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; | ||
1375 | } | ||
1376 | for (i = 0; i < PROPOSAL_MAX; i++) | ||
1377 | cprop[i] = xstrdup(myproposal[i]); | ||
1378 | |||
1379 | client_kexinit = kex_init(cprop); | ||
1380 | packet_start(SSH2_MSG_KEXINIT); | ||
1381 | packet_put_raw(buffer_ptr(client_kexinit), buffer_len(client_kexinit)); | ||
1382 | packet_send(); | ||
1383 | packet_write_wait(); | ||
1384 | |||
1385 | debug("done"); | ||
1386 | |||
1387 | packet_read_expect(&payload_len, SSH2_MSG_KEXINIT); | ||
1388 | |||
1389 | /* save payload for session_id */ | ||
1390 | server_kexinit = xmalloc(sizeof(*server_kexinit)); | ||
1391 | buffer_init(server_kexinit); | ||
1392 | ptr = packet_get_raw(&payload_len); | ||
1393 | buffer_append(server_kexinit, ptr, payload_len); | ||
1394 | |||
1395 | /* skip cookie */ | ||
1396 | for (i = 0; i < 16; i++) | ||
1397 | (void) packet_get_char(); | ||
1398 | /* kex init proposal strings */ | ||
1399 | for (i = 0; i < PROPOSAL_MAX; i++) { | ||
1400 | sprop[i] = packet_get_string(NULL); | ||
1401 | debug("got kexinit string: %s", sprop[i]); | ||
1402 | } | ||
1403 | i = (int) packet_get_char(); | ||
1404 | debug("first kex follow == %d", i); | ||
1405 | i = packet_get_int(); | ||
1406 | debug("reserved == %d", i); | ||
1407 | packet_done(); | ||
1408 | |||
1409 | debug("done read kexinit"); | ||
1410 | kex = kex_choose_conf(cprop, sprop, 0); | ||
1411 | |||
1412 | /* KEXDH */ | ||
1413 | |||
1414 | debug("Sending SSH2_MSG_KEXDH_INIT."); | ||
1415 | |||
1416 | /* generate and send 'e', client DH public key */ | ||
1417 | dh = dh_new_group1(); | ||
1418 | packet_start(SSH2_MSG_KEXDH_INIT); | ||
1419 | packet_put_bignum2(dh->pub_key); | ||
1420 | packet_send(); | ||
1421 | packet_write_wait(); | ||
1422 | |||
1423 | #ifdef DEBUG_KEXDH | ||
1424 | fprintf(stderr, "\np= "); | ||
1425 | bignum_print(dh->p); | ||
1426 | fprintf(stderr, "\ng= "); | ||
1427 | bignum_print(dh->g); | ||
1428 | fprintf(stderr, "\npub= "); | ||
1429 | bignum_print(dh->pub_key); | ||
1430 | fprintf(stderr, "\n"); | ||
1431 | DHparams_print_fp(stderr, dh); | ||
1432 | #endif | ||
1433 | |||
1434 | debug("Wait SSH2_MSG_KEXDH_REPLY."); | ||
1435 | |||
1436 | packet_read_expect(&payload_len, SSH2_MSG_KEXDH_REPLY); | ||
1437 | |||
1438 | debug("Got SSH2_MSG_KEXDH_REPLY."); | ||
1439 | |||
1440 | /* key, cert */ | ||
1441 | server_host_key_blob = packet_get_string(&sbloblen); | ||
1442 | server_host_key = dsa_serverkey_from_blob(server_host_key_blob, sbloblen); | ||
1443 | if (server_host_key == NULL) | ||
1444 | fatal("cannot decode server_host_key_blob"); | ||
1445 | |||
1446 | check_host_key(host, hostaddr, server_host_key); | ||
1447 | |||
1448 | /* DH paramter f, server public DH key */ | ||
1449 | dh_server_pub = BN_new(); | ||
1450 | if (dh_server_pub == NULL) | ||
1451 | fatal("dh_server_pub == NULL"); | ||
1452 | packet_get_bignum2(dh_server_pub, &dlen); | ||
1453 | |||
1454 | #ifdef DEBUG_KEXDH | ||
1455 | fprintf(stderr, "\ndh_server_pub= "); | ||
1456 | bignum_print(dh_server_pub); | ||
1457 | fprintf(stderr, "\n"); | ||
1458 | debug("bits %d", BN_num_bits(dh_server_pub)); | ||
1459 | #endif | ||
1460 | |||
1461 | /* signed H */ | ||
1462 | signature = packet_get_string(&slen); | ||
1463 | packet_done(); | ||
1464 | |||
1465 | if (!dh_pub_is_valid(dh, dh_server_pub)) | ||
1466 | packet_disconnect("bad server public DH value"); | ||
1467 | |||
1468 | klen = DH_size(dh); | ||
1469 | kbuf = xmalloc(klen); | ||
1470 | kout = DH_compute_key(kbuf, dh_server_pub, dh); | ||
1471 | #ifdef DEBUG_KEXDH | ||
1472 | debug("shared secret: len %d/%d", klen, kout); | ||
1473 | fprintf(stderr, "shared secret == "); | ||
1474 | for (i = 0; i< kout; i++) | ||
1475 | fprintf(stderr, "%02x", (kbuf[i])&0xff); | ||
1476 | fprintf(stderr, "\n"); | ||
1477 | #endif | ||
1478 | shared_secret = BN_new(); | ||
1479 | |||
1480 | BN_bin2bn(kbuf, kout, shared_secret); | ||
1481 | memset(kbuf, 0, klen); | ||
1482 | xfree(kbuf); | ||
1483 | |||
1484 | /* calc and verify H */ | ||
1485 | hash = kex_hash( | ||
1486 | client_version_string, | ||
1487 | server_version_string, | ||
1488 | buffer_ptr(client_kexinit), buffer_len(client_kexinit), | ||
1489 | buffer_ptr(server_kexinit), buffer_len(server_kexinit), | ||
1490 | server_host_key_blob, sbloblen, | ||
1491 | dh->pub_key, | ||
1492 | dh_server_pub, | ||
1493 | shared_secret | ||
1494 | ); | ||
1495 | buffer_free(client_kexinit); | ||
1496 | buffer_free(server_kexinit); | ||
1497 | xfree(client_kexinit); | ||
1498 | xfree(server_kexinit); | ||
1499 | #ifdef DEBUG_KEXDH | ||
1500 | fprintf(stderr, "hash == "); | ||
1501 | for (i = 0; i< 20; i++) | ||
1502 | fprintf(stderr, "%02x", (hash[i])&0xff); | ||
1503 | fprintf(stderr, "\n"); | ||
1504 | #endif | ||
1505 | dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20); | ||
1506 | key_free(server_host_key); | ||
1507 | |||
1508 | kex_derive_keys(kex, hash, shared_secret); | ||
1509 | packet_set_kex(kex); | ||
1510 | |||
1511 | /* have keys, free DH */ | ||
1512 | DH_free(dh); | ||
1513 | |||
1514 | debug("Wait SSH2_MSG_NEWKEYS."); | ||
1515 | packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); | ||
1516 | packet_done(); | ||
1517 | debug("GOT SSH2_MSG_NEWKEYS."); | ||
1518 | |||
1519 | debug("send SSH2_MSG_NEWKEYS."); | ||
1520 | packet_start(SSH2_MSG_NEWKEYS); | ||
1521 | packet_send(); | ||
1522 | packet_write_wait(); | ||
1523 | debug("done: send SSH2_MSG_NEWKEYS."); | ||
1524 | |||
1525 | #ifdef DEBUG_KEXDH | ||
1526 | /* send 1st encrypted/maced/compressed message */ | ||
1527 | packet_start(SSH2_MSG_IGNORE); | ||
1528 | packet_put_cstring("markus"); | ||
1529 | packet_send(); | ||
1530 | packet_write_wait(); | ||
1531 | #endif | ||
1532 | debug("done: KEX2."); | ||
1533 | } | ||
1534 | /* | ||
1535 | * Authenticate user | ||
1536 | */ | 664 | */ |
1537 | void | 665 | void |
1538 | ssh_userauth2(int host_key_valid, RSA *own_host_key, | 666 | ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, |
1539 | uid_t original_real_uid, char *host) | 667 | struct sockaddr *hostaddr, uid_t original_real_uid) |
1540 | { | 668 | { |
1541 | int type; | ||
1542 | int plen; | ||
1543 | unsigned int dlen; | ||
1544 | int partial; | ||
1545 | struct passwd *pw; | 669 | struct passwd *pw; |
1546 | char prompt[80]; | 670 | char *host, *cp; |
1547 | char *server_user, *local_user; | 671 | char *server_user, *local_user; |
1548 | char *auths; | ||
1549 | char *password; | ||
1550 | char *service = "ssh-connection"; /* service name */ | ||
1551 | |||
1552 | debug("send SSH2_MSG_SERVICE_REQUEST"); | ||
1553 | packet_start(SSH2_MSG_SERVICE_REQUEST); | ||
1554 | packet_put_cstring("ssh-userauth"); | ||
1555 | packet_send(); | ||
1556 | packet_write_wait(); | ||
1557 | |||
1558 | type = packet_read(&plen); | ||
1559 | if (type != SSH2_MSG_SERVICE_ACCEPT) { | ||
1560 | fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); | ||
1561 | } | ||
1562 | if (packet_remaining() > 0) { | ||
1563 | char *reply = packet_get_string(&plen); | ||
1564 | debug("service_accept: %s", reply); | ||
1565 | xfree(reply); | ||
1566 | } else { | ||
1567 | /* payload empty for ssh-2.0.13 ?? */ | ||
1568 | log("buggy server: service_accept w/o service"); | ||
1569 | } | ||
1570 | packet_done(); | ||
1571 | debug("got SSH2_MSG_SERVICE_ACCEPT"); | ||
1572 | 672 | ||
1573 | /*XX COMMONCODE: */ | ||
1574 | /* Get local user name. Use it as server user if no user name was given. */ | 673 | /* Get local user name. Use it as server user if no user name was given. */ |
1575 | pw = getpwuid(original_real_uid); | 674 | pw = getpwuid(original_real_uid); |
1576 | if (!pw) | 675 | if (!pw) |
@@ -1578,396 +677,6 @@ ssh_userauth2(int host_key_valid, RSA *own_host_key, | |||
1578 | local_user = xstrdup(pw->pw_name); | 677 | local_user = xstrdup(pw->pw_name); |
1579 | server_user = options.user ? options.user : local_user; | 678 | server_user = options.user ? options.user : local_user; |
1580 | 679 | ||
1581 | /* INITIAL request for auth */ | ||
1582 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
1583 | packet_put_cstring(server_user); | ||
1584 | packet_put_cstring(service); | ||
1585 | packet_put_cstring("none"); | ||
1586 | packet_send(); | ||
1587 | packet_write_wait(); | ||
1588 | |||
1589 | for (;;) { | ||
1590 | type = packet_read(&plen); | ||
1591 | if (type == SSH2_MSG_USERAUTH_SUCCESS) | ||
1592 | break; | ||
1593 | if (type != SSH2_MSG_USERAUTH_FAILURE) | ||
1594 | fatal("access denied: %d", type); | ||
1595 | /* SSH2_MSG_USERAUTH_FAILURE means: try again */ | ||
1596 | auths = packet_get_string(&dlen); | ||
1597 | debug("authentications that can continue: %s", auths); | ||
1598 | partial = packet_get_char(); | ||
1599 | packet_done(); | ||
1600 | if (partial) | ||
1601 | debug("partial success"); | ||
1602 | if (strstr(auths, "password") == NULL) | ||
1603 | fatal("passwd auth not supported: %s", auths); | ||
1604 | xfree(auths); | ||
1605 | /* try passwd */ | ||
1606 | snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", | ||
1607 | server_user, host); | ||
1608 | password = read_passphrase(prompt, 0); | ||
1609 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
1610 | packet_put_cstring(server_user); | ||
1611 | packet_put_cstring(service); | ||
1612 | packet_put_cstring("password"); | ||
1613 | packet_put_char(0); | ||
1614 | packet_put_cstring(password); | ||
1615 | memset(password, 0, strlen(password)); | ||
1616 | xfree(password); | ||
1617 | packet_send(); | ||
1618 | packet_write_wait(); | ||
1619 | } | ||
1620 | packet_done(); | ||
1621 | debug("ssh-userauth2 successfull"); | ||
1622 | } | ||
1623 | |||
1624 | /* | ||
1625 | * SSH1 key exchange | ||
1626 | */ | ||
1627 | void | ||
1628 | ssh_kex(char *host, struct sockaddr *hostaddr) | ||
1629 | { | ||
1630 | int i; | ||
1631 | BIGNUM *key; | ||
1632 | RSA *host_key; | ||
1633 | RSA *public_key; | ||
1634 | int bits, rbits; | ||
1635 | int ssh_cipher_default = SSH_CIPHER_3DES; | ||
1636 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | ||
1637 | unsigned char cookie[8]; | ||
1638 | unsigned int supported_ciphers; | ||
1639 | unsigned int server_flags, client_flags; | ||
1640 | int payload_len, clen, sum_len = 0; | ||
1641 | u_int32_t rand = 0; | ||
1642 | |||
1643 | debug("Waiting for server public key."); | ||
1644 | |||
1645 | /* Wait for a public key packet from the server. */ | ||
1646 | packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); | ||
1647 | |||
1648 | /* Get cookie from the packet. */ | ||
1649 | for (i = 0; i < 8; i++) | ||
1650 | cookie[i] = packet_get_char(); | ||
1651 | |||
1652 | /* Get the public key. */ | ||
1653 | public_key = RSA_new(); | ||
1654 | bits = packet_get_int();/* bits */ | ||
1655 | public_key->e = BN_new(); | ||
1656 | packet_get_bignum(public_key->e, &clen); | ||
1657 | sum_len += clen; | ||
1658 | public_key->n = BN_new(); | ||
1659 | packet_get_bignum(public_key->n, &clen); | ||
1660 | sum_len += clen; | ||
1661 | |||
1662 | rbits = BN_num_bits(public_key->n); | ||
1663 | if (bits != rbits) { | ||
1664 | log("Warning: Server lies about size of server public key: " | ||
1665 | "actual size is %d bits vs. announced %d.", rbits, bits); | ||
1666 | log("Warning: This may be due to an old implementation of ssh."); | ||
1667 | } | ||
1668 | /* Get the host key. */ | ||
1669 | host_key = RSA_new(); | ||
1670 | bits = packet_get_int();/* bits */ | ||
1671 | host_key->e = BN_new(); | ||
1672 | packet_get_bignum(host_key->e, &clen); | ||
1673 | sum_len += clen; | ||
1674 | host_key->n = BN_new(); | ||
1675 | packet_get_bignum(host_key->n, &clen); | ||
1676 | sum_len += clen; | ||
1677 | |||
1678 | rbits = BN_num_bits(host_key->n); | ||
1679 | if (bits != rbits) { | ||
1680 | log("Warning: Server lies about size of server host key: " | ||
1681 | "actual size is %d bits vs. announced %d.", rbits, bits); | ||
1682 | log("Warning: This may be due to an old implementation of ssh."); | ||
1683 | } | ||
1684 | |||
1685 | /* Get protocol flags. */ | ||
1686 | server_flags = packet_get_int(); | ||
1687 | packet_set_protocol_flags(server_flags); | ||
1688 | |||
1689 | supported_ciphers = packet_get_int(); | ||
1690 | supported_authentications = packet_get_int(); | ||
1691 | |||
1692 | debug("Received server public key (%d bits) and host key (%d bits).", | ||
1693 | BN_num_bits(public_key->n), BN_num_bits(host_key->n)); | ||
1694 | |||
1695 | packet_integrity_check(payload_len, | ||
1696 | 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, | ||
1697 | SSH_SMSG_PUBLIC_KEY); | ||
1698 | |||
1699 | check_rsa_host_key(host, hostaddr, host_key); | ||
1700 | |||
1701 | client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; | ||
1702 | |||
1703 | compute_session_id(session_id, cookie, host_key->n, public_key->n); | ||
1704 | |||
1705 | /* Generate a session key. */ | ||
1706 | arc4random_stir(); | ||
1707 | |||
1708 | /* | ||
1709 | * Generate an encryption key for the session. The key is a 256 bit | ||
1710 | * random number, interpreted as a 32-byte key, with the least | ||
1711 | * significant 8 bits being the first byte of the key. | ||
1712 | */ | ||
1713 | for (i = 0; i < 32; i++) { | ||
1714 | if (i % 4 == 0) | ||
1715 | rand = arc4random(); | ||
1716 | session_key[i] = rand & 0xff; | ||
1717 | rand >>= 8; | ||
1718 | } | ||
1719 | |||
1720 | /* | ||
1721 | * According to the protocol spec, the first byte of the session key | ||
1722 | * is the highest byte of the integer. The session key is xored with | ||
1723 | * the first 16 bytes of the session id. | ||
1724 | */ | ||
1725 | key = BN_new(); | ||
1726 | BN_set_word(key, 0); | ||
1727 | for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { | ||
1728 | BN_lshift(key, key, 8); | ||
1729 | if (i < 16) | ||
1730 | BN_add_word(key, session_key[i] ^ session_id[i]); | ||
1731 | else | ||
1732 | BN_add_word(key, session_key[i]); | ||
1733 | } | ||
1734 | |||
1735 | /* | ||
1736 | * Encrypt the integer using the public key and host key of the | ||
1737 | * server (key with smaller modulus first). | ||
1738 | */ | ||
1739 | if (BN_cmp(public_key->n, host_key->n) < 0) { | ||
1740 | /* Public key has smaller modulus. */ | ||
1741 | if (BN_num_bits(host_key->n) < | ||
1742 | BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { | ||
1743 | fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " | ||
1744 | "SSH_KEY_BITS_RESERVED %d", | ||
1745 | BN_num_bits(host_key->n), | ||
1746 | BN_num_bits(public_key->n), | ||
1747 | SSH_KEY_BITS_RESERVED); | ||
1748 | } | ||
1749 | rsa_public_encrypt(key, key, public_key); | ||
1750 | rsa_public_encrypt(key, key, host_key); | ||
1751 | } else { | ||
1752 | /* Host key has smaller modulus (or they are equal). */ | ||
1753 | if (BN_num_bits(public_key->n) < | ||
1754 | BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { | ||
1755 | fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " | ||
1756 | "SSH_KEY_BITS_RESERVED %d", | ||
1757 | BN_num_bits(public_key->n), | ||
1758 | BN_num_bits(host_key->n), | ||
1759 | SSH_KEY_BITS_RESERVED); | ||
1760 | } | ||
1761 | rsa_public_encrypt(key, key, host_key); | ||
1762 | rsa_public_encrypt(key, key, public_key); | ||
1763 | } | ||
1764 | |||
1765 | /* Destroy the public keys since we no longer need them. */ | ||
1766 | RSA_free(public_key); | ||
1767 | RSA_free(host_key); | ||
1768 | |||
1769 | if (options.cipher == SSH_CIPHER_NOT_SET) { | ||
1770 | if (cipher_mask1() & supported_ciphers & (1 << ssh_cipher_default)) | ||
1771 | options.cipher = ssh_cipher_default; | ||
1772 | else { | ||
1773 | debug("Cipher %s not supported, using %.100s instead.", | ||
1774 | cipher_name(ssh_cipher_default), | ||
1775 | cipher_name(SSH_FALLBACK_CIPHER)); | ||
1776 | options.cipher = SSH_FALLBACK_CIPHER; | ||
1777 | } | ||
1778 | } | ||
1779 | /* Check that the selected cipher is supported. */ | ||
1780 | if (!(supported_ciphers & (1 << options.cipher))) | ||
1781 | fatal("Selected cipher type %.100s not supported by server.", | ||
1782 | cipher_name(options.cipher)); | ||
1783 | |||
1784 | debug("Encryption type: %.100s", cipher_name(options.cipher)); | ||
1785 | |||
1786 | /* Send the encrypted session key to the server. */ | ||
1787 | packet_start(SSH_CMSG_SESSION_KEY); | ||
1788 | packet_put_char(options.cipher); | ||
1789 | |||
1790 | /* Send the cookie back to the server. */ | ||
1791 | for (i = 0; i < 8; i++) | ||
1792 | packet_put_char(cookie[i]); | ||
1793 | |||
1794 | /* Send and destroy the encrypted encryption key integer. */ | ||
1795 | packet_put_bignum(key); | ||
1796 | BN_clear_free(key); | ||
1797 | |||
1798 | /* Send protocol flags. */ | ||
1799 | packet_put_int(client_flags); | ||
1800 | |||
1801 | /* Send the packet now. */ | ||
1802 | packet_send(); | ||
1803 | packet_write_wait(); | ||
1804 | |||
1805 | debug("Sent encrypted session key."); | ||
1806 | |||
1807 | /* Set the encryption key. */ | ||
1808 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); | ||
1809 | |||
1810 | /* We will no longer need the session key here. Destroy any extra copies. */ | ||
1811 | memset(session_key, 0, sizeof(session_key)); | ||
1812 | |||
1813 | /* | ||
1814 | * Expect a success message from the server. Note that this message | ||
1815 | * will be received in encrypted form. | ||
1816 | */ | ||
1817 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
1818 | |||
1819 | debug("Received encrypted confirmation."); | ||
1820 | } | ||
1821 | |||
1822 | /* | ||
1823 | * Authenticate user | ||
1824 | */ | ||
1825 | void | ||
1826 | ssh_userauth(int host_key_valid, RSA *own_host_key, | ||
1827 | uid_t original_real_uid, char *host) | ||
1828 | { | ||
1829 | int i, type; | ||
1830 | int payload_len; | ||
1831 | struct passwd *pw; | ||
1832 | const char *server_user, *local_user; | ||
1833 | |||
1834 | /* Get local user name. Use it as server user if no user name was given. */ | ||
1835 | pw = getpwuid(original_real_uid); | ||
1836 | if (!pw) | ||
1837 | fatal("User id %d not found from user database.", original_real_uid); | ||
1838 | local_user = xstrdup(pw->pw_name); | ||
1839 | server_user = options.user ? options.user : local_user; | ||
1840 | |||
1841 | /* Send the name of the user to log in as on the server. */ | ||
1842 | packet_start(SSH_CMSG_USER); | ||
1843 | packet_put_string(server_user, strlen(server_user)); | ||
1844 | packet_send(); | ||
1845 | packet_write_wait(); | ||
1846 | |||
1847 | /* | ||
1848 | * The server should respond with success if no authentication is | ||
1849 | * needed (the user has no password). Otherwise the server responds | ||
1850 | * with failure. | ||
1851 | */ | ||
1852 | type = packet_read(&payload_len); | ||
1853 | |||
1854 | /* check whether the connection was accepted without authentication. */ | ||
1855 | if (type == SSH_SMSG_SUCCESS) | ||
1856 | return; | ||
1857 | if (type != SSH_SMSG_FAILURE) | ||
1858 | packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", | ||
1859 | type); | ||
1860 | |||
1861 | #ifdef AFS | ||
1862 | /* Try Kerberos tgt passing if the server supports it. */ | ||
1863 | if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && | ||
1864 | options.kerberos_tgt_passing) { | ||
1865 | if (options.cipher == SSH_CIPHER_NONE) | ||
1866 | log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); | ||
1867 | (void) send_kerberos_tgt(); | ||
1868 | } | ||
1869 | /* Try AFS token passing if the server supports it. */ | ||
1870 | if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && | ||
1871 | options.afs_token_passing && k_hasafs()) { | ||
1872 | if (options.cipher == SSH_CIPHER_NONE) | ||
1873 | log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); | ||
1874 | send_afs_tokens(); | ||
1875 | } | ||
1876 | #endif /* AFS */ | ||
1877 | |||
1878 | #ifdef KRB4 | ||
1879 | if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && | ||
1880 | options.kerberos_authentication) { | ||
1881 | debug("Trying Kerberos authentication."); | ||
1882 | if (try_kerberos_authentication()) { | ||
1883 | /* The server should respond with success or failure. */ | ||
1884 | type = packet_read(&payload_len); | ||
1885 | if (type == SSH_SMSG_SUCCESS) | ||
1886 | return; | ||
1887 | if (type != SSH_SMSG_FAILURE) | ||
1888 | packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); | ||
1889 | } | ||
1890 | } | ||
1891 | #endif /* KRB4 */ | ||
1892 | |||
1893 | /* | ||
1894 | * Use rhosts authentication if running in privileged socket and we | ||
1895 | * do not wish to remain anonymous. | ||
1896 | */ | ||
1897 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && | ||
1898 | options.rhosts_authentication) { | ||
1899 | debug("Trying rhosts authentication."); | ||
1900 | packet_start(SSH_CMSG_AUTH_RHOSTS); | ||
1901 | packet_put_string(local_user, strlen(local_user)); | ||
1902 | packet_send(); | ||
1903 | packet_write_wait(); | ||
1904 | |||
1905 | /* The server should respond with success or failure. */ | ||
1906 | type = packet_read(&payload_len); | ||
1907 | if (type == SSH_SMSG_SUCCESS) | ||
1908 | return; | ||
1909 | if (type != SSH_SMSG_FAILURE) | ||
1910 | packet_disconnect("Protocol error: got %d in response to rhosts auth", | ||
1911 | type); | ||
1912 | } | ||
1913 | /* | ||
1914 | * Try .rhosts or /etc/hosts.equiv authentication with RSA host | ||
1915 | * authentication. | ||
1916 | */ | ||
1917 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && | ||
1918 | options.rhosts_rsa_authentication && host_key_valid) { | ||
1919 | if (try_rhosts_rsa_authentication(local_user, own_host_key)) | ||
1920 | return; | ||
1921 | } | ||
1922 | /* Try RSA authentication if the server supports it. */ | ||
1923 | if ((supported_authentications & (1 << SSH_AUTH_RSA)) && | ||
1924 | options.rsa_authentication) { | ||
1925 | /* | ||
1926 | * Try RSA authentication using the authentication agent. The | ||
1927 | * agent is tried first because no passphrase is needed for | ||
1928 | * it, whereas identity files may require passphrases. | ||
1929 | */ | ||
1930 | if (try_agent_authentication()) | ||
1931 | return; | ||
1932 | |||
1933 | /* Try RSA authentication for each identity. */ | ||
1934 | for (i = 0; i < options.num_identity_files; i++) | ||
1935 | if (try_rsa_authentication(options.identity_files[i])) | ||
1936 | return; | ||
1937 | } | ||
1938 | /* Try skey authentication if the server supports it. */ | ||
1939 | if ((supported_authentications & (1 << SSH_AUTH_TIS)) && | ||
1940 | options.skey_authentication && !options.batch_mode) { | ||
1941 | if (try_skey_authentication()) | ||
1942 | return; | ||
1943 | } | ||
1944 | /* Try password authentication if the server supports it. */ | ||
1945 | if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && | ||
1946 | options.password_authentication && !options.batch_mode) { | ||
1947 | char prompt[80]; | ||
1948 | |||
1949 | snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", | ||
1950 | server_user, host); | ||
1951 | if (try_password_authentication(prompt)) | ||
1952 | return; | ||
1953 | } | ||
1954 | /* All authentication methods have failed. Exit with an error message. */ | ||
1955 | fatal("Permission denied."); | ||
1956 | /* NOTREACHED */ | ||
1957 | } | ||
1958 | /* | ||
1959 | * Starts a dialog with the server, and authenticates the current user on the | ||
1960 | * server. This does not need any extra privileges. The basic connection | ||
1961 | * to the server must already have been established before this is called. | ||
1962 | * If login fails, this function prints an error and never returns. | ||
1963 | * This function does not require super-user privileges. | ||
1964 | */ | ||
1965 | void | ||
1966 | ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, | ||
1967 | struct sockaddr *hostaddr, uid_t original_real_uid) | ||
1968 | { | ||
1969 | char *host, *cp; | ||
1970 | |||
1971 | /* Convert the user-supplied hostname into all lowercase. */ | 680 | /* Convert the user-supplied hostname into all lowercase. */ |
1972 | host = xstrdup(orighost); | 681 | host = xstrdup(orighost); |
1973 | for (cp = host; *cp; cp++) | 682 | for (cp = host; *cp; cp++) |
@@ -1984,12 +693,9 @@ ssh_login(int host_key_valid, RSA *own_host_key, const char *orighost, | |||
1984 | /* authenticate user */ | 693 | /* authenticate user */ |
1985 | if (compat20) { | 694 | if (compat20) { |
1986 | ssh_kex2(host, hostaddr); | 695 | ssh_kex2(host, hostaddr); |
1987 | ssh_userauth2(host_key_valid, own_host_key, original_real_uid, host); | 696 | ssh_userauth2(server_user, host); |
1988 | } else { | 697 | } else { |
1989 | supported_authentications = 0; | ||
1990 | ssh_kex(host, hostaddr); | 698 | ssh_kex(host, hostaddr); |
1991 | if (supported_authentications == 0) | 699 | ssh_userauth(local_user, server_user, host, host_key_valid, own_host_key); |
1992 | fatal("supported_authentications == 0."); | ||
1993 | ssh_userauth(host_key_valid, own_host_key, original_real_uid, host); | ||
1994 | } | 700 | } |
1995 | } | 701 | } |
diff --git a/sshconnect.h b/sshconnect.h new file mode 100644 index 000000000..13d395fd6 --- /dev/null +++ b/sshconnect.h | |||
@@ -0,0 +1,16 @@ | |||
1 | #ifndef SSHCONNECT_H | ||
2 | #define SSHCONNECT_H | ||
3 | |||
4 | void | ||
5 | check_host_key(char *host, struct sockaddr *hostaddr, Key *host_key, | ||
6 | const char *user_hostfile, const char *system_hostfile); | ||
7 | |||
8 | void ssh_kex(char *host, struct sockaddr *hostaddr); | ||
9 | void | ||
10 | ssh_userauth(const char* local_user, const char* server_user, char *host, | ||
11 | int host_key_valid, RSA *own_host_key); | ||
12 | |||
13 | void ssh_kex2(char *host, struct sockaddr *hostaddr); | ||
14 | void ssh_userauth2(const char *server_user, char *host); | ||
15 | |||
16 | #endif | ||
diff --git a/sshconnect1.c b/sshconnect1.c new file mode 100644 index 000000000..c5a76654b --- /dev/null +++ b/sshconnect1.c | |||
@@ -0,0 +1,1020 @@ | |||
1 | /* | ||
2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
4 | * All rights reserved | ||
5 | * Created: Sat Mar 18 22:15:47 1995 ylo | ||
6 | * Code to connect to a remote host, and to perform the client side of the | ||
7 | * login (authentication) dialog. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include "includes.h" | ||
12 | RCSID("$OpenBSD: sshconnect1.c,v 1.1 2000/04/26 21:28:33 markus Exp $"); | ||
13 | |||
14 | #include <openssl/bn.h> | ||
15 | #include <openssl/dsa.h> | ||
16 | #include <openssl/rsa.h> | ||
17 | #include <openssl/evp.h> | ||
18 | |||
19 | #include "xmalloc.h" | ||
20 | #include "rsa.h" | ||
21 | #include "ssh.h" | ||
22 | #include "buffer.h" | ||
23 | #include "packet.h" | ||
24 | #include "authfd.h" | ||
25 | #include "cipher.h" | ||
26 | #include "mpaux.h" | ||
27 | #include "uidswap.h" | ||
28 | #include "readconf.h" | ||
29 | #include "key.h" | ||
30 | #include "sshconnect.h" | ||
31 | #include "authfile.h" | ||
32 | |||
33 | /* Session id for the current session. */ | ||
34 | unsigned char session_id[16]; | ||
35 | unsigned int supported_authentications = 0; | ||
36 | |||
37 | extern Options options; | ||
38 | extern char *__progname; | ||
39 | |||
40 | /* | ||
41 | * Checks if the user has an authentication agent, and if so, tries to | ||
42 | * authenticate using the agent. | ||
43 | */ | ||
44 | int | ||
45 | try_agent_authentication() | ||
46 | { | ||
47 | int status, type; | ||
48 | char *comment; | ||
49 | AuthenticationConnection *auth; | ||
50 | unsigned char response[16]; | ||
51 | unsigned int i; | ||
52 | BIGNUM *e, *n, *challenge; | ||
53 | |||
54 | /* Get connection to the agent. */ | ||
55 | auth = ssh_get_authentication_connection(); | ||
56 | if (!auth) | ||
57 | return 0; | ||
58 | |||
59 | e = BN_new(); | ||
60 | n = BN_new(); | ||
61 | challenge = BN_new(); | ||
62 | |||
63 | /* Loop through identities served by the agent. */ | ||
64 | for (status = ssh_get_first_identity(auth, e, n, &comment); | ||
65 | status; | ||
66 | status = ssh_get_next_identity(auth, e, n, &comment)) { | ||
67 | int plen, clen; | ||
68 | |||
69 | /* Try this identity. */ | ||
70 | debug("Trying RSA authentication via agent with '%.100s'", comment); | ||
71 | xfree(comment); | ||
72 | |||
73 | /* Tell the server that we are willing to authenticate using this key. */ | ||
74 | packet_start(SSH_CMSG_AUTH_RSA); | ||
75 | packet_put_bignum(n); | ||
76 | packet_send(); | ||
77 | packet_write_wait(); | ||
78 | |||
79 | /* Wait for server's response. */ | ||
80 | type = packet_read(&plen); | ||
81 | |||
82 | /* The server sends failure if it doesn\'t like our key or | ||
83 | does not support RSA authentication. */ | ||
84 | if (type == SSH_SMSG_FAILURE) { | ||
85 | debug("Server refused our key."); | ||
86 | continue; | ||
87 | } | ||
88 | /* Otherwise it should have sent a challenge. */ | ||
89 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
90 | packet_disconnect("Protocol error during RSA authentication: %d", | ||
91 | type); | ||
92 | |||
93 | packet_get_bignum(challenge, &clen); | ||
94 | |||
95 | packet_integrity_check(plen, clen, type); | ||
96 | |||
97 | debug("Received RSA challenge from server."); | ||
98 | |||
99 | /* Ask the agent to decrypt the challenge. */ | ||
100 | if (!ssh_decrypt_challenge(auth, e, n, challenge, | ||
101 | session_id, 1, response)) { | ||
102 | /* The agent failed to authenticate this identifier although it | ||
103 | advertised it supports this. Just return a wrong value. */ | ||
104 | log("Authentication agent failed to decrypt challenge."); | ||
105 | memset(response, 0, sizeof(response)); | ||
106 | } | ||
107 | debug("Sending response to RSA challenge."); | ||
108 | |||
109 | /* Send the decrypted challenge back to the server. */ | ||
110 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
111 | for (i = 0; i < 16; i++) | ||
112 | packet_put_char(response[i]); | ||
113 | packet_send(); | ||
114 | packet_write_wait(); | ||
115 | |||
116 | /* Wait for response from the server. */ | ||
117 | type = packet_read(&plen); | ||
118 | |||
119 | /* The server returns success if it accepted the authentication. */ | ||
120 | if (type == SSH_SMSG_SUCCESS) { | ||
121 | debug("RSA authentication accepted by server."); | ||
122 | BN_clear_free(e); | ||
123 | BN_clear_free(n); | ||
124 | BN_clear_free(challenge); | ||
125 | return 1; | ||
126 | } | ||
127 | /* Otherwise it should return failure. */ | ||
128 | if (type != SSH_SMSG_FAILURE) | ||
129 | packet_disconnect("Protocol error waiting RSA auth response: %d", | ||
130 | type); | ||
131 | } | ||
132 | |||
133 | BN_clear_free(e); | ||
134 | BN_clear_free(n); | ||
135 | BN_clear_free(challenge); | ||
136 | |||
137 | debug("RSA authentication using agent refused."); | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | /* | ||
142 | * Computes the proper response to a RSA challenge, and sends the response to | ||
143 | * the server. | ||
144 | */ | ||
145 | void | ||
146 | respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) | ||
147 | { | ||
148 | unsigned char buf[32], response[16]; | ||
149 | MD5_CTX md; | ||
150 | int i, len; | ||
151 | |||
152 | /* Decrypt the challenge using the private key. */ | ||
153 | rsa_private_decrypt(challenge, challenge, prv); | ||
154 | |||
155 | /* Compute the response. */ | ||
156 | /* The response is MD5 of decrypted challenge plus session id. */ | ||
157 | len = BN_num_bytes(challenge); | ||
158 | if (len <= 0 || len > sizeof(buf)) | ||
159 | packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", | ||
160 | len); | ||
161 | |||
162 | memset(buf, 0, sizeof(buf)); | ||
163 | BN_bn2bin(challenge, buf + sizeof(buf) - len); | ||
164 | MD5_Init(&md); | ||
165 | MD5_Update(&md, buf, 32); | ||
166 | MD5_Update(&md, session_id, 16); | ||
167 | MD5_Final(response, &md); | ||
168 | |||
169 | debug("Sending response to host key RSA challenge."); | ||
170 | |||
171 | /* Send the response back to the server. */ | ||
172 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
173 | for (i = 0; i < 16; i++) | ||
174 | packet_put_char(response[i]); | ||
175 | packet_send(); | ||
176 | packet_write_wait(); | ||
177 | |||
178 | memset(buf, 0, sizeof(buf)); | ||
179 | memset(response, 0, sizeof(response)); | ||
180 | memset(&md, 0, sizeof(md)); | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Checks if the user has authentication file, and if so, tries to authenticate | ||
185 | * the user using it. | ||
186 | */ | ||
187 | int | ||
188 | try_rsa_authentication(const char *authfile) | ||
189 | { | ||
190 | BIGNUM *challenge; | ||
191 | Key *public; | ||
192 | Key *private; | ||
193 | char *passphrase, *comment; | ||
194 | int type, i; | ||
195 | int plen, clen; | ||
196 | |||
197 | /* Try to load identification for the authentication key. */ | ||
198 | public = key_new(KEY_RSA); | ||
199 | if (!load_public_key(authfile, public, &comment)) { | ||
200 | key_free(public); | ||
201 | /* Could not load it. Fail. */ | ||
202 | return 0; | ||
203 | } | ||
204 | debug("Trying RSA authentication with key '%.100s'", comment); | ||
205 | |||
206 | /* Tell the server that we are willing to authenticate using this key. */ | ||
207 | packet_start(SSH_CMSG_AUTH_RSA); | ||
208 | packet_put_bignum(public->rsa->n); | ||
209 | packet_send(); | ||
210 | packet_write_wait(); | ||
211 | |||
212 | /* We no longer need the public key. */ | ||
213 | key_free(public); | ||
214 | |||
215 | /* Wait for server's response. */ | ||
216 | type = packet_read(&plen); | ||
217 | |||
218 | /* | ||
219 | * The server responds with failure if it doesn\'t like our key or | ||
220 | * doesn\'t support RSA authentication. | ||
221 | */ | ||
222 | if (type == SSH_SMSG_FAILURE) { | ||
223 | debug("Server refused our key."); | ||
224 | xfree(comment); | ||
225 | return 0; | ||
226 | } | ||
227 | /* Otherwise, the server should respond with a challenge. */ | ||
228 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
229 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
230 | |||
231 | /* Get the challenge from the packet. */ | ||
232 | challenge = BN_new(); | ||
233 | packet_get_bignum(challenge, &clen); | ||
234 | |||
235 | packet_integrity_check(plen, clen, type); | ||
236 | |||
237 | debug("Received RSA challenge from server."); | ||
238 | |||
239 | private = key_new(KEY_RSA); | ||
240 | /* | ||
241 | * Load the private key. Try first with empty passphrase; if it | ||
242 | * fails, ask for a passphrase. | ||
243 | */ | ||
244 | if (!load_private_key(authfile, "", private, NULL)) { | ||
245 | char buf[300]; | ||
246 | snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", | ||
247 | comment); | ||
248 | if (!options.batch_mode) | ||
249 | passphrase = read_passphrase(buf, 0); | ||
250 | else { | ||
251 | debug("Will not query passphrase for %.100s in batch mode.", | ||
252 | comment); | ||
253 | passphrase = xstrdup(""); | ||
254 | } | ||
255 | |||
256 | /* Load the authentication file using the pasphrase. */ | ||
257 | if (!load_private_key(authfile, passphrase, private, NULL)) { | ||
258 | memset(passphrase, 0, strlen(passphrase)); | ||
259 | xfree(passphrase); | ||
260 | error("Bad passphrase."); | ||
261 | |||
262 | /* Send a dummy response packet to avoid protocol error. */ | ||
263 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
264 | for (i = 0; i < 16; i++) | ||
265 | packet_put_char(0); | ||
266 | packet_send(); | ||
267 | packet_write_wait(); | ||
268 | |||
269 | /* Expect the server to reject it... */ | ||
270 | packet_read_expect(&plen, SSH_SMSG_FAILURE); | ||
271 | xfree(comment); | ||
272 | return 0; | ||
273 | } | ||
274 | /* Destroy the passphrase. */ | ||
275 | memset(passphrase, 0, strlen(passphrase)); | ||
276 | xfree(passphrase); | ||
277 | } | ||
278 | /* We no longer need the comment. */ | ||
279 | xfree(comment); | ||
280 | |||
281 | /* Compute and send a response to the challenge. */ | ||
282 | respond_to_rsa_challenge(challenge, private->rsa); | ||
283 | |||
284 | /* Destroy the private key. */ | ||
285 | key_free(private); | ||
286 | |||
287 | /* We no longer need the challenge. */ | ||
288 | BN_clear_free(challenge); | ||
289 | |||
290 | /* Wait for response from the server. */ | ||
291 | type = packet_read(&plen); | ||
292 | if (type == SSH_SMSG_SUCCESS) { | ||
293 | debug("RSA authentication accepted by server."); | ||
294 | return 1; | ||
295 | } | ||
296 | if (type != SSH_SMSG_FAILURE) | ||
297 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
298 | debug("RSA authentication refused."); | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv | ||
304 | * authentication and RSA host authentication. | ||
305 | */ | ||
306 | int | ||
307 | try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) | ||
308 | { | ||
309 | int type; | ||
310 | BIGNUM *challenge; | ||
311 | int plen, clen; | ||
312 | |||
313 | debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); | ||
314 | |||
315 | /* Tell the server that we are willing to authenticate using this key. */ | ||
316 | packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); | ||
317 | packet_put_string(local_user, strlen(local_user)); | ||
318 | packet_put_int(BN_num_bits(host_key->n)); | ||
319 | packet_put_bignum(host_key->e); | ||
320 | packet_put_bignum(host_key->n); | ||
321 | packet_send(); | ||
322 | packet_write_wait(); | ||
323 | |||
324 | /* Wait for server's response. */ | ||
325 | type = packet_read(&plen); | ||
326 | |||
327 | /* The server responds with failure if it doesn't admit our | ||
328 | .rhosts authentication or doesn't know our host key. */ | ||
329 | if (type == SSH_SMSG_FAILURE) { | ||
330 | debug("Server refused our rhosts authentication or host key."); | ||
331 | return 0; | ||
332 | } | ||
333 | /* Otherwise, the server should respond with a challenge. */ | ||
334 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
335 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
336 | |||
337 | /* Get the challenge from the packet. */ | ||
338 | challenge = BN_new(); | ||
339 | packet_get_bignum(challenge, &clen); | ||
340 | |||
341 | packet_integrity_check(plen, clen, type); | ||
342 | |||
343 | debug("Received RSA challenge for host key from server."); | ||
344 | |||
345 | /* Compute a response to the challenge. */ | ||
346 | respond_to_rsa_challenge(challenge, host_key); | ||
347 | |||
348 | /* We no longer need the challenge. */ | ||
349 | BN_clear_free(challenge); | ||
350 | |||
351 | /* Wait for response from the server. */ | ||
352 | type = packet_read(&plen); | ||
353 | if (type == SSH_SMSG_SUCCESS) { | ||
354 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); | ||
355 | return 1; | ||
356 | } | ||
357 | if (type != SSH_SMSG_FAILURE) | ||
358 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
359 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); | ||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | #ifdef KRB4 | ||
364 | int | ||
365 | try_kerberos_authentication() | ||
366 | { | ||
367 | KTEXT_ST auth; /* Kerberos data */ | ||
368 | char *reply; | ||
369 | char inst[INST_SZ]; | ||
370 | char *realm; | ||
371 | CREDENTIALS cred; | ||
372 | int r, type, plen; | ||
373 | socklen_t slen; | ||
374 | Key_schedule schedule; | ||
375 | u_long checksum, cksum; | ||
376 | MSG_DAT msg_data; | ||
377 | struct sockaddr_in local, foreign; | ||
378 | struct stat st; | ||
379 | |||
380 | /* Don't do anything if we don't have any tickets. */ | ||
381 | if (stat(tkt_string(), &st) < 0) | ||
382 | return 0; | ||
383 | |||
384 | strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); | ||
385 | |||
386 | realm = (char *) krb_realmofhost(get_canonical_hostname()); | ||
387 | if (!realm) { | ||
388 | debug("Kerberos V4: no realm for %s", get_canonical_hostname()); | ||
389 | return 0; | ||
390 | } | ||
391 | /* This can really be anything. */ | ||
392 | checksum = (u_long) getpid(); | ||
393 | |||
394 | r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); | ||
395 | if (r != KSUCCESS) { | ||
396 | debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); | ||
397 | return 0; | ||
398 | } | ||
399 | /* Get session key to decrypt the server's reply with. */ | ||
400 | r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); | ||
401 | if (r != KSUCCESS) { | ||
402 | debug("get_cred failed: %s", krb_err_txt[r]); | ||
403 | return 0; | ||
404 | } | ||
405 | des_key_sched((des_cblock *) cred.session, schedule); | ||
406 | |||
407 | /* Send authentication info to server. */ | ||
408 | packet_start(SSH_CMSG_AUTH_KERBEROS); | ||
409 | packet_put_string((char *) auth.dat, auth.length); | ||
410 | packet_send(); | ||
411 | packet_write_wait(); | ||
412 | |||
413 | /* Zero the buffer. */ | ||
414 | (void) memset(auth.dat, 0, MAX_KTXT_LEN); | ||
415 | |||
416 | slen = sizeof(local); | ||
417 | memset(&local, 0, sizeof(local)); | ||
418 | if (getsockname(packet_get_connection_in(), | ||
419 | (struct sockaddr *) & local, &slen) < 0) | ||
420 | debug("getsockname failed: %s", strerror(errno)); | ||
421 | |||
422 | slen = sizeof(foreign); | ||
423 | memset(&foreign, 0, sizeof(foreign)); | ||
424 | if (getpeername(packet_get_connection_in(), | ||
425 | (struct sockaddr *) & foreign, &slen) < 0) { | ||
426 | debug("getpeername failed: %s", strerror(errno)); | ||
427 | fatal_cleanup(); | ||
428 | } | ||
429 | /* Get server reply. */ | ||
430 | type = packet_read(&plen); | ||
431 | switch (type) { | ||
432 | case SSH_SMSG_FAILURE: | ||
433 | /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ | ||
434 | debug("Kerberos V4 authentication failed."); | ||
435 | return 0; | ||
436 | break; | ||
437 | |||
438 | case SSH_SMSG_AUTH_KERBEROS_RESPONSE: | ||
439 | /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ | ||
440 | debug("Kerberos V4 authentication accepted."); | ||
441 | |||
442 | /* Get server's response. */ | ||
443 | reply = packet_get_string((unsigned int *) &auth.length); | ||
444 | memcpy(auth.dat, reply, auth.length); | ||
445 | xfree(reply); | ||
446 | |||
447 | packet_integrity_check(plen, 4 + auth.length, type); | ||
448 | |||
449 | /* | ||
450 | * If his response isn't properly encrypted with the session | ||
451 | * key, and the decrypted checksum fails to match, he's | ||
452 | * bogus. Bail out. | ||
453 | */ | ||
454 | r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, | ||
455 | &foreign, &local, &msg_data); | ||
456 | if (r != KSUCCESS) { | ||
457 | debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); | ||
458 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
459 | } | ||
460 | /* Fetch the (incremented) checksum that we supplied in the request. */ | ||
461 | (void) memcpy((char *) &cksum, (char *) msg_data.app_data, sizeof(cksum)); | ||
462 | cksum = ntohl(cksum); | ||
463 | |||
464 | /* If it matches, we're golden. */ | ||
465 | if (cksum == checksum + 1) { | ||
466 | debug("Kerberos V4 challenge successful."); | ||
467 | return 1; | ||
468 | } else | ||
469 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
470 | break; | ||
471 | |||
472 | default: | ||
473 | packet_disconnect("Protocol error on Kerberos V4 response: %d", type); | ||
474 | } | ||
475 | return 0; | ||
476 | } | ||
477 | |||
478 | #endif /* KRB4 */ | ||
479 | |||
480 | #ifdef AFS | ||
481 | int | ||
482 | send_kerberos_tgt() | ||
483 | { | ||
484 | CREDENTIALS *creds; | ||
485 | char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; | ||
486 | int r, type, plen; | ||
487 | char buffer[8192]; | ||
488 | struct stat st; | ||
489 | |||
490 | /* Don't do anything if we don't have any tickets. */ | ||
491 | if (stat(tkt_string(), &st) < 0) | ||
492 | return 0; | ||
493 | |||
494 | creds = xmalloc(sizeof(*creds)); | ||
495 | |||
496 | if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { | ||
497 | debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]); | ||
498 | return 0; | ||
499 | } | ||
500 | if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { | ||
501 | debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); | ||
502 | return 0; | ||
503 | } | ||
504 | if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { | ||
505 | debug("Kerberos V4 ticket expired: %s", TKT_FILE); | ||
506 | return 0; | ||
507 | } | ||
508 | creds_to_radix(creds, (unsigned char *)buffer); | ||
509 | xfree(creds); | ||
510 | |||
511 | packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); | ||
512 | packet_put_string(buffer, strlen(buffer)); | ||
513 | packet_send(); | ||
514 | packet_write_wait(); | ||
515 | |||
516 | type = packet_read(&plen); | ||
517 | |||
518 | if (type == SSH_SMSG_FAILURE) | ||
519 | debug("Kerberos TGT for realm %s rejected.", prealm); | ||
520 | else if (type != SSH_SMSG_SUCCESS) | ||
521 | packet_disconnect("Protocol error on Kerberos TGT response: %d", type); | ||
522 | |||
523 | return 1; | ||
524 | } | ||
525 | |||
526 | void | ||
527 | send_afs_tokens(void) | ||
528 | { | ||
529 | CREDENTIALS creds; | ||
530 | struct ViceIoctl parms; | ||
531 | struct ClearToken ct; | ||
532 | int i, type, len, plen; | ||
533 | char buf[2048], *p, *server_cell; | ||
534 | char buffer[8192]; | ||
535 | |||
536 | /* Move over ktc_GetToken, here's something leaner. */ | ||
537 | for (i = 0; i < 100; i++) { /* just in case */ | ||
538 | parms.in = (char *) &i; | ||
539 | parms.in_size = sizeof(i); | ||
540 | parms.out = buf; | ||
541 | parms.out_size = sizeof(buf); | ||
542 | if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) | ||
543 | break; | ||
544 | p = buf; | ||
545 | |||
546 | /* Get secret token. */ | ||
547 | memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); | ||
548 | if (creds.ticket_st.length > MAX_KTXT_LEN) | ||
549 | break; | ||
550 | p += sizeof(unsigned int); | ||
551 | memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); | ||
552 | p += creds.ticket_st.length; | ||
553 | |||
554 | /* Get clear token. */ | ||
555 | memcpy(&len, p, sizeof(len)); | ||
556 | if (len != sizeof(struct ClearToken)) | ||
557 | break; | ||
558 | p += sizeof(len); | ||
559 | memcpy(&ct, p, len); | ||
560 | p += len; | ||
561 | p += sizeof(len); /* primary flag */ | ||
562 | server_cell = p; | ||
563 | |||
564 | /* Flesh out our credentials. */ | ||
565 | strlcpy(creds.service, "afs", sizeof creds.service); | ||
566 | creds.instance[0] = '\0'; | ||
567 | strlcpy(creds.realm, server_cell, REALM_SZ); | ||
568 | memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); | ||
569 | creds.issue_date = ct.BeginTimestamp; | ||
570 | creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); | ||
571 | creds.kvno = ct.AuthHandle; | ||
572 | snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); | ||
573 | creds.pinst[0] = '\0'; | ||
574 | |||
575 | /* Encode token, ship it off. */ | ||
576 | if (!creds_to_radix(&creds, (unsigned char*) buffer)) | ||
577 | break; | ||
578 | packet_start(SSH_CMSG_HAVE_AFS_TOKEN); | ||
579 | packet_put_string(buffer, strlen(buffer)); | ||
580 | packet_send(); | ||
581 | packet_write_wait(); | ||
582 | |||
583 | /* Roger, Roger. Clearance, Clarence. What's your vector, | ||
584 | Victor? */ | ||
585 | type = packet_read(&plen); | ||
586 | |||
587 | if (type == SSH_SMSG_FAILURE) | ||
588 | debug("AFS token for cell %s rejected.", server_cell); | ||
589 | else if (type != SSH_SMSG_SUCCESS) | ||
590 | packet_disconnect("Protocol error on AFS token response: %d", type); | ||
591 | } | ||
592 | } | ||
593 | |||
594 | #endif /* AFS */ | ||
595 | |||
596 | /* | ||
597 | * Tries to authenticate with any string-based challenge/response system. | ||
598 | * Note that the client code is not tied to s/key or TIS. | ||
599 | */ | ||
600 | int | ||
601 | try_skey_authentication() | ||
602 | { | ||
603 | int type, i; | ||
604 | int payload_len; | ||
605 | unsigned int clen; | ||
606 | char *challenge, *response; | ||
607 | |||
608 | debug("Doing skey authentication."); | ||
609 | |||
610 | /* request a challenge */ | ||
611 | packet_start(SSH_CMSG_AUTH_TIS); | ||
612 | packet_send(); | ||
613 | packet_write_wait(); | ||
614 | |||
615 | type = packet_read(&payload_len); | ||
616 | if (type != SSH_SMSG_FAILURE && | ||
617 | type != SSH_SMSG_AUTH_TIS_CHALLENGE) { | ||
618 | packet_disconnect("Protocol error: got %d in response " | ||
619 | "to skey-auth", type); | ||
620 | } | ||
621 | if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { | ||
622 | debug("No challenge for skey authentication."); | ||
623 | return 0; | ||
624 | } | ||
625 | challenge = packet_get_string(&clen); | ||
626 | packet_integrity_check(payload_len, (4 + clen), type); | ||
627 | if (options.cipher == SSH_CIPHER_NONE) | ||
628 | log("WARNING: Encryption is disabled! " | ||
629 | "Reponse will be transmitted in clear text."); | ||
630 | fprintf(stderr, "%s\n", challenge); | ||
631 | xfree(challenge); | ||
632 | fflush(stderr); | ||
633 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
634 | if (i != 0) | ||
635 | error("Permission denied, please try again."); | ||
636 | response = read_passphrase("Response: ", 0); | ||
637 | packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); | ||
638 | packet_put_string(response, strlen(response)); | ||
639 | memset(response, 0, strlen(response)); | ||
640 | xfree(response); | ||
641 | packet_send(); | ||
642 | packet_write_wait(); | ||
643 | type = packet_read(&payload_len); | ||
644 | if (type == SSH_SMSG_SUCCESS) | ||
645 | return 1; | ||
646 | if (type != SSH_SMSG_FAILURE) | ||
647 | packet_disconnect("Protocol error: got %d in response " | ||
648 | "to skey-auth-reponse", type); | ||
649 | } | ||
650 | /* failure */ | ||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | /* | ||
655 | * Tries to authenticate with plain passwd authentication. | ||
656 | */ | ||
657 | int | ||
658 | try_password_authentication(char *prompt) | ||
659 | { | ||
660 | int type, i, payload_len; | ||
661 | char *password; | ||
662 | |||
663 | debug("Doing password authentication."); | ||
664 | if (options.cipher == SSH_CIPHER_NONE) | ||
665 | log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); | ||
666 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
667 | if (i != 0) | ||
668 | error("Permission denied, please try again."); | ||
669 | password = read_passphrase(prompt, 0); | ||
670 | packet_start(SSH_CMSG_AUTH_PASSWORD); | ||
671 | packet_put_string(password, strlen(password)); | ||
672 | memset(password, 0, strlen(password)); | ||
673 | xfree(password); | ||
674 | packet_send(); | ||
675 | packet_write_wait(); | ||
676 | |||
677 | type = packet_read(&payload_len); | ||
678 | if (type == SSH_SMSG_SUCCESS) | ||
679 | return 1; | ||
680 | if (type != SSH_SMSG_FAILURE) | ||
681 | packet_disconnect("Protocol error: got %d in response to passwd auth", type); | ||
682 | } | ||
683 | /* failure */ | ||
684 | return 0; | ||
685 | } | ||
686 | |||
687 | /* | ||
688 | * SSH1 key exchange | ||
689 | */ | ||
690 | void | ||
691 | ssh_kex(char *host, struct sockaddr *hostaddr) | ||
692 | { | ||
693 | int i; | ||
694 | BIGNUM *key; | ||
695 | RSA *host_key; | ||
696 | RSA *public_key; | ||
697 | Key k; | ||
698 | int bits, rbits; | ||
699 | int ssh_cipher_default = SSH_CIPHER_3DES; | ||
700 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | ||
701 | unsigned char cookie[8]; | ||
702 | unsigned int supported_ciphers; | ||
703 | unsigned int server_flags, client_flags; | ||
704 | int payload_len, clen, sum_len = 0; | ||
705 | u_int32_t rand = 0; | ||
706 | |||
707 | debug("Waiting for server public key."); | ||
708 | |||
709 | /* Wait for a public key packet from the server. */ | ||
710 | packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); | ||
711 | |||
712 | /* Get cookie from the packet. */ | ||
713 | for (i = 0; i < 8; i++) | ||
714 | cookie[i] = packet_get_char(); | ||
715 | |||
716 | /* Get the public key. */ | ||
717 | public_key = RSA_new(); | ||
718 | bits = packet_get_int();/* bits */ | ||
719 | public_key->e = BN_new(); | ||
720 | packet_get_bignum(public_key->e, &clen); | ||
721 | sum_len += clen; | ||
722 | public_key->n = BN_new(); | ||
723 | packet_get_bignum(public_key->n, &clen); | ||
724 | sum_len += clen; | ||
725 | |||
726 | rbits = BN_num_bits(public_key->n); | ||
727 | if (bits != rbits) { | ||
728 | log("Warning: Server lies about size of server public key: " | ||
729 | "actual size is %d bits vs. announced %d.", rbits, bits); | ||
730 | log("Warning: This may be due to an old implementation of ssh."); | ||
731 | } | ||
732 | /* Get the host key. */ | ||
733 | host_key = RSA_new(); | ||
734 | bits = packet_get_int();/* bits */ | ||
735 | host_key->e = BN_new(); | ||
736 | packet_get_bignum(host_key->e, &clen); | ||
737 | sum_len += clen; | ||
738 | host_key->n = BN_new(); | ||
739 | packet_get_bignum(host_key->n, &clen); | ||
740 | sum_len += clen; | ||
741 | |||
742 | rbits = BN_num_bits(host_key->n); | ||
743 | if (bits != rbits) { | ||
744 | log("Warning: Server lies about size of server host key: " | ||
745 | "actual size is %d bits vs. announced %d.", rbits, bits); | ||
746 | log("Warning: This may be due to an old implementation of ssh."); | ||
747 | } | ||
748 | |||
749 | /* Get protocol flags. */ | ||
750 | server_flags = packet_get_int(); | ||
751 | packet_set_protocol_flags(server_flags); | ||
752 | |||
753 | supported_ciphers = packet_get_int(); | ||
754 | supported_authentications = packet_get_int(); | ||
755 | |||
756 | debug("Received server public key (%d bits) and host key (%d bits).", | ||
757 | BN_num_bits(public_key->n), BN_num_bits(host_key->n)); | ||
758 | |||
759 | packet_integrity_check(payload_len, | ||
760 | 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, | ||
761 | SSH_SMSG_PUBLIC_KEY); | ||
762 | k.type = KEY_RSA; | ||
763 | k.rsa = host_key; | ||
764 | check_host_key(host, hostaddr, &k, | ||
765 | options.user_hostfile, options.system_hostfile); | ||
766 | |||
767 | client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; | ||
768 | |||
769 | compute_session_id(session_id, cookie, host_key->n, public_key->n); | ||
770 | |||
771 | /* Generate a session key. */ | ||
772 | arc4random_stir(); | ||
773 | |||
774 | /* | ||
775 | * Generate an encryption key for the session. The key is a 256 bit | ||
776 | * random number, interpreted as a 32-byte key, with the least | ||
777 | * significant 8 bits being the first byte of the key. | ||
778 | */ | ||
779 | for (i = 0; i < 32; i++) { | ||
780 | if (i % 4 == 0) | ||
781 | rand = arc4random(); | ||
782 | session_key[i] = rand & 0xff; | ||
783 | rand >>= 8; | ||
784 | } | ||
785 | |||
786 | /* | ||
787 | * According to the protocol spec, the first byte of the session key | ||
788 | * is the highest byte of the integer. The session key is xored with | ||
789 | * the first 16 bytes of the session id. | ||
790 | */ | ||
791 | key = BN_new(); | ||
792 | BN_set_word(key, 0); | ||
793 | for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { | ||
794 | BN_lshift(key, key, 8); | ||
795 | if (i < 16) | ||
796 | BN_add_word(key, session_key[i] ^ session_id[i]); | ||
797 | else | ||
798 | BN_add_word(key, session_key[i]); | ||
799 | } | ||
800 | |||
801 | /* | ||
802 | * Encrypt the integer using the public key and host key of the | ||
803 | * server (key with smaller modulus first). | ||
804 | */ | ||
805 | if (BN_cmp(public_key->n, host_key->n) < 0) { | ||
806 | /* Public key has smaller modulus. */ | ||
807 | if (BN_num_bits(host_key->n) < | ||
808 | BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { | ||
809 | fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " | ||
810 | "SSH_KEY_BITS_RESERVED %d", | ||
811 | BN_num_bits(host_key->n), | ||
812 | BN_num_bits(public_key->n), | ||
813 | SSH_KEY_BITS_RESERVED); | ||
814 | } | ||
815 | rsa_public_encrypt(key, key, public_key); | ||
816 | rsa_public_encrypt(key, key, host_key); | ||
817 | } else { | ||
818 | /* Host key has smaller modulus (or they are equal). */ | ||
819 | if (BN_num_bits(public_key->n) < | ||
820 | BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { | ||
821 | fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " | ||
822 | "SSH_KEY_BITS_RESERVED %d", | ||
823 | BN_num_bits(public_key->n), | ||
824 | BN_num_bits(host_key->n), | ||
825 | SSH_KEY_BITS_RESERVED); | ||
826 | } | ||
827 | rsa_public_encrypt(key, key, host_key); | ||
828 | rsa_public_encrypt(key, key, public_key); | ||
829 | } | ||
830 | |||
831 | /* Destroy the public keys since we no longer need them. */ | ||
832 | RSA_free(public_key); | ||
833 | RSA_free(host_key); | ||
834 | |||
835 | if (options.cipher == SSH_CIPHER_NOT_SET) { | ||
836 | if (cipher_mask1() & supported_ciphers & (1 << ssh_cipher_default)) | ||
837 | options.cipher = ssh_cipher_default; | ||
838 | else { | ||
839 | debug("Cipher %s not supported, using %.100s instead.", | ||
840 | cipher_name(ssh_cipher_default), | ||
841 | cipher_name(SSH_FALLBACK_CIPHER)); | ||
842 | options.cipher = SSH_FALLBACK_CIPHER; | ||
843 | } | ||
844 | } | ||
845 | /* Check that the selected cipher is supported. */ | ||
846 | if (!(supported_ciphers & (1 << options.cipher))) | ||
847 | fatal("Selected cipher type %.100s not supported by server.", | ||
848 | cipher_name(options.cipher)); | ||
849 | |||
850 | debug("Encryption type: %.100s", cipher_name(options.cipher)); | ||
851 | |||
852 | /* Send the encrypted session key to the server. */ | ||
853 | packet_start(SSH_CMSG_SESSION_KEY); | ||
854 | packet_put_char(options.cipher); | ||
855 | |||
856 | /* Send the cookie back to the server. */ | ||
857 | for (i = 0; i < 8; i++) | ||
858 | packet_put_char(cookie[i]); | ||
859 | |||
860 | /* Send and destroy the encrypted encryption key integer. */ | ||
861 | packet_put_bignum(key); | ||
862 | BN_clear_free(key); | ||
863 | |||
864 | /* Send protocol flags. */ | ||
865 | packet_put_int(client_flags); | ||
866 | |||
867 | /* Send the packet now. */ | ||
868 | packet_send(); | ||
869 | packet_write_wait(); | ||
870 | |||
871 | debug("Sent encrypted session key."); | ||
872 | |||
873 | /* Set the encryption key. */ | ||
874 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); | ||
875 | |||
876 | /* We will no longer need the session key here. Destroy any extra copies. */ | ||
877 | memset(session_key, 0, sizeof(session_key)); | ||
878 | |||
879 | /* | ||
880 | * Expect a success message from the server. Note that this message | ||
881 | * will be received in encrypted form. | ||
882 | */ | ||
883 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
884 | |||
885 | debug("Received encrypted confirmation."); | ||
886 | } | ||
887 | |||
888 | /* | ||
889 | * Authenticate user | ||
890 | */ | ||
891 | void | ||
892 | ssh_userauth( | ||
893 | const char* local_user, | ||
894 | const char* server_user, | ||
895 | char *host, | ||
896 | int host_key_valid, RSA *own_host_key) | ||
897 | { | ||
898 | int i, type; | ||
899 | int payload_len; | ||
900 | |||
901 | if (supported_authentications == 0) | ||
902 | fatal("ssh_userauth: server supports no auth methods"); | ||
903 | |||
904 | /* Send the name of the user to log in as on the server. */ | ||
905 | packet_start(SSH_CMSG_USER); | ||
906 | packet_put_string(server_user, strlen(server_user)); | ||
907 | packet_send(); | ||
908 | packet_write_wait(); | ||
909 | |||
910 | /* | ||
911 | * The server should respond with success if no authentication is | ||
912 | * needed (the user has no password). Otherwise the server responds | ||
913 | * with failure. | ||
914 | */ | ||
915 | type = packet_read(&payload_len); | ||
916 | |||
917 | /* check whether the connection was accepted without authentication. */ | ||
918 | if (type == SSH_SMSG_SUCCESS) | ||
919 | return; | ||
920 | if (type != SSH_SMSG_FAILURE) | ||
921 | packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", | ||
922 | type); | ||
923 | |||
924 | #ifdef AFS | ||
925 | /* Try Kerberos tgt passing if the server supports it. */ | ||
926 | if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && | ||
927 | options.kerberos_tgt_passing) { | ||
928 | if (options.cipher == SSH_CIPHER_NONE) | ||
929 | log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); | ||
930 | (void) send_kerberos_tgt(); | ||
931 | } | ||
932 | /* Try AFS token passing if the server supports it. */ | ||
933 | if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && | ||
934 | options.afs_token_passing && k_hasafs()) { | ||
935 | if (options.cipher == SSH_CIPHER_NONE) | ||
936 | log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); | ||
937 | send_afs_tokens(); | ||
938 | } | ||
939 | #endif /* AFS */ | ||
940 | |||
941 | #ifdef KRB4 | ||
942 | if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && | ||
943 | options.kerberos_authentication) { | ||
944 | debug("Trying Kerberos authentication."); | ||
945 | if (try_kerberos_authentication()) { | ||
946 | /* The server should respond with success or failure. */ | ||
947 | type = packet_read(&payload_len); | ||
948 | if (type == SSH_SMSG_SUCCESS) | ||
949 | return; | ||
950 | if (type != SSH_SMSG_FAILURE) | ||
951 | packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); | ||
952 | } | ||
953 | } | ||
954 | #endif /* KRB4 */ | ||
955 | |||
956 | /* | ||
957 | * Use rhosts authentication if running in privileged socket and we | ||
958 | * do not wish to remain anonymous. | ||
959 | */ | ||
960 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && | ||
961 | options.rhosts_authentication) { | ||
962 | debug("Trying rhosts authentication."); | ||
963 | packet_start(SSH_CMSG_AUTH_RHOSTS); | ||
964 | packet_put_string(local_user, strlen(local_user)); | ||
965 | packet_send(); | ||
966 | packet_write_wait(); | ||
967 | |||
968 | /* The server should respond with success or failure. */ | ||
969 | type = packet_read(&payload_len); | ||
970 | if (type == SSH_SMSG_SUCCESS) | ||
971 | return; | ||
972 | if (type != SSH_SMSG_FAILURE) | ||
973 | packet_disconnect("Protocol error: got %d in response to rhosts auth", | ||
974 | type); | ||
975 | } | ||
976 | /* | ||
977 | * Try .rhosts or /etc/hosts.equiv authentication with RSA host | ||
978 | * authentication. | ||
979 | */ | ||
980 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && | ||
981 | options.rhosts_rsa_authentication && host_key_valid) { | ||
982 | if (try_rhosts_rsa_authentication(local_user, own_host_key)) | ||
983 | return; | ||
984 | } | ||
985 | /* Try RSA authentication if the server supports it. */ | ||
986 | if ((supported_authentications & (1 << SSH_AUTH_RSA)) && | ||
987 | options.rsa_authentication) { | ||
988 | /* | ||
989 | * Try RSA authentication using the authentication agent. The | ||
990 | * agent is tried first because no passphrase is needed for | ||
991 | * it, whereas identity files may require passphrases. | ||
992 | */ | ||
993 | if (try_agent_authentication()) | ||
994 | return; | ||
995 | |||
996 | /* Try RSA authentication for each identity. */ | ||
997 | for (i = 0; i < options.num_identity_files; i++) | ||
998 | if (try_rsa_authentication(options.identity_files[i])) | ||
999 | return; | ||
1000 | } | ||
1001 | /* Try skey authentication if the server supports it. */ | ||
1002 | if ((supported_authentications & (1 << SSH_AUTH_TIS)) && | ||
1003 | options.skey_authentication && !options.batch_mode) { | ||
1004 | if (try_skey_authentication()) | ||
1005 | return; | ||
1006 | } | ||
1007 | /* Try password authentication if the server supports it. */ | ||
1008 | if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && | ||
1009 | options.password_authentication && !options.batch_mode) { | ||
1010 | char prompt[80]; | ||
1011 | |||
1012 | snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", | ||
1013 | server_user, host); | ||
1014 | if (try_password_authentication(prompt)) | ||
1015 | return; | ||
1016 | } | ||
1017 | /* All authentication methods have failed. Exit with an error message. */ | ||
1018 | fatal("Permission denied."); | ||
1019 | /* NOTREACHED */ | ||
1020 | } | ||
diff --git a/sshconnect2.c b/sshconnect2.c new file mode 100644 index 000000000..31ef3084c --- /dev/null +++ b/sshconnect2.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | ||
3 | * | ||
4 | * Redistribution and use in source and binary forms, with or without | ||
5 | * modification, are permitted provided that the following conditions | ||
6 | * are met: | ||
7 | * 1. Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * 2. Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * 3. All advertising materials mentioning features or use of this software | ||
13 | * must display the following acknowledgement: | ||
14 | * This product includes software developed by Markus Friedl. | ||
15 | * 4. 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 BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | ||
19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | ||
20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||
21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||
22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||
23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||
27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
28 | */ | ||
29 | |||
30 | #include "includes.h" | ||
31 | RCSID("$OpenBSD: sshconnect2.c,v 1.4 2000/04/27 17:54:01 markus Exp $"); | ||
32 | |||
33 | #include <openssl/bn.h> | ||
34 | #include <openssl/rsa.h> | ||
35 | #include <openssl/dsa.h> | ||
36 | #include <openssl/md5.h> | ||
37 | #include <openssl/dh.h> | ||
38 | #include <openssl/hmac.h> | ||
39 | |||
40 | #include "ssh.h" | ||
41 | #include "xmalloc.h" | ||
42 | #include "rsa.h" | ||
43 | #include "buffer.h" | ||
44 | #include "packet.h" | ||
45 | #include "cipher.h" | ||
46 | #include "uidswap.h" | ||
47 | #include "compat.h" | ||
48 | #include "readconf.h" | ||
49 | #include "bufaux.h" | ||
50 | #include "ssh2.h" | ||
51 | #include "kex.h" | ||
52 | #include "myproposal.h" | ||
53 | #include "key.h" | ||
54 | #include "dsa.h" | ||
55 | #include "sshconnect.h" | ||
56 | #include "authfile.h" | ||
57 | |||
58 | /* import */ | ||
59 | extern char *client_version_string; | ||
60 | extern char *server_version_string; | ||
61 | extern Options options; | ||
62 | |||
63 | /* | ||
64 | * SSH2 key exchange | ||
65 | */ | ||
66 | |||
67 | unsigned char *session_id2 = NULL; | ||
68 | int session_id2_len = 0; | ||
69 | |||
70 | void | ||
71 | ssh_kex2(char *host, struct sockaddr *hostaddr) | ||
72 | { | ||
73 | Kex *kex; | ||
74 | char *cprop[PROPOSAL_MAX]; | ||
75 | char *sprop[PROPOSAL_MAX]; | ||
76 | Buffer *client_kexinit; | ||
77 | Buffer *server_kexinit; | ||
78 | int payload_len, dlen; | ||
79 | unsigned int klen, kout; | ||
80 | char *ptr; | ||
81 | char *signature = NULL; | ||
82 | unsigned int slen; | ||
83 | char *server_host_key_blob = NULL; | ||
84 | Key *server_host_key; | ||
85 | unsigned int sbloblen; | ||
86 | DH *dh; | ||
87 | BIGNUM *dh_server_pub = 0; | ||
88 | BIGNUM *shared_secret = 0; | ||
89 | int i; | ||
90 | unsigned char *kbuf; | ||
91 | unsigned char *hash; | ||
92 | |||
93 | /* KEXINIT */ | ||
94 | |||
95 | debug("Sending KEX init."); | ||
96 | if (options.ciphers != NULL) { | ||
97 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
98 | myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; | ||
99 | } else if ( | ||
100 | options.cipher == SSH_CIPHER_ARCFOUR || | ||
101 | options.cipher == SSH_CIPHER_3DES_CBC || | ||
102 | options.cipher == SSH_CIPHER_CAST128_CBC || | ||
103 | options.cipher == SSH_CIPHER_BLOWFISH_CBC) { | ||
104 | myproposal[PROPOSAL_ENC_ALGS_CTOS] = | ||
105 | myproposal[PROPOSAL_ENC_ALGS_STOC] = cipher_name(options.cipher); | ||
106 | } | ||
107 | if (options.compression) { | ||
108 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "zlib"; | ||
109 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "zlib"; | ||
110 | } else { | ||
111 | myproposal[PROPOSAL_COMP_ALGS_CTOS] = "none"; | ||
112 | myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; | ||
113 | } | ||
114 | for (i = 0; i < PROPOSAL_MAX; i++) | ||
115 | cprop[i] = xstrdup(myproposal[i]); | ||
116 | |||
117 | client_kexinit = kex_init(cprop); | ||
118 | packet_start(SSH2_MSG_KEXINIT); | ||
119 | packet_put_raw(buffer_ptr(client_kexinit), buffer_len(client_kexinit)); | ||
120 | packet_send(); | ||
121 | packet_write_wait(); | ||
122 | |||
123 | debug("done"); | ||
124 | |||
125 | packet_read_expect(&payload_len, SSH2_MSG_KEXINIT); | ||
126 | |||
127 | /* save payload for session_id */ | ||
128 | server_kexinit = xmalloc(sizeof(*server_kexinit)); | ||
129 | buffer_init(server_kexinit); | ||
130 | ptr = packet_get_raw(&payload_len); | ||
131 | buffer_append(server_kexinit, ptr, payload_len); | ||
132 | |||
133 | /* skip cookie */ | ||
134 | for (i = 0; i < 16; i++) | ||
135 | (void) packet_get_char(); | ||
136 | /* kex init proposal strings */ | ||
137 | for (i = 0; i < PROPOSAL_MAX; i++) { | ||
138 | sprop[i] = packet_get_string(NULL); | ||
139 | debug("got kexinit string: %s", sprop[i]); | ||
140 | } | ||
141 | i = (int) packet_get_char(); | ||
142 | debug("first kex follow == %d", i); | ||
143 | i = packet_get_int(); | ||
144 | debug("reserved == %d", i); | ||
145 | packet_done(); | ||
146 | |||
147 | debug("done read kexinit"); | ||
148 | kex = kex_choose_conf(cprop, sprop, 0); | ||
149 | |||
150 | /* KEXDH */ | ||
151 | |||
152 | debug("Sending SSH2_MSG_KEXDH_INIT."); | ||
153 | |||
154 | /* generate and send 'e', client DH public key */ | ||
155 | dh = dh_new_group1(); | ||
156 | packet_start(SSH2_MSG_KEXDH_INIT); | ||
157 | packet_put_bignum2(dh->pub_key); | ||
158 | packet_send(); | ||
159 | packet_write_wait(); | ||
160 | |||
161 | #ifdef DEBUG_KEXDH | ||
162 | fprintf(stderr, "\np= "); | ||
163 | bignum_print(dh->p); | ||
164 | fprintf(stderr, "\ng= "); | ||
165 | bignum_print(dh->g); | ||
166 | fprintf(stderr, "\npub= "); | ||
167 | bignum_print(dh->pub_key); | ||
168 | fprintf(stderr, "\n"); | ||
169 | DHparams_print_fp(stderr, dh); | ||
170 | #endif | ||
171 | |||
172 | debug("Wait SSH2_MSG_KEXDH_REPLY."); | ||
173 | |||
174 | packet_read_expect(&payload_len, SSH2_MSG_KEXDH_REPLY); | ||
175 | |||
176 | debug("Got SSH2_MSG_KEXDH_REPLY."); | ||
177 | |||
178 | /* key, cert */ | ||
179 | server_host_key_blob = packet_get_string(&sbloblen); | ||
180 | server_host_key = dsa_key_from_blob(server_host_key_blob, sbloblen); | ||
181 | if (server_host_key == NULL) | ||
182 | fatal("cannot decode server_host_key_blob"); | ||
183 | |||
184 | check_host_key(host, hostaddr, server_host_key, | ||
185 | options.user_hostfile2, options.system_hostfile2); | ||
186 | |||
187 | /* DH paramter f, server public DH key */ | ||
188 | dh_server_pub = BN_new(); | ||
189 | if (dh_server_pub == NULL) | ||
190 | fatal("dh_server_pub == NULL"); | ||
191 | packet_get_bignum2(dh_server_pub, &dlen); | ||
192 | |||
193 | #ifdef DEBUG_KEXDH | ||
194 | fprintf(stderr, "\ndh_server_pub= "); | ||
195 | bignum_print(dh_server_pub); | ||
196 | fprintf(stderr, "\n"); | ||
197 | debug("bits %d", BN_num_bits(dh_server_pub)); | ||
198 | #endif | ||
199 | |||
200 | /* signed H */ | ||
201 | signature = packet_get_string(&slen); | ||
202 | packet_done(); | ||
203 | |||
204 | if (!dh_pub_is_valid(dh, dh_server_pub)) | ||
205 | packet_disconnect("bad server public DH value"); | ||
206 | |||
207 | klen = DH_size(dh); | ||
208 | kbuf = xmalloc(klen); | ||
209 | kout = DH_compute_key(kbuf, dh_server_pub, dh); | ||
210 | #ifdef DEBUG_KEXDH | ||
211 | debug("shared secret: len %d/%d", klen, kout); | ||
212 | fprintf(stderr, "shared secret == "); | ||
213 | for (i = 0; i< kout; i++) | ||
214 | fprintf(stderr, "%02x", (kbuf[i])&0xff); | ||
215 | fprintf(stderr, "\n"); | ||
216 | #endif | ||
217 | shared_secret = BN_new(); | ||
218 | |||
219 | BN_bin2bn(kbuf, kout, shared_secret); | ||
220 | memset(kbuf, 0, klen); | ||
221 | xfree(kbuf); | ||
222 | |||
223 | /* calc and verify H */ | ||
224 | hash = kex_hash( | ||
225 | client_version_string, | ||
226 | server_version_string, | ||
227 | buffer_ptr(client_kexinit), buffer_len(client_kexinit), | ||
228 | buffer_ptr(server_kexinit), buffer_len(server_kexinit), | ||
229 | server_host_key_blob, sbloblen, | ||
230 | dh->pub_key, | ||
231 | dh_server_pub, | ||
232 | shared_secret | ||
233 | ); | ||
234 | xfree(server_host_key_blob); | ||
235 | buffer_free(client_kexinit); | ||
236 | buffer_free(server_kexinit); | ||
237 | xfree(client_kexinit); | ||
238 | xfree(server_kexinit); | ||
239 | #ifdef DEBUG_KEXDH | ||
240 | fprintf(stderr, "hash == "); | ||
241 | for (i = 0; i< 20; i++) | ||
242 | fprintf(stderr, "%02x", (hash[i])&0xff); | ||
243 | fprintf(stderr, "\n"); | ||
244 | #endif | ||
245 | if (dsa_verify(server_host_key, (unsigned char *)signature, slen, hash, 20) != 1) | ||
246 | fatal("dsa_verify failed for server_host_key"); | ||
247 | key_free(server_host_key); | ||
248 | |||
249 | kex_derive_keys(kex, hash, shared_secret); | ||
250 | packet_set_kex(kex); | ||
251 | |||
252 | /* have keys, free DH */ | ||
253 | DH_free(dh); | ||
254 | |||
255 | /* save session id */ | ||
256 | session_id2_len = 20; | ||
257 | session_id2 = xmalloc(session_id2_len); | ||
258 | memcpy(session_id2, hash, session_id2_len); | ||
259 | |||
260 | debug("Wait SSH2_MSG_NEWKEYS."); | ||
261 | packet_read_expect(&payload_len, SSH2_MSG_NEWKEYS); | ||
262 | packet_done(); | ||
263 | debug("GOT SSH2_MSG_NEWKEYS."); | ||
264 | |||
265 | debug("send SSH2_MSG_NEWKEYS."); | ||
266 | packet_start(SSH2_MSG_NEWKEYS); | ||
267 | packet_send(); | ||
268 | packet_write_wait(); | ||
269 | debug("done: send SSH2_MSG_NEWKEYS."); | ||
270 | |||
271 | #ifdef DEBUG_KEXDH | ||
272 | /* send 1st encrypted/maced/compressed message */ | ||
273 | packet_start(SSH2_MSG_IGNORE); | ||
274 | packet_put_cstring("markus"); | ||
275 | packet_send(); | ||
276 | packet_write_wait(); | ||
277 | #endif | ||
278 | debug("done: KEX2."); | ||
279 | } | ||
280 | /* | ||
281 | * Authenticate user | ||
282 | */ | ||
283 | int | ||
284 | ssh2_try_passwd(const char *server_user, const char *host, const char *service) | ||
285 | { | ||
286 | char prompt[80]; | ||
287 | char *password; | ||
288 | |||
289 | snprintf(prompt, sizeof(prompt), "%.30s@%.40s's password: ", | ||
290 | server_user, host); | ||
291 | password = read_passphrase(prompt, 0); | ||
292 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
293 | packet_put_cstring(server_user); | ||
294 | packet_put_cstring(service); | ||
295 | packet_put_cstring("password"); | ||
296 | packet_put_char(0); | ||
297 | packet_put_cstring(password); | ||
298 | memset(password, 0, strlen(password)); | ||
299 | xfree(password); | ||
300 | packet_send(); | ||
301 | packet_write_wait(); | ||
302 | return 1; | ||
303 | } | ||
304 | |||
305 | int | ||
306 | ssh2_try_pubkey(char *filename, | ||
307 | const char *server_user, const char *host, const char *service) | ||
308 | { | ||
309 | Buffer b; | ||
310 | Key *k; | ||
311 | unsigned char *blob, *signature; | ||
312 | int bloblen, slen; | ||
313 | |||
314 | debug("try pubkey: %s", filename); | ||
315 | |||
316 | k = key_new(KEY_DSA); | ||
317 | if (!load_private_key(filename, "", k, NULL)) { | ||
318 | int success = 0; | ||
319 | char *passphrase; | ||
320 | char prompt[300]; | ||
321 | snprintf(prompt, sizeof prompt, | ||
322 | "Enter passphrase for DSA key '%.100s': ", | ||
323 | filename); | ||
324 | passphrase = read_passphrase(prompt, 0); | ||
325 | success = load_private_key(filename, passphrase, k, NULL); | ||
326 | memset(passphrase, 0, strlen(passphrase)); | ||
327 | xfree(passphrase); | ||
328 | if (!success) | ||
329 | return 0; | ||
330 | } | ||
331 | dsa_make_key_blob(k, &blob, &bloblen); | ||
332 | |||
333 | /* data to be signed */ | ||
334 | buffer_init(&b); | ||
335 | buffer_append(&b, session_id2, session_id2_len); | ||
336 | buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); | ||
337 | buffer_put_cstring(&b, server_user); | ||
338 | buffer_put_cstring(&b, service); | ||
339 | buffer_put_cstring(&b, "publickey"); | ||
340 | buffer_put_char(&b, 1); | ||
341 | buffer_put_cstring(&b, KEX_DSS); | ||
342 | buffer_put_string(&b, blob, bloblen); | ||
343 | xfree(blob); | ||
344 | |||
345 | /* generate signature */ | ||
346 | dsa_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b)); | ||
347 | key_free(k); | ||
348 | #ifdef DEBUG_DSS | ||
349 | buffer_dump(&b); | ||
350 | #endif | ||
351 | /* append signature */ | ||
352 | buffer_put_string(&b, signature, slen); | ||
353 | xfree(signature); | ||
354 | |||
355 | /* skip session id and packet type */ | ||
356 | if (buffer_len(&b) < session_id2_len + 1) | ||
357 | fatal("ssh2_try_pubkey: internal error"); | ||
358 | buffer_consume(&b, session_id2_len + 1); | ||
359 | |||
360 | /* put remaining data from buffer into packet */ | ||
361 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
362 | packet_put_raw(buffer_ptr(&b), buffer_len(&b)); | ||
363 | buffer_free(&b); | ||
364 | |||
365 | /* send */ | ||
366 | packet_send(); | ||
367 | packet_write_wait(); | ||
368 | return 1; | ||
369 | } | ||
370 | |||
371 | void | ||
372 | ssh_userauth2(const char *server_user, char *host) | ||
373 | { | ||
374 | int type; | ||
375 | int plen; | ||
376 | int sent; | ||
377 | unsigned int dlen; | ||
378 | int partial; | ||
379 | int i = 0; | ||
380 | char *auths; | ||
381 | char *service = "ssh-connection"; /* service name */ | ||
382 | |||
383 | debug("send SSH2_MSG_SERVICE_REQUEST"); | ||
384 | packet_start(SSH2_MSG_SERVICE_REQUEST); | ||
385 | packet_put_cstring("ssh-userauth"); | ||
386 | packet_send(); | ||
387 | packet_write_wait(); | ||
388 | |||
389 | type = packet_read(&plen); | ||
390 | if (type != SSH2_MSG_SERVICE_ACCEPT) { | ||
391 | fatal("denied SSH2_MSG_SERVICE_ACCEPT: %d", type); | ||
392 | } | ||
393 | if (packet_remaining() > 0) { | ||
394 | char *reply = packet_get_string(&plen); | ||
395 | debug("service_accept: %s", reply); | ||
396 | xfree(reply); | ||
397 | } else { | ||
398 | /* payload empty for ssh-2.0.13 ?? */ | ||
399 | log("buggy server: service_accept w/o service"); | ||
400 | } | ||
401 | packet_done(); | ||
402 | debug("got SSH2_MSG_SERVICE_ACCEPT"); | ||
403 | |||
404 | /* INITIAL request for auth */ | ||
405 | packet_start(SSH2_MSG_USERAUTH_REQUEST); | ||
406 | packet_put_cstring(server_user); | ||
407 | packet_put_cstring(service); | ||
408 | packet_put_cstring("none"); | ||
409 | packet_send(); | ||
410 | packet_write_wait(); | ||
411 | |||
412 | for (;;) { | ||
413 | sent = 0; | ||
414 | type = packet_read(&plen); | ||
415 | if (type == SSH2_MSG_USERAUTH_SUCCESS) | ||
416 | break; | ||
417 | if (type != SSH2_MSG_USERAUTH_FAILURE) | ||
418 | fatal("access denied: %d", type); | ||
419 | /* SSH2_MSG_USERAUTH_FAILURE means: try again */ | ||
420 | auths = packet_get_string(&dlen); | ||
421 | debug("authentications that can continue: %s", auths); | ||
422 | partial = packet_get_char(); | ||
423 | packet_done(); | ||
424 | if (partial) | ||
425 | debug("partial success"); | ||
426 | if (options.rsa_authentication && | ||
427 | strstr(auths, "publickey") != NULL) { | ||
428 | while (i < options.num_identity_files2) { | ||
429 | sent = ssh2_try_pubkey( | ||
430 | options.identity_files2[i++], | ||
431 | server_user, host, service); | ||
432 | if (sent) | ||
433 | break; | ||
434 | } | ||
435 | } | ||
436 | if (!sent) { | ||
437 | if (options.password_authentication && | ||
438 | !options.batch_mode && | ||
439 | strstr(auths, "password") != NULL) { | ||
440 | sent = ssh2_try_passwd(server_user, host, service); | ||
441 | } | ||
442 | } | ||
443 | if (!sent) | ||
444 | fatal("Permission denied (%s).", auths); | ||
445 | xfree(auths); | ||
446 | } | ||
447 | packet_done(); | ||
448 | debug("ssh-userauth2 successfull"); | ||
449 | } | ||
@@ -14,7 +14,7 @@ | |||
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$OpenBSD: sshd.c,v 1.107 2000/04/19 07:05:50 deraadt Exp $"); | 17 | RCSID("$OpenBSD: sshd.c,v 1.111 2000/04/27 08:01:28 markus Exp $"); |
18 | 18 | ||
19 | #include "xmalloc.h" | 19 | #include "xmalloc.h" |
20 | #include "rsa.h" | 20 | #include "rsa.h" |
@@ -40,6 +40,7 @@ RCSID("$OpenBSD: sshd.c,v 1.107 2000/04/19 07:05:50 deraadt Exp $"); | |||
40 | 40 | ||
41 | #include "auth.h" | 41 | #include "auth.h" |
42 | #include "myproposal.h" | 42 | #include "myproposal.h" |
43 | #include "authfile.h" | ||
43 | 44 | ||
44 | #ifdef LIBWRAP | 45 | #ifdef LIBWRAP |
45 | #include <tcpd.h> | 46 | #include <tcpd.h> |
@@ -112,8 +113,9 @@ char *server_version_string = NULL; | |||
112 | * not very useful. Currently, memory locking is not implemented. | 113 | * not very useful. Currently, memory locking is not implemented. |
113 | */ | 114 | */ |
114 | struct { | 115 | struct { |
115 | RSA *private_key; /* Private part of server key. */ | 116 | RSA *private_key; /* Private part of empheral server key. */ |
116 | RSA *host_key; /* Private part of host key. */ | 117 | RSA *host_key; /* Private part of host key. */ |
118 | Key *dsa_host_key; /* Private DSA host key. */ | ||
117 | } sensitive_data; | 119 | } sensitive_data; |
118 | 120 | ||
119 | /* | 121 | /* |
@@ -132,6 +134,10 @@ RSA *public_key; | |||
132 | /* session identifier, used by RSA-auth */ | 134 | /* session identifier, used by RSA-auth */ |
133 | unsigned char session_id[16]; | 135 | unsigned char session_id[16]; |
134 | 136 | ||
137 | /* same for ssh2 */ | ||
138 | unsigned char *session_id2 = NULL; | ||
139 | int session_id2_len = 0; | ||
140 | |||
135 | /* Prototypes for various functions defined later in this file. */ | 141 | /* Prototypes for various functions defined later in this file. */ |
136 | void do_ssh1_kex(); | 142 | void do_ssh1_kex(); |
137 | void do_ssh2_kex(); | 143 | void do_ssh2_kex(); |
@@ -224,6 +230,7 @@ grace_alarm_handler(int sig) | |||
224 | * Thus there should be no concurrency control/asynchronous execution | 230 | * Thus there should be no concurrency control/asynchronous execution |
225 | * problems. | 231 | * problems. |
226 | */ | 232 | */ |
233 | /* XXX do we really want this work to be done in a signal handler ? -m */ | ||
227 | void | 234 | void |
228 | key_regeneration_alarm(int sig) | 235 | key_regeneration_alarm(int sig) |
229 | { | 236 | { |
@@ -344,6 +351,13 @@ sshd_exchange_identification(int sock_in, int sock_out) | |||
344 | mismatch = 0; | 351 | mismatch = 0; |
345 | switch(remote_major) { | 352 | switch(remote_major) { |
346 | case 1: | 353 | case 1: |
354 | if (remote_minor == 99) { | ||
355 | if (options.protocol & SSH_PROTO_2) | ||
356 | enable_compat20(); | ||
357 | else | ||
358 | mismatch = 1; | ||
359 | break; | ||
360 | } | ||
347 | if (!(options.protocol & SSH_PROTO_1)) { | 361 | if (!(options.protocol & SSH_PROTO_1)) { |
348 | mismatch = 1; | 362 | mismatch = 1; |
349 | break; | 363 | break; |
@@ -355,12 +369,6 @@ sshd_exchange_identification(int sock_in, int sock_out) | |||
355 | /* note that this disables agent-forwarding */ | 369 | /* note that this disables agent-forwarding */ |
356 | enable_compat13(); | 370 | enable_compat13(); |
357 | } | 371 | } |
358 | if (remote_minor == 99) { | ||
359 | if (options.protocol & SSH_PROTO_2) | ||
360 | enable_compat20(); | ||
361 | else | ||
362 | mismatch = 1; | ||
363 | } | ||
364 | break; | 372 | break; |
365 | case 2: | 373 | case 2: |
366 | if (options.protocol & SSH_PROTO_2) { | 374 | if (options.protocol & SSH_PROTO_2) { |
@@ -386,6 +394,20 @@ sshd_exchange_identification(int sock_in, int sock_out) | |||
386 | server_version_string, client_version_string); | 394 | server_version_string, client_version_string); |
387 | fatal_cleanup(); | 395 | fatal_cleanup(); |
388 | } | 396 | } |
397 | if (compat20) | ||
398 | packet_set_ssh2_format(); | ||
399 | } | ||
400 | |||
401 | |||
402 | void | ||
403 | destroy_sensitive_data(void) | ||
404 | { | ||
405 | /* Destroy the private and public keys. They will no longer be needed. */ | ||
406 | RSA_free(public_key); | ||
407 | RSA_free(sensitive_data.private_key); | ||
408 | RSA_free(sensitive_data.host_key); | ||
409 | if (sensitive_data.dsa_host_key != NULL) | ||
410 | key_free(sensitive_data.dsa_host_key); | ||
389 | } | 411 | } |
390 | 412 | ||
391 | /* | 413 | /* |
@@ -399,12 +421,11 @@ main(int ac, char **av) | |||
399 | int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1; | 421 | int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1; |
400 | pid_t pid; | 422 | pid_t pid; |
401 | socklen_t fromlen; | 423 | socklen_t fromlen; |
402 | int silentrsa = 0; | 424 | int silent = 0; |
403 | fd_set *fdset; | 425 | fd_set *fdset; |
404 | struct sockaddr_storage from; | 426 | struct sockaddr_storage from; |
405 | const char *remote_ip; | 427 | const char *remote_ip; |
406 | int remote_port; | 428 | int remote_port; |
407 | char *comment; | ||
408 | FILE *f; | 429 | FILE *f; |
409 | struct linger linger; | 430 | struct linger linger; |
410 | struct addrinfo *ai; | 431 | struct addrinfo *ai; |
@@ -441,7 +462,7 @@ main(int ac, char **av) | |||
441 | inetd_flag = 1; | 462 | inetd_flag = 1; |
442 | break; | 463 | break; |
443 | case 'Q': | 464 | case 'Q': |
444 | silentrsa = 1; | 465 | silent = 1; |
445 | break; | 466 | break; |
446 | case 'q': | 467 | case 'q': |
447 | options.log_level = SYSLOG_LEVEL_QUIET; | 468 | options.log_level = SYSLOG_LEVEL_QUIET; |
@@ -497,27 +518,14 @@ main(int ac, char **av) | |||
497 | log_init(av0, | 518 | log_init(av0, |
498 | options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, | 519 | options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, |
499 | options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, | 520 | options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility, |
500 | !inetd_flag); | 521 | !silent && !inetd_flag); |
501 | 522 | ||
502 | /* check if RSA support exists */ | ||
503 | if (rsa_alive() == 0) { | ||
504 | if (silentrsa == 0) | ||
505 | printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); | ||
506 | log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); | ||
507 | exit(1); | ||
508 | } | ||
509 | /* Read server configuration options from the configuration file. */ | 523 | /* Read server configuration options from the configuration file. */ |
510 | read_server_config(&options, config_file_name); | 524 | read_server_config(&options, config_file_name); |
511 | 525 | ||
512 | /* Fill in default values for those options not explicitly set. */ | 526 | /* Fill in default values for those options not explicitly set. */ |
513 | fill_default_server_options(&options); | 527 | fill_default_server_options(&options); |
514 | 528 | ||
515 | /* Check certain values for sanity. */ | ||
516 | if (options.server_key_bits < 512 || | ||
517 | options.server_key_bits > 32768) { | ||
518 | fprintf(stderr, "Bad server key size.\n"); | ||
519 | exit(1); | ||
520 | } | ||
521 | /* Check that there are no remaining arguments. */ | 529 | /* Check that there are no remaining arguments. */ |
522 | if (optind < ac) { | 530 | if (optind < ac) { |
523 | fprintf(stderr, "Extra argument %s.\n", av[optind]); | 531 | fprintf(stderr, "Extra argument %s.\n", av[optind]); |
@@ -526,26 +534,79 @@ main(int ac, char **av) | |||
526 | 534 | ||
527 | debug("sshd version %.100s", SSH_VERSION); | 535 | debug("sshd version %.100s", SSH_VERSION); |
528 | 536 | ||
529 | sensitive_data.host_key = RSA_new(); | 537 | sensitive_data.dsa_host_key = NULL; |
530 | errno = 0; | 538 | sensitive_data.host_key = NULL; |
531 | /* Load the host key. It must have empty passphrase. */ | 539 | |
532 | if (!load_private_key(options.host_key_file, "", | 540 | /* check if RSA support exists */ |
533 | sensitive_data.host_key, &comment)) { | 541 | if ((options.protocol & SSH_PROTO_1) && |
534 | error("Could not load host key: %.200s: %.100s", | 542 | rsa_alive() == 0) { |
535 | options.host_key_file, strerror(errno)); | 543 | log("no RSA support in libssl and libcrypto. See ssl(8)"); |
544 | log("Disabling protocol version 1"); | ||
545 | options.protocol &= ~SSH_PROTO_1; | ||
546 | } | ||
547 | /* Load the RSA/DSA host key. It must have empty passphrase. */ | ||
548 | if (options.protocol & SSH_PROTO_1) { | ||
549 | Key k; | ||
550 | sensitive_data.host_key = RSA_new(); | ||
551 | k.type = KEY_RSA; | ||
552 | k.rsa = sensitive_data.host_key; | ||
553 | errno = 0; | ||
554 | if (!load_private_key(options.host_key_file, "", &k, NULL)) { | ||
555 | error("Could not load host key: %.200s: %.100s", | ||
556 | options.host_key_file, strerror(errno)); | ||
557 | log("Disabling protocol version 1"); | ||
558 | options.protocol &= ~SSH_PROTO_1; | ||
559 | } | ||
560 | k.rsa = NULL; | ||
561 | } | ||
562 | if (options.protocol & SSH_PROTO_2) { | ||
563 | sensitive_data.dsa_host_key = key_new(KEY_DSA); | ||
564 | if (!load_private_key(options.dsa_key_file, "", sensitive_data.dsa_host_key, NULL)) { | ||
565 | error("Could not load DSA host key: %.200s", options.dsa_key_file); | ||
566 | log("Disabling protocol version 2"); | ||
567 | options.protocol &= ~SSH_PROTO_2; | ||
568 | } | ||
569 | } | ||
570 | if (! options.protocol & (SSH_PROTO_1|SSH_PROTO_2)) { | ||
571 | if (silent == 0) | ||
572 | fprintf(stderr, "sshd: no hostkeys available -- exiting.\n"); | ||
573 | log("sshd: no hostkeys available -- exiting.\n"); | ||
536 | exit(1); | 574 | exit(1); |
537 | } | 575 | } |
538 | xfree(comment); | ||
539 | 576 | ||
540 | /* Initialize the log (it is reinitialized below in case we | 577 | /* Check certain values for sanity. */ |
541 | forked). */ | 578 | if (options.protocol & SSH_PROTO_1) { |
579 | if (options.server_key_bits < 512 || | ||
580 | options.server_key_bits > 32768) { | ||
581 | fprintf(stderr, "Bad server key size.\n"); | ||
582 | exit(1); | ||
583 | } | ||
584 | /* | ||
585 | * Check that server and host key lengths differ sufficiently. This | ||
586 | * is necessary to make double encryption work with rsaref. Oh, I | ||
587 | * hate software patents. I dont know if this can go? Niels | ||
588 | */ | ||
589 | if (options.server_key_bits > | ||
590 | BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && | ||
591 | options.server_key_bits < | ||
592 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { | ||
593 | options.server_key_bits = | ||
594 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; | ||
595 | debug("Forcing server key to %d bits to make it differ from host key.", | ||
596 | options.server_key_bits); | ||
597 | } | ||
598 | } | ||
599 | |||
600 | /* Initialize the log (it is reinitialized below in case we forked). */ | ||
542 | if (debug_flag && !inetd_flag) | 601 | if (debug_flag && !inetd_flag) |
543 | log_stderr = 1; | 602 | log_stderr = 1; |
544 | log_init(av0, options.log_level, options.log_facility, log_stderr); | 603 | log_init(av0, options.log_level, options.log_facility, log_stderr); |
545 | 604 | ||
546 | /* If not in debugging mode, and not started from inetd, | 605 | /* |
547 | disconnect from the controlling terminal, and fork. The | 606 | * If not in debugging mode, and not started from inetd, disconnect |
548 | original process exits. */ | 607 | * from the controlling terminal, and fork. The original process |
608 | * exits. | ||
609 | */ | ||
549 | if (!debug_flag && !inetd_flag) { | 610 | if (!debug_flag && !inetd_flag) { |
550 | #ifdef TIOCNOTTY | 611 | #ifdef TIOCNOTTY |
551 | int fd; | 612 | int fd; |
@@ -565,18 +626,6 @@ main(int ac, char **av) | |||
565 | /* Reinitialize the log (because of the fork above). */ | 626 | /* Reinitialize the log (because of the fork above). */ |
566 | log_init(av0, options.log_level, options.log_facility, log_stderr); | 627 | log_init(av0, options.log_level, options.log_facility, log_stderr); |
567 | 628 | ||
568 | /* Check that server and host key lengths differ sufficiently. | ||
569 | This is necessary to make double encryption work with rsaref. | ||
570 | Oh, I hate software patents. I dont know if this can go? Niels */ | ||
571 | if (options.server_key_bits > | ||
572 | BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && | ||
573 | options.server_key_bits < | ||
574 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { | ||
575 | options.server_key_bits = | ||
576 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; | ||
577 | debug("Forcing server key to %d bits to make it differ from host key.", | ||
578 | options.server_key_bits); | ||
579 | } | ||
580 | /* Do not display messages to stdout in RSA code. */ | 629 | /* Do not display messages to stdout in RSA code. */ |
581 | rsa_set_verbose(0); | 630 | rsa_set_verbose(0); |
582 | 631 | ||
@@ -594,20 +643,22 @@ main(int ac, char **av) | |||
594 | s2 = dup(s1); | 643 | s2 = dup(s1); |
595 | sock_in = dup(0); | 644 | sock_in = dup(0); |
596 | sock_out = dup(1); | 645 | sock_out = dup(1); |
597 | /* We intentionally do not close the descriptors 0, 1, and 2 | 646 | /* |
598 | as our code for setting the descriptors won\'t work | 647 | * We intentionally do not close the descriptors 0, 1, and 2 |
599 | if ttyfd happens to be one of those. */ | 648 | * as our code for setting the descriptors won\'t work if |
649 | * ttyfd happens to be one of those. | ||
650 | */ | ||
600 | debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); | 651 | debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); |
601 | 652 | ||
602 | public_key = RSA_new(); | 653 | if (options.protocol & SSH_PROTO_1) { |
603 | sensitive_data.private_key = RSA_new(); | 654 | public_key = RSA_new(); |
604 | 655 | sensitive_data.private_key = RSA_new(); | |
605 | /* XXX check options.protocol */ | 656 | log("Generating %d bit RSA key.", options.server_key_bits); |
606 | log("Generating %d bit RSA key.", options.server_key_bits); | 657 | rsa_generate_key(sensitive_data.private_key, public_key, |
607 | rsa_generate_key(sensitive_data.private_key, public_key, | 658 | options.server_key_bits); |
608 | options.server_key_bits); | 659 | arc4random_stir(); |
609 | arc4random_stir(); | 660 | log("RSA key generation complete."); |
610 | log("RSA key generation complete."); | 661 | } |
611 | } else { | 662 | } else { |
612 | for (ai = options.listen_addrs; ai; ai = ai->ai_next) { | 663 | for (ai = options.listen_addrs; ai; ai = ai->ai_next) { |
613 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) | 664 | if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) |
@@ -684,19 +735,20 @@ main(int ac, char **av) | |||
684 | fclose(f); | 735 | fclose(f); |
685 | } | 736 | } |
686 | } | 737 | } |
738 | if (options.protocol & SSH_PROTO_1) { | ||
739 | public_key = RSA_new(); | ||
740 | sensitive_data.private_key = RSA_new(); | ||
687 | 741 | ||
688 | public_key = RSA_new(); | 742 | log("Generating %d bit RSA key.", options.server_key_bits); |
689 | sensitive_data.private_key = RSA_new(); | 743 | rsa_generate_key(sensitive_data.private_key, public_key, |
690 | 744 | options.server_key_bits); | |
691 | log("Generating %d bit RSA key.", options.server_key_bits); | 745 | arc4random_stir(); |
692 | rsa_generate_key(sensitive_data.private_key, public_key, | 746 | log("RSA key generation complete."); |
693 | options.server_key_bits); | ||
694 | arc4random_stir(); | ||
695 | log("RSA key generation complete."); | ||
696 | 747 | ||
697 | /* Schedule server key regeneration alarm. */ | 748 | /* Schedule server key regeneration alarm. */ |
698 | signal(SIGALRM, key_regeneration_alarm); | 749 | signal(SIGALRM, key_regeneration_alarm); |
699 | alarm(options.key_regeneration_time); | 750 | alarm(options.key_regeneration_time); |
751 | } | ||
700 | 752 | ||
701 | /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ | 753 | /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ |
702 | signal(SIGHUP, sighup_handler); | 754 | signal(SIGHUP, sighup_handler); |
@@ -1069,9 +1121,7 @@ do_ssh1_kex() | |||
1069 | sensitive_data.private_key->n); | 1121 | sensitive_data.private_key->n); |
1070 | 1122 | ||
1071 | /* Destroy the private and public keys. They will no longer be needed. */ | 1123 | /* Destroy the private and public keys. They will no longer be needed. */ |
1072 | RSA_free(public_key); | 1124 | destroy_sensitive_data(); |
1073 | RSA_free(sensitive_data.private_key); | ||
1074 | RSA_free(sensitive_data.host_key); | ||
1075 | 1125 | ||
1076 | /* | 1126 | /* |
1077 | * Extract session key from the decrypted integer. The key is in the | 1127 | * Extract session key from the decrypted integer. The key is in the |
@@ -1130,7 +1180,6 @@ do_ssh2_kex() | |||
1130 | unsigned char *kbuf; | 1180 | unsigned char *kbuf; |
1131 | unsigned char *hash; | 1181 | unsigned char *hash; |
1132 | Kex *kex; | 1182 | Kex *kex; |
1133 | Key *server_host_key; | ||
1134 | char *cprop[PROPOSAL_MAX]; | 1183 | char *cprop[PROPOSAL_MAX]; |
1135 | char *sprop[PROPOSAL_MAX]; | 1184 | char *sprop[PROPOSAL_MAX]; |
1136 | 1185 | ||
@@ -1231,8 +1280,8 @@ do_ssh2_kex() | |||
1231 | memset(kbuf, 0, klen); | 1280 | memset(kbuf, 0, klen); |
1232 | xfree(kbuf); | 1281 | xfree(kbuf); |
1233 | 1282 | ||
1234 | server_host_key = dsa_get_serverkey(options.dsa_key_file); | 1283 | /* XXX precompute? */ |
1235 | dsa_make_serverkey_blob(server_host_key, &server_host_key_blob, &sbloblen); | 1284 | dsa_make_key_blob(sensitive_data.dsa_host_key, &server_host_key_blob, &sbloblen); |
1236 | 1285 | ||
1237 | /* calc H */ /* XXX depends on 'kex' */ | 1286 | /* calc H */ /* XXX depends on 'kex' */ |
1238 | hash = kex_hash( | 1287 | hash = kex_hash( |
@@ -1255,10 +1304,17 @@ do_ssh2_kex() | |||
1255 | fprintf(stderr, "%02x", (hash[i])&0xff); | 1304 | fprintf(stderr, "%02x", (hash[i])&0xff); |
1256 | fprintf(stderr, "\n"); | 1305 | fprintf(stderr, "\n"); |
1257 | #endif | 1306 | #endif |
1307 | /* save session id := H */ | ||
1308 | /* XXX hashlen depends on KEX */ | ||
1309 | session_id2_len = 20; | ||
1310 | session_id2 = xmalloc(session_id2_len); | ||
1311 | memcpy(session_id2, hash, session_id2_len); | ||
1312 | |||
1258 | /* sign H */ | 1313 | /* sign H */ |
1259 | dsa_sign(server_host_key, &signature, &slen, hash, 20); | 1314 | /* XXX hashlen depends on KEX */ |
1260 | /* hashlen depends on KEX */ | 1315 | dsa_sign(sensitive_data.dsa_host_key, &signature, &slen, hash, 20); |
1261 | key_free(server_host_key); | 1316 | |
1317 | destroy_sensitive_data(); | ||
1262 | 1318 | ||
1263 | /* send server hostkey, DH pubkey 'f' and singed H */ | 1319 | /* send server hostkey, DH pubkey 'f' and singed H */ |
1264 | packet_start(SSH2_MSG_KEXDH_REPLY); | 1320 | packet_start(SSH2_MSG_KEXDH_REPLY); |
@@ -1267,6 +1323,7 @@ do_ssh2_kex() | |||
1267 | packet_put_string((char *)signature, slen); | 1323 | packet_put_string((char *)signature, slen); |
1268 | packet_send(); | 1324 | packet_send(); |
1269 | xfree(signature); | 1325 | xfree(signature); |
1326 | xfree(server_host_key_blob); | ||
1270 | packet_write_wait(); | 1327 | packet_write_wait(); |
1271 | 1328 | ||
1272 | kex_derive_keys(kex, hash, shared_secret); | 1329 | kex_derive_keys(kex, hash, shared_secret); |
diff --git a/uuencode.c b/uuencode.c new file mode 100644 index 000000000..626890050 --- /dev/null +++ b/uuencode.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. | ||
3 | * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 | ||
4 | * and placed in the public domain. | ||
5 | * | ||
6 | * Dug Song <dugsong@UMICH.EDU> | ||
7 | */ | ||
8 | |||
9 | #include "includes.h" | ||
10 | #include "xmalloc.h" | ||
11 | |||
12 | char six2pr[64] = { | ||
13 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', | ||
14 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', | ||
15 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', | ||
16 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', | ||
17 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' | ||
18 | }; | ||
19 | |||
20 | unsigned char pr2six[256]; | ||
21 | |||
22 | int | ||
23 | uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) | ||
24 | { | ||
25 | /* ENC is the basic 1 character encoding function to make a char printing */ | ||
26 | #define ENC(c) six2pr[c] | ||
27 | |||
28 | register char *outptr = bufcoded; | ||
29 | unsigned int i; | ||
30 | |||
31 | for (i = 0; i < nbytes; i += 3) { | ||
32 | *(outptr++) = ENC(*bufin >> 2); /* c1 */ | ||
33 | *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ | ||
34 | *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ | ||
35 | *(outptr++) = ENC(bufin[2] & 077); /* c4 */ | ||
36 | bufin += 3; | ||
37 | } | ||
38 | if (i == nbytes + 1) { | ||
39 | outptr[-1] = '='; | ||
40 | } else if (i == nbytes + 2) { | ||
41 | outptr[-1] = '='; | ||
42 | outptr[-2] = '='; | ||
43 | } else if (i == nbytes) { | ||
44 | *(outptr++) = '='; | ||
45 | } | ||
46 | *outptr = '\0'; | ||
47 | return (outptr - bufcoded); | ||
48 | } | ||
49 | |||
50 | int | ||
51 | 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++) | ||
67 | pr2six[j] = MAXVAL + 1; | ||
68 | for (j = 0; j < 64; j++) | ||
69 | pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; | ||
70 | } | ||
71 | /* Strip leading whitespace. */ | ||
72 | while (*bufcoded == ' ' || *bufcoded == '\t') | ||
73 | bufcoded++; | ||
74 | |||
75 | /* | ||
76 | * Figure out how many characters are in the input buffer. If this | ||
77 | * would decode into more bytes than would fit into the output | ||
78 | * buffer, adjust the number of input bytes downwards. | ||
79 | */ | ||
80 | bufin = bufcoded; | ||
81 | while (DEC(*(bufin++)) <= MAXVAL) | ||
82 | ; | ||
83 | nprbytes = bufin - bufcoded - 1; | ||
84 | nbytesdecoded = ((nprbytes + 3) / 4) * 3; | ||
85 | if (nbytesdecoded > outbufsize) | ||
86 | nprbytes = (outbufsize * 4) / 3; | ||
87 | |||
88 | bufin = bufcoded; | ||
89 | |||
90 | while (nprbytes > 0) { | ||
91 | *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); | ||
92 | *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); | ||
93 | *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); | ||
94 | bufin += 4; | ||
95 | nprbytes -= 4; | ||
96 | } | ||
97 | if (nprbytes & 03) { | ||
98 | if (DEC(bufin[-2]) > MAXVAL) | ||
99 | nbytesdecoded -= 2; | ||
100 | else | ||
101 | nbytesdecoded -= 1; | ||
102 | } | ||
103 | return (nbytesdecoded); | ||
104 | } | ||
105 | |||
106 | void | ||
107 | dump_base64(FILE *fp, unsigned char *data, int len) | ||
108 | { | ||
109 | unsigned char *buf = xmalloc(2*len); | ||
110 | int i, n; | ||
111 | n = uuencode(data, len, buf); | ||
112 | for (i = 0; i < n; i++) { | ||
113 | fprintf(fp, "%c", buf[i]); | ||
114 | if (i % 70 == 69) | ||
115 | fprintf(fp, "\n"); | ||
116 | } | ||
117 | if (i % 70 != 69) | ||
118 | fprintf(fp, "\n"); | ||
119 | xfree(buf); | ||
120 | } | ||
diff --git a/uuencode.h b/uuencode.h new file mode 100644 index 000000000..d3f446284 --- /dev/null +++ b/uuencode.h | |||
@@ -0,0 +1,6 @@ | |||
1 | #ifndef UUENCODE_H | ||
2 | #define UUENCODE_H | ||
3 | int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded); | ||
4 | int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize); | ||
5 | void dump_base64(FILE *fp, unsigned char *data, int len); | ||
6 | #endif | ||
@@ -1 +1 @@ | |||
#define SSH_VERSION "OpenSSH-1.2.3" | #define SSH_VERSION "OpenSSH-2.0" | ||