Annotation of src/usr.bin/ssh/roaming_common.c, Revision 1.8
1.8 ! djm 1: /* $OpenBSD: roaming_common.c,v 1.7 2009/12/06 23:53:45 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"
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.6 andreas 144: if (out_buf_size > 0 &&
145: (ret == 0 || (ret == -1 && errno == EPIPE))) {
146: if (wait_for_roaming_reconnect() != 0) {
147: ret = 0;
148: *cont = 1;
149: } else {
150: ret = -1;
151: errno = EAGAIN;
152: }
153: }
1.1 andreas 154: return ret;
155: }
156:
157: ssize_t
158: roaming_read(int fd, void *buf, size_t count, int *cont)
159: {
160: ssize_t ret = read(fd, buf, count);
161: if (ret > 0) {
162: if (!resume_in_progress) {
163: read_bytes += ret;
164: }
1.6 andreas 165: } else if (out_buf_size > 0 &&
166: (ret == 0 || (ret == -1 && (errno == ECONNRESET
167: || errno == ECONNABORTED || errno == ETIMEDOUT
168: || errno == EHOSTUNREACH)))) {
169: debug("roaming_read failed for %d ret=%ld errno=%d",
170: fd, (long)ret, errno);
171: ret = 0;
172: if (wait_for_roaming_reconnect() == 0)
173: *cont = 1;
1.1 andreas 174: }
175: return ret;
176: }
177:
1.2 andreas 178: size_t
179: roaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf,
180: size_t count)
1.1 andreas 181: {
1.2 andreas 182: size_t ret = atomicio(f, fd, buf, count);
1.1 andreas 183:
1.2 andreas 184: if (f == vwrite && ret > 0 && !resume_in_progress) {
1.1 andreas 185: write_bytes += ret;
186: } else if (f == read && ret > 0 && !resume_in_progress) {
187: read_bytes += ret;
188: }
189: return ret;
1.5 andreas 190: }
191:
192: void
193: resend_bytes(int fd, u_int64_t *offset)
194: {
195: size_t available, needed;
196:
197: if (out_start < out_last)
198: available = out_last - out_start;
199: else
200: available = out_buf_size;
201: needed = write_bytes - *offset;
202: debug3("resend_bytes: resend %lu bytes from %llu",
203: (unsigned long)needed, (unsigned long long)*offset);
204: if (needed > available)
205: fatal("Needed to resend more data than in the cache");
206: if (out_last < needed) {
207: int chunkend = needed - out_last;
208: atomicio(vwrite, fd, out_buf + out_buf_size - chunkend,
209: chunkend);
210: atomicio(vwrite, fd, out_buf, out_last);
211: } else {
212: atomicio(vwrite, fd, out_buf + (out_last - needed), needed);
213: }
1.6 andreas 214: }
215:
216: /*
217: * Caclulate a new key after a reconnect
218: */
219: void
220: calculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge)
221: {
222: const EVP_MD *md = EVP_sha1();
223: EVP_MD_CTX ctx;
224: char hash[EVP_MAX_MD_SIZE];
225: Buffer b;
226:
227: buffer_init(&b);
228: buffer_put_int64(&b, *key);
229: buffer_put_int64(&b, cookie);
230: buffer_put_int64(&b, challenge);
231:
232: EVP_DigestInit(&ctx, md);
233: EVP_DigestUpdate(&ctx, buffer_ptr(&b), buffer_len(&b));
234: EVP_DigestFinal(&ctx, hash, NULL);
235:
236: buffer_clear(&b);
237: buffer_append(&b, hash, EVP_MD_size(md));
238: *key = buffer_get_int64(&b);
239: buffer_free(&b);
1.1 andreas 240: }