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

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