1 /* $OpenBSD: sshbuf-getput-basic.c,v 1.4 2015/01/14 15:02:39 djm Exp $ */ 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 #define SSHBUF_INTERNAL 19 #include "includes.h" 20 21 #include <sys/types.h> 22 #include <stdlib.h> 23 #include <stdio.h> 24 #include <string.h> 25 26 #include "ssherr.h" 27 #include "sshbuf.h" 28 29 int 30 sshbuf_get(struct sshbuf *buf, void *v, size_t len) 31 { 32 const u_char *p = sshbuf_ptr(buf); 33 int r; 34 35 if ((r = sshbuf_consume(buf, len)) < 0) 36 return r; 37 if (v != NULL && len != 0) 38 memcpy(v, p, len); 39 return 0; 40 } 41 42 int 43 sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) 44 { 45 const u_char *p = sshbuf_ptr(buf); 46 int r; 47 48 if ((r = sshbuf_consume(buf, 8)) < 0) 49 return r; 50 if (valp != NULL) 51 *valp = PEEK_U64(p); 52 return 0; 53 } 54 55 int 56 sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) 57 { 58 const u_char *p = sshbuf_ptr(buf); 59 int r; 60 61 if ((r = sshbuf_consume(buf, 4)) < 0) 62 return r; 63 if (valp != NULL) 64 *valp = PEEK_U32(p); 65 return 0; 66 } 67 68 int 69 sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) 70 { 71 const u_char *p = sshbuf_ptr(buf); 72 int r; 73 74 if ((r = sshbuf_consume(buf, 2)) < 0) 75 return r; 76 if (valp != NULL) 77 *valp = PEEK_U16(p); 78 return 0; 79 } 80 81 int 82 sshbuf_get_u8(struct sshbuf *buf, u_char *valp) 83 { 84 const u_char *p = sshbuf_ptr(buf); 85 int r; 86 87 if ((r = sshbuf_consume(buf, 1)) < 0) 88 return r; 89 if (valp != NULL) 90 *valp = (u_int8_t)*p; 91 return 0; 92 } 93 94 int 95 sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) 96 { 97 const u_char *val; 98 size_t len; 99 int r; 100 101 if (valp != NULL) 102 *valp = NULL; 103 if (lenp != NULL) 104 *lenp = 0; 105 if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) 106 return r; 107 if (valp != NULL) { 108 if ((*valp = malloc(len + 1)) == NULL) { 109 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 110 return SSH_ERR_ALLOC_FAIL; 111 } 112 if (len != 0) 113 memcpy(*valp, val, len); 114 (*valp)[len] = '\0'; 115 } 116 if (lenp != NULL) 117 *lenp = len; 118 return 0; 119 } 120 121 int 122 sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) 123 { 124 size_t len; 125 const u_char *p; 126 int r; 127 128 if (valp != NULL) 129 *valp = NULL; 130 if (lenp != NULL) 131 *lenp = 0; 132 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) 133 return r; 134 if (valp != 0) 135 *valp = p; 136 if (lenp != NULL) 137 *lenp = len; 138 if (sshbuf_consume(buf, len + 4) != 0) { 139 /* Shouldn't happen */ 140 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 141 SSHBUF_ABORT(); 142 return SSH_ERR_INTERNAL_ERROR; 143 } 144 return 0; 145 } 146 147 int 148 sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, 149 size_t *lenp) 150 { 151 u_int32_t len; 152 const u_char *p = sshbuf_ptr(buf); 153 154 if (valp != NULL) 155 *valp = NULL; 156 if (lenp != NULL) 157 *lenp = 0; 158 if (sshbuf_len(buf) < 4) { 159 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 160 return SSH_ERR_MESSAGE_INCOMPLETE; 161 } 162 len = PEEK_U32(p); 163 if (len > SSHBUF_SIZE_MAX - 4) { 164 SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE")); 165 return SSH_ERR_STRING_TOO_LARGE; 166 } 167 if (sshbuf_len(buf) - 4 < len) { 168 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 169 return SSH_ERR_MESSAGE_INCOMPLETE; 170 } 171 if (valp != 0) 172 *valp = p + 4; 173 if (lenp != NULL) 174 *lenp = len; 175 return 0; 176 } 177 178 int 179 sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) 180 { 181 size_t len; 182 const u_char *p, *z; 183 int r; 184 185 if (valp != NULL) 186 *valp = NULL; 187 if (lenp != NULL) 188 *lenp = 0; 189 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 190 return r; 191 /* Allow a \0 only at the end of the string */ 192 if (len > 0 && 193 (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) { 194 SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); 195 return SSH_ERR_INVALID_FORMAT; 196 } 197 if ((r = sshbuf_skip_string(buf)) != 0) 198 return -1; 199 if (valp != NULL) { 200 if ((*valp = malloc(len + 1)) == NULL) { 201 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 202 return SSH_ERR_ALLOC_FAIL; 203 } 204 if (len != 0) 205 memcpy(*valp, p, len); 206 (*valp)[len] = '\0'; 207 } 208 if (lenp != NULL) 209 *lenp = (size_t)len; 210 return 0; 211 } 212 213 int 214 sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v) 215 { 216 u_int32_t len; 217 u_char *p; 218 int r; 219 220 /* 221 * Use sshbuf_peek_string_direct() to figure out if there is 222 * a complete string in 'buf' and copy the string directly 223 * into 'v'. 224 */ 225 if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 || 226 (r = sshbuf_get_u32(buf, &len)) != 0 || 227 (r = sshbuf_reserve(v, len, &p)) != 0 || 228 (r = sshbuf_get(buf, p, len)) != 0) 229 return r; 230 return 0; 231 } 232 233 int 234 sshbuf_put(struct sshbuf *buf, const void *v, size_t len) 235 { 236 u_char *p; 237 int r; 238 239 if ((r = sshbuf_reserve(buf, len, &p)) < 0) 240 return r; 241 if (len != 0) 242 memcpy(p, v, len); 243 return 0; 244 } 245 246 int 247 sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) 248 { 249 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); 250 } 251 252 int 253 sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) 254 { 255 va_list ap; 256 int r; 257 258 va_start(ap, fmt); 259 r = sshbuf_putfv(buf, fmt, ap); 260 va_end(ap); 261 return r; 262 } 263 264 int 265 sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) 266 { 267 va_list ap2; 268 int r, len; 269 u_char *p; 270 271 va_copy(ap2, ap); 272 if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { 273 r = SSH_ERR_INVALID_ARGUMENT; 274 goto out; 275 } 276 if (len == 0) { 277 r = 0; 278 goto out; /* Nothing to do */ 279 } 280 va_end(ap2); 281 va_copy(ap2, ap); 282 if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) 283 goto out; 284 if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { 285 r = SSH_ERR_INTERNAL_ERROR; 286 goto out; /* Shouldn't happen */ 287 } 288 /* Consume terminating \0 */ 289 if ((r = sshbuf_consume_end(buf, 1)) != 0) 290 goto out; 291 r = 0; 292 out: 293 va_end(ap2); 294 return r; 295 } 296 297 int 298 sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) 299 { 300 u_char *p; 301 int r; 302 303 if ((r = sshbuf_reserve(buf, 8, &p)) < 0) 304 return r; 305 POKE_U64(p, val); 306 return 0; 307 } 308 309 int 310 sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) 311 { 312 u_char *p; 313 int r; 314 315 if ((r = sshbuf_reserve(buf, 4, &p)) < 0) 316 return r; 317 POKE_U32(p, val); 318 return 0; 319 } 320 321 int 322 sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) 323 { 324 u_char *p; 325 int r; 326 327 if ((r = sshbuf_reserve(buf, 2, &p)) < 0) 328 return r; 329 POKE_U16(p, val); 330 return 0; 331 } 332 333 int 334 sshbuf_put_u8(struct sshbuf *buf, u_char val) 335 { 336 u_char *p; 337 int r; 338 339 if ((r = sshbuf_reserve(buf, 1, &p)) < 0) 340 return r; 341 p[0] = val; 342 return 0; 343 } 344 345 int 346 sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) 347 { 348 u_char *d; 349 int r; 350 351 if (len > SSHBUF_SIZE_MAX - 4) { 352 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 353 return SSH_ERR_NO_BUFFER_SPACE; 354 } 355 if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) 356 return r; 357 POKE_U32(d, len); 358 if (len != 0) 359 memcpy(d + 4, v, len); 360 return 0; 361 } 362 363 int 364 sshbuf_put_cstring(struct sshbuf *buf, const char *v) 365 { 366 return sshbuf_put_string(buf, (u_char *)v, v == NULL ? 0 : strlen(v)); 367 } 368 369 int 370 sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) 371 { 372 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); 373 } 374 375 int 376 sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp) 377 { 378 const u_char *p; 379 size_t len; 380 struct sshbuf *ret; 381 int r; 382 383 if (buf == NULL || bufp == NULL) 384 return SSH_ERR_INVALID_ARGUMENT; 385 *bufp = NULL; 386 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 387 return r; 388 if ((ret = sshbuf_from(p, len)) == NULL) 389 return SSH_ERR_ALLOC_FAIL; 390 if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */ 391 (r = sshbuf_set_parent(ret, buf)) != 0) { 392 sshbuf_free(ret); 393 return r; 394 } 395 *bufp = ret; 396 return 0; 397 } 398 399 int 400 sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len) 401 { 402 u_char *d; 403 const u_char *s = (const u_char *)v; 404 int r, prepend; 405 406 if (len > SSHBUF_SIZE_MAX - 5) { 407 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 408 return SSH_ERR_NO_BUFFER_SPACE; 409 } 410 /* Skip leading zero bytes */ 411 for (; len > 0 && *s == 0; len--, s++) 412 ; 413 /* 414 * If most significant bit is set then prepend a zero byte to 415 * avoid interpretation as a negative number. 416 */ 417 prepend = len > 0 && (s[0] & 0x80) != 0; 418 if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0) 419 return r; 420 POKE_U32(d, len + prepend); 421 if (prepend) 422 d[4] = 0; 423 if (len != 0) 424 memcpy(d + 4 + prepend, s, len); 425 return 0; 426 } 427 428 int 429 sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, 430 const u_char **valp, size_t *lenp) 431 { 432 const u_char *d; 433 size_t len, olen; 434 int r; 435 436 if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0) 437 return r; 438 len = olen; 439 /* Refuse negative (MSB set) bignums */ 440 if ((len != 0 && (*d & 0x80) != 0)) 441 return SSH_ERR_BIGNUM_IS_NEGATIVE; 442 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ 443 if (len > SSHBUF_MAX_BIGNUM + 1 || 444 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) 445 return SSH_ERR_BIGNUM_TOO_LARGE; 446 /* Trim leading zeros */ 447 while (len > 0 && *d == 0x00) { 448 d++; 449 len--; 450 } 451 if (valp != 0) 452 *valp = d; 453 if (lenp != NULL) 454 *lenp = len; 455 if (sshbuf_consume(buf, olen + 4) != 0) { 456 /* Shouldn't happen */ 457 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 458 SSHBUF_ABORT(); 459 return SSH_ERR_INTERNAL_ERROR; 460 } 461 return 0; 462 } 463