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