[BACK]Return to sshbuf.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / ssh

Annotation of src/usr.bin/ssh/sshbuf.c, Revision 1.17

1.17    ! djm         1: /*     $OpenBSD: sshbuf.c,v 1.16 2022/04/08 04:40:40 djm Exp $ */
1.1       djm         2: /*
                      3:  * Copyright (c) 2011 Damien Miller
                      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 <signal.h>
                     20: #include <stdlib.h>
                     21: #include <stdio.h>
                     22: #include <string.h>
                     23:
                     24: #include "ssherr.h"
                     25: #define SSHBUF_INTERNAL
                     26: #include "sshbuf.h"
1.7       deraadt    27: #include "misc.h"
1.1       djm        28:
                     29: static inline int
                     30: sshbuf_check_sanity(const struct sshbuf *buf)
                     31: {
                     32:        SSHBUF_TELL("sanity");
                     33:        if (__predict_false(buf == NULL ||
                     34:            (!buf->readonly && buf->d != buf->cd) ||
                     35:            buf->refcount < 1 || buf->refcount > SSHBUF_REFS_MAX ||
                     36:            buf->cd == NULL ||
                     37:            buf->max_size > SSHBUF_SIZE_MAX ||
                     38:            buf->alloc > buf->max_size ||
                     39:            buf->size > buf->alloc ||
                     40:            buf->off > buf->size)) {
                     41:                /* Do not try to recover from corrupted buffer internals */
