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