Annotation of src/usr.bin/ssh/roaming_common.c, Revision 1.13
1.13 ! okan 1: /* $OpenBSD: roaming_common.c,v 1.12 2014/01/09 23:20:00 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 <stdarg.h>
1.5 andreas 24: #include <string.h>
1.1 andreas 25: #include <unistd.h>
26:
27: #include "atomicio.h"
28: #include "log.h"
29: #include "packet.h"
30: #include "xmalloc.h"
31: #include "cipher.h"
32: #include "buffer.h"
33: #include "roaming.h"
1.12 djm 34: #include "digest.h"
1.1 andreas 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
1.11 djm 48: get_snd_buf_size(void)
1.5 andreas 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
1.11 djm 60: get_recv_buf_size(void)
1.5 andreas 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: {
1.9 djm 74: if (size == 0 || size > MAX_ROAMBUF)
75: fatal("%s: bad buffer size %lu", __func__, (u_long)size);
1.5 andreas 76: /*
77: * The buffer size can only be set once and the buffer will live
78: * as long as the session lives.
79: */
80: if (out_buf == NULL) {
81: out_buf_size = size;
82: out_buf = xmalloc(size);
83: out_start = 0;
84: out_last = 0;
85: }
86: }
87:
1.1 andreas 88: u_int64_t
89: get_recv_bytes(void)
90: {
91: return read_bytes;
92: }
93:
94: void
95: add_recv_bytes(u_int64_t num)
96: {
97: read_bytes += num;
98: }
99:
100: u_int64_t
101: get_sent_bytes(void)
102: {
103: return write_bytes;
104: }
105:
106: void
1.2 andreas 107: roam_set_bytes(u_int64_t sent, u_int64_t recvd)
1.1 andreas 108: {
1.2 andreas 109: read_bytes = recvd;
1.1 andreas 110: write_bytes = sent;
111: }
112:
1.5 andreas 113: static void
114: buf_append(const char *buf, size_t count)
115: {
116: if (count > out_buf_size) {
117: buf += count - out_buf_size;
118: count = out_buf_size;
119: }
120: if (count < out_buf_size - out_last) {
121: memcpy(out_buf + out_last, buf, count);
122: if (out_start > out_last)
123: out_start += count;
124: out_last += count;
125: } else {
126: /* data will wrap */
127: size_t chunk = out_buf_size - out_last;
128: memcpy(out_buf + out_last, buf, chunk);
129: memcpy(out_buf, buf + chunk, count - chunk);
130: out_last = count - chunk;
131: out_start = out_last + 1;
132: }
133: }
134:
1.1 andreas 135: ssize_t
136: roaming_write(int fd, const void *buf, size_t count, int *cont)
137: {
138: ssize_t ret;
139:
140: ret = write(fd, buf, count);
141: if (ret > 0 && !resume_in_progress) {
142: write_bytes += ret;
1.5 andreas 143: if (out_buf_size > 0)
144: buf_append(buf, ret);
1.1 andreas 145: }
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: {
1.12 djm 224: u_char hash[SSH_DIGEST_MAX_LENGTH];
1.6 andreas 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:
1.12 djm 232: if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, hash, sizeof(hash)) != 0)
233: fatal("%s: digest_buffer failed", __func__);
1.6 andreas 234:
235: buffer_clear(&b);
1.12 djm 236: buffer_append(&b, hash, ssh_digest_bytes(SSH_DIGEST_SHA1));
1.6 andreas 237: *key = buffer_get_int64(&b);
238: buffer_free(&b);
1.1 andreas 239: }