1.17    ! djm        42:                SSHBUF_DBG("SSH_ERR_INTERNAL_ERROR");
1.14      dtucker    43:                ssh_signal(SIGSEGV, SIG_DFL);
1.1       djm        44:                raise(SIGSEGV);
                     45:                return SSH_ERR_INTERNAL_ERROR;
                     46:        }
                     47:        return 0;
                     48: }
                     49:
                     50: static void
                     51: sshbuf_maybe_pack(struct sshbuf *buf, int force)
                     52: {
1.17    ! djm        53:        SSHBUF_DBG("force %d", force);
1.1       djm        54:        SSHBUF_TELL("pre-pack");
                     55:        if (buf->off == 0 || buf->readonly || buf->refcount > 1)
                     56:                return;
                     57:        if (force ||
                     58:            (buf->off >= SSHBUF_PACK_MIN && buf->off >= buf->size / 2)) {
                     59:                memmove(buf->d, buf->d + buf->off, buf->size - buf->off);
                     60:                buf->size -= buf->off;
                     61:                buf->off = 0;
                     62:                SSHBUF_TELL("packed");
                     63:        }
                     64: }
                     65:
                     66: struct sshbuf *
                     67: sshbuf_new(void)
                     68: {
                     69:        struct sshbuf *ret;
                     70:
                     71:        if ((ret = calloc(sizeof(*ret), 1)) == NULL)
                     72:                return NULL;
                     73:        ret->alloc = SSHBUF_SIZE_INIT;
                     74:        ret->max_size = SSHBUF_SIZE_MAX;
                     75:        ret->readonly = 0;
                     76:        ret->refcount = 1;
                     77:        ret->parent = NULL;
                     78:        if ((ret->cd = ret->d = calloc(1, ret->alloc)) == NULL) {
                     79:                free(ret);
                     80:                return NULL;
                     81:        }
                     82:        return ret;
                     83: }
                     84:
                     85: struct sshbuf *
                     86: sshbuf_from(const void *blob, size_t len)
                     87: {
                     88:        struct sshbuf *ret;
                     89:
                     90:        if (blob == NULL || len > SSHBUF_SIZE_MAX ||
                     91:            (ret = calloc(sizeof(*ret), 1)) == NULL)
                     92:                return NULL;
                     93:        ret->alloc = ret->size = ret->max_size = len;
                     94:        ret->readonly = 1;
                     95:        ret->refcount = 1;
                     96:        ret->parent = NULL;
                     97:        ret->cd = blob;
                     98:        ret->d = NULL;
                     99:        return ret;
                    100: }
                    101:
                    102: int
                    103: sshbuf_set_parent(struct sshbuf *child, struct sshbuf *parent)
                    104: {
                    105:        int r;
                    106:
                    107:        if ((r = sshbuf_check_sanity(child)) != 0 ||
                    108:            (r = sshbuf_check_sanity(parent)) != 0)
                    109:                return r;
1.16      djm       110:        if (child->parent != NULL && child->parent != parent)
                    111:                return SSH_ERR_INTERNAL_ERROR;
1.1       djm       112:        child->parent = parent;
                    113:        child->parent->refcount++;
                    114:        return 0;
                    115: }
                    116:
                    117: struct sshbuf *
                    118: sshbuf_fromb(struct sshbuf *buf)
                    119: {
                    120:        struct sshbuf *ret;
                    121:
                    122:        if (sshbuf_check_sanity(buf) != 0)
                    123:                return NULL;
                    124:        if ((ret = sshbuf_from(sshbuf_ptr(buf), sshbuf_len(buf))) == NULL)
                    125:                return NULL;
                    126:        if (sshbuf_set_parent(ret, buf) != 0) {
                    127:                sshbuf_free(ret);
                    128:                return NULL;
                    129:        }
                    130:        return ret;
                    131: }
                    132:
                    133: void
                    134: sshbuf_free(struct sshbuf *buf)
                    135: {
                    136:        if (buf == NULL)
                    137:                return;
                    138:        /*
                    139:         * The following will leak on insane buffers, but this is the safest
                    140:         * course of action - an invalid pointer or already-freed pointer may
                    141:         * have been passed to us and continuing to scribble over memory would
                    142:         * be bad.
                    143:         */
                    144:        if (sshbuf_check_sanity(buf) != 0)
                    145:                return;
1.13      djm       146:
1.1       djm       147:        /*
                    148:         * If we are a parent with still-extant children, then don't free just
                    149:         * yet. The last child's call to sshbuf_free should decrement our
                    150:         * refcount to 0 and trigger the actual free.
                    151:         */
                    152:        buf->refcount--;
                    153:        if (buf->refcount > 0)
                    154:                return;
1.13      djm       155:
                    156:        /*
                    157:         * If we are a child, the free our parent to decrement its reference
                    158:         * count and possibly free it.
                    159:         */
                    160:        sshbuf_free(buf->parent);
                    161:        buf->parent = NULL;
                    162:
1.1       djm       163:        if (!buf->readonly) {
1.4       djm       164:                explicit_bzero(buf->d, buf->alloc);
1.1       djm       165:                free(buf->d);
                    166:        }
1.15      jsg       167:        freezero(buf, sizeof(*buf));
1.1       djm       168: }
                    169:
                    170: void
                    171: sshbuf_reset(struct sshbuf *buf)
                    172: {
                    173:        u_char *d;
                    174:
                    175:        if (buf->readonly || buf->refcount > 1) {
                    176:                /* Nonsensical. Just make buffer appear empty */
                    177:                buf->off = buf->size;
                    178:                return;
                    179:        }
1.16      djm       180:        if (sshbuf_check_sanity(buf) != 0)
                    181:                return;
1.1       djm       182:        buf->off = buf->size = 0;
                    183:        if (buf->alloc != SSHBUF_SIZE_INIT) {
1.10      deraadt   184:                if ((d = recallocarray(buf->d, buf->alloc, SSHBUF_SIZE_INIT,
                    185:                    1)) != NULL) {
1.1       djm       186:                        buf->cd = buf->d = d;
                    187:                        buf->alloc = SSHBUF_SIZE_INIT;
                    188:                }
1.11      djm       189:        }
1.16      djm       190:        explicit_bzero(buf->d, buf->alloc);
1.1       djm       191: }
                    192:
                    193: size_t
                    194: sshbuf_max_size(const struct sshbuf *buf)
                    195: {
                    196:        return buf->max_size;
                    197: }
                    198:
                    199: size_t
                    200: sshbuf_alloc(const struct sshbuf *buf)
                    201: {
                    202:        return buf->alloc;
                    203: }
                    204:
                    205: const struct sshbuf *
                    206: sshbuf_parent(const struct sshbuf *buf)
                    207: {
                    208:        return buf->parent;
                    209: }
                    210:
                    211: u_int
                    212: sshbuf_refcount(const struct sshbuf *buf)
                    213: {
                    214:        return buf->refcount;
                    215: }
                    216:
                    217: int
                    218: sshbuf_set_max_size(struct sshbuf *buf, size_t max_size)
                    219: {
                    220:        size_t rlen;
                    221:        u_char *dp;
                    222:        int r;
                    223:
1.17    ! djm       224:        SSHBUF_DBG("set max buf = %p len = %zu", buf, max_size);
1.1       djm       225:        if ((r = sshbuf_check_sanity(buf)) != 0)
                    226:                return r;
                    227:        if (max_size == buf->max_size)
                    228:                return 0;
                    229:        if (buf->readonly || buf->refcount > 1)
                    230:                return SSH_ERR_BUFFER_READ_ONLY;
                    231:        if (max_size > SSHBUF_SIZE_MAX)
                    232:                return SSH_ERR_NO_BUFFER_SPACE;
                    233:        /* pack and realloc if necessary */
                    234:        sshbuf_maybe_pack(buf, max_size < buf->size);
                    235:        if (max_size < buf->alloc && max_size > buf->size) {
                    236:                if (buf->size < SSHBUF_SIZE_INIT)
                    237:                        rlen = SSHBUF_SIZE_INIT;
                    238:                else
1.7       deraadt   239:                        rlen = ROUNDUP(buf->size, SSHBUF_SIZE_INC);
1.1       djm       240:                if (rlen > max_size)
                    241:                        rlen = max_size;
1.17    ! djm       242:                SSHBUF_DBG("new alloc = %zu", rlen);
1.10      deraadt   243:                if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL)
1.1       djm       244:                        return SSH_ERR_ALLOC_FAIL;
                    245:                buf->cd = buf->d = dp;
                    246:                buf->alloc = rlen;
                    247:        }
                    248:        SSHBUF_TELL("new-max");
                    249:        if (max_size < buf->alloc)
                    250:                return SSH_ERR_NO_BUFFER_SPACE;
                    251:        buf->max_size = max_size;
                    252:        return 0;
                    253: }
                    254:
                    255: size_t
                    256: sshbuf_len(const struct sshbuf *buf)
                    257: {
                    258:        if (sshbuf_check_sanity(buf) != 0)
                    259:                return 0;
                    260:        return buf->size - buf->off;
                    261: }
                    262:
                    263: size_t
                    264: sshbuf_avail(const struct sshbuf *buf)
                    265: {
                    266:        if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
                    267:                return 0;
                    268:        return buf->max_size - (buf->size - buf->off);
                    269: }
                    270:
                    271: const u_char *
                    272: sshbuf_ptr(const struct sshbuf *buf)
                    273: {
                    274:        if (sshbuf_check_sanity(buf) != 0)
                    275:                return NULL;
                    276:        return buf->cd + buf->off;
                    277: }
                    278:
                    279: u_char *
                    280: sshbuf_mutable_ptr(const struct sshbuf *buf)
                    281: {
                    282:        if (sshbuf_check_sanity(buf) != 0 || buf->readonly || buf->refcount > 1)
                    283:                return NULL;
                    284:        return buf->d + buf->off;
                    285: }
                    286:
                    287: int
                    288: sshbuf_check_reserve(const struct sshbuf *buf, size_t len)
                    289: {
                    290:        int r;
                    291:
                    292:        if ((r = sshbuf_check_sanity(buf)) != 0)
                    293:                return r;
                    294:        if (buf->readonly || buf->refcount > 1)
                    295:                return SSH_ERR_BUFFER_READ_ONLY;
                    296:        SSHBUF_TELL("check");
                    297:        /* Check that len is reasonable and that max_size + available < len */
                    298:        if (len > buf->max_size || buf->max_size - len < buf->size - buf->off)
                    299:                return SSH_ERR_NO_BUFFER_SPACE;
                    300:        return 0;
                    301: }
                    302:
                    303: int
