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