1ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman/* $OpenBSD: sshbuf.c,v 1.4 2015/10/05 17:11:21 djm Exp $ */ 2d059297112922cabb0c674840589be8db821fd9aAdam Langley/* 3d059297112922cabb0c674840589be8db821fd9aAdam Langley * Copyright (c) 2011 Damien Miller 4d059297112922cabb0c674840589be8db821fd9aAdam Langley * 5d059297112922cabb0c674840589be8db821fd9aAdam Langley * Permission to use, copy, modify, and distribute this software for any 6d059297112922cabb0c674840589be8db821fd9aAdam Langley * purpose with or without fee is hereby granted, provided that the above 7d059297112922cabb0c674840589be8db821fd9aAdam Langley * copyright notice and this permission notice appear in all copies. 8d059297112922cabb0c674840589be8db821fd9aAdam Langley * 9d059297112922cabb0c674840589be8db821fd9aAdam Langley * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10d059297112922cabb0c674840589be8db821fd9aAdam Langley * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11d059297112922cabb0c674840589be8db821fd9aAdam Langley * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12d059297112922cabb0c674840589be8db821fd9aAdam Langley * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13d059297112922cabb0c674840589be8db821fd9aAdam Langley * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14d059297112922cabb0c674840589be8db821fd9aAdam Langley * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15d059297112922cabb0c674840589be8db821fd9aAdam Langley * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 17d059297112922cabb0c674840589be8db821fd9aAdam Langley 18d059297112922cabb0c674840589be8db821fd9aAdam Langley#define SSHBUF_INTERNAL 19d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "includes.h" 20d059297112922cabb0c674840589be8db821fd9aAdam Langley 21d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <sys/param.h> /* roundup */ 22d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <sys/types.h> 23d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <signal.h> 24d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <stdlib.h> 25d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <stdio.h> 26d059297112922cabb0c674840589be8db821fd9aAdam Langley#include <string.h> 27d059297112922cabb0c674840589be8db821fd9aAdam Langley 28d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "ssherr.h" 29d059297112922cabb0c674840589be8db821fd9aAdam Langley#include "sshbuf.h" 30d059297112922cabb0c674840589be8db821fd9aAdam Langley 31d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic inline int 32d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_check_sanity(const struct sshbuf *buf) 33d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 34d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("sanity"); 35d059297112922cabb0c674840589be8db821fd9aAdam Langley if (__predict_false(buf == NULL || 36d059297112922cabb0c674840589be8db821fd9aAdam Langley (!buf->readonly && buf->d != buf->cd) || 37d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX || 38d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->cd == NULL || 39d059297112922cabb0c674840589be8db821fd9aAdam Langley (buf->dont_free && (buf->readonly || buf->parent != NULL)) || 40d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->max_size > SSHBUF_SIZE_MAX || 41d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->alloc > buf->max_size || 42d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->size > buf->alloc || 43d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->off > buf->size)) { 44d059297112922cabb0c674840589be8db821fd9aAdam Langley /* Do not try to recover from corrupted buffer internals */ 45d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 46d059297112922cabb0c674840589be8db821fd9aAdam Langley signal(SIGSEGV, SIG_DFL); 47d059297112922cabb0c674840589be8db821fd9aAdam Langley raise(SIGSEGV); 48d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_INTERNAL_ERROR; 49d059297112922cabb0c674840589be8db821fd9aAdam Langley } 50d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 51d059297112922cabb0c674840589be8db821fd9aAdam Langley} 52d059297112922cabb0c674840589be8db821fd9aAdam Langley 53d059297112922cabb0c674840589be8db821fd9aAdam Langleystatic void 54d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_maybe_pack(struct sshbuf *buf, int force) 55d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 56d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("force %d", force)); 57d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("pre-pack"); 58d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->off == 0 || buf->readonly || buf->refcount > 1) 59d059297112922cabb0c674840589be8db821fd9aAdam Langley return; 60d059297112922cabb0c674840589be8db821fd9aAdam Langley if (force || 61d059297112922cabb0c674840589be8db821fd9aAdam Langley (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) { 62d059297112922cabb0c674840589be8db821fd9aAdam Langley memmove(buf->d, buf->d + buf->off, buf->size - buf->off); 63d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->size -= buf->off; 64d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->off = 0; 65d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("packed"); 66d059297112922cabb0c674840589be8db821fd9aAdam Langley } 67d059297112922cabb0c674840589be8db821fd9aAdam Langley} 68d059297112922cabb0c674840589be8db821fd9aAdam Langley 69d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct sshbuf * 70d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_new(void) 71d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 72d059297112922cabb0c674840589be8db821fd9aAdam Langley struct sshbuf *ret; 73d059297112922cabb0c674840589be8db821fd9aAdam Langley 74d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((ret = calloc(sizeof(*ret), 1)) == NULL) 75d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 76d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->alloc = SSHBUF_SIZE_INIT; 77d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->max_size = SSHBUF_SIZE_MAX; 78d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->readonly = 0; 79d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->refcount = 1; 80d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->parent = NULL; 81d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) { 82d059297112922cabb0c674840589be8db821fd9aAdam Langley free(ret); 83d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 84d059297112922cabb0c674840589be8db821fd9aAdam Langley } 85d059297112922cabb0c674840589be8db821fd9aAdam Langley return ret; 86d059297112922cabb0c674840589be8db821fd9aAdam Langley} 87d059297112922cabb0c674840589be8db821fd9aAdam Langley 88d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct sshbuf * 89d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_from(const void *blob, size_t len) 90d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 91d059297112922cabb0c674840589be8db821fd9aAdam Langley struct sshbuf *ret; 92d059297112922cabb0c674840589be8db821fd9aAdam Langley 93d059297112922cabb0c674840589be8db821fd9aAdam Langley if (blob == NULL || len > SSHBUF_SIZE_MAX || 94d059297112922cabb0c674840589be8db821fd9aAdam Langley (ret = calloc(sizeof(*ret), 1)) == NULL) 95d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 96d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->alloc = ret->size = ret->max_size = len; 97d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->readonly = 1; 98d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->refcount = 1; 99d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->parent = NULL; 100d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->cd = blob; 101d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->d = NULL; 102d059297112922cabb0c674840589be8db821fd9aAdam Langley return ret; 103d059297112922cabb0c674840589be8db821fd9aAdam Langley} 104d059297112922cabb0c674840589be8db821fd9aAdam Langley 105d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 106d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent) 107d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 108d059297112922cabb0c674840589be8db821fd9aAdam Langley int r; 109d059297112922cabb0c674840589be8db821fd9aAdam Langley 110d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_sanity(child)) != 0 || 111d059297112922cabb0c674840589be8db821fd9aAdam Langley (r = sshbuf_check_sanity(parent)) != 0) 112d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 113d059297112922cabb0c674840589be8db821fd9aAdam Langley child->parent = parent; 114d059297112922cabb0c674840589be8db821fd9aAdam Langley child->parent->refcount++; 115d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 116d059297112922cabb0c674840589be8db821fd9aAdam Langley} 117d059297112922cabb0c674840589be8db821fd9aAdam Langley 118d059297112922cabb0c674840589be8db821fd9aAdam Langleystruct sshbuf * 119d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_fromb(struct sshbuf *buf) 120d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 121d059297112922cabb0c674840589be8db821fd9aAdam Langley struct sshbuf *ret; 122d059297112922cabb0c674840589be8db821fd9aAdam Langley 123d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) != 0) 124d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 125d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL) 126d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 127d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_set_parent(ret, buf) != 0) { 128d059297112922cabb0c674840589be8db821fd9aAdam Langley sshbuf_free(ret); 129d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 130d059297112922cabb0c674840589be8db821fd9aAdam Langley } 131d059297112922cabb0c674840589be8db821fd9aAdam Langley return ret; 132d059297112922cabb0c674840589be8db821fd9aAdam Langley} 133d059297112922cabb0c674840589be8db821fd9aAdam Langley 134d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid 135d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_init(struct sshbuf *ret) 136d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 137ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman explicit_bzero(ret, sizeof(*ret)); 138d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->alloc = SSHBUF_SIZE_INIT; 139d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->max_size = SSHBUF_SIZE_MAX; 140d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->readonly = 0; 141d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->dont_free = 1; 142d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->refcount = 1; 143d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) 144d059297112922cabb0c674840589be8db821fd9aAdam Langley ret->alloc = 0; 145d059297112922cabb0c674840589be8db821fd9aAdam Langley} 146d059297112922cabb0c674840589be8db821fd9aAdam Langley 147d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid 148d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_free(struct sshbuf *buf) 149d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 150d059297112922cabb0c674840589be8db821fd9aAdam Langley int dont_free = 0; 151d059297112922cabb0c674840589be8db821fd9aAdam Langley 152d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf == NULL) 153d059297112922cabb0c674840589be8db821fd9aAdam Langley return; 154d059297112922cabb0c674840589be8db821fd9aAdam Langley /* 155d059297112922cabb0c674840589be8db821fd9aAdam Langley * The following will leak on insane buffers, but this is the safest 156d059297112922cabb0c674840589be8db821fd9aAdam Langley * course of action - an invalid pointer or already-freed pointer may 157d059297112922cabb0c674840589be8db821fd9aAdam Langley * have been passed to us and continuing to scribble over memory would 158d059297112922cabb0c674840589be8db821fd9aAdam Langley * be bad. 159d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 160d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) != 0) 161d059297112922cabb0c674840589be8db821fd9aAdam Langley return; 162d059297112922cabb0c674840589be8db821fd9aAdam Langley /* 163d059297112922cabb0c674840589be8db821fd9aAdam Langley * If we are a child, the free our parent to decrement its reference 164d059297112922cabb0c674840589be8db821fd9aAdam Langley * count and possibly free it. 165d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 166d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->parent != NULL) { 167d059297112922cabb0c674840589be8db821fd9aAdam Langley sshbuf_free(buf->parent); 168d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->parent = NULL; 169d059297112922cabb0c674840589be8db821fd9aAdam Langley } 170d059297112922cabb0c674840589be8db821fd9aAdam Langley /* 171d059297112922cabb0c674840589be8db821fd9aAdam Langley * If we are a parent with still-extant children, then don't free just 172d059297112922cabb0c674840589be8db821fd9aAdam Langley * yet. The last child's call to sshbuf_free should decrement our 173d059297112922cabb0c674840589be8db821fd9aAdam Langley * refcount to 0 and trigger the actual free. 174d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 175d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->refcount--; 176d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->refcount > 0) 177d059297112922cabb0c674840589be8db821fd9aAdam Langley return; 178d059297112922cabb0c674840589be8db821fd9aAdam Langley dont_free = buf->dont_free; 179d059297112922cabb0c674840589be8db821fd9aAdam Langley if (!buf->readonly) { 180ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman explicit_bzero(buf->d, buf->alloc); 181d059297112922cabb0c674840589be8db821fd9aAdam Langley free(buf->d); 182d059297112922cabb0c674840589be8db821fd9aAdam Langley } 183ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman explicit_bzero(buf, sizeof(*buf)); 184d059297112922cabb0c674840589be8db821fd9aAdam Langley if (!dont_free) 185d059297112922cabb0c674840589be8db821fd9aAdam Langley free(buf); 186d059297112922cabb0c674840589be8db821fd9aAdam Langley} 187d059297112922cabb0c674840589be8db821fd9aAdam Langley 188d059297112922cabb0c674840589be8db821fd9aAdam Langleyvoid 189d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_reset(struct sshbuf *buf) 190d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 191d059297112922cabb0c674840589be8db821fd9aAdam Langley u_char *d; 192d059297112922cabb0c674840589be8db821fd9aAdam Langley 193d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->readonly || buf->refcount > 1) { 194d059297112922cabb0c674840589be8db821fd9aAdam Langley /* Nonsensical. Just make buffer appear empty */ 195d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->off = buf->size; 196d059297112922cabb0c674840589be8db821fd9aAdam Langley return; 197d059297112922cabb0c674840589be8db821fd9aAdam Langley } 198d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) == 0) 199ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman explicit_bzero(buf->d, buf->alloc); 200d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->off = buf->size = 0; 201d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->alloc != SSHBUF_SIZE_INIT) { 202d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((d = realloc(buf->d, SSHBUF_SIZE_INIT)) != NULL) { 203d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->cd = buf->d = d; 204d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->alloc = SSHBUF_SIZE_INIT; 205d059297112922cabb0c674840589be8db821fd9aAdam Langley } 206d059297112922cabb0c674840589be8db821fd9aAdam Langley } 207d059297112922cabb0c674840589be8db821fd9aAdam Langley} 208d059297112922cabb0c674840589be8db821fd9aAdam Langley 209d059297112922cabb0c674840589be8db821fd9aAdam Langleysize_t 210d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_max_size(const struct sshbuf *buf) 211d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 212d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->max_size; 213d059297112922cabb0c674840589be8db821fd9aAdam Langley} 214d059297112922cabb0c674840589be8db821fd9aAdam Langley 215d059297112922cabb0c674840589be8db821fd9aAdam Langleysize_t 216d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_alloc(const struct sshbuf *buf) 217d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 218d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->alloc; 219d059297112922cabb0c674840589be8db821fd9aAdam Langley} 220d059297112922cabb0c674840589be8db821fd9aAdam Langley 221d059297112922cabb0c674840589be8db821fd9aAdam Langleyconst struct sshbuf * 222d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_parent(const struct sshbuf *buf) 223d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 224d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->parent; 225d059297112922cabb0c674840589be8db821fd9aAdam Langley} 226d059297112922cabb0c674840589be8db821fd9aAdam Langley 227d059297112922cabb0c674840589be8db821fd9aAdam Langleyu_int 228d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_refcount(const struct sshbuf *buf) 229d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 230d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->refcount; 231d059297112922cabb0c674840589be8db821fd9aAdam Langley} 232d059297112922cabb0c674840589be8db821fd9aAdam Langley 233d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 234d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_set_max_size(struct sshbuf *buf, size_t max_size) 235d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 236d059297112922cabb0c674840589be8db821fd9aAdam Langley size_t rlen; 237d059297112922cabb0c674840589be8db821fd9aAdam Langley u_char *dp; 238d059297112922cabb0c674840589be8db821fd9aAdam Langley int r; 239d059297112922cabb0c674840589be8db821fd9aAdam Langley 240d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("set max buf = %p len = %zu", buf, max_size)); 241d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_sanity(buf)) != 0) 242d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 243d059297112922cabb0c674840589be8db821fd9aAdam Langley if (max_size == buf->max_size) 244d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 245d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->readonly || buf->refcount > 1) 246d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_BUFFER_READ_ONLY; 247d059297112922cabb0c674840589be8db821fd9aAdam Langley if (max_size > SSHBUF_SIZE_MAX) 248d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_NO_BUFFER_SPACE; 249d059297112922cabb0c674840589be8db821fd9aAdam Langley /* pack and realloc if necessary */ 250d059297112922cabb0c674840589be8db821fd9aAdam Langley sshbuf_maybe_pack(buf, max_size < buf->size); 251d059297112922cabb0c674840589be8db821fd9aAdam Langley if (max_size < buf->alloc && max_size > buf->size) { 252d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->size < SSHBUF_SIZE_INIT) 253d059297112922cabb0c674840589be8db821fd9aAdam Langley rlen = SSHBUF_SIZE_INIT; 254d059297112922cabb0c674840589be8db821fd9aAdam Langley else 255d059297112922cabb0c674840589be8db821fd9aAdam Langley rlen = roundup(buf->size, SSHBUF_SIZE_INC); 256d059297112922cabb0c674840589be8db821fd9aAdam Langley if (rlen > max_size) 257d059297112922cabb0c674840589be8db821fd9aAdam Langley rlen = max_size; 258ccacbc9b0331c30b8be12e8e0349e983abf28fc0Greg Hartman explicit_bzero(buf->d + buf->size, buf->alloc - buf->size); 259d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("new alloc = %zu", rlen)); 260d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((dp = realloc(buf->d, rlen)) == NULL) 261d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_ALLOC_FAIL; 262d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->cd = buf->d = dp; 263d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->alloc = rlen; 264d059297112922cabb0c674840589be8db821fd9aAdam Langley } 265d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("new-max"); 266d059297112922cabb0c674840589be8db821fd9aAdam Langley if (max_size < buf->alloc) 267d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_NO_BUFFER_SPACE; 268d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->max_size = max_size; 269d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 270d059297112922cabb0c674840589be8db821fd9aAdam Langley} 271d059297112922cabb0c674840589be8db821fd9aAdam Langley 272d059297112922cabb0c674840589be8db821fd9aAdam Langleysize_t 273d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_len(const struct sshbuf *buf) 274d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 275d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) != 0) 276d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 277d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->size - buf->off; 278d059297112922cabb0c674840589be8db821fd9aAdam Langley} 279d059297112922cabb0c674840589be8db821fd9aAdam Langley 280d059297112922cabb0c674840589be8db821fd9aAdam Langleysize_t 281d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_avail(const struct sshbuf *buf) 282d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 283d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 284d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 285d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->max_size - (buf->size - buf->off); 286d059297112922cabb0c674840589be8db821fd9aAdam Langley} 287d059297112922cabb0c674840589be8db821fd9aAdam Langley 288d059297112922cabb0c674840589be8db821fd9aAdam Langleyconst u_char * 289d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_ptr(const struct sshbuf *buf) 290d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 291d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) != 0) 292d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 293d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->cd + buf->off; 294d059297112922cabb0c674840589be8db821fd9aAdam Langley} 295d059297112922cabb0c674840589be8db821fd9aAdam Langley 296d059297112922cabb0c674840589be8db821fd9aAdam Langleyu_char * 297d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_mutable_ptr(const struct sshbuf *buf) 298d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 299d059297112922cabb0c674840589be8db821fd9aAdam Langley if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1) 300d059297112922cabb0c674840589be8db821fd9aAdam Langley return NULL; 301d059297112922cabb0c674840589be8db821fd9aAdam Langley return buf->d + buf->off; 302d059297112922cabb0c674840589be8db821fd9aAdam Langley} 303d059297112922cabb0c674840589be8db821fd9aAdam Langley 304d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 305d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_check_reserve(const struct sshbuf *buf, size_t len) 306d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 307d059297112922cabb0c674840589be8db821fd9aAdam Langley int r; 308d059297112922cabb0c674840589be8db821fd9aAdam Langley 309d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_sanity(buf)) != 0) 310d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 311d059297112922cabb0c674840589be8db821fd9aAdam Langley if (buf->readonly || buf->refcount > 1) 312d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_BUFFER_READ_ONLY; 313d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("check"); 314d059297112922cabb0c674840589be8db821fd9aAdam Langley /* Check that len is reasonable and that max_size + available < len */ 315d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) 316d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_NO_BUFFER_SPACE; 317d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 318d059297112922cabb0c674840589be8db821fd9aAdam Langley} 319d059297112922cabb0c674840589be8db821fd9aAdam Langley 320d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 321d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) 322d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 323d059297112922cabb0c674840589be8db821fd9aAdam Langley size_t rlen, need; 324d059297112922cabb0c674840589be8db821fd9aAdam Langley u_char *dp; 325d059297112922cabb0c674840589be8db821fd9aAdam Langley int r; 326d059297112922cabb0c674840589be8db821fd9aAdam Langley 327d059297112922cabb0c674840589be8db821fd9aAdam Langley if (dpp != NULL) 328d059297112922cabb0c674840589be8db821fd9aAdam Langley *dpp = NULL; 329d059297112922cabb0c674840589be8db821fd9aAdam Langley 330d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); 331d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_reserve(buf, len)) != 0) 332d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 333d059297112922cabb0c674840589be8db821fd9aAdam Langley /* 334d059297112922cabb0c674840589be8db821fd9aAdam Langley * If the requested allocation appended would push us past max_size 335d059297112922cabb0c674840589be8db821fd9aAdam Langley * then pack the buffer, zeroing buf->off. 336d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 337d059297112922cabb0c674840589be8db821fd9aAdam Langley sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); 338d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("reserve"); 339d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len + buf->size > buf->alloc) { 340d059297112922cabb0c674840589be8db821fd9aAdam Langley /* 341d059297112922cabb0c674840589be8db821fd9aAdam Langley * Prefer to alloc in SSHBUF_SIZE_INC units, but 342d059297112922cabb0c674840589be8db821fd9aAdam Langley * allocate less if doing so would overflow max_size. 343d059297112922cabb0c674840589be8db821fd9aAdam Langley */ 344d059297112922cabb0c674840589be8db821fd9aAdam Langley need = len + buf->size - buf->alloc; 345d059297112922cabb0c674840589be8db821fd9aAdam Langley rlen = roundup(buf->alloc + need, SSHBUF_SIZE_INC); 346d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); 347d059297112922cabb0c674840589be8db821fd9aAdam Langley if (rlen > buf->max_size) 348d059297112922cabb0c674840589be8db821fd9aAdam Langley rlen = buf->alloc + need; 349d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("adjusted rlen %zu", rlen)); 350d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((dp = realloc(buf->d, rlen)) == NULL) { 351d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("realloc fail")); 352d059297112922cabb0c674840589be8db821fd9aAdam Langley if (dpp != NULL) 353d059297112922cabb0c674840589be8db821fd9aAdam Langley *dpp = NULL; 354d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_ALLOC_FAIL; 355d059297112922cabb0c674840589be8db821fd9aAdam Langley } 356d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->alloc = rlen; 357d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->cd = buf->d = dp; 358d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_reserve(buf, len)) < 0) { 359d059297112922cabb0c674840589be8db821fd9aAdam Langley /* shouldn't fail */ 360d059297112922cabb0c674840589be8db821fd9aAdam Langley if (dpp != NULL) 361d059297112922cabb0c674840589be8db821fd9aAdam Langley *dpp = NULL; 362d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 363d059297112922cabb0c674840589be8db821fd9aAdam Langley } 364d059297112922cabb0c674840589be8db821fd9aAdam Langley } 365d059297112922cabb0c674840589be8db821fd9aAdam Langley dp = buf->d + buf->size; 366d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->size += len; 367d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("done"); 368d059297112922cabb0c674840589be8db821fd9aAdam Langley if (dpp != NULL) 369d059297112922cabb0c674840589be8db821fd9aAdam Langley *dpp = dp; 370d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 371d059297112922cabb0c674840589be8db821fd9aAdam Langley} 372d059297112922cabb0c674840589be8db821fd9aAdam Langley 373d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 374d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_consume(struct sshbuf *buf, size_t len) 375d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 376d059297112922cabb0c674840589be8db821fd9aAdam Langley int r; 377d059297112922cabb0c674840589be8db821fd9aAdam Langley 378d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("len = %zu", len)); 379d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_sanity(buf)) != 0) 380d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 381d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len == 0) 382d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 383d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len > sshbuf_len(buf)) 384d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_MESSAGE_INCOMPLETE; 385d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->off += len; 386d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("done"); 387d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 388d059297112922cabb0c674840589be8db821fd9aAdam Langley} 389d059297112922cabb0c674840589be8db821fd9aAdam Langley 390d059297112922cabb0c674840589be8db821fd9aAdam Langleyint 391d059297112922cabb0c674840589be8db821fd9aAdam Langleysshbuf_consume_end(struct sshbuf *buf, size_t len) 392d059297112922cabb0c674840589be8db821fd9aAdam Langley{ 393d059297112922cabb0c674840589be8db821fd9aAdam Langley int r; 394d059297112922cabb0c674840589be8db821fd9aAdam Langley 395d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_DBG(("len = %zu", len)); 396d059297112922cabb0c674840589be8db821fd9aAdam Langley if ((r = sshbuf_check_sanity(buf)) != 0) 397d059297112922cabb0c674840589be8db821fd9aAdam Langley return r; 398d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len == 0) 399d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 400d059297112922cabb0c674840589be8db821fd9aAdam Langley if (len > sshbuf_len(buf)) 401d059297112922cabb0c674840589be8db821fd9aAdam Langley return SSH_ERR_MESSAGE_INCOMPLETE; 402d059297112922cabb0c674840589be8db821fd9aAdam Langley buf->size -= len; 403d059297112922cabb0c674840589be8db821fd9aAdam Langley SSHBUF_TELL("done"); 404d059297112922cabb0c674840589be8db821fd9aAdam Langley return 0; 405d059297112922cabb0c674840589be8db821fd9aAdam Langley} 406d059297112922cabb0c674840589be8db821fd9aAdam Langley 407