Annotation of src/usr.bin/ssh/roaming_client.c, Revision 1.6
1.6 ! djm 1: /* $OpenBSD: roaming_client.c,v 1.5 2013/05/17 00:13:14 djm Exp $ */
1.1 andreas 2: /*
3: * Copyright (c) 2004-2009 AppGate Network Security AB
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18: #include <sys/queue.h>
19: #include <sys/types.h>
20: #include <sys/socket.h>
21:
22: #include <inttypes.h>
23: #include <signal.h>
24: #include <string.h>
25: #include <unistd.h>
26:
27: #include <openssl/crypto.h>
28: #include <openssl/sha.h>
29:
30: #include "xmalloc.h"
31: #include "buffer.h"
32: #include "channels.h"
33: #include "cipher.h"
34: #include "dispatch.h"
35: #include "clientloop.h"
36: #include "log.h"
37: #include "match.h"
38: #include "misc.h"
39: #include "packet.h"
40: #include "ssh.h"
41: #include "key.h"
42: #include "kex.h"
43: #include "readconf.h"
44: #include "roaming.h"
45: #include "ssh2.h"
46: #include "sshconnect.h"
47:
48: /* import */
49: extern Options options;
50: extern char *host;
51: extern struct sockaddr_storage hostaddr;
52: extern int session_resumed;
53:
54: static u_int32_t roaming_id;
55: static u_int64_t cookie;
56: static u_int64_t lastseenchall;
57: static u_int64_t key1, key2, oldkey1, oldkey2;
58:
59: void
60: roaming_reply(int type, u_int32_t seq, void *ctxt)
61: {
62: if (type == SSH2_MSG_REQUEST_FAILURE) {
63: logit("Server denied roaming");
64: return;
65: }
66: verbose("Roaming enabled");
67: roaming_id = packet_get_int();
68: cookie = packet_get_int64();
69: key1 = oldkey1 = packet_get_int64();
70: key2 = oldkey2 = packet_get_int64();
1.4 djm 71: set_out_buffer_size(packet_get_int() + get_snd_buf_size());
1.1 andreas 72: roaming_enabled = 1;
73: }
74:
75: void
76: request_roaming(void)
77: {
78: packet_start(SSH2_MSG_GLOBAL_REQUEST);
79: packet_put_cstring(ROAMING_REQUEST);
80: packet_put_char(1);
81: packet_put_int(get_recv_buf_size());
82: packet_send();
83: client_register_global_confirm(roaming_reply, NULL);
84: }
85:
86: static void
87: roaming_auth_required(void)
88: {
89: u_char digest[SHA_DIGEST_LENGTH];
90: EVP_MD_CTX md;
91: Buffer b;
92: const EVP_MD *evp_md = EVP_sha1();
93: u_int64_t chall, oldchall;
94:
95: chall = packet_get_int64();
96: oldchall = packet_get_int64();
97: if (oldchall != lastseenchall) {
98: key1 = oldkey1;
99: key2 = oldkey2;
100: }
101: lastseenchall = chall;
102:
103: buffer_init(&b);
104: buffer_put_int64(&b, cookie);
105: buffer_put_int64(&b, chall);
106: EVP_DigestInit(&md, evp_md);
107: EVP_DigestUpdate(&md, buffer_ptr(&b), buffer_len(&b));
108: EVP_DigestFinal(&md, digest, NULL);
109: buffer_free(&b);
110:
111: packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
112: packet_put_int64(key1 ^ get_recv_bytes());
113: packet_put_raw(digest, sizeof(digest));
114: packet_send();
115:
116: oldkey1 = key1;
117: oldkey2 = key2;
118: calculate_new_key(&key1, cookie, chall);
119: calculate_new_key(&key2, cookie, chall);
120:
1.3 dtucker 121: debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
1.1 andreas 122: debug("Sent roaming_auth packet");
123: }
124:
125: int
126: resume_kex(void)
127: {
128: /*
129: * This should not happen - if the client sends the kex method
130: * resume@appgate.com then the kex is done in roaming_resume().
131: */
132: return 1;
133: }
134:
135: static int
136: roaming_resume(void)
137: {
138: u_int64_t recv_bytes;
139: char *str = NULL, *kexlist = NULL, *c;
140: int i, type;
141: int timeout_ms = options.connection_timeout * 1000;
142: u_int len;
143: u_int32_t rnd = 0;
144:
145: resume_in_progress = 1;
146:
147: /* Exchange banners */
148: ssh_exchange_identification(timeout_ms);
149: packet_set_nonblocking();
150:
151: /* Send a kexinit message with resume@appgate.com as only kex algo */
152: packet_start(SSH2_MSG_KEXINIT);
153: for (i = 0; i < KEX_COOKIE_LEN; i++) {
154: if (i % 4 == 0)
155: rnd = arc4random();
156: packet_put_char(rnd & 0xff);
157: rnd >>= 8;
158: }
159: packet_put_cstring(KEX_RESUME);
160: for (i = 1; i < PROPOSAL_MAX; i++) {
161: /* kex algorithm added so start with i=1 and not 0 */
162: packet_put_cstring(""); /* Not used when we resume */
163: }
164: packet_put_char(1); /* first kex_packet follows */
165: packet_put_int(0); /* reserved */
166: packet_send();
167:
168: /* Assume that resume@appgate.com will be accepted */
169: packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
170: packet_put_int(roaming_id);
171: packet_send();
172:
173: /* Read the server's kexinit and check for resume@appgate.com */
174: if ((type = packet_read()) != SSH2_MSG_KEXINIT) {
175: debug("expected kexinit on resume, got %d", type);
176: goto fail;
177: }
178: for (i = 0; i < KEX_COOKIE_LEN; i++)
179: (void)packet_get_char();
180: kexlist = packet_get_string(&len);
181: if (!kexlist
182: || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
183: debug("server doesn't allow resume");
184: goto fail;
185: }
1.5 djm 186: free(str);
1.1 andreas 187: for (i = 1; i < PROPOSAL_MAX; i++) {
188: /* kex algorithm taken care of so start with i=1 and not 0 */
1.5 djm 189: free(packet_get_string(&len));
1.1 andreas 190: }
191: i = packet_get_char(); /* first_kex_packet_follows */
192: if (i && (c = strchr(kexlist, ',')))
193: *c = 0;
194: if (i && strcmp(kexlist, KEX_RESUME)) {
195: debug("server's kex guess (%s) was wrong, skipping", kexlist);
196: (void)packet_read(); /* Wrong guess - discard packet */
197: }
198:
199: /*
200: * Read the ROAMING_AUTH_REQUIRED challenge from the server and
201: * send ROAMING_AUTH
202: */
203: if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
204: debug("expected roaming_auth_required, got %d", type);
205: goto fail;
206: }
207: roaming_auth_required();
208:
209: /* Read ROAMING_AUTH_OK from the server */
210: if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) {
211: debug("expected roaming_auth_ok, got %d", type);
212: goto fail;
213: }
214: recv_bytes = packet_get_int64() ^ oldkey2;
1.3 dtucker 215: debug("Peer received %llu bytes", (unsigned long long)recv_bytes);
1.1 andreas 216: resend_bytes(packet_get_connection_out(), &recv_bytes);
217:
218: resume_in_progress = 0;
219:
220: session_resumed = 1; /* Tell clientloop */
221:
222: return 0;
223:
224: fail:
1.5 djm 225: free(kexlist);
1.1 andreas 226: if (packet_get_connection_in() == packet_get_connection_out())
227: close(packet_get_connection_in());
228: else {
229: close(packet_get_connection_in());
230: close(packet_get_connection_out());
231: }
232: return 1;
233: }
234:
235: int
236: wait_for_roaming_reconnect(void)
237: {
238: static int reenter_guard = 0;
239: int timeout_ms = options.connection_timeout * 1000;
240: int c;
241:
242: if (reenter_guard != 0)
243: fatal("Server refused resume, roaming timeout may be exceeded");
244: reenter_guard = 1;
245:
246: fprintf(stderr, "[connection suspended, press return to resume]");
247: fflush(stderr);
248: packet_backup_state();
249: /* TODO Perhaps we should read from tty here */
250: while ((c = fgetc(stdin)) != EOF) {
251: if (c == 'Z' - 64) {
252: kill(getpid(), SIGTSTP);
253: continue;
254: }
255: if (c != '\n' && c != '\r')
256: continue;
257:
1.6 ! djm 258: if (ssh_connect(host, NULL, &hostaddr, options.port,
1.1 andreas 259: options.address_family, 1, &timeout_ms,
1.6 ! djm 260: options.tcp_keep_alive, options.use_privileged_port) == 0 &&
! 261: roaming_resume() == 0) {
1.1 andreas 262: packet_restore_state();
263: reenter_guard = 0;
264: fprintf(stderr, "[connection resumed]\n");
265: fflush(stderr);
266: return 0;
267: }
268:
269: fprintf(stderr, "[reconnect failed, press return to retry]");
270: fflush(stderr);
271: }
272: fprintf(stderr, "[exiting]\n");
273: fflush(stderr);
274: exit(0);
275: }