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