1 /* $NetBSD: buffer.c,v 1.2 2011/02/12 23:21:32 christos Exp $ */ 2 3 /* Copyright (c) 2010 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by the NetBSD 17 * Foundation, Inc. and its contributors. 18 * 4. Neither the name of The NetBSD Foundation nor the names of its 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 * POSSIBILITY OF SUCH DAMAGE. 33 */ 34 #include <sys/cdefs.h> 35 __RCSID("$NetBSD: buffer.c,v 1.2 2011/02/12 23:21:32 christos Exp $"); 36 37 #include <sys/param.h> /* for MIN() */ 38 39 #include <assert.h> 40 #include <saslc.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 45 #include "buffer.h" 46 #include "error.h" 47 #include "saslc_private.h" 48 49 /* 50 * XXX: Should we rename saslc__buffer_* and saslc__buffer32_* to 51 * something reflecting their encode and decode, resp, context? 52 */ 53 54 /** 55 * encode buffer context 56 */ 57 struct saslc__buffer_context_t { 58 saslc_sess_t *sess; /* session pointer (for error messages) */ 59 size_t maxbuf; /* allocated length of payload buffer (maxbuf) */ 60 size_t bufneed; /* bytes needed in payload buffer */ 61 62 /* XXX: must be at end */ 63 uint8_t buf[1]; /* payload buffer */ 64 }; 65 66 /** 67 * decode buffer context 68 * 69 * the actual packet looks like: 70 * 71 * struct { 72 * uint8_t size[4]; // length of packet following this (big endian order) 73 * uint8_t payload[]; // variable length payload area 74 * struct { 75 * uint8_t mac_0_9[10]; // truncated MD5_HMAC hash of size and payload 76 * uint8_t version[2]; // always 1 (big endian order) 77 * uint8_t seqnum[4]; // sequence number (big endian order) 78 * } mac __packed; 79 * } __packed 80 */ 81 struct saslc__buffer32_context_t { 82 saslc_sess_t *sess; /* session pointer (for error messages) */ 83 size_t szneed; /* bytes needed in size buffer */ 84 size_t bufsize; /* size of payload buffer */ 85 size_t maxbuf; /* allocated length of payload buffer */ 86 size_t bufneed; /* bytes needed in payload buffer */ 87 88 /* XXX: these must be sequential and at the end! */ 89 uint8_t szbuf[4]; /* size buffer */ 90 uint8_t buf[1]; /* payload buffer */ 91 } __packed; 92 93 /**************************************** 94 * saslc__buffer_* routines. 95 * For fetching unencoded data. 96 */ 97 98 /** 99 * @brief destroy a buffer context 100 * @param ctx context to destroy 101 * @return nothing 102 */ 103 void 104 saslc__buffer_destroy(saslc__buffer_context_t *ctx) 105 { 106 107 free(ctx); 108 } 109 110 /** 111 * @brief create a buffer context 112 * @param sess saslc session 113 * @param maxbuf maximum buffer size 114 * @return buffer context 115 */ 116 saslc__buffer_context_t * 117 saslc__buffer_create(saslc_sess_t *sess, size_t maxbuf) 118 { 119 saslc__buffer_context_t *ctx; 120 size_t buflen; 121 122 buflen = sizeof(*ctx) - sizeof(ctx->buf) + maxbuf; 123 ctx = malloc(buflen); 124 if (ctx == NULL) { 125 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 126 return NULL; 127 } 128 memset(ctx, 0, sizeof(*ctx) - sizeof(ctx->buf)); 129 130 ctx->maxbuf = maxbuf; 131 ctx->bufneed = ctx->maxbuf; 132 ctx->sess = sess; 133 return ctx; 134 } 135 136 /** 137 * @brief fetch a block of data from the input stream. 138 * @param ctx context 139 * @param in input buffer 140 * @param inlen input buffer length 141 * @param out pointer to output buffer 142 * @param outlen pointer to output buffer length 143 * @return number of bytes consumed by the current call, or -1 on 144 * failure. 145 * 146 * NOTE: Output is buffered, so if the return is success and outlen is 147 * zero, then more data is needed to fill the packet. The internal 148 * buffer can be flushed by calling with inlen = 0. 149 */ 150 ssize_t 151 saslc__buffer_fetch(saslc__buffer_context_t *ctx, const uint8_t *in, 152 size_t inlen, uint8_t **out, size_t *outlen) 153 { 154 uint8_t *p; 155 size_t len; 156 157 if (inlen == 0) { /* flush internal buffer */ 158 *outlen = ctx->maxbuf - ctx->bufneed; 159 *out = *outlen != 0 ? ctx->buf : NULL; 160 ctx->bufneed = ctx->maxbuf; /* for next call */ 161 return 0; 162 } 163 164 len = 0; 165 if (ctx->bufneed > 0) { 166 p = ctx->buf + ctx->maxbuf - ctx->bufneed; 167 len = MIN(inlen, ctx->bufneed); 168 memcpy(p, in, len); 169 ctx->bufneed -= len; 170 if (ctx->bufneed > 0) { 171 *out = NULL; 172 *outlen = 0; 173 return len; 174 } 175 *out = ctx->buf; 176 *outlen = ctx->maxbuf; 177 ctx->bufneed = ctx->maxbuf; /* for next call */ 178 return len; 179 } 180 assert(/*CONSTCOND*/0); /* should not happen! */ 181 saslc__error_set(ERR(ctx->sess), ERROR_MECH, "buffer coding error"); 182 *out = NULL; 183 *outlen = 0; 184 ctx->bufneed = ctx->maxbuf; /* for next call */ 185 return -1; 186 } 187 188 /**************************************** 189 * saslc__buffer32_* routines. 190 * For fetching an encoded packet. 191 * The packet is of the form: 192 * struct { 193 * uint8_t size[4]; // bytes in payload 194 * uint8_t payload[]; // packet payload (including any trailing HMAC) 195 * } __packed; 196 */ 197 198 /** 199 * @brief destroy a buffer32 context 200 * @param ctx context to destroy 201 * @return nothing 202 */ 203 void 204 saslc__buffer32_destroy(saslc__buffer32_context_t *ctx) 205 { 206 207 free(ctx); 208 } 209 210 /** 211 * @brief create a buffer32 context 212 * @param sess saslc session 213 * @param maxbuf maximum buffer size 214 * @return buffer context 215 */ 216 saslc__buffer32_context_t * 217 saslc__buffer32_create(saslc_sess_t *sess, size_t maxbuf) 218 { 219 saslc__buffer32_context_t *ctx; 220 size_t buflen; 221 222 buflen = sizeof(*ctx) - sizeof(ctx->buf) + maxbuf; 223 ctx = malloc(buflen); 224 if (ctx == NULL) { 225 saslc__error_set_errno(ERR(sess), ERROR_NOMEM); 226 return NULL; 227 } 228 memset(ctx, 0, sizeof(*ctx) - sizeof(ctx->buf)); 229 230 ctx->maxbuf = maxbuf; 231 ctx->szneed = sizeof(ctx->szbuf); 232 ctx->sess = sess; 233 return ctx; 234 } 235 236 /** 237 * @brief fetch a block of data from the input stream. The block is 238 * prefixed in the stream by a 4 byte length field (in network byte 239 * order). 240 * @param ctx context 241 * @param in input buffer 242 * @param inlen input buffer length 243 * @param out pointer to output buffer 244 * @param outlen pointer to output buffer length 245 * @return number of bytes consumed by the current call on success, 0 246 * if more data is needed, or -1 on failure. 247 */ 248 ssize_t 249 saslc__buffer32_fetch(saslc__buffer32_context_t *ctx, const uint8_t *in, 250 size_t inlen, uint8_t **out, size_t *outlen) 251 { 252 uint8_t *p; 253 size_t ate, len; 254 255 if (inlen == 0) { /* we cannot flush the decode buffer */ 256 saslc__error_set(ERR(ctx->sess), ERROR_BADARG, 257 "bad inlen: cannot flush decode buffer"); 258 return -1; 259 } 260 ate = 0; 261 if (ctx->szneed) { 262 p = ctx->szbuf + sizeof(ctx->szbuf) - ctx->szneed; 263 len = MIN(inlen, ctx->szneed); 264 memcpy(p, in, len); 265 ctx->szneed -= len; 266 ate += len; 267 if (ctx->szneed > 0) 268 goto need_more; 269 270 ctx->bufsize = be32dec(ctx->szbuf); 271 if (ctx->bufsize == 0) { 272 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 273 "pack with no payload"); 274 return -1; 275 } 276 if (ctx->bufsize > ctx->maxbuf) { 277 saslc__error_set(ERR(ctx->sess), ERROR_MECH, 278 "payload longer than maxbuf"); 279 return -1; 280 } 281 in += len; 282 inlen -= len; 283 ctx->bufneed = ctx->bufsize; 284 } 285 if (ctx->bufneed) { 286 p = ctx->buf + ctx->bufsize - ctx->bufneed; 287 len = MIN(inlen, ctx->bufneed); 288 memcpy(p, in, len); 289 ctx->bufneed -= len; 290 ate += len; 291 if (ctx->bufneed > 0) 292 goto need_more; 293 } 294 ctx->szneed = sizeof(ctx->szbuf); /* for next call */ 295 *out = ctx->szbuf; 296 *outlen = sizeof(ctx->szbuf) + ctx->bufsize; 297 return ate; 298 need_more: 299 *out = NULL; 300 *outlen = 0; 301 return ate; 302 } 303