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