diff options
Diffstat (limited to 'sshconnect.c')
-rw-r--r-- | sshconnect.c | 1495 |
1 files changed, 1495 insertions, 0 deletions
diff --git a/sshconnect.c b/sshconnect.c new file mode 100644 index 000000000..3437b04ca --- /dev/null +++ b/sshconnect.c | |||
@@ -0,0 +1,1495 @@ | |||
1 | /* | ||
2 | |||
3 | sshconnect.c | ||
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Mar 18 22:15:47 1995 ylo | ||
11 | |||
12 | Code to connect to a remote host, and to perform the client side of the | ||
13 | login (authentication) dialog. | ||
14 | |||
15 | */ | ||
16 | |||
17 | #include "includes.h" | ||
18 | RCSID("$Id: sshconnect.c,v 1.1 1999/10/27 03:42:45 damien Exp $"); | ||
19 | |||
20 | #include <openssl/bn.h> | ||
21 | #include "xmalloc.h" | ||
22 | #include "rsa.h" | ||
23 | #include "ssh.h" | ||
24 | #include "packet.h" | ||
25 | #include "authfd.h" | ||
26 | #include "cipher.h" | ||
27 | #include "mpaux.h" | ||
28 | #include "uidswap.h" | ||
29 | #include "compat.h" | ||
30 | |||
31 | #include <openssl/md5.h> | ||
32 | |||
33 | /* Session id for the current session. */ | ||
34 | unsigned char session_id[16]; | ||
35 | |||
36 | /* Connect to the given ssh server using a proxy command. */ | ||
37 | |||
38 | int | ||
39 | ssh_proxy_connect(const char *host, int port, uid_t original_real_uid, | ||
40 | const char *proxy_command) | ||
41 | { | ||
42 | Buffer command; | ||
43 | const char *cp; | ||
44 | char *command_string; | ||
45 | int pin[2], pout[2]; | ||
46 | int pid; | ||
47 | char portstring[100]; | ||
48 | |||
49 | /* Convert the port number into a string. */ | ||
50 | snprintf(portstring, sizeof portstring, "%d", port); | ||
51 | |||
52 | /* Build the final command string in the buffer by making the appropriate | ||
53 | substitutions to the given proxy command. */ | ||
54 | buffer_init(&command); | ||
55 | for (cp = proxy_command; *cp; cp++) | ||
56 | { | ||
57 | if (cp[0] == '%' && cp[1] == '%') | ||
58 | { | ||
59 | buffer_append(&command, "%", 1); | ||
60 | cp++; | ||
61 | continue; | ||
62 | } | ||
63 | if (cp[0] == '%' && cp[1] == 'h') | ||
64 | { | ||
65 | buffer_append(&command, host, strlen(host)); | ||
66 | cp++; | ||
67 | continue; | ||
68 | } | ||
69 | if (cp[0] == '%' && cp[1] == 'p') | ||
70 | { | ||
71 | buffer_append(&command, portstring, strlen(portstring)); | ||
72 | cp++; | ||
73 | continue; | ||
74 | } | ||
75 | buffer_append(&command, cp, 1); | ||
76 | } | ||
77 | buffer_append(&command, "\0", 1); | ||
78 | |||
79 | /* Get the final command string. */ | ||
80 | command_string = buffer_ptr(&command); | ||
81 | |||
82 | /* Create pipes for communicating with the proxy. */ | ||
83 | if (pipe(pin) < 0 || pipe(pout) < 0) | ||
84 | fatal("Could not create pipes to communicate with the proxy: %.100s", | ||
85 | strerror(errno)); | ||
86 | |||
87 | debug("Executing proxy command: %.500s", command_string); | ||
88 | |||
89 | /* Fork and execute the proxy command. */ | ||
90 | if ((pid = fork()) == 0) | ||
91 | { | ||
92 | char *argv[10]; | ||
93 | |||
94 | /* Child. Permanently give up superuser privileges. */ | ||
95 | permanently_set_uid(original_real_uid); | ||
96 | |||
97 | /* Redirect stdin and stdout. */ | ||
98 | close(pin[1]); | ||
99 | if (pin[0] != 0) | ||
100 | { | ||
101 | if (dup2(pin[0], 0) < 0) | ||
102 | perror("dup2 stdin"); | ||
103 | close(pin[0]); | ||
104 | } | ||
105 | close(pout[0]); | ||
106 | if (dup2(pout[1], 1) < 0) | ||
107 | perror("dup2 stdout"); | ||
108 | close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */ | ||
109 | |||
110 | /* Stderr is left as it is so that error messages get printed on | ||
111 | the user's terminal. */ | ||
112 | argv[0] = "/bin/sh"; | ||
113 | argv[1] = "-c"; | ||
114 | argv[2] = command_string; | ||
115 | argv[3] = NULL; | ||
116 | |||
117 | /* Execute the proxy command. Note that we gave up any extra | ||
118 | privileges above. */ | ||
119 | execv("/bin/sh", argv); | ||
120 | perror("/bin/sh"); | ||
121 | exit(1); | ||
122 | } | ||
123 | /* Parent. */ | ||
124 | if (pid < 0) | ||
125 | fatal("fork failed: %.100s", strerror(errno)); | ||
126 | |||
127 | /* Close child side of the descriptors. */ | ||
128 | close(pin[0]); | ||
129 | close(pout[1]); | ||
130 | |||
131 | /* Free the command name. */ | ||
132 | buffer_free(&command); | ||
133 | |||
134 | /* Set the connection file descriptors. */ | ||
135 | packet_set_connection(pout[0], pin[1]); | ||
136 | |||
137 | return 1; | ||
138 | } | ||
139 | |||
140 | /* Creates a (possibly privileged) socket for use as the ssh connection. */ | ||
141 | |||
142 | int ssh_create_socket(uid_t original_real_uid, int privileged) | ||
143 | { | ||
144 | int sock; | ||
145 | |||
146 | /* If we are running as root and want to connect to a privileged port, | ||
147 | bind our own socket to a privileged port. */ | ||
148 | if (privileged) | ||
149 | { | ||
150 | int p = IPPORT_RESERVED - 1; | ||
151 | |||
152 | sock = rresvport(&p); | ||
153 | if (sock < 0) | ||
154 | fatal("rresvport: %.100s", strerror(errno)); | ||
155 | debug("Allocated local port %d.", p); | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | /* Just create an ordinary socket on arbitrary port. We use the | ||
160 | user's uid to create the socket. */ | ||
161 | temporarily_use_uid(original_real_uid); | ||
162 | sock = socket(AF_INET, SOCK_STREAM, 0); | ||
163 | if (sock < 0) | ||
164 | fatal("socket: %.100s", strerror(errno)); | ||
165 | restore_uid(); | ||
166 | } | ||
167 | return sock; | ||
168 | } | ||
169 | |||
170 | /* Opens a TCP/IP connection to the remote server on the given host. If | ||
171 | port is 0, the default port will be used. If anonymous is zero, | ||
172 | a privileged port will be allocated to make the connection. | ||
173 | This requires super-user privileges if anonymous is false. | ||
174 | Connection_attempts specifies the maximum number of tries (one per | ||
175 | second). If proxy_command is non-NULL, it specifies the command (with %h | ||
176 | and %p substituted for host and port, respectively) to use to contact | ||
177 | the daemon. */ | ||
178 | |||
179 | int ssh_connect(const char *host, struct sockaddr_in *hostaddr, | ||
180 | int port, int connection_attempts, | ||
181 | int anonymous, uid_t original_real_uid, | ||
182 | const char *proxy_command) | ||
183 | { | ||
184 | int sock = -1, attempt, i; | ||
185 | int on = 1; | ||
186 | struct servent *sp; | ||
187 | struct hostent *hp; | ||
188 | struct linger linger; | ||
189 | |||
190 | debug("ssh_connect: getuid %d geteuid %d anon %d", | ||
191 | (int)getuid(), (int)geteuid(), anonymous); | ||
192 | |||
193 | /* Get default port if port has not been set. */ | ||
194 | if (port == 0) | ||
195 | { | ||
196 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | ||
197 | if (sp) | ||
198 | port = ntohs(sp->s_port); | ||
199 | else | ||
200 | port = SSH_DEFAULT_PORT; | ||
201 | } | ||
202 | |||
203 | /* If a proxy command is given, connect using it. */ | ||
204 | if (proxy_command != NULL) | ||
205 | return ssh_proxy_connect(host, port, original_real_uid, proxy_command); | ||
206 | |||
207 | /* No proxy command. */ | ||
208 | |||
209 | /* No host lookup made yet. */ | ||
210 | hp = NULL; | ||
211 | |||
212 | /* Try to connect several times. On some machines, the first time will | ||
213 | sometimes fail. In general socket code appears to behave quite | ||
214 | magically on many machines. */ | ||
215 | for (attempt = 0; attempt < connection_attempts; attempt++) | ||
216 | { | ||
217 | if (attempt > 0) | ||
218 | debug("Trying again..."); | ||
219 | |||
220 | /* Try to parse the host name as a numeric inet address. */ | ||
221 | memset(hostaddr, 0, sizeof(hostaddr)); | ||
222 | hostaddr->sin_family = AF_INET; | ||
223 | hostaddr->sin_port = htons(port); | ||
224 | hostaddr->sin_addr.s_addr = inet_addr(host); | ||
225 | if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) | ||
226 | { | ||
227 | /* Valid numeric IP address */ | ||
228 | debug("Connecting to %.100s port %d.", | ||
229 | inet_ntoa(hostaddr->sin_addr), port); | ||
230 | |||
231 | /* Create a socket. */ | ||
232 | sock = ssh_create_socket(original_real_uid, | ||
233 | !anonymous && geteuid() == 0 && | ||
234 | port < IPPORT_RESERVED); | ||
235 | |||
236 | /* Connect to the host. We use the user's uid in the hope that | ||
237 | it will help with the problems of tcp_wrappers showing the | ||
238 | remote uid as root. */ | ||
239 | temporarily_use_uid(original_real_uid); | ||
240 | if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr)) | ||
241 | >= 0) | ||
242 | { | ||
243 | /* Successful connect. */ | ||
244 | restore_uid(); | ||
245 | break; | ||
246 | } | ||
247 | debug("connect: %.100s", strerror(errno)); | ||
248 | restore_uid(); | ||
249 | |||
250 | /* Destroy the failed socket. */ | ||
251 | shutdown(sock, SHUT_RDWR); | ||
252 | close(sock); | ||
253 | } | ||
254 | else | ||
255 | { | ||
256 | /* Not a valid numeric inet address. */ | ||
257 | /* Map host name to an address. */ | ||
258 | if (!hp) | ||
259 | hp = gethostbyname(host); | ||
260 | if (!hp) | ||
261 | fatal("Bad host name: %.100s", host); | ||
262 | if (!hp->h_addr_list[0]) | ||
263 | fatal("Host does not have an IP address: %.100s", host); | ||
264 | |||
265 | /* Loop through addresses for this host, and try each one in | ||
266 | sequence until the connection succeeds. */ | ||
267 | for (i = 0; hp->h_addr_list[i]; i++) | ||
268 | { | ||
269 | /* Set the address to connect to. */ | ||
270 | hostaddr->sin_family = hp->h_addrtype; | ||
271 | memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], | ||
272 | sizeof(hostaddr->sin_addr)); | ||
273 | |||
274 | debug("Connecting to %.200s [%.100s] port %d.", | ||
275 | host, inet_ntoa(hostaddr->sin_addr), port); | ||
276 | |||
277 | /* Create a socket for connecting. */ | ||
278 | sock = ssh_create_socket(original_real_uid, | ||
279 | !anonymous && geteuid() == 0 && | ||
280 | port < IPPORT_RESERVED); | ||
281 | |||
282 | /* Connect to the host. We use the user's uid in the hope that | ||
283 | it will help with tcp_wrappers showing the remote uid as | ||
284 | root. */ | ||
285 | temporarily_use_uid(original_real_uid); | ||
286 | if (connect(sock, (struct sockaddr *)hostaddr, | ||
287 | sizeof(*hostaddr)) >= 0) | ||
288 | { | ||
289 | /* Successful connection. */ | ||
290 | restore_uid(); | ||
291 | break; | ||
292 | } | ||
293 | debug("connect: %.100s", strerror(errno)); | ||
294 | restore_uid(); | ||
295 | |||
296 | /* Close the failed socket; there appear to be some problems | ||
297 | when reusing a socket for which connect() has already | ||
298 | returned an error. */ | ||
299 | shutdown(sock, SHUT_RDWR); | ||
300 | close(sock); | ||
301 | } | ||
302 | if (hp->h_addr_list[i]) | ||
303 | break; /* Successful connection. */ | ||
304 | } | ||
305 | |||
306 | /* Sleep a moment before retrying. */ | ||
307 | sleep(1); | ||
308 | } | ||
309 | /* Return failure if we didn't get a successful connection. */ | ||
310 | if (attempt >= connection_attempts) | ||
311 | return 0; | ||
312 | |||
313 | debug("Connection established."); | ||
314 | |||
315 | /* Set socket options. We would like the socket to disappear as soon as | ||
316 | it has been closed for whatever reason. */ | ||
317 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | ||
318 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); | ||
319 | linger.l_onoff = 1; | ||
320 | linger.l_linger = 5; | ||
321 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); | ||
322 | |||
323 | /* Set the connection. */ | ||
324 | packet_set_connection(sock, sock); | ||
325 | |||
326 | return 1; | ||
327 | } | ||
328 | |||
329 | /* Checks if the user has an authentication agent, and if so, tries to | ||
330 | authenticate using the agent. */ | ||
331 | |||
332 | int | ||
333 | try_agent_authentication() | ||
334 | { | ||
335 | int status, type, bits; | ||
336 | char *comment; | ||
337 | AuthenticationConnection *auth; | ||
338 | unsigned char response[16]; | ||
339 | unsigned int i; | ||
340 | BIGNUM *e, *n, *challenge; | ||
341 | |||
342 | /* Get connection to the agent. */ | ||
343 | auth = ssh_get_authentication_connection(); | ||
344 | if (!auth) | ||
345 | return 0; | ||
346 | |||
347 | e = BN_new(); | ||
348 | n = BN_new(); | ||
349 | challenge = BN_new(); | ||
350 | |||
351 | /* Loop through identities served by the agent. */ | ||
352 | for (status = ssh_get_first_identity(auth, &bits, e, n, &comment); | ||
353 | status; | ||
354 | status = ssh_get_next_identity(auth, &bits, e, n, &comment)) | ||
355 | { | ||
356 | int plen, clen; | ||
357 | |||
358 | /* Try this identity. */ | ||
359 | debug("Trying RSA authentication via agent with '%.100s'", comment); | ||
360 | xfree(comment); | ||
361 | |||
362 | /* Tell the server that we are willing to authenticate using this key. */ | ||
363 | packet_start(SSH_CMSG_AUTH_RSA); | ||
364 | packet_put_bignum(n); | ||
365 | packet_send(); | ||
366 | packet_write_wait(); | ||
367 | |||
368 | /* Wait for server's response. */ | ||
369 | type = packet_read(&plen); | ||
370 | |||
371 | /* The server sends failure if it doesn\'t like our key or does not | ||
372 | support RSA authentication. */ | ||
373 | if (type == SSH_SMSG_FAILURE) | ||
374 | { | ||
375 | debug("Server refused our key."); | ||
376 | continue; | ||
377 | } | ||
378 | |||
379 | /* Otherwise it should have sent a challenge. */ | ||
380 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
381 | packet_disconnect("Protocol error during RSA authentication: %d", | ||
382 | type); | ||
383 | |||
384 | packet_get_bignum(challenge, &clen); | ||
385 | |||
386 | packet_integrity_check(plen, clen, type); | ||
387 | |||
388 | debug("Received RSA challenge from server."); | ||
389 | |||
390 | /* Ask the agent to decrypt the challenge. */ | ||
391 | if (!ssh_decrypt_challenge(auth, bits, e, n, challenge, | ||
392 | session_id, 1, response)) | ||
393 | { | ||
394 | /* The agent failed to authenticate this identifier although it | ||
395 | advertised it supports this. Just return a wrong value. */ | ||
396 | log("Authentication agent failed to decrypt challenge."); | ||
397 | memset(response, 0, sizeof(response)); | ||
398 | } | ||
399 | |||
400 | debug("Sending response to RSA challenge."); | ||
401 | |||
402 | /* Send the decrypted challenge back to the server. */ | ||
403 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
404 | for (i = 0; i < 16; i++) | ||
405 | packet_put_char(response[i]); | ||
406 | packet_send(); | ||
407 | packet_write_wait(); | ||
408 | |||
409 | /* Wait for response from the server. */ | ||
410 | type = packet_read(&plen); | ||
411 | |||
412 | /* The server returns success if it accepted the authentication. */ | ||
413 | if (type == SSH_SMSG_SUCCESS) | ||
414 | { | ||
415 | debug("RSA authentication accepted by server."); | ||
416 | BN_clear_free(e); | ||
417 | BN_clear_free(n); | ||
418 | BN_clear_free(challenge); | ||
419 | return 1; | ||
420 | } | ||
421 | |||
422 | /* Otherwise it should return failure. */ | ||
423 | if (type != SSH_SMSG_FAILURE) | ||
424 | packet_disconnect("Protocol error waiting RSA auth response: %d", | ||
425 | type); | ||
426 | } | ||
427 | |||
428 | BN_clear_free(e); | ||
429 | BN_clear_free(n); | ||
430 | BN_clear_free(challenge); | ||
431 | |||
432 | debug("RSA authentication using agent refused."); | ||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Computes the proper response to a RSA challenge, and sends the response to | ||
437 | the server. */ | ||
438 | |||
439 | void | ||
440 | respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv) | ||
441 | { | ||
442 | unsigned char buf[32], response[16]; | ||
443 | MD5_CTX md; | ||
444 | int i, len; | ||
445 | |||
446 | /* Decrypt the challenge using the private key. */ | ||
447 | rsa_private_decrypt(challenge, challenge, prv); | ||
448 | |||
449 | /* Compute the response. */ | ||
450 | /* The response is MD5 of decrypted challenge plus session id. */ | ||
451 | len = BN_num_bytes(challenge); | ||
452 | assert(len <= sizeof(buf) && len); | ||
453 | memset(buf, 0, sizeof(buf)); | ||
454 | BN_bn2bin(challenge, buf + sizeof(buf) - len); | ||
455 | MD5_Init(&md); | ||
456 | MD5_Update(&md, buf, 32); | ||
457 | MD5_Update(&md, session_id, 16); | ||
458 | MD5_Final(response, &md); | ||
459 | |||
460 | debug("Sending response to host key RSA challenge."); | ||
461 | |||
462 | /* Send the response back to the server. */ | ||
463 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
464 | for (i = 0; i < 16; i++) | ||
465 | packet_put_char(response[i]); | ||
466 | packet_send(); | ||
467 | packet_write_wait(); | ||
468 | |||
469 | memset(buf, 0, sizeof(buf)); | ||
470 | memset(response, 0, sizeof(response)); | ||
471 | memset(&md, 0, sizeof(md)); | ||
472 | } | ||
473 | |||
474 | /* Checks if the user has authentication file, and if so, tries to authenticate | ||
475 | the user using it. */ | ||
476 | |||
477 | int | ||
478 | try_rsa_authentication(struct passwd *pw, const char *authfile, | ||
479 | int may_ask_passphrase) | ||
480 | { | ||
481 | BIGNUM *challenge; | ||
482 | RSA *private_key; | ||
483 | RSA *public_key; | ||
484 | char *passphrase, *comment; | ||
485 | int type, i; | ||
486 | int plen, clen; | ||
487 | |||
488 | /* Try to load identification for the authentication key. */ | ||
489 | public_key = RSA_new(); | ||
490 | if (!load_public_key(authfile, public_key, &comment)) { | ||
491 | RSA_free(public_key); | ||
492 | return 0; /* Could not load it. Fail. */ | ||
493 | } | ||
494 | |||
495 | debug("Trying RSA authentication with key '%.100s'", comment); | ||
496 | |||
497 | /* Tell the server that we are willing to authenticate using this key. */ | ||
498 | packet_start(SSH_CMSG_AUTH_RSA); | ||
499 | packet_put_bignum(public_key->n); | ||
500 | packet_send(); | ||
501 | packet_write_wait(); | ||
502 | |||
503 | /* We no longer need the public key. */ | ||
504 | RSA_free(public_key); | ||
505 | |||
506 | /* Wait for server's response. */ | ||
507 | type = packet_read(&plen); | ||
508 | |||
509 | /* The server responds with failure if it doesn\'t like our key or doesn\'t | ||
510 | support RSA authentication. */ | ||
511 | if (type == SSH_SMSG_FAILURE) | ||
512 | { | ||
513 | debug("Server refused our key."); | ||
514 | xfree(comment); | ||
515 | return 0; /* Server refuses to authenticate with this key. */ | ||
516 | } | ||
517 | |||
518 | /* Otherwise, the server should respond with a challenge. */ | ||
519 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
520 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
521 | |||
522 | /* Get the challenge from the packet. */ | ||
523 | challenge = BN_new(); | ||
524 | packet_get_bignum(challenge, &clen); | ||
525 | |||
526 | packet_integrity_check(plen, clen, type); | ||
527 | |||
528 | debug("Received RSA challenge from server."); | ||
529 | |||
530 | private_key = RSA_new(); | ||
531 | /* Load the private key. Try first with empty passphrase; if it fails, | ||
532 | ask for a passphrase. */ | ||
533 | if (!load_private_key(authfile, "", private_key, NULL)) | ||
534 | { | ||
535 | char buf[300]; | ||
536 | /* Request passphrase from the user. We read from /dev/tty to make | ||
537 | this work even if stdin has been redirected. If running in | ||
538 | batch mode, we just use the empty passphrase, which will fail and | ||
539 | return. */ | ||
540 | snprintf(buf, sizeof buf, | ||
541 | "Enter passphrase for RSA key '%.100s': ", comment); | ||
542 | if (may_ask_passphrase) | ||
543 | passphrase = read_passphrase(buf, 0); | ||
544 | else | ||
545 | { | ||
546 | debug("Will not query passphrase for %.100s in batch mode.", | ||
547 | comment); | ||
548 | passphrase = xstrdup(""); | ||
549 | } | ||
550 | |||
551 | /* Load the authentication file using the pasphrase. */ | ||
552 | if (!load_private_key(authfile, passphrase, private_key, NULL)) | ||
553 | { | ||
554 | memset(passphrase, 0, strlen(passphrase)); | ||
555 | xfree(passphrase); | ||
556 | error("Bad passphrase."); | ||
557 | |||
558 | /* Send a dummy response packet to avoid protocol error. */ | ||
559 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
560 | for (i = 0; i < 16; i++) | ||
561 | packet_put_char(0); | ||
562 | packet_send(); | ||
563 | packet_write_wait(); | ||
564 | |||
565 | /* Expect the server to reject it... */ | ||
566 | packet_read_expect(&plen, SSH_SMSG_FAILURE); | ||
567 | xfree(comment); | ||
568 | return 0; | ||
569 | } | ||
570 | |||
571 | /* Destroy the passphrase. */ | ||
572 | memset(passphrase, 0, strlen(passphrase)); | ||
573 | xfree(passphrase); | ||
574 | } | ||
575 | |||
576 | /* We no longer need the comment. */ | ||
577 | xfree(comment); | ||
578 | |||
579 | /* Compute and send a response to the challenge. */ | ||
580 | respond_to_rsa_challenge(challenge, private_key); | ||
581 | |||
582 | /* Destroy the private key. */ | ||
583 | RSA_free(private_key); | ||
584 | |||
585 | /* We no longer need the challenge. */ | ||
586 | BN_clear_free(challenge); | ||
587 | |||
588 | /* Wait for response from the server. */ | ||
589 | type = packet_read(&plen); | ||
590 | if (type == SSH_SMSG_SUCCESS) | ||
591 | { | ||
592 | debug("RSA authentication accepted by server."); | ||
593 | return 1; | ||
594 | } | ||
595 | if (type != SSH_SMSG_FAILURE) | ||
596 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
597 | debug("RSA authentication refused."); | ||
598 | return 0; | ||
599 | } | ||
600 | |||
601 | /* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv | ||
602 | authentication and RSA host authentication. */ | ||
603 | |||
604 | int | ||
605 | try_rhosts_rsa_authentication(const char *local_user, RSA *host_key) | ||
606 | { | ||
607 | int type; | ||
608 | BIGNUM *challenge; | ||
609 | int plen, clen; | ||
610 | |||
611 | debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); | ||
612 | |||
613 | /* Tell the server that we are willing to authenticate using this key. */ | ||
614 | packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); | ||
615 | packet_put_string(local_user, strlen(local_user)); | ||
616 | packet_put_int(BN_num_bits(host_key->n)); | ||
617 | packet_put_bignum(host_key->e); | ||
618 | packet_put_bignum(host_key->n); | ||
619 | packet_send(); | ||
620 | packet_write_wait(); | ||
621 | |||
622 | /* Wait for server's response. */ | ||
623 | type = packet_read(&plen); | ||
624 | |||
625 | /* The server responds with failure if it doesn't admit our .rhosts | ||
626 | authentication or doesn't know our host key. */ | ||
627 | if (type == SSH_SMSG_FAILURE) | ||
628 | { | ||
629 | debug("Server refused our rhosts authentication or host key."); | ||
630 | return 0; /* Server refuses to authenticate us with this method. */ | ||
631 | } | ||
632 | |||
633 | /* Otherwise, the server should respond with a challenge. */ | ||
634 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
635 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
636 | |||
637 | /* Get the challenge from the packet. */ | ||
638 | challenge = BN_new(); | ||
639 | packet_get_bignum(challenge, &clen); | ||
640 | |||
641 | packet_integrity_check(plen, clen, type); | ||
642 | |||
643 | debug("Received RSA challenge for host key from server."); | ||
644 | |||
645 | /* Compute a response to the challenge. */ | ||
646 | respond_to_rsa_challenge(challenge, host_key); | ||
647 | |||
648 | /* We no longer need the challenge. */ | ||
649 | BN_clear_free(challenge); | ||
650 | |||
651 | /* Wait for response from the server. */ | ||
652 | type = packet_read(&plen); | ||
653 | if (type == SSH_SMSG_SUCCESS) | ||
654 | { | ||
655 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); | ||
656 | return 1; | ||
657 | } | ||
658 | if (type != SSH_SMSG_FAILURE) | ||
659 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
660 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); | ||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | #ifdef KRB4 | ||
665 | int try_kerberos_authentication() | ||
666 | { | ||
667 | KTEXT_ST auth; /* Kerberos data */ | ||
668 | char *reply; | ||
669 | char inst[INST_SZ]; | ||
670 | char *realm; | ||
671 | CREDENTIALS cred; | ||
672 | int r, type, plen; | ||
673 | Key_schedule schedule; | ||
674 | u_long checksum, cksum; | ||
675 | MSG_DAT msg_data; | ||
676 | struct sockaddr_in local, foreign; | ||
677 | struct stat st; | ||
678 | |||
679 | /* Don't do anything if we don't have any tickets. */ | ||
680 | if (stat(tkt_string(), &st) < 0) return 0; | ||
681 | |||
682 | strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); | ||
683 | |||
684 | realm = (char *)krb_realmofhost(get_canonical_hostname()); | ||
685 | if (!realm) { | ||
686 | debug("Kerberos V4: no realm for %s", get_canonical_hostname()); | ||
687 | return 0; | ||
688 | } | ||
689 | /* This can really be anything. */ | ||
690 | checksum = (u_long) getpid(); | ||
691 | |||
692 | r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); | ||
693 | if (r != KSUCCESS) { | ||
694 | debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); | ||
695 | return 0; | ||
696 | } | ||
697 | /* Get session key to decrypt the server's reply with. */ | ||
698 | r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); | ||
699 | if (r != KSUCCESS) { | ||
700 | debug("get_cred failed: %s", krb_err_txt[r]); | ||
701 | return 0; | ||
702 | } | ||
703 | des_key_sched((des_cblock *)cred.session, schedule); | ||
704 | |||
705 | /* Send authentication info to server. */ | ||
706 | packet_start(SSH_CMSG_AUTH_KERBEROS); | ||
707 | packet_put_string((char *)auth.dat, auth.length); | ||
708 | packet_send(); | ||
709 | packet_write_wait(); | ||
710 | |||
711 | /* Zero the buffer. */ | ||
712 | (void) memset(auth.dat, 0, MAX_KTXT_LEN); | ||
713 | |||
714 | r = sizeof(local); | ||
715 | memset(&local, 0, sizeof(local)); | ||
716 | if (getsockname(packet_get_connection_in(), | ||
717 | (struct sockaddr *) &local, &r) < 0) | ||
718 | debug("getsockname failed: %s", strerror(errno)); | ||
719 | |||
720 | r = sizeof(foreign); | ||
721 | memset(&foreign, 0, sizeof(foreign)); | ||
722 | if (getpeername(packet_get_connection_in(), | ||
723 | (struct sockaddr *)&foreign, &r) < 0) | ||
724 | debug("getpeername failed: %s", strerror(errno)); | ||
725 | |||
726 | /* Get server reply. */ | ||
727 | type = packet_read(&plen); | ||
728 | switch(type) { | ||
729 | |||
730 | case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ | ||
731 | debug("Kerberos V4 authentication failed."); | ||
732 | return 0; | ||
733 | break; | ||
734 | |||
735 | case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ | ||
736 | debug("Kerberos V4 authentication accepted."); | ||
737 | |||
738 | /* Get server's response. */ | ||
739 | reply = packet_get_string((unsigned int *)&auth.length); | ||
740 | memcpy(auth.dat, reply, auth.length); | ||
741 | xfree(reply); | ||
742 | |||
743 | packet_integrity_check(plen, 4 + auth.length, type); | ||
744 | |||
745 | /* If his response isn't properly encrypted with the session key, | ||
746 | and the decrypted checksum fails to match, he's bogus. Bail out. */ | ||
747 | r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, | ||
748 | &foreign, &local, &msg_data); | ||
749 | if (r != KSUCCESS) { | ||
750 | debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); | ||
751 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
752 | } | ||
753 | /* Fetch the (incremented) checksum that we supplied in the request. */ | ||
754 | (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum)); | ||
755 | cksum = ntohl(cksum); | ||
756 | |||
757 | /* If it matches, we're golden. */ | ||
758 | if (cksum == checksum + 1) { | ||
759 | debug("Kerberos V4 challenge successful."); | ||
760 | return 1; | ||
761 | } | ||
762 | else | ||
763 | packet_disconnect("Kerberos V4 challenge failed!"); | ||
764 | break; | ||
765 | |||
766 | default: | ||
767 | packet_disconnect("Protocol error on Kerberos V4 response: %d", type); | ||
768 | } | ||
769 | return 0; | ||
770 | } | ||
771 | #endif /* KRB4 */ | ||
772 | |||
773 | #ifdef AFS | ||
774 | int send_kerberos_tgt() | ||
775 | { | ||
776 | CREDENTIALS *creds; | ||
777 | char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; | ||
778 | int r, type, plen; | ||
779 | unsigned char buffer[8192]; | ||
780 | struct stat st; | ||
781 | |||
782 | /* Don't do anything if we don't have any tickets. */ | ||
783 | if (stat(tkt_string(), &st) < 0) return 0; | ||
784 | |||
785 | creds = xmalloc(sizeof(*creds)); | ||
786 | |||
787 | if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { | ||
788 | debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]); | ||
789 | return 0; | ||
790 | } | ||
791 | if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { | ||
792 | debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); | ||
793 | return 0; | ||
794 | } | ||
795 | if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { | ||
796 | debug("Kerberos V4 ticket expired: %s", TKT_FILE); | ||
797 | return 0; | ||
798 | } | ||
799 | |||
800 | creds_to_radix(creds, buffer); | ||
801 | xfree(creds); | ||
802 | |||
803 | packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); | ||
804 | packet_put_string((char *)buffer, strlen(buffer)); | ||
805 | packet_send(); | ||
806 | packet_write_wait(); | ||
807 | |||
808 | type = packet_read(&plen); | ||
809 | |||
810 | if (type == SSH_SMSG_FAILURE) | ||
811 | debug("Kerberos TGT for realm %s rejected.", prealm); | ||
812 | else if (type != SSH_SMSG_SUCCESS) | ||
813 | packet_disconnect("Protocol error on Kerberos TGT response: %d", type); | ||
814 | |||
815 | return 1; | ||
816 | } | ||
817 | |||
818 | void send_afs_tokens(void) | ||
819 | { | ||
820 | CREDENTIALS creds; | ||
821 | struct ViceIoctl parms; | ||
822 | struct ClearToken ct; | ||
823 | int i, type, len, plen; | ||
824 | char buf[2048], *p, *server_cell; | ||
825 | unsigned char buffer[8192]; | ||
826 | |||
827 | /* Move over ktc_GetToken, here's something leaner. */ | ||
828 | for (i = 0; i < 100; i++) { /* just in case */ | ||
829 | parms.in = (char *)&i; | ||
830 | parms.in_size = sizeof(i); | ||
831 | parms.out = buf; | ||
832 | parms.out_size = sizeof(buf); | ||
833 | if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break; | ||
834 | p = buf; | ||
835 | |||
836 | /* Get secret token. */ | ||
837 | memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); | ||
838 | if (creds.ticket_st.length > MAX_KTXT_LEN) break; | ||
839 | p += sizeof(unsigned int); | ||
840 | memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); | ||
841 | p += creds.ticket_st.length; | ||
842 | |||
843 | /* Get clear token. */ | ||
844 | memcpy(&len, p, sizeof(len)); | ||
845 | if (len != sizeof(struct ClearToken)) break; | ||
846 | p += sizeof(len); | ||
847 | memcpy(&ct, p, len); | ||
848 | p += len; | ||
849 | p += sizeof(len); /* primary flag */ | ||
850 | server_cell = p; | ||
851 | |||
852 | /* Flesh out our credentials. */ | ||
853 | strlcpy(creds.service, "afs", sizeof creds.service); | ||
854 | creds.instance[0] = '\0'; | ||
855 | strlcpy(creds.realm, server_cell, REALM_SZ); | ||
856 | memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); | ||
857 | creds.issue_date = ct.BeginTimestamp; | ||
858 | creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); | ||
859 | creds.kvno = ct.AuthHandle; | ||
860 | snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); | ||
861 | creds.pinst[0] = '\0'; | ||
862 | |||
863 | /* Encode token, ship it off. */ | ||
864 | if (!creds_to_radix(&creds, buffer)) break; | ||
865 | packet_start(SSH_CMSG_HAVE_AFS_TOKEN); | ||
866 | packet_put_string((char *)buffer, strlen(buffer)); | ||
867 | packet_send(); | ||
868 | packet_write_wait(); | ||
869 | |||
870 | /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */ | ||
871 | type = packet_read(&plen); | ||
872 | |||
873 | if (type == SSH_SMSG_FAILURE) | ||
874 | debug("AFS token for cell %s rejected.", server_cell); | ||
875 | else if (type != SSH_SMSG_SUCCESS) | ||
876 | packet_disconnect("Protocol error on AFS token response: %d", type); | ||
877 | } | ||
878 | } | ||
879 | #endif /* AFS */ | ||
880 | |||
881 | /* Waits for the server identification string, and sends our own identification | ||
882 | string. */ | ||
883 | |||
884 | void ssh_exchange_identification() | ||
885 | { | ||
886 | char buf[256], remote_version[256]; /* must be same size! */ | ||
887 | int remote_major, remote_minor, i; | ||
888 | int connection_in = packet_get_connection_in(); | ||
889 | int connection_out = packet_get_connection_out(); | ||
890 | extern Options options; | ||
891 | |||
892 | /* Read other side\'s version identification. */ | ||
893 | for (i = 0; i < sizeof(buf) - 1; i++) | ||
894 | { | ||
895 | if (read(connection_in, &buf[i], 1) != 1) | ||
896 | fatal("read: %.100s", strerror(errno)); | ||
897 | if (buf[i] == '\r') | ||
898 | { | ||
899 | buf[i] = '\n'; | ||
900 | buf[i + 1] = 0; | ||
901 | break; | ||
902 | } | ||
903 | if (buf[i] == '\n') | ||
904 | { | ||
905 | buf[i + 1] = 0; | ||
906 | break; | ||
907 | } | ||
908 | } | ||
909 | buf[sizeof(buf) - 1] = 0; | ||
910 | |||
911 | /* Check that the versions match. In future this might accept several | ||
912 | versions and set appropriate flags to handle them. */ | ||
913 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, | ||
914 | remote_version) != 3) | ||
915 | fatal("Bad remote protocol version identification: '%.100s'", buf); | ||
916 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
917 | remote_major, remote_minor, remote_version); | ||
918 | |||
919 | /* Check if the remote protocol version is too old. */ | ||
920 | if (remote_major == 1 && remote_minor < 3) | ||
921 | fatal("Remote machine has too old SSH software version."); | ||
922 | |||
923 | /* We speak 1.3, too. */ | ||
924 | if (remote_major == 1 && remote_minor == 3) { | ||
925 | enable_compat13(); | ||
926 | if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) { | ||
927 | log("Agent forwarding disabled, remote version '%s' is not compatible.", | ||
928 | remote_version); | ||
929 | options.forward_agent = 0; | ||
930 | } | ||
931 | } | ||
932 | #if 0 | ||
933 | /* Removed for now, to permit compatibility with latter versions. The server | ||
934 | will reject our version and disconnect if it doesn't support it. */ | ||
935 | if (remote_major != PROTOCOL_MAJOR) | ||
936 | fatal("Protocol major versions differ: %d vs. %d", | ||
937 | PROTOCOL_MAJOR, remote_major); | ||
938 | #endif | ||
939 | |||
940 | /* Send our own protocol version identification. */ | ||
941 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | ||
942 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); | ||
943 | if (write(connection_out, buf, strlen(buf)) != strlen(buf)) | ||
944 | fatal("write: %.100s", strerror(errno)); | ||
945 | } | ||
946 | |||
947 | int ssh_cipher_default = SSH_CIPHER_3DES; | ||
948 | |||
949 | int read_yes_or_no(const char *prompt, int defval) | ||
950 | { | ||
951 | char buf[1024]; | ||
952 | FILE *f; | ||
953 | int retval = -1; | ||
954 | |||
955 | if (isatty(0)) | ||
956 | f = stdin; | ||
957 | else | ||
958 | f = fopen("/dev/tty", "rw"); | ||
959 | |||
960 | if (f == NULL) | ||
961 | return 0; | ||
962 | |||
963 | fflush(stdout); | ||
964 | |||
965 | while (1) | ||
966 | { | ||
967 | fprintf(stderr, "%s", prompt); | ||
968 | if (fgets(buf, sizeof(buf), f) == NULL) | ||
969 | { | ||
970 | /* Print a newline (the prompt probably didn\'t have one). */ | ||
971 | fprintf(stderr, "\n"); | ||
972 | strlcpy(buf, "no", sizeof buf); | ||
973 | } | ||
974 | /* Remove newline from response. */ | ||
975 | if (strchr(buf, '\n')) | ||
976 | *strchr(buf, '\n') = 0; | ||
977 | |||
978 | if (buf[0] == 0) | ||
979 | retval = defval; | ||
980 | if (strcmp(buf, "yes") == 0) | ||
981 | retval = 1; | ||
982 | if (strcmp(buf, "no") == 0) | ||
983 | retval = 0; | ||
984 | |||
985 | if (retval != -1) | ||
986 | { | ||
987 | if (f != stdin) | ||
988 | fclose(f); | ||
989 | return retval; | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | /* Starts a dialog with the server, and authenticates the current user on the | ||
995 | server. This does not need any extra privileges. The basic connection | ||
996 | to the server must already have been established before this is called. | ||
997 | User is the remote user; if it is NULL, the current local user name will | ||
998 | be used. Anonymous indicates that no rhosts authentication will be used. | ||
999 | If login fails, this function prints an error and never returns. | ||
1000 | This function does not require super-user privileges. */ | ||
1001 | |||
1002 | void ssh_login(int host_key_valid, | ||
1003 | RSA *own_host_key, | ||
1004 | const char *orighost, | ||
1005 | struct sockaddr_in *hostaddr, | ||
1006 | Options *options, uid_t original_real_uid) | ||
1007 | { | ||
1008 | int i, type; | ||
1009 | char *password; | ||
1010 | struct passwd *pw; | ||
1011 | BIGNUM *key; | ||
1012 | RSA *host_key, *file_key; | ||
1013 | RSA *public_key; | ||
1014 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | ||
1015 | const char *server_user, *local_user; | ||
1016 | char *cp, *host, *ip = NULL; | ||
1017 | unsigned char check_bytes[8]; | ||
1018 | unsigned int supported_ciphers, supported_authentications, protocol_flags; | ||
1019 | HostStatus host_status; | ||
1020 | HostStatus ip_status; | ||
1021 | int host_ip_differ = 0; | ||
1022 | int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | ||
1023 | int payload_len, clen, sum_len = 0; | ||
1024 | u_int32_t rand = 0; | ||
1025 | |||
1026 | if (options->check_host_ip) | ||
1027 | ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); | ||
1028 | |||
1029 | /* Convert the user-supplied hostname into all lowercase. */ | ||
1030 | host = xstrdup(orighost); | ||
1031 | for (cp = host; *cp; cp++) | ||
1032 | if (isupper(*cp)) | ||
1033 | *cp = tolower(*cp); | ||
1034 | |||
1035 | /* Exchange protocol version identification strings with the server. */ | ||
1036 | ssh_exchange_identification(); | ||
1037 | |||
1038 | /* Put the connection into non-blocking mode. */ | ||
1039 | packet_set_nonblocking(); | ||
1040 | |||
1041 | /* Get local user name. Use it as server user if no user name | ||
1042 | was given. */ | ||
1043 | pw = getpwuid(original_real_uid); | ||
1044 | if (!pw) | ||
1045 | fatal("User id %d not found from user database.", original_real_uid); | ||
1046 | local_user = xstrdup(pw->pw_name); | ||
1047 | server_user = options->user ? options->user : local_user; | ||
1048 | |||
1049 | debug("Waiting for server public key."); | ||
1050 | |||
1051 | /* Wait for a public key packet from the server. */ | ||
1052 | packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); | ||
1053 | |||
1054 | /* Get check bytes from the packet. */ | ||
1055 | for (i = 0; i < 8; i++) | ||
1056 | check_bytes[i] = packet_get_char(); | ||
1057 | |||
1058 | /* Get the public key. */ | ||
1059 | public_key = RSA_new(); | ||
1060 | packet_get_int(); /* bits */ | ||
1061 | public_key->e = BN_new(); | ||
1062 | packet_get_bignum(public_key->e, &clen); | ||
1063 | sum_len += clen; | ||
1064 | public_key->n = BN_new(); | ||
1065 | packet_get_bignum(public_key->n, &clen); | ||
1066 | sum_len += clen; | ||
1067 | |||
1068 | /* Get the host key. */ | ||
1069 | host_key = RSA_new(); | ||
1070 | packet_get_int(); /* bits */ | ||
1071 | host_key->e = BN_new(); | ||
1072 | packet_get_bignum(host_key->e, &clen); | ||
1073 | sum_len += clen; | ||
1074 | host_key->n = BN_new(); | ||
1075 | packet_get_bignum(host_key->n, &clen); | ||
1076 | sum_len += clen; | ||
1077 | |||
1078 | /* Store the host key from the known host file in here | ||
1079 | * so that we can compare it with the key for the IP | ||
1080 | * address. */ | ||
1081 | file_key = RSA_new(); | ||
1082 | file_key->n = BN_new(); | ||
1083 | file_key->e = BN_new(); | ||
1084 | |||
1085 | /* Get protocol flags. */ | ||
1086 | protocol_flags = packet_get_int(); | ||
1087 | packet_set_protocol_flags(protocol_flags); | ||
1088 | |||
1089 | /* Get supported cipher types. */ | ||
1090 | supported_ciphers = packet_get_int(); | ||
1091 | |||
1092 | /* Get supported authentication types. */ | ||
1093 | supported_authentications = packet_get_int(); | ||
1094 | |||
1095 | debug("Received server public key (%d bits) and host key (%d bits).", | ||
1096 | BN_num_bits(public_key->n), BN_num_bits(host_key->n)); | ||
1097 | |||
1098 | packet_integrity_check(payload_len, | ||
1099 | 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, | ||
1100 | SSH_SMSG_PUBLIC_KEY); | ||
1101 | |||
1102 | /* Compute the session id. */ | ||
1103 | compute_session_id(session_id, check_bytes, | ||
1104 | BN_num_bits(host_key->n), host_key->n, | ||
1105 | BN_num_bits(public_key->n), public_key->n); | ||
1106 | |||
1107 | /* Check if the host key is present in the user\'s list of known hosts | ||
1108 | or in the systemwide list. */ | ||
1109 | host_status = check_host_in_hostfile(options->user_hostfile, | ||
1110 | host, BN_num_bits(host_key->n), | ||
1111 | host_key->e, host_key->n, | ||
1112 | file_key->e, file_key->n); | ||
1113 | if (host_status == HOST_NEW) | ||
1114 | host_status = check_host_in_hostfile(options->system_hostfile, host, | ||
1115 | BN_num_bits(host_key->n), | ||
1116 | host_key->e, host_key->n, | ||
1117 | file_key->e, file_key->n); | ||
1118 | /* Force accepting of the host key for localhost and 127.0.0.1. | ||
1119 | The problem is that if the home directory is NFS-mounted to multiple | ||
1120 | machines, localhost will refer to a different machine in each of them, | ||
1121 | and the user will get bogus HOST_CHANGED warnings. This essentially | ||
1122 | disables host authentication for localhost; however, this is probably | ||
1123 | not a real problem. */ | ||
1124 | if (local) { | ||
1125 | debug("Forcing accepting of host key for localhost."); | ||
1126 | host_status = HOST_OK; | ||
1127 | } | ||
1128 | |||
1129 | /* Also perform check for the ip address, skip the check if we are | ||
1130 | localhost or the hostname was an ip address to begin with */ | ||
1131 | if (options->check_host_ip && !local && strcmp(host, ip)) { | ||
1132 | RSA *ip_key = RSA_new(); | ||
1133 | ip_key->n = BN_new(); | ||
1134 | ip_key->e = BN_new(); | ||
1135 | ip_status = check_host_in_hostfile(options->user_hostfile, ip, | ||
1136 | BN_num_bits(host_key->n), | ||
1137 | host_key->e, host_key->n, | ||
1138 | ip_key->e, ip_key->n); | ||
1139 | |||
1140 | if (ip_status == HOST_NEW) | ||
1141 | ip_status = check_host_in_hostfile(options->system_hostfile, ip, | ||
1142 | BN_num_bits(host_key->n), | ||
1143 | host_key->e, host_key->n, | ||
1144 | ip_key->e, ip_key->n); | ||
1145 | if (host_status == HOST_CHANGED && | ||
1146 | (ip_status != HOST_CHANGED || | ||
1147 | (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n)))) | ||
1148 | host_ip_differ = 1; | ||
1149 | |||
1150 | RSA_free(ip_key); | ||
1151 | } else | ||
1152 | ip_status = host_status; | ||
1153 | |||
1154 | RSA_free(file_key); | ||
1155 | |||
1156 | switch (host_status) { | ||
1157 | case HOST_OK: | ||
1158 | /* The host is known and the key matches. */ | ||
1159 | debug("Host '%.200s' is known and matches the host key.", host); | ||
1160 | if (options->check_host_ip) { | ||
1161 | if (ip_status == HOST_NEW) { | ||
1162 | if (!add_host_to_hostfile(options->user_hostfile, ip, | ||
1163 | BN_num_bits(host_key->n), | ||
1164 | host_key->e, host_key->n)) | ||
1165 | log("Failed to add the host ip to the list of known hosts (%.30s).", | ||
1166 | options->user_hostfile); | ||
1167 | else | ||
1168 | log("Warning: Permanently added host ip '%.30s' to the list of known hosts.", ip); | ||
1169 | } else if (ip_status != HOST_OK) | ||
1170 | log("Warning: the host key differ from the key of the ip address '%.30s' differs", ip); | ||
1171 | } | ||
1172 | |||
1173 | break; | ||
1174 | case HOST_NEW: | ||
1175 | { | ||
1176 | char hostline[1000], *hostp = hostline; | ||
1177 | /* The host is new. */ | ||
1178 | if (options->strict_host_key_checking == 1) { | ||
1179 | /* User has requested strict host key checking. We will not | ||
1180 | add the host key automatically. The only alternative left | ||
1181 | is to abort. */ | ||
1182 | fatal("No host key is known for %.200s and you have requested strict checking.", host); | ||
1183 | } else if (options->strict_host_key_checking == 2) { /* The default */ | ||
1184 | char prompt[1024]; | ||
1185 | snprintf(prompt, sizeof(prompt), | ||
1186 | "The authenticity of host '%.200s' can't be established.\n" | ||
1187 | "Are you sure you want to continue connecting (yes/no)? ", | ||
1188 | host); | ||
1189 | if (!read_yes_or_no(prompt, -1)) | ||
1190 | fatal("Aborted by user!\n"); | ||
1191 | } | ||
1192 | |||
1193 | if (options->check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) | ||
1194 | snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); | ||
1195 | else | ||
1196 | hostp = host; | ||
1197 | |||
1198 | /* If not in strict mode, add the key automatically to the local | ||
1199 | known_hosts file. */ | ||
1200 | if (!add_host_to_hostfile(options->user_hostfile, hostp, | ||
1201 | BN_num_bits(host_key->n), | ||
1202 | host_key->e, host_key->n)) | ||
1203 | log("Failed to add the host to the list of known hosts (%.500s).", | ||
1204 | options->user_hostfile); | ||
1205 | else | ||
1206 | log("Warning: Permanently added '%.200s' to the list of known hosts.", | ||
1207 | hostp); | ||
1208 | break; | ||
1209 | } | ||
1210 | case HOST_CHANGED: | ||
1211 | if (options->check_host_ip) { | ||
1212 | if (host_ip_differ) { | ||
1213 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1214 | error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); | ||
1215 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1216 | error("The host key for %s has changed,", host); | ||
1217 | error("but the key for the according IP address %s has", ip); | ||
1218 | error("a different status. This could either mean that DNS"); | ||
1219 | error("SPOOFING is happening or the IP address for the host"); | ||
1220 | error("and its host key have changed at the same time"); | ||
1221 | } | ||
1222 | } | ||
1223 | |||
1224 | /* The host key has changed. */ | ||
1225 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1226 | error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @"); | ||
1227 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | ||
1228 | error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); | ||
1229 | error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); | ||
1230 | error("It is also possible that the host key has just been changed."); | ||
1231 | error("Please contact your system administrator."); | ||
1232 | error("Add correct host key in %.100s to get rid of this message.", | ||
1233 | options->user_hostfile); | ||
1234 | |||
1235 | /* If strict host key checking is in use, the user will have to edit | ||
1236 | the key manually and we can only abort. */ | ||
1237 | if (options->strict_host_key_checking) | ||
1238 | fatal("Host key for %.200s has changed and you have requested strict checking.", host); | ||
1239 | |||
1240 | /* If strict host key checking has not been requested, allow the | ||
1241 | connection but without password authentication or | ||
1242 | agent forwarding. */ | ||
1243 | if (options->password_authentication) { | ||
1244 | error("Password authentication is disabled to avoid trojan horses."); | ||
1245 | options->password_authentication = 0; | ||
1246 | } | ||
1247 | if (options->forward_agent) { | ||
1248 | error("Agent forwarding is disabled to avoid trojan horses."); | ||
1249 | options->forward_agent = 0; | ||
1250 | } | ||
1251 | /* XXX Should permit the user to change to use the new id. This could | ||
1252 | be done by converting the host key to an identifying sentence, tell | ||
1253 | that the host identifies itself by that sentence, and ask the user | ||
1254 | if he/she whishes to accept the authentication. */ | ||
1255 | break; | ||
1256 | } | ||
1257 | |||
1258 | if (options->check_host_ip) | ||
1259 | xfree(ip); | ||
1260 | |||
1261 | /* Generate a session key. */ | ||
1262 | arc4random_stir(); | ||
1263 | |||
1264 | /* Generate an encryption key for the session. The key is a 256 bit | ||
1265 | random number, interpreted as a 32-byte key, with the least significant | ||
1266 | 8 bits being the first byte of the key. */ | ||
1267 | for (i = 0; i < 32; i++) { | ||
1268 | if (i % 4 == 0) | ||
1269 | rand = arc4random(); | ||
1270 | session_key[i] = rand & 0xff; | ||
1271 | rand >>= 8; | ||
1272 | } | ||
1273 | |||
1274 | /* According to the protocol spec, the first byte of the session key is | ||
1275 | the highest byte of the integer. The session key is xored with the | ||
1276 | first 16 bytes of the session id. */ | ||
1277 | key = BN_new(); | ||
1278 | BN_set_word(key, 0); | ||
1279 | for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) | ||
1280 | { | ||
1281 | BN_lshift(key, key, 8); | ||
1282 | if (i < 16) | ||
1283 | BN_add_word(key, session_key[i] ^ session_id[i]); | ||
1284 | else | ||
1285 | BN_add_word(key, session_key[i]); | ||
1286 | } | ||
1287 | |||
1288 | /* Encrypt the integer using the public key and host key of the server | ||
1289 | (key with smaller modulus first). */ | ||
1290 | if (BN_cmp(public_key->n, host_key->n) < 0) | ||
1291 | { | ||
1292 | /* Public key has smaller modulus. */ | ||
1293 | assert(BN_num_bits(host_key->n) >= | ||
1294 | BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED); | ||
1295 | |||
1296 | rsa_public_encrypt(key, key, public_key); | ||
1297 | rsa_public_encrypt(key, key, host_key); | ||
1298 | } | ||
1299 | else | ||
1300 | { | ||
1301 | /* Host key has smaller modulus (or they are equal). */ | ||
1302 | assert(BN_num_bits(public_key->n) >= | ||
1303 | BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED); | ||
1304 | |||
1305 | rsa_public_encrypt(key, key, host_key); | ||
1306 | rsa_public_encrypt(key, key, public_key); | ||
1307 | } | ||
1308 | |||
1309 | if (options->cipher == SSH_CIPHER_NOT_SET) { | ||
1310 | if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default)) | ||
1311 | options->cipher = ssh_cipher_default; | ||
1312 | else { | ||
1313 | debug("Cipher %d not supported, using %.100s instead.", | ||
1314 | cipher_name(ssh_cipher_default), | ||
1315 | cipher_name(SSH_FALLBACK_CIPHER)); | ||
1316 | options->cipher = SSH_FALLBACK_CIPHER; | ||
1317 | } | ||
1318 | } | ||
1319 | |||
1320 | /* Check that the selected cipher is supported. */ | ||
1321 | if (!(supported_ciphers & (1 << options->cipher))) | ||
1322 | fatal("Selected cipher type %.100s not supported by server.", | ||
1323 | cipher_name(options->cipher)); | ||
1324 | |||
1325 | debug("Encryption type: %.100s", cipher_name(options->cipher)); | ||
1326 | |||
1327 | /* Send the encrypted session key to the server. */ | ||
1328 | packet_start(SSH_CMSG_SESSION_KEY); | ||
1329 | packet_put_char(options->cipher); | ||
1330 | |||
1331 | /* Send the check bytes back to the server. */ | ||
1332 | for (i = 0; i < 8; i++) | ||
1333 | packet_put_char(check_bytes[i]); | ||
1334 | |||
1335 | /* Send the encrypted encryption key. */ | ||
1336 | packet_put_bignum(key); | ||
1337 | |||
1338 | /* Send protocol flags. */ | ||
1339 | packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | ||
1340 | |||
1341 | /* Send the packet now. */ | ||
1342 | packet_send(); | ||
1343 | packet_write_wait(); | ||
1344 | |||
1345 | /* Destroy the session key integer and the public keys since we no longer | ||
1346 | need them. */ | ||
1347 | BN_clear_free(key); | ||
1348 | RSA_free(public_key); | ||
1349 | RSA_free(host_key); | ||
1350 | |||
1351 | debug("Sent encrypted session key."); | ||
1352 | |||
1353 | /* Set the encryption key. */ | ||
1354 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, | ||
1355 | options->cipher, 1); | ||
1356 | |||
1357 | /* We will no longer need the session key here. Destroy any extra copies. */ | ||
1358 | memset(session_key, 0, sizeof(session_key)); | ||
1359 | |||
1360 | /* Expect a success message from the server. Note that this message will | ||
1361 | be received in encrypted form. */ | ||
1362 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
1363 | |||
1364 | debug("Received encrypted confirmation."); | ||
1365 | |||
1366 | /* Send the name of the user to log in as on the server. */ | ||
1367 | packet_start(SSH_CMSG_USER); | ||
1368 | packet_put_string(server_user, strlen(server_user)); | ||
1369 | packet_send(); | ||
1370 | packet_write_wait(); | ||
1371 | |||
1372 | /* The server should respond with success if no authentication is needed | ||
1373 | (the user has no password). Otherwise the server responds with | ||
1374 | failure. */ | ||
1375 | type = packet_read(&payload_len); | ||
1376 | if (type == SSH_SMSG_SUCCESS) | ||
1377 | return; /* Connection was accepted without authentication. */ | ||
1378 | if (type != SSH_SMSG_FAILURE) | ||
1379 | packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", | ||
1380 | type); | ||
1381 | |||
1382 | #ifdef AFS | ||
1383 | /* Try Kerberos tgt passing if the server supports it. */ | ||
1384 | if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && | ||
1385 | options->kerberos_tgt_passing) | ||
1386 | { | ||
1387 | if (options->cipher == SSH_CIPHER_NONE) | ||
1388 | log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); | ||
1389 | (void)send_kerberos_tgt(); | ||
1390 | } | ||
1391 | |||
1392 | /* Try AFS token passing if the server supports it. */ | ||
1393 | if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && | ||
1394 | options->afs_token_passing && k_hasafs()) { | ||
1395 | if (options->cipher == SSH_CIPHER_NONE) | ||
1396 | log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); | ||
1397 | send_afs_tokens(); | ||
1398 | } | ||
1399 | #endif /* AFS */ | ||
1400 | |||
1401 | #ifdef KRB4 | ||
1402 | if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && | ||
1403 | options->kerberos_authentication) | ||
1404 | { | ||
1405 | debug("Trying Kerberos authentication."); | ||
1406 | if (try_kerberos_authentication()) { | ||
1407 | /* The server should respond with success or failure. */ | ||
1408 | type = packet_read(&payload_len); | ||
1409 | if (type == SSH_SMSG_SUCCESS) | ||
1410 | return; /* Successful connection. */ | ||
1411 | if (type != SSH_SMSG_FAILURE) | ||
1412 | packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); | ||
1413 | } | ||
1414 | } | ||
1415 | #endif /* KRB4 */ | ||
1416 | |||
1417 | /* Use rhosts authentication if running in privileged socket and we do not | ||
1418 | wish to remain anonymous. */ | ||
1419 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && | ||
1420 | options->rhosts_authentication) | ||
1421 | { | ||
1422 | debug("Trying rhosts authentication."); | ||
1423 | packet_start(SSH_CMSG_AUTH_RHOSTS); | ||
1424 | packet_put_string(local_user, strlen(local_user)); | ||
1425 | packet_send(); | ||
1426 | packet_write_wait(); | ||
1427 | |||
1428 | /* The server should respond with success or failure. */ | ||
1429 | type = packet_read(&payload_len); | ||
1430 | if (type == SSH_SMSG_SUCCESS) | ||
1431 | return; /* Successful connection. */ | ||
1432 | if (type != SSH_SMSG_FAILURE) | ||
1433 | packet_disconnect("Protocol error: got %d in response to rhosts auth", | ||
1434 | type); | ||
1435 | } | ||
1436 | |||
1437 | /* Try .rhosts or /etc/hosts.equiv authentication with RSA host | ||
1438 | authentication. */ | ||
1439 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && | ||
1440 | options->rhosts_rsa_authentication && host_key_valid) | ||
1441 | { | ||
1442 | if (try_rhosts_rsa_authentication(local_user, own_host_key)) | ||
1443 | return; /* Successful authentication. */ | ||
1444 | } | ||
1445 | |||
1446 | /* Try RSA authentication if the server supports it. */ | ||
1447 | if ((supported_authentications & (1 << SSH_AUTH_RSA)) && | ||
1448 | options->rsa_authentication) | ||
1449 | { | ||
1450 | /* Try RSA authentication using the authentication agent. The agent | ||
1451 | is tried first because no passphrase is needed for it, whereas | ||
1452 | identity files may require passphrases. */ | ||
1453 | if (try_agent_authentication()) | ||
1454 | return; /* Successful connection. */ | ||
1455 | |||
1456 | /* Try RSA authentication for each identity. */ | ||
1457 | for (i = 0; i < options->num_identity_files; i++) | ||
1458 | if (try_rsa_authentication(pw, options->identity_files[i], | ||
1459 | !options->batch_mode)) | ||
1460 | return; /* Successful connection. */ | ||
1461 | } | ||
1462 | |||
1463 | /* Try password authentication if the server supports it. */ | ||
1464 | if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && | ||
1465 | options->password_authentication && !options->batch_mode) | ||
1466 | { | ||
1467 | char prompt[80]; | ||
1468 | snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ", | ||
1469 | server_user, host); | ||
1470 | debug("Doing password authentication."); | ||
1471 | if (options->cipher == SSH_CIPHER_NONE) | ||
1472 | log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); | ||
1473 | for (i = 0; i < options->number_of_password_prompts; i++) { | ||
1474 | if (i != 0) | ||
1475 | error("Permission denied, please try again."); | ||
1476 | password = read_passphrase(prompt, 0); | ||
1477 | packet_start(SSH_CMSG_AUTH_PASSWORD); | ||
1478 | packet_put_string(password, strlen(password)); | ||
1479 | memset(password, 0, strlen(password)); | ||
1480 | xfree(password); | ||
1481 | packet_send(); | ||
1482 | packet_write_wait(); | ||
1483 | |||
1484 | type = packet_read(&payload_len); | ||
1485 | if (type == SSH_SMSG_SUCCESS) | ||
1486 | return; /* Successful connection. */ | ||
1487 | if (type != SSH_SMSG_FAILURE) | ||
1488 | packet_disconnect("Protocol error: got %d in response to passwd auth", type); | ||
1489 | } | ||
1490 | } | ||
1491 | |||
1492 | /* All authentication methods have failed. Exit with an error message. */ | ||
1493 | fatal("Permission denied."); | ||
1494 | /*NOTREACHED*/ | ||
1495 | } | ||