1263970Sdes/* $OpenBSD: roaming_common.c,v 1.12 2014/01/09 23:20:00 djm Exp $ */
2197670Sdes/*
3197670Sdes * Copyright (c) 2004-2009 AppGate Network Security AB
4197670Sdes *
5197670Sdes * Permission to use, copy, modify, and distribute this software for any
6197670Sdes * purpose with or without fee is hereby granted, provided that the above
7197670Sdes * copyright notice and this permission notice appear in all copies.
8197670Sdes *
9197670Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10197670Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11197670Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12197670Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13197670Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14197670Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15197670Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16197670Sdes */
17197670Sdes
18197670Sdes#include "includes.h"
19197670Sdes
20197670Sdes#include <sys/types.h>
21197670Sdes#include <sys/socket.h>
22197670Sdes#include <sys/uio.h>
23197670Sdes
24197670Sdes#include <errno.h>
25197670Sdes#ifdef HAVE_INTTYPES_H
26197670Sdes#include <inttypes.h>
27197670Sdes#endif
28197670Sdes#include <stdarg.h>
29197670Sdes#include <string.h>
30197670Sdes#include <unistd.h>
31197670Sdes
32197670Sdes#include "atomicio.h"
33197670Sdes#include "log.h"
34197670Sdes#include "packet.h"
35197670Sdes#include "xmalloc.h"
36197670Sdes#include "cipher.h"
37197670Sdes#include "buffer.h"
38197670Sdes#include "roaming.h"
39263970Sdes#include "digest.h"
40197670Sdes
41197670Sdesstatic size_t out_buf_size = 0;
42197670Sdesstatic char *out_buf = NULL;
43197670Sdesstatic size_t out_start;
44197670Sdesstatic size_t out_last;
45197670Sdes
46197670Sdesstatic u_int64_t write_bytes = 0;
47197670Sdesstatic u_int64_t read_bytes = 0;
48197670Sdes
49197670Sdesint roaming_enabled = 0;
50197670Sdesint resume_in_progress = 0;
51197670Sdes
52197670Sdesint
53263970Sdesget_snd_buf_size(void)
54197670Sdes{
55197670Sdes	int fd = packet_get_connection_out();
56204917Sdes	int optval;
57204917Sdes	socklen_t optvallen = sizeof(optval);
58197670Sdes
59197670Sdes	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0)
60197670Sdes		optval = DEFAULT_ROAMBUF;
61197670Sdes	return optval;
62197670Sdes}
63197670Sdes
64197670Sdesint
65263970Sdesget_recv_buf_size(void)
66197670Sdes{
67197670Sdes	int fd = packet_get_connection_in();
68204917Sdes	int optval;
69204917Sdes	socklen_t optvallen = sizeof(optval);
70197670Sdes
71197670Sdes	if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0)
72197670Sdes		optval = DEFAULT_ROAMBUF;
73197670Sdes	return optval;
74197670Sdes}
75197670Sdes
76197670Sdesvoid
77197670Sdesset_out_buffer_size(size_t size)
78197670Sdes{
79247485Sdes	if (size == 0 || size > MAX_ROAMBUF)
80247485Sdes		fatal("%s: bad buffer size %lu", __func__, (u_long)size);
81197670Sdes	/*
82197670Sdes	 * The buffer size can only be set once and the buffer will live
83197670Sdes	 * as long as the session lives.
84197670Sdes	 */
85197670Sdes	if (out_buf == NULL) {
86197670Sdes		out_buf_size = size;
87197670Sdes		out_buf = xmalloc(size);
88197670Sdes		out_start = 0;
89197670Sdes		out_last = 0;
90197670Sdes	}
91197670Sdes}
92197670Sdes
93197670Sdesu_int64_t
94197670Sdesget_recv_bytes(void)
95197670Sdes{
96197670Sdes	return read_bytes;
97197670Sdes}
98197670Sdes
99197670Sdesvoid
100197670Sdesadd_recv_bytes(u_int64_t num)
101197670Sdes{
102197670Sdes	read_bytes += num;
103197670Sdes}
104197670Sdes
105197670Sdesu_int64_t
106197670Sdesget_sent_bytes(void)
107197670Sdes{
108197670Sdes	return write_bytes;
109197670Sdes}
110197670Sdes
111197670Sdesvoid
112197670Sdesroam_set_bytes(u_int64_t sent, u_int64_t recvd)
113197670Sdes{
114197670Sdes	read_bytes = recvd;
115197670Sdes	write_bytes = sent;
116197670Sdes}
117197670Sdes
118197670Sdesstatic void
119197670Sdesbuf_append(const char *buf, size_t count)
120197670Sdes{
121197670Sdes	if (count > out_buf_size) {
122197670Sdes		buf += count - out_buf_size;
123197670Sdes		count = out_buf_size;
124197670Sdes	}
125197670Sdes	if (count < out_buf_size - out_last) {
126197670Sdes		memcpy(out_buf + out_last, buf, count);
127197670Sdes		if (out_start > out_last)
128197670Sdes			out_start += count;
129197670Sdes		out_last += count;
130197670Sdes	} else {
131197670Sdes		/* data will wrap */
132197670Sdes		size_t chunk = out_buf_size - out_last;
133197670Sdes		memcpy(out_buf + out_last, buf, chunk);
134197670Sdes		memcpy(out_buf, buf + chunk, count - chunk);
135197670Sdes		out_last = count - chunk;
136197670Sdes		out_start = out_last + 1;
137197670Sdes	}
138197670Sdes}
139197670Sdes
140197670Sdesssize_t
141197670Sdesroaming_write(int fd, const void *buf, size_t count, int *cont)
142197670Sdes{
143197670Sdes	ssize_t ret;
144197670Sdes
145197670Sdes	ret = write(fd, buf, count);
146197670Sdes	if (ret > 0 && !resume_in_progress) {
147197670Sdes		write_bytes += ret;
148197670Sdes		if (out_buf_size > 0)
149197670Sdes			buf_append(buf, ret);
150197670Sdes	}
151204917Sdes	if (out_buf_size > 0 &&
152204917Sdes	    (ret == 0 || (ret == -1 && errno == EPIPE))) {
153204917Sdes		if (wait_for_roaming_reconnect() != 0) {
154204917Sdes			ret = 0;
155204917Sdes			*cont = 1;
156204917Sdes		} else {
157204917Sdes			ret = -1;
158204917Sdes			errno = EAGAIN;
159204917Sdes		}
160204917Sdes	}
161197670Sdes	return ret;
162197670Sdes}
163197670Sdes
164197670Sdesssize_t
165197670Sdesroaming_read(int fd, void *buf, size_t count, int *cont)
166197670Sdes{
167197670Sdes	ssize_t ret = read(fd, buf, count);
168197670Sdes	if (ret > 0) {
169197670Sdes		if (!resume_in_progress) {
170197670Sdes			read_bytes += ret;
171197670Sdes		}
172204917Sdes	} else if (out_buf_size > 0 &&
173204917Sdes	    (ret == 0 || (ret == -1 && (errno == ECONNRESET
174204917Sdes	    || errno == ECONNABORTED || errno == ETIMEDOUT
175204917Sdes	    || errno == EHOSTUNREACH)))) {
176204917Sdes		debug("roaming_read failed for %d  ret=%ld  errno=%d",
177204917Sdes		    fd, (long)ret, errno);
178204917Sdes		ret = 0;
179204917Sdes		if (wait_for_roaming_reconnect() == 0)
180204917Sdes			*cont = 1;
181197670Sdes	}
182197670Sdes	return ret;
183197670Sdes}
184197670Sdes
185197670Sdessize_t
186197670Sdesroaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf,
187197670Sdes    size_t count)
188197670Sdes{
189197670Sdes	size_t ret = atomicio(f, fd, buf, count);
190197670Sdes
191197670Sdes	if (f == vwrite && ret > 0 && !resume_in_progress) {
192197670Sdes		write_bytes += ret;
193197670Sdes	} else if (f == read && ret > 0 && !resume_in_progress) {
194197670Sdes		read_bytes += ret;
195197670Sdes	}
196197670Sdes	return ret;
197197670Sdes}
198197670Sdes
199197670Sdesvoid
200197670Sdesresend_bytes(int fd, u_int64_t *offset)
201197670Sdes{
202197670Sdes	size_t available, needed;
203197670Sdes
204197670Sdes	if (out_start < out_last)
205197670Sdes		available = out_last - out_start;
206197670Sdes	else
207197670Sdes		available = out_buf_size;
208197670Sdes	needed = write_bytes - *offset;
209197670Sdes	debug3("resend_bytes: resend %lu bytes from %llu",
210197670Sdes	    (unsigned long)needed, (unsigned long long)*offset);
211197670Sdes	if (needed > available)
212197670Sdes		fatal("Needed to resend more data than in the cache");
213197670Sdes	if (out_last < needed) {
214197670Sdes		int chunkend = needed - out_last;
215197670Sdes		atomicio(vwrite, fd, out_buf + out_buf_size - chunkend,
216197670Sdes		    chunkend);
217197670Sdes		atomicio(vwrite, fd, out_buf, out_last);
218197670Sdes	} else {
219197670Sdes		atomicio(vwrite, fd, out_buf + (out_last - needed), needed);
220197670Sdes	}
221197670Sdes}
222204917Sdes
223204917Sdes/*
224204917Sdes * Caclulate a new key after a reconnect
225204917Sdes */
226204917Sdesvoid
227204917Sdescalculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge)
228204917Sdes{
229263970Sdes	u_char hash[SSH_DIGEST_MAX_LENGTH];
230204917Sdes	Buffer b;
231204917Sdes
232204917Sdes	buffer_init(&b);
233204917Sdes	buffer_put_int64(&b, *key);
234204917Sdes	buffer_put_int64(&b, cookie);
235204917Sdes	buffer_put_int64(&b, challenge);
236204917Sdes
237263970Sdes	if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, hash, sizeof(hash)) != 0)
238263970Sdes		fatal("%s: digest_buffer failed", __func__);
239204917Sdes
240204917Sdes	buffer_clear(&b);
241263970Sdes	buffer_append(&b, hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
242204917Sdes	*key = buffer_get_int64(&b);
243204917Sdes	buffer_free(&b);
244204917Sdes}
245