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