David Benjamin | 4854ec1 | 2024-12-19 17:33:20 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 3 | * |
David Benjamin | 4854ec1 | 2024-12-19 17:33:20 -0500 | [diff] [blame] | 4 | * Licensed under the OpenSSL license (the "License"). You may not use |
| 5 | * this file except in compliance with the License. You can obtain a copy |
| 6 | * in the file LICENSE in the source distribution or at |
| 7 | * https://www.openssl.org/source/license.html |
| 8 | */ |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 9 | |
| 10 | #include <openssl/bio.h> |
| 11 | |
| 12 | #include <limits.h> |
Adam Langley | 2b2d66d | 2015-01-30 17:08:37 -0800 | [diff] [blame] | 13 | #include <string.h> |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 14 | |
| 15 | #include <openssl/buf.h> |
| 16 | #include <openssl/err.h> |
| 17 | #include <openssl/mem.h> |
| 18 | |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 19 | #include "../internal.h" |
| 20 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 21 | |
David Benjamin | 7ac94aa | 2022-10-24 15:44:37 -0400 | [diff] [blame] | 22 | BIO *BIO_new_mem_buf(const void *buf, ossl_ssize_t len) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 23 | BIO *ret; |
| 24 | BUF_MEM *b; |
| 25 | const size_t size = len < 0 ? strlen((char *)buf) : (size_t)len; |
| 26 | |
| 27 | if (!buf && len != 0) { |
David Benjamin | 3570d73 | 2015-06-29 00:28:17 -0400 | [diff] [blame] | 28 | OPENSSL_PUT_ERROR(BIO, BIO_R_NULL_PARAMETER); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 29 | return NULL; |
| 30 | } |
| 31 | |
| 32 | ret = BIO_new(BIO_s_mem()); |
| 33 | if (ret == NULL) { |
| 34 | return NULL; |
| 35 | } |
| 36 | |
| 37 | b = (BUF_MEM *)ret->ptr; |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 38 | // BIO_FLAGS_MEM_RDONLY ensures |b->data| is not written to. |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 39 | b->data = reinterpret_cast<char *>(const_cast<void *>(buf)); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 40 | b->length = size; |
| 41 | b->max = size; |
| 42 | |
| 43 | ret->flags |= BIO_FLAGS_MEM_RDONLY; |
| 44 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 45 | // |num| is used to store the value that this BIO will return when it runs |
| 46 | // out of data. If it's negative then the retry flags will also be set. Since |
| 47 | // this is static data, retrying wont help |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 48 | ret->num = 0; |
| 49 | |
| 50 | return ret; |
| 51 | } |
| 52 | |
| 53 | static int mem_new(BIO *bio) { |
| 54 | BUF_MEM *b; |
| 55 | |
| 56 | b = BUF_MEM_new(); |
| 57 | if (b == NULL) { |
| 58 | return 0; |
| 59 | } |
| 60 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 61 | // |shutdown| is used to store the close flag: whether the BIO has ownership |
| 62 | // of the BUF_MEM. |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 63 | bio->shutdown = 1; |
| 64 | bio->init = 1; |
| 65 | bio->num = -1; |
| 66 | bio->ptr = (char *)b; |
| 67 | |
| 68 | return 1; |
| 69 | } |
| 70 | |
| 71 | static int mem_free(BIO *bio) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 72 | if (!bio->shutdown || !bio->init || bio->ptr == NULL) { |
| 73 | return 1; |
| 74 | } |
| 75 | |
David Benjamin | 2e68a05 | 2021-08-11 18:14:10 -0400 | [diff] [blame] | 76 | BUF_MEM *b = (BUF_MEM *)bio->ptr; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 77 | if (bio->flags & BIO_FLAGS_MEM_RDONLY) { |
| 78 | b->data = NULL; |
| 79 | } |
| 80 | BUF_MEM_free(b); |
| 81 | bio->ptr = NULL; |
| 82 | return 1; |
| 83 | } |
| 84 | |
| 85 | static int mem_read(BIO *bio, char *out, int outl) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 86 | BIO_clear_retry_flags(bio); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 87 | if (outl <= 0) { |
| 88 | return 0; |
| 89 | } |
| 90 | |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 91 | BUF_MEM *b = reinterpret_cast<BUF_MEM *>(bio->ptr); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 92 | int ret = outl; |
| 93 | if ((size_t)ret > b->length) { |
| 94 | ret = (int)b->length; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 95 | } |
| 96 | |
| 97 | if (ret > 0) { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 98 | OPENSSL_memcpy(out, b->data, ret); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 99 | b->length -= ret; |
| 100 | if (bio->flags & BIO_FLAGS_MEM_RDONLY) { |
| 101 | b->data += ret; |
| 102 | } else { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 103 | OPENSSL_memmove(b->data, &b->data[ret], b->length); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 104 | } |
| 105 | } else if (b->length == 0) { |
| 106 | ret = bio->num; |
| 107 | if (ret != 0) { |
| 108 | BIO_set_retry_read(bio); |
| 109 | } |
| 110 | } |
| 111 | return ret; |
| 112 | } |
| 113 | |
| 114 | static int mem_write(BIO *bio, const char *in, int inl) { |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 115 | BIO_clear_retry_flags(bio); |
| 116 | if (inl <= 0) { |
| 117 | return 0; // Successfully write zero bytes. |
| 118 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 119 | |
| 120 | if (bio->flags & BIO_FLAGS_MEM_RDONLY) { |
David Benjamin | 3570d73 | 2015-06-29 00:28:17 -0400 | [diff] [blame] | 121 | OPENSSL_PUT_ERROR(BIO, BIO_R_WRITE_TO_READ_ONLY_BIO); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 122 | return -1; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 123 | } |
| 124 | |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 125 | BUF_MEM *b = reinterpret_cast<BUF_MEM *>(bio->ptr); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 126 | if (!BUF_MEM_append(b, in, inl)) { |
| 127 | return -1; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 128 | } |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 129 | |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 130 | return inl; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 131 | } |
| 132 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 133 | static int mem_gets(BIO *bio, char *buf, int size) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 134 | BIO_clear_retry_flags(bio); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 135 | if (size <= 0) { |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 136 | return 0; |
| 137 | } |
| 138 | |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 139 | // The buffer size includes space for the trailing NUL, so we can read at most |
| 140 | // one fewer byte. |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 141 | BUF_MEM *b = reinterpret_cast<BUF_MEM *>(bio->ptr); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 142 | int ret = size - 1; |
| 143 | if ((size_t)ret > b->length) { |
| 144 | ret = (int)b->length; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 145 | } |
| 146 | |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 147 | // Stop at the first newline. |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 148 | const char *newline = |
| 149 | reinterpret_cast<char *>(OPENSSL_memchr(b->data, '\n', ret)); |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 150 | if (newline != NULL) { |
| 151 | ret = (int)(newline - b->data + 1); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 152 | } |
David Benjamin | e8b168d | 2023-04-01 17:23:30 +0900 | [diff] [blame] | 153 | |
| 154 | ret = mem_read(bio, buf, ret); |
| 155 | if (ret >= 0) { |
| 156 | buf[ret] = '\0'; |
| 157 | } |
| 158 | return ret; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 159 | } |
| 160 | |
| 161 | static long mem_ctrl(BIO *bio, int cmd, long num, void *ptr) { |
| 162 | long ret = 1; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 163 | |
| 164 | BUF_MEM *b = (BUF_MEM *)bio->ptr; |
| 165 | |
| 166 | switch (cmd) { |
| 167 | case BIO_CTRL_RESET: |
| 168 | if (b->data != NULL) { |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 169 | // For read only case reset to the start again |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 170 | if (bio->flags & BIO_FLAGS_MEM_RDONLY) { |
| 171 | b->data -= b->max - b->length; |
| 172 | b->length = b->max; |
| 173 | } else { |
David Benjamin | 17cf2cb | 2016-12-13 01:07:13 -0500 | [diff] [blame] | 174 | OPENSSL_memset(b->data, 0, b->max); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 175 | b->length = 0; |
| 176 | } |
| 177 | } |
| 178 | break; |
| 179 | case BIO_CTRL_EOF: |
| 180 | ret = (long)(b->length == 0); |
| 181 | break; |
| 182 | case BIO_C_SET_BUF_MEM_EOF_RETURN: |
| 183 | bio->num = (int)num; |
| 184 | break; |
| 185 | case BIO_CTRL_INFO: |
| 186 | ret = (long)b->length; |
| 187 | if (ptr != NULL) { |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 188 | char **pptr = reinterpret_cast<char **>(ptr); |
David Benjamin | 8e16c04 | 2023-06-08 11:03:59 -0400 | [diff] [blame] | 189 | *pptr = b->data; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 190 | } |
| 191 | break; |
| 192 | case BIO_C_SET_BUF_MEM: |
| 193 | mem_free(bio); |
| 194 | bio->shutdown = (int)num; |
| 195 | bio->ptr = ptr; |
| 196 | break; |
| 197 | case BIO_C_GET_BUF_MEM_PTR: |
| 198 | if (ptr != NULL) { |
Adam Langley | 5813c2c | 2024-10-30 14:48:00 -0700 | [diff] [blame] | 199 | BUF_MEM **pptr = reinterpret_cast<BUF_MEM **>(ptr); |
David Benjamin | 8e16c04 | 2023-06-08 11:03:59 -0400 | [diff] [blame] | 200 | *pptr = b; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 201 | } |
| 202 | break; |
| 203 | case BIO_CTRL_GET_CLOSE: |
| 204 | ret = (long)bio->shutdown; |
| 205 | break; |
| 206 | case BIO_CTRL_SET_CLOSE: |
| 207 | bio->shutdown = (int)num; |
| 208 | break; |
| 209 | |
| 210 | case BIO_CTRL_WPENDING: |
| 211 | ret = 0L; |
| 212 | break; |
| 213 | case BIO_CTRL_PENDING: |
| 214 | ret = (long)b->length; |
| 215 | break; |
| 216 | case BIO_CTRL_FLUSH: |
| 217 | ret = 1; |
| 218 | break; |
| 219 | default: |
| 220 | ret = 0; |
| 221 | break; |
| 222 | } |
| 223 | return ret; |
| 224 | } |
| 225 | |
| 226 | static const BIO_METHOD mem_method = { |
David Benjamin | 6342111 | 2017-01-26 21:50:04 -0500 | [diff] [blame] | 227 | BIO_TYPE_MEM, "memory buffer", |
| 228 | mem_write, mem_read, |
| 229 | NULL /* puts */, mem_gets, |
| 230 | mem_ctrl, mem_new, |
| 231 | mem_free, NULL /* callback_ctrl */, |
| 232 | }; |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 233 | |
| 234 | const BIO_METHOD *BIO_s_mem(void) { return &mem_method; } |
| 235 | |
Adam Langley | 37a623c | 2014-07-18 09:28:40 -0700 | [diff] [blame] | 236 | int BIO_mem_contents(const BIO *bio, const uint8_t **out_contents, |
| 237 | size_t *out_len) { |
| 238 | const BUF_MEM *b; |
| 239 | if (bio->method != &mem_method) { |
| 240 | return 0; |
| 241 | } |
| 242 | |
| 243 | b = (BUF_MEM *)bio->ptr; |
| 244 | *out_contents = (uint8_t *)b->data; |
| 245 | *out_len = b->length; |
| 246 | return 1; |
| 247 | } |
| 248 | |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 249 | long BIO_get_mem_data(BIO *bio, char **contents) { |
David Benjamin | 8e16c04 | 2023-06-08 11:03:59 -0400 | [diff] [blame] | 250 | return BIO_ctrl(bio, BIO_CTRL_INFO, 0, contents); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 251 | } |
| 252 | |
| 253 | int BIO_get_mem_ptr(BIO *bio, BUF_MEM **out) { |
David Benjamin | 8e16c04 | 2023-06-08 11:03:59 -0400 | [diff] [blame] | 254 | return (int)BIO_ctrl(bio, BIO_C_GET_BUF_MEM_PTR, 0, out); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 255 | } |
| 256 | |
| 257 | int BIO_set_mem_buf(BIO *bio, BUF_MEM *b, int take_ownership) { |
David Benjamin | 8e16c04 | 2023-06-08 11:03:59 -0400 | [diff] [blame] | 258 | return (int)BIO_ctrl(bio, BIO_C_SET_BUF_MEM, take_ownership, b); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | int BIO_set_mem_eof_return(BIO *bio, int eof_value) { |
David Benjamin | 02f7705 | 2022-11-23 13:00:33 -0500 | [diff] [blame] | 262 | return (int)BIO_ctrl(bio, BIO_C_SET_BUF_MEM_EOF_RETURN, eof_value, NULL); |
Adam Langley | 95c29f3 | 2014-06-20 12:00:00 -0700 | [diff] [blame] | 263 | } |