[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.3

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