1.8       djm       304: sshbuf_allocate(struct sshbuf *buf, size_t len)
1.1       djm       305: {
                    306:        size_t rlen, need;
                    307:        u_char *dp;
                    308:        int r;
                    309:
1.17    ! djm       310:        SSHBUF_DBG("allocate buf = %p len = %zu", buf, len);
1.1       djm       311:        if ((r = sshbuf_check_reserve(buf, len)) != 0)
                    312:                return r;
                    313:        /*
                    314:         * If the requested allocation appended would push us past max_size
                    315:         * then pack the buffer, zeroing buf->off.
                    316:         */
                    317:        sshbuf_maybe_pack(buf, buf->size + len > buf->max_size);
1.8       djm       318:        SSHBUF_TELL("allocate");
                    319:        if (len + buf->size <= buf->alloc)
                    320:                return 0; /* already have it. */
                    321:
                    322:        /*
                    323:         * Prefer to alloc in SSHBUF_SIZE_INC units, but
                    324:         * allocate less if doing so would overflow max_size.
                    325:         */
                    326:        need = len + buf->size - buf->alloc;
                    327:        rlen = ROUNDUP(buf->alloc + need, SSHBUF_SIZE_INC);
1.17    ! djm       328:        SSHBUF_DBG("need %zu initial rlen %zu", need, rlen);
1.8       djm       329:        if (rlen > buf->max_size)
                    330:                rlen = buf->alloc + need;
1.17    ! djm       331:        SSHBUF_DBG("adjusted rlen %zu", rlen);
1.10      deraadt   332:        if ((dp = recallocarray(buf->d, buf->alloc, rlen, 1)) == NULL) {
1.17    ! djm       333:                SSHBUF_DBG("realloc fail");
1.8       djm       334:                return SSH_ERR_ALLOC_FAIL;
                    335:        }
                    336:        buf->alloc = rlen;
                    337:        buf->cd = buf->d = dp;
                    338:        if ((r = sshbuf_check_reserve(buf, len)) < 0) {
                    339:                /* shouldn't fail */
                    340:                return r;
1.1       djm       341:        }
1.8       djm       342:        SSHBUF_TELL("done");
                    343:        return 0;
                    344: }
                    345:
                    346: int
                    347: sshbuf_reserve(struct sshbuf *buf, size_t len, u_char **dpp)
                    348: {
                    349:        u_char *dp;
                    350:        int r;
                    351:
                    352:        if (dpp != NULL)
                    353:                *dpp = NULL;
                    354:
1.17    ! djm       355:        SSHBUF_DBG("reserve buf = %p len = %zu", buf, len);
1.8       djm       356:        if ((r = sshbuf_allocate(buf, len)) != 0)
                    357:                return r;
                    358:
1.1       djm       359:        dp = buf->d + buf->size;
                    360:        buf->size += len;
                    361:        if (dpp != NULL)
                    362:                *dpp = dp;
                    363:        return 0;
                    364: }
                    365:
                    366: int
                    367: sshbuf_consume(struct sshbuf *buf, size_t len)
                    368: {
                    369:        int r;
                    370:
1.17    ! djm       371:        SSHBUF_DBG("len = %zu", len);
1.1       djm       372:        if ((r = sshbuf_check_sanity(buf)) != 0)
                    373:                return r;
                    374:        if (len == 0)
                    375:                return 0;
                    376:        if (len > sshbuf_len(buf))
                    377:                return SSH_ERR_MESSAGE_INCOMPLETE;
                    378:        buf->off += len;
1.9       markus    379:        /* deal with empty buffer */
                    380:        if (buf->off == buf->size)
                    381:                buf->off = buf->size = 0;
1.1       djm       382:        SSHBUF_TELL("done");
                    383:        return 0;
                    384: }
                    385:
                    386: int
                    387: sshbuf_consume_end(struct sshbuf *buf, size_t len)
                    388: {
                    389:        int r;
                    390:
1.17    ! djm       391:        SSHBUF_DBG("len = %zu", len);
1.1       djm       392:        if ((r = sshbuf_check_sanity(buf)) != 0)
                    393:                return r;
                    394:        if (len == 0)
                    395:                return 0;
                    396:        if (len > sshbuf_len(buf))
                    397:                return SSH_ERR_MESSAGE_INCOMPLETE;
                    398:        buf->size -= len;
                    399:        SSHBUF_TELL("done");
                    400:        return 0;
                    401: }
                    402: