1262566Sdes/* $OpenBSD: roaming_common.c,v 1.12 2014/01/09 23:20:00 djm Exp $ */ 2197670Sdes/* 3197670Sdes * Copyright (c) 2004-2009 AppGate Network Security AB 4197670Sdes * 5197670Sdes * Permission to use, copy, modify, and distribute this software for any 6197670Sdes * purpose with or without fee is hereby granted, provided that the above 7197670Sdes * copyright notice and this permission notice appear in all copies. 8197670Sdes * 9197670Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10197670Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11197670Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12197670Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13197670Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14197670Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15197670Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16197670Sdes */ 17197670Sdes 18197670Sdes#include "includes.h" 19197670Sdes 20197670Sdes#include <sys/types.h> 21197670Sdes#include <sys/socket.h> 22197670Sdes#include <sys/uio.h> 23197670Sdes 24197670Sdes#include <errno.h> 25197670Sdes#ifdef HAVE_INTTYPES_H 26197670Sdes#include <inttypes.h> 27197670Sdes#endif 28197670Sdes#include <stdarg.h> 29197670Sdes#include <string.h> 30197670Sdes#include <unistd.h> 31197670Sdes 32197670Sdes#include "atomicio.h" 33197670Sdes#include "log.h" 34197670Sdes#include "packet.h" 35197670Sdes#include "xmalloc.h" 36197670Sdes#include "cipher.h" 37197670Sdes#include "buffer.h" 38197670Sdes#include "roaming.h" 39262566Sdes#include "digest.h" 40197670Sdes 41197670Sdesstatic size_t out_buf_size = 0; 42197670Sdesstatic char *out_buf = NULL; 43197670Sdesstatic size_t out_start; 44197670Sdesstatic size_t out_last; 45197670Sdes 46197670Sdesstatic u_int64_t write_bytes = 0; 47197670Sdesstatic u_int64_t read_bytes = 0; 48197670Sdes 49197670Sdesint roaming_enabled = 0; 50197670Sdesint resume_in_progress = 0; 51197670Sdes 52197670Sdesint 53262566Sdesget_snd_buf_size(void) 54197670Sdes{ 55197670Sdes int fd = packet_get_connection_out(); 56204917Sdes int optval; 57204917Sdes socklen_t optvallen = sizeof(optval); 58197670Sdes 59197670Sdes if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &optval, &optvallen) != 0) 60197670Sdes optval = DEFAULT_ROAMBUF; 61197670Sdes return optval; 62197670Sdes} 63197670Sdes 64197670Sdesint 65262566Sdesget_recv_buf_size(void) 66197670Sdes{ 67197670Sdes int fd = packet_get_connection_in(); 68204917Sdes int optval; 69204917Sdes socklen_t optvallen = sizeof(optval); 70197670Sdes 71197670Sdes if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &optval, &optvallen) != 0) 72197670Sdes optval = DEFAULT_ROAMBUF; 73197670Sdes return optval; 74197670Sdes} 75197670Sdes 76197670Sdesvoid 77197670Sdesset_out_buffer_size(size_t size) 78197670Sdes{ 79240075Sdes if (size == 0 || size > MAX_ROAMBUF) 80240075Sdes fatal("%s: bad buffer size %lu", __func__, (u_long)size); 81197670Sdes /* 82197670Sdes * The buffer size can only be set once and the buffer will live 83197670Sdes * as long as the session lives. 84197670Sdes */ 85197670Sdes if (out_buf == NULL) { 86197670Sdes out_buf_size = size; 87197670Sdes out_buf = xmalloc(size); 88197670Sdes out_start = 0; 89197670Sdes out_last = 0; 90197670Sdes } 91197670Sdes} 92197670Sdes 93197670Sdesu_int64_t 94197670Sdesget_recv_bytes(void) 95197670Sdes{ 96197670Sdes return read_bytes; 97197670Sdes} 98197670Sdes 99197670Sdesvoid 100197670Sdesadd_recv_bytes(u_int64_t num) 101197670Sdes{ 102197670Sdes read_bytes += num; 103197670Sdes} 104197670Sdes 105197670Sdesu_int64_t 106197670Sdesget_sent_bytes(void) 107197670Sdes{ 108197670Sdes return write_bytes; 109197670Sdes} 110197670Sdes 111197670Sdesvoid 112197670Sdesroam_set_bytes(u_int64_t sent, u_int64_t recvd) 113197670Sdes{ 114197670Sdes read_bytes = recvd; 115197670Sdes write_bytes = sent; 116197670Sdes} 117197670Sdes 118197670Sdesstatic void 119197670Sdesbuf_append(const char *buf, size_t count) 120197670Sdes{ 121197670Sdes if (count > out_buf_size) { 122197670Sdes buf += count - out_buf_size; 123197670Sdes count = out_buf_size; 124197670Sdes } 125197670Sdes if (count < out_buf_size - out_last) { 126197670Sdes memcpy(out_buf + out_last, buf, count); 127197670Sdes if (out_start > out_last) 128197670Sdes out_start += count; 129197670Sdes out_last += count; 130197670Sdes } else { 131197670Sdes /* data will wrap */ 132197670Sdes size_t chunk = out_buf_size - out_last; 133197670Sdes memcpy(out_buf + out_last, buf, chunk); 134197670Sdes memcpy(out_buf, buf + chunk, count - chunk); 135197670Sdes out_last = count - chunk; 136197670Sdes out_start = out_last + 1; 137197670Sdes } 138197670Sdes} 139197670Sdes 140197670Sdesssize_t 141197670Sdesroaming_write(int fd, const void *buf, size_t count, int *cont) 142197670Sdes{ 143197670Sdes ssize_t ret; 144197670Sdes 145197670Sdes ret = write(fd, buf, count); 146197670Sdes if (ret > 0 && !resume_in_progress) { 147197670Sdes write_bytes += ret; 148197670Sdes if (out_buf_size > 0) 149197670Sdes buf_append(buf, ret); 150197670Sdes } 151204917Sdes if (out_buf_size > 0 && 152204917Sdes (ret == 0 || (ret == -1 && errno == EPIPE))) { 153204917Sdes if (wait_for_roaming_reconnect() != 0) { 154204917Sdes ret = 0; 155204917Sdes *cont = 1; 156204917Sdes } else { 157204917Sdes ret = -1; 158204917Sdes errno = EAGAIN; 159204917Sdes } 160204917Sdes } 161197670Sdes return ret; 162197670Sdes} 163197670Sdes 164197670Sdesssize_t 165197670Sdesroaming_read(int fd, void *buf, size_t count, int *cont) 166197670Sdes{ 167197670Sdes ssize_t ret = read(fd, buf, count); 168197670Sdes if (ret > 0) { 169197670Sdes if (!resume_in_progress) { 170197670Sdes read_bytes += ret; 171197670Sdes } 172204917Sdes } else if (out_buf_size > 0 && 173204917Sdes (ret == 0 || (ret == -1 && (errno == ECONNRESET 174204917Sdes || errno == ECONNABORTED || errno == ETIMEDOUT 175204917Sdes || errno == EHOSTUNREACH)))) { 176204917Sdes debug("roaming_read failed for %d ret=%ld errno=%d", 177204917Sdes fd, (long)ret, errno); 178204917Sdes ret = 0; 179204917Sdes if (wait_for_roaming_reconnect() == 0) 180204917Sdes *cont = 1; 181197670Sdes } 182197670Sdes return ret; 183197670Sdes} 184197670Sdes 185197670Sdessize_t 186197670Sdesroaming_atomicio(ssize_t(*f)(int, void*, size_t), int fd, void *buf, 187197670Sdes size_t count) 188197670Sdes{ 189197670Sdes size_t ret = atomicio(f, fd, buf, count); 190197670Sdes 191197670Sdes if (f == vwrite && ret > 0 && !resume_in_progress) { 192197670Sdes write_bytes += ret; 193197670Sdes } else if (f == read && ret > 0 && !resume_in_progress) { 194197670Sdes read_bytes += ret; 195197670Sdes } 196197670Sdes return ret; 197197670Sdes} 198197670Sdes 199197670Sdesvoid 200197670Sdesresend_bytes(int fd, u_int64_t *offset) 201197670Sdes{ 202197670Sdes size_t available, needed; 203197670Sdes 204197670Sdes if (out_start < out_last) 205197670Sdes available = out_last - out_start; 206197670Sdes else 207197670Sdes available = out_buf_size; 208197670Sdes needed = write_bytes - *offset; 209197670Sdes debug3("resend_bytes: resend %lu bytes from %llu", 210197670Sdes (unsigned long)needed, (unsigned long long)*offset); 211197670Sdes if (needed > available) 212197670Sdes fatal("Needed to resend more data than in the cache"); 213197670Sdes if (out_last < needed) { 214197670Sdes int chunkend = needed - out_last; 215197670Sdes atomicio(vwrite, fd, out_buf + out_buf_size - chunkend, 216197670Sdes chunkend); 217197670Sdes atomicio(vwrite, fd, out_buf, out_last); 218197670Sdes } else { 219197670Sdes atomicio(vwrite, fd, out_buf + (out_last - needed), needed); 220197670Sdes } 221197670Sdes} 222204917Sdes 223204917Sdes/* 224204917Sdes * Caclulate a new key after a reconnect 225204917Sdes */ 226204917Sdesvoid 227204917Sdescalculate_new_key(u_int64_t *key, u_int64_t cookie, u_int64_t challenge) 228204917Sdes{ 229262566Sdes u_char hash[SSH_DIGEST_MAX_LENGTH]; 230204917Sdes Buffer b; 231204917Sdes 232204917Sdes buffer_init(&b); 233204917Sdes buffer_put_int64(&b, *key); 234204917Sdes buffer_put_int64(&b, cookie); 235204917Sdes buffer_put_int64(&b, challenge); 236204917Sdes 237262566Sdes if (ssh_digest_buffer(SSH_DIGEST_SHA1, &b, hash, sizeof(hash)) != 0) 238262566Sdes fatal("%s: digest_buffer failed", __func__); 239204917Sdes 240204917Sdes buffer_clear(&b); 241262566Sdes buffer_append(&b, hash, ssh_digest_bytes(SSH_DIGEST_SHA1)); 242204917Sdes *key = buffer_get_int64(&b); 243204917Sdes buffer_free(&b); 244204917Sdes} 245