Annotation of src/usr.bin/ssh/roaming_common.c, Revision 1.12
1.12 ! djm 1: /* $OpenBSD: roaming_common.c,v 1.11 2013/11/03 10:37:19 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/types.h>
19: #include <sys/socket.h>
20: #include <sys/uio.h>
21:
22: #include <errno.h>
23: #include <inttypes.h>
24: #include <stdarg.h>
1.5 andreas 25: #include <string.h>
1.1 andreas 26: #include <unistd.h>
27:
28: #include "atomicio.h"
29: #include "log.h"
30: #include "packet.h"
31: #include "xmalloc.h"
32: #include "cipher.h"
33: #include "buffer.h"
34: #include "roaming.h"
1.12 ! djm 35: #include "digest.h"
1.1 andreas 36:
1.5 andreas 37: static size_t out_buf_size = 0;
38: static char *out_buf = NULL;
39: static size_t out_start;
40: static size_t out_last;
41:
1.1 andreas 42: static u_int64_t write_bytes = 0;
43: static u_int64_t read_bytes = 0;
44:
1.5 andreas 45: int roaming_enabled = 0;
1.1 andreas 46: int resume_in_progress = 0;
47:
1.5 andreas 48: int
1.11 djm 49: get_snd_buf_size(void)
1.5 andreas 50: {
51: int fd = packet_get_connection_out();
1.7 djm 52: int optval;
53: socklen_t optvallen = sizeof(optval);
1.5 andreas 54:
55: if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0)
56: optval = DEFAULT_ROAMBUF;
57: return optval;
58: }
59:
60: int
1.11 djm 61: get_recv_buf_size(void)
1.5 andreas 62: {
63: int fd = packet_get_connection_in();
1.7 djm 64: int optval;
65: socklen_t optvallen = sizeof(optval);
1.5 andreas 66:
67: if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0)
68: optval = DEFAULT_ROAMBUF;
69: return optval;
70: }
71:
72: void
73: set_out_buffer_size(size_t size)
74: {
1.9 djm 75: if (size == 0 || size > MAX_ROAMBUF)
76: fatal("%s: bad buffer size %lu", __func__, (u_long)size);
1.5 andreas 77: /*
78: * The buffer size can only be set once and the buffer will live
79: * as long as the session lives.
80: */
81: if (out_buf == NULL) {
82: out_buf_size = size;
83: out_buf = xmalloc(size);
84: out_start = 0;
85: out_last = 0;
86: }
87: }
88:
1.1 andreas 89: u_int64_t
90: get_recv_bytes(void)
91: {
92: return read_bytes;
93: }
94:
95: void
96: add_recv_bytes(u_int64_t num)
97: {
98: read_bytes += num;
99: }
100:
101: u_int64_t
102: get_sent_bytes(void)
103: {
104: return write_bytes;
105: }
106:
107: void
1.2 andreas 108: roam_set_bytes(u_int64_t sent, u_int64_t recvd)
1.1 andreas 109: {
1.2 andreas 110: read_bytes = recvd;
1.1 andreas 111: write_bytes = sent;
112: }
113:
1.5 andreas 114: static void
115: buf_append(const char *buf, size_t count)
116: {
117: if (count > out_buf_size) {
118: buf += count - out_buf_size;
119: count = out_buf_size;
120: }
121: if (count < out_buf_size - out_last) {
122: memcpy(out_buf + out_last, buf, count);
123: if (out_start > out_last)
124: out_start += count;
125: out_last += count;
126: } else {
127: /* data will wrap */
128: size_t chunk = out_buf_size - out_last;
129: memcpy(out_buf + out_last, buf, chunk);
130: memcpy(out_buf, buf + chunk, count - chunk);
131: out_last = count - chunk;
132: out_start = out_last + 1;
133: }
134: }
135:
1.1 andreas 136: ssize_t
137: roaming_write(int fd, const void *buf, size_t count, int *cont)
138: {
139: ssize_t ret;
140:
141: ret = write(fd, buf, count);
142: if (ret > 0 && !resume_in_progress) {
143: write_bytes += ret;
1.5 andreas 144: if (out_buf_size > 0)
145: buf_append(buf, ret);
1.1 andreas 146: }
1.6 andreas 147: if (out_buf_size > 0 &&
148: (ret == 0 || (ret == -1 && errno == EPIPE))) {
149: if (wait_for_roaming_reconnect() != 0) {
150: ret = 0;
151: *cont = 1;
152: } else {
153: ret = -1;
154: errno = EAGAIN;
155: }
156: }
1.1 andreas 157: return ret;
158: }
159:
160: ssize_t
161: roaming_read(int fd, void *buf, size_t count, int *cont)
162: {
163: ssize_t ret = read(fd, buf, count);
164: if (ret > 0) {
165: if (!resume_in_progress) {
166: read_bytes += ret;
167: }
1.6 andreas 168: } else if (out_buf_size > 0 &&
169: (ret == 0 || (ret == -1 && (errno == ECONNRESET
170: || errno == ECONNABORTED || errno == ETIMEDOUT
171: || errno == EHOSTUNREACH)))) {
172: debug("roaming_read failed for %d ret=%ld errno=%d",
173: fd, (long)ret, errno);
174: ret = 0;
175: if (wait_for_roaming_reconnect() == 0)
176: *cont = 1;
1.1 andreas 177: }
178: return ret;
179: }
180:
1.2 andreas 181: size_t
182: roaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf,
183: size_t count)
1.1 andreas 184: {
1.2 andreas 185: size_t ret = atomicio(f, fd, buf, count);
1.1 andreas 186:
1.2 andreas 187: if (f == vwrite && ret > 0 && !resume_in_progress) {
1.1 andreas 188: write_bytes += ret;
189: } else if (f == read && ret > 0 && !resume_in_progress) {
190: read_bytes += ret;
191: }
192: return ret;
1.5 andreas 193: }
194:
195: void
196: resend_bytes(int fd, u_int64_t *offset)
197: {
198: size_t available, needed;
199:
200: if (out_start < out_last)
201: available = out_last - out_start;
202: else
203: available = out_buf_size;
204: needed = write_bytes - *offset;
205: debug3("resend_bytes: resend %lu bytes from %llu",
206: (unsigned long)needed, (unsigned long long)*offset);
207: if (needed > available)
208: fatal("Needed to resend more data than in the cache");
209: if (out_last < needed) {
210: int chunkend = needed - out_last;
211: atomicio(vwrite, fd, out_buf + out_buf_size - chunkend,
212: chunkend);
213: atomicio(vwrite, fd, out_buf, out_last);
214: } else {
215: atomicio(vwrite, fd, out_buf + (out_last - needed), needed);
216: }
1.6 andreas 217: }
218:
219: /*
220: * Caclulate a new key after a reconnect
221: */
222: void
223: calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge)
224: {
1.12 ! djm 225: u_char hash[SSH_DIGEST_MAX_LENGTH];
1.6 andreas 226: Buffer b;
227:
228: buffer_init(&b);
229: buffer_put_int64(&b, *key);
230: buffer_put_int64(&b, cookie);
231: buffer_put_int64(&b, challenge);
232:
1.12 ! djm 233: if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, hash, sizeof(hash)) != 0)
! 234: fatal("%s: digest_buffer failed", __func__);
1.6 andreas 235:
236: buffer_clear(&b);
1.12 ! djm 237: buffer_append(&b, hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
1.6 andreas 238: *key = buffer_get_int64(&b);
239: buffer_free(&b);
1.1 andreas 240: }