Annotation of src/usr.bin/ssh/roaming_client.c, Revision 1.7
1.7 ! djm 1: /* $OpenBSD: roaming_client.c,v 1.6 2013/10/16 02:31:46 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"
1.7 ! djm 47: #include "digest.h"
1.1 andreas 48:
49: /* import */
50: extern Options options;
51: extern char *host;
52: extern struct sockaddr_storage hostaddr;
53: extern int session_resumed;
54:
55: static u_int32_t roaming_id;
56: static u_int64_t cookie;
57: static u_int64_t lastseenchall;
58: static u_int64_t key1, key2, oldkey1, oldkey2;
59:
60: void
61: roaming_reply(int type, u_int32_t seq, void *ctxt)
62: {
63: if (type == SSH2_MSG_REQUEST_FAILURE) {
64: logit("Server denied roaming");
65: return;
66: }
67: verbose("Roaming enabled");
68: roaming_id = packet_get_int();
69: cookie = packet_get_int64();
70: key1 = oldkey1 = packet_get_int64();
71: key2 = oldkey2 = packet_get_int64();
1.4 djm 72: set_out_buffer_size(packet_get_int() + get_snd_buf_size());
1.1 andreas 73: roaming_enabled = 1;
74: }
75:
76: void
77: request_roaming(void)
78: {
79: packet_start(SSH2_MSG_GLOBAL_REQUEST);
80: packet_put_cstring(ROAMING_REQUEST);
81: packet_put_char(1);
82: packet_put_int(get_recv_buf_size());
83: packet_send();
84: client_register_global_confirm(roaming_reply, NULL);
85: }
86:
87: static void
88: roaming_auth_required(void)
89: {
1.7 ! djm 90: u_char digest[SSH_DIGEST_MAX_LENGTH];
1.1 andreas 91: Buffer b;
92: u_int64_t chall, oldchall;
93:
94: chall = packet_get_int64();
95: oldchall = packet_get_int64();
96: if (oldchall != lastseenchall) {
97: key1 = oldkey1;
98: key2 = oldkey2;
99: }
100: lastseenchall = chall;
101:
102: buffer_init(&b);
103: buffer_put_int64(&b, cookie);
104: buffer_put_int64(&b, chall);
1.7 ! djm 105: if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, digest, sizeof(digest)) != 0)
! 106: fatal("%s: ssh_digest_buffer failed", __func__);
1.1 andreas 107: buffer_free(&b);
108:
109: packet_start(SSH2_MSG_KEX_ROAMING_AUTH);
110: packet_put_int64(key1 ^ get_recv_bytes());
1.7 ! djm 111: packet_put_raw(digest, ssh_digest_bytes(SSH_DIGEST_SHA1));
1.1 andreas 112: packet_send();
113:
114: oldkey1 = key1;
115: oldkey2 = key2;
116: calculate_new_key(&key1, cookie, chall);
117: calculate_new_key(&key2, cookie, chall);
118:
1.3 dtucker 119: debug("Received %llu bytes", (unsigned long long)get_recv_bytes());
1.1 andreas 120: debug("Sent roaming_auth packet");
121: }
122:
123: int
124: resume_kex(void)
125: {
126: /*
127: * This should not happen - if the client sends the kex method
128: * resume@appgate.com then the kex is done in roaming_resume().
129: */
130: return 1;
131: }
132:
133: static int
134: roaming_resume(void)
135: {
136: u_int64_t recv_bytes;
137: char *str = NULL, *kexlist = NULL, *c;
138: int i, type;
139: int timeout_ms = options.connection_timeout * 1000;
140: u_int len;
141: u_int32_t rnd = 0;
142:
143: resume_in_progress = 1;
144:
145: /* Exchange banners */
146: ssh_exchange_identification(timeout_ms);
147: packet_set_nonblocking();
148:
149: /* Send a kexinit message with resume@appgate.com as only kex algo */
150: packet_start(SSH2_MSG_KEXINIT);
151: for (i = 0; i < KEX_COOKIE_LEN; i++) {
152: if (i % 4 == 0)
153: rnd = arc4random();
154: packet_put_char(rnd & 0xff);
155: rnd >>= 8;
156: }
157: packet_put_cstring(KEX_RESUME);
158: for (i = 1; i < PROPOSAL_MAX; i++) {
159: /* kex algorithm added so start with i=1 and not 0 */
160: packet_put_cstring(""); /* Not used when we resume */
161: }
162: packet_put_char(1); /* first kex_packet follows */
163: packet_put_int(0); /* reserved */
164: packet_send();
165:
166: /* Assume that resume@appgate.com will be accepted */
167: packet_start(SSH2_MSG_KEX_ROAMING_RESUME);
168: packet_put_int(roaming_id);
169: packet_send();
170:
171: /* Read the server's kexinit and check for resume@appgate.com */
172: if ((type = packet_read()) != SSH2_MSG_KEXINIT) {
173: debug("expected kexinit on resume, got %d", type);
174: goto fail;
175: }
176: for (i = 0; i < KEX_COOKIE_LEN; i++)
177: (void)packet_get_char();
178: kexlist = packet_get_string(&len);
179: if (!kexlist
180: || (str = match_list(KEX_RESUME, kexlist, NULL)) == NULL) {
181: debug("server doesn't allow resume");
182: goto fail;
183: }
1.5 djm 184: free(str);
1.1 andreas 185: for (i = 1; i < PROPOSAL_MAX; i++) {
186: /* kex algorithm taken care of so start with i=1 and not 0 */
1.5 djm 187: free(packet_get_string(&len));
1.1 andreas 188: }
189: i = packet_get_char(); /* first_kex_packet_follows */
190: if (i && (c = strchr(kexlist, ',')))
191: *c = 0;
192: if (i && strcmp(kexlist, KEX_RESUME)) {
193: debug("server's kex guess (%s) was wrong, skipping", kexlist);
194: (void)packet_read(); /* Wrong guess - discard packet */
195: }
196:
197: /*
198: * Read the ROAMING_AUTH_REQUIRED challenge from the server and
199: * send ROAMING_AUTH
200: */
201: if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_REQUIRED) {
202: debug("expected roaming_auth_required, got %d", type);
203: goto fail;
204: }
205: roaming_auth_required();
206:
207: /* Read ROAMING_AUTH_OK from the server */
208: if ((type = packet_read()) != SSH2_MSG_KEX_ROAMING_AUTH_OK) {
209: debug("expected roaming_auth_ok, got %d", type);
210: goto fail;
211: }
212: recv_bytes = packet_get_int64() ^ oldkey2;
1.3 dtucker 213: debug("Peer received %llu bytes", (unsigned long long)recv_bytes);
1.1 andreas 214: resend_bytes(packet_get_connection_out(), &recv_bytes);
215:
216: resume_in_progress = 0;
217:
218: session_resumed = 1; /* Tell clientloop */
219:
220: return 0;
221:
222: fail:
1.5 djm 223: free(kexlist);
1.1 andreas 224: if (packet_get_connection_in() == packet_get_connection_out())
225: close(packet_get_connection_in());
226: else {
227: close(packet_get_connection_in());
228: close(packet_get_connection_out());
229: }
230: return 1;
231: }
232:
233: int
234: wait_for_roaming_reconnect(void)
235: {
236: static int reenter_guard = 0;
237: int timeout_ms = options.connection_timeout * 1000;
238: int c;
239:
240: if (reenter_guard != 0)
241: fatal("Server refused resume, roaming timeout may be exceeded");
242: reenter_guard = 1;
243:
244: fprintf(stderr, "[connection suspended, press return to resume]");
245: fflush(stderr);
246: packet_backup_state();
247: /* TODO Perhaps we should read from tty here */
248: while ((c = fgetc(stdin)) != EOF) {
249: if (c == 'Z' - 64) {
250: kill(getpid(), SIGTSTP);
251: continue;
252: }
253: if (c != '\n' && c != '\r')
254: continue;
255:
1.6 djm 256: if (ssh_connect(host, NULL, &hostaddr, options.port,
1.1 andreas 257: options.address_family, 1, &timeout_ms,
1.6 djm 258: options.tcp_keep_alive, options.use_privileged_port) == 0 &&
259: roaming_resume() == 0) {
1.1 andreas 260: packet_restore_state();
261: reenter_guard = 0;
262: fprintf(stderr, "[connection resumed]\n");
263: fflush(stderr);
264: return 0;
265: }
266:
267: fprintf(stderr, "[reconnect failed, press return to retry]");
268: fflush(stderr);
269: }
270: fprintf(stderr, "[exiting]\n");
271: fflush(stderr);
272: exit(0);
273: }