Created
November 1, 2018 16:43
-
-
Save borisisok/34489025ba0c48fae04bba74df2dc75f to your computer and use it in GitHub Desktop.
Anatomy of sshbuf.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* sshbuf_playground.c | |
* | |
* All sshbuf code from openssh-portable for learning how sshbuf | |
* is used for storing ssh cert information during before signing it. | |
* | |
* Original Copyright Notice: | |
* | |
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. | |
* Copyright (c) 2008 Alexander von Gernler. All rights reserved. | |
* Copyright (c) 2010,2011 Damien Miller. All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions | |
* are met: | |
* 1. Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include <stdio.h> | |
#include <error.h> | |
#include <sys/types.h> | |
#include <stdarg.h> | |
#include <string.h> | |
#define SSHBUF_SIZE_MAX 0x8000000 /* Hard maximum size */ | |
#define SSHBUF_REFS_MAX 0x100000 /* Max child buffers */ | |
#define SSHBUF_MAX_BIGNUM (16384 / 8) /* Max bignum *bytes* */ | |
#define SSHBUF_MAX_ECPOINT ((528 * 2 / 8) + 1) /* Max EC point *bytes* */ | |
#define SSHBUF_SIZE_INIT 256 /* Initial allocation */ | |
#define SSHBUF_SIZE_INC 256 /* Preferred increment length */ | |
#define SSHBUF_PACK_MIN 8192 /* Minimim packable offset */ | |
/* Macros for decoding/encoding integers */ | |
#define PEEK_U64(p) \ | |
(((u_int64_t)(((const u_char *)(p))[0]) << 56) | \ | |
((u_int64_t)(((const u_char *)(p))[1]) << 48) | \ | |
((u_int64_t)(((const u_char *)(p))[2]) << 40) | \ | |
((u_int64_t)(((const u_char *)(p))[3]) << 32) | \ | |
((u_int64_t)(((const u_char *)(p))[4]) << 24) | \ | |
((u_int64_t)(((const u_char *)(p))[5]) << 16) | \ | |
((u_int64_t)(((const u_char *)(p))[6]) << 8) | \ | |
(u_int64_t)(((const u_char *)(p))[7])) | |
#define PEEK_U32(p) \ | |
(((u_int32_t)(((const u_char *)(p))[0]) << 24) | \ | |
((u_int32_t)(((const u_char *)(p))[1]) << 16) | \ | |
((u_int32_t)(((const u_char *)(p))[2]) << 8) | \ | |
(u_int32_t)(((const u_char *)(p))[3])) | |
#define PEEK_U16(p) \ | |
(((u_int16_t)(((const u_char *)(p))[0]) << 8) | \ | |
(u_int16_t)(((const u_char *)(p))[1])) | |
#define POKE_U64(p, v) \ | |
do { \ | |
const u_int64_t __v = (v); \ | |
((u_char *)(p))[0] = (__v >> 56) & 0xff; \ | |
((u_char *)(p))[1] = (__v >> 48) & 0xff; \ | |
((u_char *)(p))[2] = (__v >> 40) & 0xff; \ | |
((u_char *)(p))[3] = (__v >> 32) & 0xff; \ | |
((u_char *)(p))[4] = (__v >> 24) & 0xff; \ | |
((u_char *)(p))[5] = (__v >> 16) & 0xff; \ | |
((u_char *)(p))[6] = (__v >> 8) & 0xff; \ | |
((u_char *)(p))[7] = __v & 0xff; \ | |
} while (0) | |
#define POKE_U32(p, v) \ | |
do { \ | |
const u_int32_t __v = (v); \ | |
((u_char *)(p))[0] = (__v >> 24) & 0xff; \ | |
((u_char *)(p))[1] = (__v >> 16) & 0xff; \ | |
((u_char *)(p))[2] = (__v >> 8) & 0xff; \ | |
((u_char *)(p))[3] = __v & 0xff; \ | |
} while (0) | |
#define POKE_U16(p, v) \ | |
do { \ | |
const u_int16_t __v = (v); \ | |
((u_char *)(p))[0] = (__v >> 8) & 0xff; \ | |
((u_char *)(p))[1] = __v & 0xff; \ | |
} while (0) | |
/* Error codes */ | |
#define SSH_ERR_SUCCESS 0 | |
#define SSH_ERR_INTERNAL_ERROR -1 | |
#define SSH_ERR_ALLOC_FAIL -2 | |
#define SSH_ERR_MESSAGE_INCOMPLETE -3 | |
#define SSH_ERR_INVALID_FORMAT -4 | |
#define SSH_ERR_BIGNUM_IS_NEGATIVE -5 | |
#define SSH_ERR_STRING_TOO_LARGE -6 | |
#define SSH_ERR_BIGNUM_TOO_LARGE -7 | |
#define SSH_ERR_ECPOINT_TOO_LARGE -8 | |
#define SSH_ERR_NO_BUFFER_SPACE -9 | |
#define SSH_ERR_INVALID_ARGUMENT -10 | |
#define SSH_ERR_KEY_BITS_MISMATCH -11 | |
#define SSH_ERR_EC_CURVE_INVALID -12 | |
#define SSH_ERR_KEY_TYPE_MISMATCH -13 | |
#define SSH_ERR_KEY_TYPE_UNKNOWN -14 /* XXX UNSUPPORTED? */ | |
#define SSH_ERR_EC_CURVE_MISMATCH -15 | |
#define SSH_ERR_EXPECTED_CERT -16 | |
#define SSH_ERR_KEY_LACKS_CERTBLOB -17 | |
#define SSH_ERR_KEY_CERT_UNKNOWN_TYPE -18 | |
#define SSH_ERR_KEY_CERT_INVALID_SIGN_KEY -19 | |
#define SSH_ERR_KEY_INVALID_EC_VALUE -20 | |
#define SSH_ERR_SIGNATURE_INVALID -21 | |
#define SSH_ERR_LIBCRYPTO_ERROR -22 | |
#define SSH_ERR_UNEXPECTED_TRAILING_DATA -23 | |
#define SSH_ERR_SYSTEM_ERROR -24 | |
#define SSH_ERR_KEY_CERT_INVALID -25 | |
#define SSH_ERR_AGENT_COMMUNICATION -26 | |
#define SSH_ERR_AGENT_FAILURE -27 | |
#define SSH_ERR_DH_GEX_OUT_OF_RANGE -28 | |
#define SSH_ERR_DISCONNECTED -29 | |
#define SSH_ERR_MAC_INVALID -30 | |
#define SSH_ERR_NO_CIPHER_ALG_MATCH -31 | |
#define SSH_ERR_NO_MAC_ALG_MATCH -32 | |
#define SSH_ERR_NO_COMPRESS_ALG_MATCH -33 | |
#define SSH_ERR_NO_KEX_ALG_MATCH -34 | |
#define SSH_ERR_NO_HOSTKEY_ALG_MATCH -35 | |
#define SSH_ERR_NO_HOSTKEY_LOADED -36 | |
#define SSH_ERR_PROTOCOL_MISMATCH -37 | |
#define SSH_ERR_NO_PROTOCOL_VERSION -38 | |
#define SSH_ERR_NEED_REKEY -39 | |
#define SSH_ERR_PASSPHRASE_TOO_SHORT -40 | |
#define SSH_ERR_FILE_CHANGED -41 | |
#define SSH_ERR_KEY_UNKNOWN_CIPHER -42 | |
#define SSH_ERR_KEY_WRONG_PASSPHRASE -43 | |
#define SSH_ERR_KEY_BAD_PERMISSIONS -44 | |
#define SSH_ERR_KEY_CERT_MISMATCH -45 | |
#define SSH_ERR_KEY_NOT_FOUND -46 | |
#define SSH_ERR_AGENT_NOT_PRESENT -47 | |
#define SSH_ERR_AGENT_NO_IDENTITIES -48 | |
#define SSH_ERR_BUFFER_READ_ONLY -49 | |
#define SSH_ERR_KRL_BAD_MAGIC -50 | |
#define SSH_ERR_KEY_REVOKED -51 | |
#define SSH_ERR_CONN_CLOSED -52 | |
#define SSH_ERR_CONN_TIMEOUT -53 | |
#define SSH_ERR_CONN_CORRUPT -54 | |
#define SSH_ERR_PROTOCOL_ERROR -55 | |
#define SSH_ERR_KEY_LENGTH -56 | |
#define SSH_ERR_NUMBER_TOO_LARGE -57 | |
#define SSH_ERR_SIGN_ALG_UNSUPPORTED -58 | |
#define ENOMEM 12 | |
#define EINVAL 22 | |
#define __predict_true(exp) ((exp) != 0) | |
#define __predict_false(exp) ((exp) != 0) | |
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) | |
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b)) | |
#define ROUNDUP(x, y) ((((x)+((y)-1))/(y))*(y)) | |
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) | |
#define SIZE_MAX ((unsigned long long)-1) | |
/* # define SSHBUF_ABORT abort */ | |
# define SSHBUF_DEBUG | |
# ifndef SSHBUF_ABORT | |
# define SSHBUF_ABORT() | |
# endif | |
# ifdef SSHBUF_DEBUG | |
# define SSHBUF_TELL(what) do { \ | |
printf("%s:%d %s: %s size %zu alloc %zu off %zu max %zu\n", \ | |
__FILE__, __LINE__, __func__, what, \ | |
buf->size, buf->alloc, buf->off, buf->max_size); \ | |
fflush(stdout); \ | |
} while (0) | |
# define SSHBUF_DBG(x) do { \ | |
printf("%s:%d %s: ", __FILE__, __LINE__, __func__); \ | |
printf x; \ | |
printf("\n"); \ | |
fflush(stdout); \ | |
} while (0) | |
# else | |
# define SSHBUF_TELL(what) | |
# define SSHBUF_DBG(x) | |
# endif | |
/* Skip past a string */ | |
#define sshbuf_skip_string(buf) sshbuf_get_string_direct(buf, NULL, NULL) | |
#define VA_COPY(dest, src) va_copy(dest, src) | |
/* | |
* NB. do not depend on the internals of this. It will be made opaque | |
* one day. | |
*/ | |
struct sshbuf { | |
u_char *d; /* Data */ | |
const u_char *cd; /* Const data */ | |
size_t off; /* First available byte is buf->d + buf->off */ | |
size_t size; /* Last byte is buf->d + buf->size - 1 */ | |
size_t max_size; /* Maximum size of buffer */ | |
size_t alloc; /* Total bytes allocated to buf->d */ | |
int readonly; /* Refers to external, const data */ | |
int dont_free; /* Kludge to support sshbuf_init */ | |
u_int refcount; /* Tracks self and number of child buffers */ | |
struct sshbuf *parent; /* If child, pointer to parent */ | |
}; | |
static inline int | |
sshbuf_check_sanity(const struct sshbuf *buf) | |
{ | |
SSHBUF_TELL("sanity"); | |
if (__predict_false(buf == NULL || | |
(!buf->readonly && buf->d != buf->cd) || | |
buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX || | |
buf->cd == NULL || | |
buf->max_size > SSHBUF_SIZE_MAX || | |
buf->alloc > buf->max_size || | |
buf->size > buf->alloc || | |
buf->off > buf->size)) { | |
/* Do not try to recover from corrupted buffer internals */ | |
SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); | |
//signal(SIGSEGV, SIG_DFL); | |
//raise(SIGSEGV); | |
return SSH_ERR_INTERNAL_ERROR; | |
} | |
return 0; | |
} | |
static void | |
sshbuf_maybe_pack(struct sshbuf *buf, int force) | |
{ | |
SSHBUF_DBG(("force %d", force)); | |
SSHBUF_TELL("pre-pack"); | |
if (buf->off == 0 || buf->readonly || buf->refcount > 1) | |
return; | |
if (force || | |
(buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) { | |
memmove(buf->d, buf->d + buf->off, buf->size - buf->off); | |
buf->size -= buf->off; | |
buf->off = 0; | |
SSHBUF_TELL("packed"); | |
} | |
} | |
static void (* volatile ssh_bzero)(void *, size_t) = bzero; | |
void | |
explicit_bzero(void *p, size_t n) | |
{ | |
if (n == 0) | |
return; | |
/* | |
* clang -fsanitize=memory needs to intercept memset-like functions | |
* to correctly detect memory initialisation. Make sure one is called | |
* directly since our indirection trick above successfully confuses it. | |
*/ | |
memset(p, 0, n); | |
ssh_bzero(p, n); | |
} | |
void * | |
recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) | |
{ | |
int errno; | |
size_t oldsize, newsize; | |
void *newptr; | |
if (ptr == NULL) | |
return calloc(newnmemb, size); | |
if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | |
newnmemb > 0 && SIZE_MAX / newnmemb < size) { | |
errno = ENOMEM; | |
return NULL; | |
} | |
newsize = newnmemb * size; | |
if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && | |
oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { | |
errno = EINVAL; | |
return NULL; | |
} | |
oldsize = oldnmemb * size; | |
/* | |
* Don't bother too much if we're shrinking just a bit, | |
* we do not shrink for series of small steps, oh well. | |
*/ | |
if (newsize <= oldsize) { | |
size_t d = oldsize - newsize; | |
if (d < oldsize / 2 && d < (size_t)getpagesize()) { | |
memset((char *)ptr + newsize, 0, d); | |
return ptr; | |
} | |
} | |
newptr = malloc(newsize); | |
if (newptr == NULL) | |
return NULL; | |
if (newsize > oldsize) { | |
memcpy(newptr, ptr, oldsize); | |
memset((char *)newptr + oldsize, 0, newsize - oldsize); | |
} else | |
memcpy(newptr, ptr, newsize); | |
explicit_bzero(ptr, oldsize); | |
free(ptr); | |
return newptr; | |
} | |
const u_char * | |
sshbuf_ptr(const struct sshbuf *buf) | |
{ | |
if (sshbuf_check_sanity(buf) != 0) | |
return NULL; | |
return buf->cd + buf->off; | |
} | |
int | |
sshbuf_check_reserve(const struct sshbuf *buf, size_t len) | |
{ | |
int r; | |
if ((r = sshbuf_check_sanity(buf)) != 0) | |
return r; | |
if (buf->readonly || buf->refcount > 1) | |
return SSH_ERR_BUFFER_READ_ONLY; | |
SSHBUF_TELL("check"); | |
/* Check that len is reasonable and that max_size + available < len */ | |
if (len > buf->max_size || buf->max_size - len < buf->size - buf->off) | |
return SSH_ERR_NO_BUFFER_SPACE; | |
return 0; | |
} | |
struct sshbuf * | |
sshbuf_new(void) | |
{ | |
struct sshbuf *ret; | |
if ((ret = calloc(sizeof(*ret), 1)) == NULL) | |
return NULL; | |
ret->alloc = SSHBUF_SIZE_INIT; | |
ret->max_size = SSHBUF_SIZE_MAX; | |
ret->readonly = 0; | |
ret->refcount = 1; | |
ret->parent = NULL; | |
if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) { | |
free(ret); | |
return NULL; | |
} | |
return ret; | |
} | |
int | |
sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp) | |
{ | |
u_char *dp; | |
int r; | |
if (dpp != NULL) | |
*dpp = NULL; | |
SSHBUF_DBG(("reserve buf = %p len = %zu", buf, len)); | |
if ((r = sshbuf_allocate(buf, len)) != 0) | |
return r; | |
dp = buf->d + buf->size; | |
buf->size += len; | |
if (dpp != NULL) | |
*dpp = dp; | |
return 0; | |
} | |
size_t | |
sshbuf_len(const struct sshbuf *buf) | |
{ | |
if (sshbuf_check_sanity(buf) != 0) | |
return 0; | |
return buf->size - buf->off; | |
} | |
int | |
sshbuf_allocate(struct sshbuf *buf, size_t len) | |
{ | |
size_t rlen, need; | |
u_char *dp; | |
int r; | |
SSHBUF_DBG(("allocate buf = %p len = %zu", buf, len)); | |
if ((r = sshbuf_check_reserve(buf, len)) != 0) | |
return r; | |
/* | |
* If the requested allocation appended would push us past max_size | |
* then pack the buffer, zeroing buf->off. | |
*/ | |
sshbuf_maybe_pack(buf, buf->size + len > buf->max_size); | |
SSHBUF_TELL("allocate"); | |
if (len + buf->size <= buf->alloc) | |
return 0; /* already have it. */ | |
/* | |
* Prefer to alloc in SSHBUF_SIZE_INC units, but | |
* allocate less if doing so would overflow max_size. | |
*/ | |
need = len + buf->size - buf->alloc; | |
rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC); | |
SSHBUF_DBG(("need %zu initial rlen %zu", need, rlen)); | |
if (rlen > buf->max_size) | |
rlen = buf->alloc + need; | |
SSHBUF_DBG(("adjusted rlen %zu", rlen)); | |
if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) { | |
SSHBUF_DBG(("realloc fail")); | |
return SSH_ERR_ALLOC_FAIL; | |
} | |
buf->alloc = rlen; | |
buf->cd = buf->d = dp; | |
if ((r = sshbuf_check_reserve(buf, len)) < 0) { | |
/* shouldn't fail */ | |
return r; | |
} | |
SSHBUF_TELL("done"); | |
return 0; | |
} | |
int | |
sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) | |
{ | |
u_char *d; | |
int r; | |
if (len > SSHBUF_SIZE_MAX - 4) { | |
SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); | |
return SSH_ERR_NO_BUFFER_SPACE; | |
} | |
if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) | |
return r; | |
POKE_U32(d, len); | |
if (len != 0) | |
memcpy(d + 4, v, len); | |
return 0; | |
} | |
int | |
sshbuf_put_cstring(struct sshbuf *buf, const char *v) | |
{ | |
return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); | |
} | |
int | |
sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) | |
{ | |
return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); | |
} | |
int | |
sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) | |
{ | |
const u_char *val; | |
size_t len; | |
int r; | |
if (valp != NULL) | |
*valp = NULL; | |
if (lenp != NULL) | |
*lenp = 0; | |
if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) | |
return r; | |
if (valp != NULL) { | |
if ((*valp = malloc(len + 1)) == NULL) { | |
SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); | |
return SSH_ERR_ALLOC_FAIL; | |
} | |
if (len != 0) | |
memcpy(*valp, val, len); | |
(*valp)[len] = '\0'; | |
} | |
if (lenp != NULL) | |
*lenp = len; | |
return 0; | |
} | |
int | |
sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) | |
{ | |
size_t len; | |
const u_char *p; | |
int r; | |
if (valp != NULL) | |
*valp = NULL; | |
if (lenp != NULL) | |
*lenp = 0; | |
if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) | |
return r; | |
if (valp != NULL) | |
*valp = p; | |
if (lenp != NULL) | |
*lenp = len; | |
if (sshbuf_consume(buf, len + 4) != 0) { | |
/* Shouldn't happen */ | |
SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); | |
SSHBUF_ABORT(); | |
return SSH_ERR_INTERNAL_ERROR; | |
} | |
return 0; | |
} | |
int | |
sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, | |
size_t *lenp) | |
{ | |
u_int32_t len; | |
const u_char *p = sshbuf_ptr(buf); | |
if (valp != NULL) | |
*valp = NULL; | |
if (lenp != NULL) | |
*lenp = 0; | |
if (sshbuf_len(buf) < 4) { | |
SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); | |
return SSH_ERR_MESSAGE_INCOMPLETE; | |
} | |
len = PEEK_U32(p); | |
if (len > SSHBUF_SIZE_MAX - 4) { | |
SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE")); | |
return SSH_ERR_STRING_TOO_LARGE; | |
} | |
if (sshbuf_len(buf) - 4 < len) { | |
SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); | |
return SSH_ERR_MESSAGE_INCOMPLETE; | |
} | |
if (valp != NULL) | |
*valp = p + 4; | |
if (lenp != NULL) | |
*lenp = len; | |
return 0; | |
} | |
int | |
sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) | |
{ | |
size_t len; | |
const u_char *p, *z; | |
int r; | |
if (valp != NULL) | |
*valp = NULL; | |
if (lenp != NULL) | |
*lenp = 0; | |
if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) | |
return r; | |
/* Allow a \0 only at the end of the string */ | |
if (len > 0 && | |
(z = memchr(p , '\0', len)) != NULL && z < p + len - 1) { | |
SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); | |
return SSH_ERR_INVALID_FORMAT; | |
} | |
if ((r = sshbuf_skip_string(buf)) != 0) | |
return -1; | |
if (valp != NULL) { | |
if ((*valp = malloc(len + 1)) == NULL) { | |
SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); | |
return SSH_ERR_ALLOC_FAIL; | |
} | |
if (len != 0) | |
memcpy(*valp, p, len); | |
(*valp)[len] = '\0'; | |
} | |
if (lenp != NULL) | |
*lenp = (size_t)len; | |
return 0; | |
} | |
int | |
sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v) | |
{ | |
u_int32_t len; | |
u_char *p; | |
int r; | |
/* | |
* Use sshbuf_peek_string_direct() to figure out if there is | |
* a complete string in 'buf' and copy the string directly | |
* into 'v'. | |
*/ | |
if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 || | |
(r = sshbuf_get_u32(buf, &len)) != 0 || | |
(r = sshbuf_reserve(v, len, &p)) != 0 || | |
(r = sshbuf_get(buf, p, len)) != 0) | |
return r; | |
return 0; | |
} | |
int | |
sshbuf_consume(struct sshbuf *buf, size_t len) | |
{ | |
int r; | |
SSHBUF_DBG(("len = %zu", len)); | |
if ((r = sshbuf_check_sanity(buf)) != 0) | |
return r; | |
if (len == 0) | |
return 0; | |
if (len > sshbuf_len(buf)) | |
return SSH_ERR_MESSAGE_INCOMPLETE; | |
buf->off += len; | |
/* deal with empty buffer */ | |
if (buf->off == buf->size) | |
buf->off = buf->size = 0; | |
SSHBUF_TELL("done"); | |
return 0; | |
} | |
int | |
sshbuf_get(struct sshbuf *buf, void *v, size_t len) | |
{ | |
const u_char *p = sshbuf_ptr(buf); | |
int r; | |
if ((r = sshbuf_consume(buf, len)) < 0) | |
return r; | |
if (v != NULL && len != 0) | |
memcpy(v, p, len); | |
return 0; | |
} | |
int | |
sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) | |
{ | |
const u_char *p = sshbuf_ptr(buf); | |
int r; | |
if ((r = sshbuf_consume(buf, 8)) < 0) | |
return r; | |
if (valp != NULL) | |
*valp = PEEK_U64(p); | |
return 0; | |
} | |
int | |
sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) | |
{ | |
const u_char *p = sshbuf_ptr(buf); | |
int r; | |
if ((r = sshbuf_consume(buf, 4)) < 0) | |
return r; | |
if (valp != NULL) | |
*valp = PEEK_U32(p); | |
return 0; | |
} | |
int | |
sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) | |
{ | |
const u_char *p = sshbuf_ptr(buf); | |
int r; | |
if ((r = sshbuf_consume(buf, 2)) < 0) | |
return r; | |
if (valp != NULL) | |
*valp = PEEK_U16(p); | |
return 0; | |
} | |
int | |
sshbuf_get_u8(struct sshbuf *buf, u_char *valp) | |
{ | |
const u_char *p = sshbuf_ptr(buf); | |
int r; | |
if ((r = sshbuf_consume(buf, 1)) < 0) | |
return r; | |
if (valp != NULL) | |
*valp = (u_int8_t)*p; | |
return 0; | |
} | |
int | |
sshbuf_put(struct sshbuf *buf, const void *v, size_t len) | |
{ | |
u_char *p; | |
int r; | |
if ((r = sshbuf_reserve(buf, len, &p)) < 0) | |
return r; | |
if (len != 0) | |
memcpy(p, v, len); | |
return 0; | |
} | |
int | |
sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) | |
{ | |
return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); | |
} | |
int | |
sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) | |
{ | |
va_list ap; | |
int r; | |
va_start(ap, fmt); | |
r = sshbuf_putfv(buf, fmt, ap); | |
va_end(ap); | |
return r; | |
} | |
int | |
sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) | |
{ | |
va_list ap2; | |
int r, len; | |
u_char *p; | |
VA_COPY(ap2, ap); | |
if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { | |
r = SSH_ERR_INVALID_ARGUMENT; | |
goto out; | |
} | |
if (len == 0) { | |
r = 0; | |
goto out; /* Nothing to do */ | |
} | |
va_end(ap2); | |
VA_COPY(ap2, ap); | |
if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) | |
goto out; | |
if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { | |
r = SSH_ERR_INTERNAL_ERROR; | |
goto out; /* Shouldn't happen */ | |
} | |
/* Consume terminating \0 */ | |
if ((r = sshbuf_consume_end(buf, 1)) != 0) | |
goto out; | |
r = 0; | |
out: | |
va_end(ap2); | |
return r; | |
} | |
int | |
sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) | |
{ | |
u_char *p; | |
int r; | |
if ((r = sshbuf_reserve(buf, 8, &p)) < 0) | |
return r; | |
POKE_U64(p, val); | |
return 0; | |
} | |
int | |
sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) | |
{ | |
u_char *p; | |
int r; | |
if ((r = sshbuf_reserve(buf, 4, &p)) < 0) | |
return r; | |
POKE_U32(p, val); | |
return 0; | |
} | |
int | |
sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) | |
{ | |
u_char *p; | |
int r; | |
if ((r = sshbuf_reserve(buf, 2, &p)) < 0) | |
return r; | |
POKE_U16(p, val); | |
return 0; | |
} | |
int | |
sshbuf_put_u8(struct sshbuf *buf, u_char val) | |
{ | |
u_char *p; | |
int r; | |
if ((r = sshbuf_reserve(buf, 1, &p)) < 0) | |
return r; | |
p[0] = val; | |
return 0; | |
} | |
int | |
sshbuf_consume_end(struct sshbuf *buf, size_t len) | |
{ | |
int r; | |
SSHBUF_DBG(("len = %zu", len)); | |
if ((r = sshbuf_check_sanity(buf)) != 0) | |
return r; | |
if (len == 0) | |
return 0; | |
if (len > sshbuf_len(buf)) | |
return SSH_ERR_MESSAGE_INCOMPLETE; | |
buf->size -= len; | |
SSHBUF_TELL("done"); | |
return 0; | |
} | |
void | |
sshbuf_free(struct sshbuf *buf) | |
{ | |
if (buf == NULL) | |
return; | |
/* | |
* The following will leak on insane buffers, but this is the safest | |
* course of action - an invalid pointer or already-freed pointer may | |
* have been passed to us and continuing to scribble over memory would | |
* be bad. | |
*/ | |
if (sshbuf_check_sanity(buf) != 0) | |
return; | |
/* | |
* If we are a child, the free our parent to decrement its reference | |
* count and possibly free it. | |
*/ | |
sshbuf_free(buf->parent); | |
buf->parent = NULL; | |
/* | |
* If we are a parent with still-extant children, then don't free just | |
* yet. The last child's call to sshbuf_free should decrement our | |
* refcount to 0 and trigger the actual free. | |
*/ | |
buf->refcount--; | |
if (buf->refcount > 0) | |
return; | |
if (!buf->readonly) { | |
explicit_bzero(buf->d, buf->alloc); | |
free(buf->d); | |
} | |
explicit_bzero(buf, sizeof(*buf)); | |
free(buf); | |
} | |
void hexDump(char *desc, void *addr, int len) | |
{ | |
int i; | |
unsigned char buff[17]; | |
unsigned char *pc = (unsigned char*)addr; | |
// Output description if given. | |
if (desc != NULL) | |
printf ("%s:\n", desc); | |
// Process every byte in the data. | |
for (i = 0; i < len; i++) { | |
// Multiple of 16 means new line (with line offset). | |
if ((i % 16) == 0) { | |
// Just don't print ASCII for the zeroth line. | |
if (i != 0) | |
printf(" %s\n", buff); | |
// Output the offset. | |
printf(" %04x ", i); | |
} | |
// Now the hex code for the specific character. | |
printf(" %02x", pc[i]); | |
// And store a printable ASCII character for later. | |
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) { | |
buff[i % 16] = '.'; | |
} else { | |
buff[i % 16] = pc[i]; | |
} | |
buff[(i % 16) + 1] = '\0'; | |
} | |
// Pad out last line if not exactly 16 characters. | |
while ((i % 16) != 0) { | |
printf(" "); | |
i++; | |
} | |
// And print the final ASCII bit. | |
printf(" %s\n", buff); | |
} | |
static void | |
put_opt(struct sshbuf *b, const char *name, const char *value) | |
{ | |
struct sshbuf *sect; | |
sect = sshbuf_new(); | |
sshbuf_put_cstring(b, name); | |
if (value != NULL) | |
sshbuf_put_cstring(sect, value); | |
sshbuf_put_stringb(b, sect); | |
sshbuf_free(sect); | |
} | |
int main() | |
{ | |
int i = 666; | |
int ret; | |
struct sshbuf *buf = NULL; | |
buf = sshbuf_new(); | |
printf("Value %d\n", i); | |
u_char nonce[32] = "12345678123456781234567812345678"; | |
printf("Len before %d\n", sshbuf_len(buf) ); | |
char s_1[] = "a"; | |
sshbuf_put_string(buf, s_1 , strlen(s_1)); | |
char s_2[] = "iamalongstring"; | |
sshbuf_put_cstring(buf, s_2 ); | |
u_int32_t n_32 = 0xaaaaaaaaUL; | |
u_int64_t n_64 = 0xffffffffffffffffULL; | |
sshbuf_put_u32 ( buf, n_32); | |
sshbuf_put_u64 ( buf, n_64); | |
struct sshbuf *critical = sshbuf_new(); | |
struct sshbuf *extensions = sshbuf_new(); | |
put_opt(critical, "force-command", "/usr/bin/true"); | |
put_opt(critical, "source-address", "127.0.0.1"); | |
put_opt(extensions, "permit-X11-forwarding", NULL); | |
put_opt(extensions, "permit-agent-forwarding", NULL); | |
sshbuf_put_stringb(buf, critical); | |
sshbuf_put_stringb(buf, extensions); | |
printf("Len after %d\n", sshbuf_len(buf) ); | |
hexDump("cert buffer: ", sshbuf_ptr(buf), sshbuf_len(buf)); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment