1 /* $OpenBSD: sshbuf-getput-basic.c,v 1.7 2017/06/01 04:51:58 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 23 #include <stdarg.h> 24 #include <stdlib.h> 25 #include <stdio.h> 26 #include <string.h> 27 28 #include "xmalloc.h" 29 #include "ssherr.h" 30 #include "sshbuf.h" 31 32 int 33 sshbuf_get(struct sshbuf *buf, void *v, size_t len) 34 { 35 const u_char *p = sshbuf_ptr(buf); 36 int r; 37 38 if ((r = sshbuf_consume(buf, len)) < 0) 39 return r; 40 if (v != NULL && len != 0) 41 memcpy(v, p, len); 42 return 0; 43 } 44 45 int 46 sshbuf_get_u64(struct sshbuf *buf, u_int64_t *valp) 47 { 48 const u_char *p = sshbuf_ptr(buf); 49 int r; 50 51 if ((r = sshbuf_consume(buf, 8)) < 0) 52 return r; 53 if (valp != NULL) 54 *valp = PEEK_U64(p); 55 return 0; 56 } 57 58 int 59 sshbuf_get_u32(struct sshbuf *buf, u_int32_t *valp) 60 { 61 const u_char *p = sshbuf_ptr(buf); 62 int r; 63 64 if ((r = sshbuf_consume(buf, 4)) < 0) 65 return r; 66 if (valp != NULL) 67 *valp = PEEK_U32(p); 68 return 0; 69 } 70 71 int 72 sshbuf_get_u16(struct sshbuf *buf, u_int16_t *valp) 73 { 74 const u_char *p = sshbuf_ptr(buf); 75 int r; 76 77 if ((r = sshbuf_consume(buf, 2)) < 0) 78 return r; 79 if (valp != NULL) 80 *valp = PEEK_U16(p); 81 return 0; 82 } 83 84 int 85 sshbuf_get_u8(struct sshbuf *buf, u_char *valp) 86 { 87 const u_char *p = sshbuf_ptr(buf); 88 int r; 89 90 if ((r = sshbuf_consume(buf, 1)) < 0) 91 return r; 92 if (valp != NULL) 93 *valp = (u_int8_t)*p; 94 return 0; 95 } 96 97 int 98 sshbuf_get_string(struct sshbuf *buf, u_char **valp, size_t *lenp) 99 { 100 const u_char *val; 101 size_t len; 102 int r; 103 104 if (valp != NULL) 105 *valp = NULL; 106 if (lenp != NULL) 107 *lenp = 0; 108 if ((r = sshbuf_get_string_direct(buf, &val, &len)) < 0) 109 return r; 110 if (valp != NULL) { 111 if ((*valp = malloc(len + 1)) == NULL) { 112 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 113 return SSH_ERR_ALLOC_FAIL; 114 } 115 if (len != 0) 116 memcpy(*valp, val, len); 117 (*valp)[len] = '\0'; 118 } 119 if (lenp != NULL) 120 *lenp = len; 121 return 0; 122 } 123 124 int 125 sshbuf_get_string_direct(struct sshbuf *buf, const u_char **valp, size_t *lenp) 126 { 127 size_t len; 128 const u_char *p; 129 int r; 130 131 if (valp != NULL) 132 *valp = NULL; 133 if (lenp != NULL) 134 *lenp = 0; 135 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) < 0) 136 return r; 137 if (valp != NULL) 138 *valp = p; 139 if (lenp != NULL) 140 *lenp = len; 141 if (sshbuf_consume(buf, len + 4) != 0) { 142 /* Shouldn't happen */ 143 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 144 SSHBUF_ABORT(); 145 return SSH_ERR_INTERNAL_ERROR; 146 } 147 return 0; 148 } 149 150 int 151 sshbuf_peek_string_direct(const struct sshbuf *buf, const u_char **valp, 152 size_t *lenp) 153 { 154 u_int32_t len; 155 const u_char *p = sshbuf_ptr(buf); 156 157 if (valp != NULL) 158 *valp = NULL; 159 if (lenp != NULL) 160 *lenp = 0; 161 if (sshbuf_len(buf) < 4) { 162 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 163 return SSH_ERR_MESSAGE_INCOMPLETE; 164 } 165 len = PEEK_U32(p); 166 if (len > SSHBUF_SIZE_MAX - 4) { 167 SSHBUF_DBG(("SSH_ERR_STRING_TOO_LARGE")); 168 return SSH_ERR_STRING_TOO_LARGE; 169 } 170 if (sshbuf_len(buf) - 4 < len) { 171 SSHBUF_DBG(("SSH_ERR_MESSAGE_INCOMPLETE")); 172 return SSH_ERR_MESSAGE_INCOMPLETE; 173 } 174 if (valp != NULL) 175 *valp = p + 4; 176 if (lenp != NULL) 177 *lenp = len; 178 return 0; 179 } 180 181 int 182 sshbuf_get_cstring(struct sshbuf *buf, char **valp, size_t *lenp) 183 { 184 size_t len; 185 const u_char *p, *z; 186 int r; 187 188 if (valp != NULL) 189 *valp = NULL; 190 if (lenp != NULL) 191 *lenp = 0; 192 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 193 return r; 194 /* Allow a \0 only at the end of the string */ 195 if (len > 0 && 196 (z = memchr(p , '\0', len)) != NULL && z < p + len - 1) { 197 SSHBUF_DBG(("SSH_ERR_INVALID_FORMAT")); 198 return SSH_ERR_INVALID_FORMAT; 199 } 200 if ((r = sshbuf_skip_string(buf)) != 0) 201 return -1; 202 if (valp != NULL) { 203 if ((*valp = malloc(len + 1)) == NULL) { 204 SSHBUF_DBG(("SSH_ERR_ALLOC_FAIL")); 205 return SSH_ERR_ALLOC_FAIL; 206 } 207 if (len != 0) 208 memcpy(*valp, p, len); 209 (*valp)[len] = '\0'; 210 } 211 if (lenp != NULL) 212 *lenp = (size_t)len; 213 return 0; 214 } 215 216 int 217 sshbuf_get_stringb(struct sshbuf *buf, struct sshbuf *v) 218 { 219 u_int32_t len; 220 u_char *p; 221 int r; 222 223 /* 224 * Use sshbuf_peek_string_direct() to figure out if there is 225 * a complete string in 'buf' and copy the string directly 226 * into 'v'. 227 */ 228 if ((r = sshbuf_peek_string_direct(buf, NULL, NULL)) != 0 || 229 (r = sshbuf_get_u32(buf, &len)) != 0 || 230 (r = sshbuf_reserve(v, len, &p)) != 0 || 231 (r = sshbuf_get(buf, p, len)) != 0) 232 return r; 233 return 0; 234 } 235 236 int 237 sshbuf_put(struct sshbuf *buf, const void *v, size_t len) 238 { 239 u_char *p; 240 int r; 241 242 if ((r = sshbuf_reserve(buf, len, &p)) < 0) 243 return r; 244 if (len != 0) 245 memcpy(p, v, len); 246 return 0; 247 } 248 249 int 250 sshbuf_putb(struct sshbuf *buf, const struct sshbuf *v) 251 { 252 return sshbuf_put(buf, sshbuf_ptr(v), sshbuf_len(v)); 253 } 254 255 int 256 sshbuf_putf(struct sshbuf *buf, const char *fmt, ...) 257 { 258 va_list ap; 259 int r; 260 261 va_start(ap, fmt); 262 r = sshbuf_putfv(buf, fmt, ap); 263 va_end(ap); 264 return r; 265 } 266 267 int 268 sshbuf_putfv(struct sshbuf *buf, const char *fmt, va_list ap) 269 { 270 va_list ap2; 271 int r, len; 272 u_char *p; 273 274 VA_COPY(ap2, ap); 275 if ((len = vsnprintf(NULL, 0, fmt, ap2)) < 0) { 276 r = SSH_ERR_INVALID_ARGUMENT; 277 goto out; 278 } 279 if (len == 0) { 280 r = 0; 281 goto out; /* Nothing to do */ 282 } 283 va_end(ap2); 284 VA_COPY(ap2, ap); 285 if ((r = sshbuf_reserve(buf, (size_t)len + 1, &p)) < 0) 286 goto out; 287 if ((r = vsnprintf((char *)p, len + 1, fmt, ap2)) != len) { 288 r = SSH_ERR_INTERNAL_ERROR; 289 goto out; /* Shouldn't happen */ 290 } 291 /* Consume terminating \0 */ 292 if ((r = sshbuf_consume_end(buf, 1)) != 0) 293 goto out; 294 r = 0; 295 out: 296 va_end(ap2); 297 return r; 298 } 299 300 int 301 sshbuf_put_u64(struct sshbuf *buf, u_int64_t val) 302 { 303 u_char *p; 304 int r; 305 306 if ((r = sshbuf_reserve(buf, 8, &p)) < 0) 307 return r; 308 POKE_U64(p, val); 309 return 0; 310 } 311 312 int 313 sshbuf_put_u32(struct sshbuf *buf, u_int32_t val) 314 { 315 u_char *p; 316 int r; 317 318 if ((r = sshbuf_reserve(buf, 4, &p)) < 0) 319 return r; 320 POKE_U32(p, val); 321 return 0; 322 } 323 324 int 325 sshbuf_put_u16(struct sshbuf *buf, u_int16_t val) 326 { 327 u_char *p; 328 int r; 329 330 if ((r = sshbuf_reserve(buf, 2, &p)) < 0) 331 return r; 332 POKE_U16(p, val); 333 return 0; 334 } 335 336 int 337 sshbuf_put_u8(struct sshbuf *buf, u_char val) 338 { 339 u_char *p; 340 int r; 341 342 if ((r = sshbuf_reserve(buf, 1, &p)) < 0) 343 return r; 344 p[0] = val; 345 return 0; 346 } 347 348 int 349 sshbuf_put_string(struct sshbuf *buf, const void *v, size_t len) 350 { 351 u_char *d; 352 int r; 353 354 if (len > SSHBUF_SIZE_MAX - 4) { 355 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 356 return SSH_ERR_NO_BUFFER_SPACE; 357 } 358 if ((r = sshbuf_reserve(buf, len + 4, &d)) < 0) 359 return r; 360 POKE_U32(d, len); 361 if (len != 0) 362 memcpy(d + 4, v, len); 363 return 0; 364 } 365 366 int 367 sshbuf_put_cstring(struct sshbuf *buf, const char *v) 368 { 369 return sshbuf_put_string(buf, v, v == NULL ? 0 : strlen(v)); 370 } 371 372 int 373 sshbuf_put_stringb(struct sshbuf *buf, const struct sshbuf *v) 374 { 375 return sshbuf_put_string(buf, sshbuf_ptr(v), sshbuf_len(v)); 376 } 377 378 int 379 sshbuf_froms(struct sshbuf *buf, struct sshbuf **bufp) 380 { 381 const u_char *p; 382 size_t len; 383 struct sshbuf *ret; 384 int r; 385 386 if (buf == NULL || bufp == NULL) 387 return SSH_ERR_INVALID_ARGUMENT; 388 *bufp = NULL; 389 if ((r = sshbuf_peek_string_direct(buf, &p, &len)) != 0) 390 return r; 391 if ((ret = sshbuf_from(p, len)) == NULL) 392 return SSH_ERR_ALLOC_FAIL; 393 if ((r = sshbuf_consume(buf, len + 4)) != 0 || /* Shouldn't happen */ 394 (r = sshbuf_set_parent(ret, buf)) != 0) { 395 sshbuf_free(ret); 396 return r; 397 } 398 *bufp = ret; 399 return 0; 400 } 401 402 int 403 sshbuf_put_bignum2_bytes(struct sshbuf *buf, const void *v, size_t len) 404 { 405 u_char *d; 406 const u_char *s = (const u_char *)v; 407 int r, prepend; 408 409 if (len > SSHBUF_SIZE_MAX - 5) { 410 SSHBUF_DBG(("SSH_ERR_NO_BUFFER_SPACE")); 411 return SSH_ERR_NO_BUFFER_SPACE; 412 } 413 /* Skip leading zero bytes */ 414 for (; len > 0 && *s == 0; len--, s++) 415 ; 416 /* 417 * If most significant bit is set then prepend a zero byte to 418 * avoid interpretation as a negative number. 419 */ 420 prepend = len > 0 && (s[0] & 0x80) != 0; 421 if ((r = sshbuf_reserve(buf, len + 4 + prepend, &d)) < 0) 422 return r; 423 POKE_U32(d, len + prepend); 424 if (prepend) 425 d[4] = 0; 426 if (len != 0) 427 memcpy(d + 4 + prepend, s, len); 428 return 0; 429 } 430 431 int 432 sshbuf_get_bignum2_bytes_direct(struct sshbuf *buf, 433 const u_char **valp, size_t *lenp) 434 { 435 const u_char *d; 436 size_t len, olen; 437 int r; 438 439 if ((r = sshbuf_peek_string_direct(buf, &d, &olen)) < 0) 440 return r; 441 len = olen; 442 /* Refuse negative (MSB set) bignums */ 443 if ((len != 0 && (*d & 0x80) != 0)) 444 return SSH_ERR_BIGNUM_IS_NEGATIVE; 445 /* Refuse overlong bignums, allow prepended \0 to avoid MSB set */ 446 if (len > SSHBUF_MAX_BIGNUM + 1 || 447 (len == SSHBUF_MAX_BIGNUM + 1 && *d != 0)) 448 return SSH_ERR_BIGNUM_TOO_LARGE; 449 /* Trim leading zeros */ 450 while (len > 0 && *d == 0x00) { 451 d++; 452 len--; 453 } 454 if (valp != NULL) 455 *valp = d; 456 if (lenp != NULL) 457 *lenp = len; 458 if (sshbuf_consume(buf, olen + 4) != 0) { 459 /* Shouldn't happen */ 460 SSHBUF_DBG(("SSH_ERR_INTERNAL_ERROR")); 461 SSHBUF_ABORT(); 462 return SSH_ERR_INTERNAL_ERROR; 463 } 464 return 0; 465 } 466 467 /* 468 * store struct pwd 469 */ 470 int 471 sshbuf_put_passwd(struct sshbuf *buf, const struct passwd *pwent) 472 { 473 int r; 474 475 /* 476 * We never send pointer values of struct passwd. 477 * It is safe from wild pointer even if a new pointer member is added. 478 */ 479 480 if ((r = sshbuf_put_u64(buf, sizeof(*pwent)) != 0) || 481 (r = sshbuf_put_cstring(buf, pwent->pw_name)) != 0 || 482 (r = sshbuf_put_cstring(buf, "*")) != 0 || 483 (r = sshbuf_put_u32(buf, pwent->pw_uid)) != 0 || 484 (r = sshbuf_put_u32(buf, pwent->pw_gid)) != 0 || 485 #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE 486 (r = sshbuf_put_time(buf, pwent->pw_change)) != 0 || 487 #endif 488 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS 489 (r = sshbuf_put_cstring(buf, pwent->pw_gecos)) != 0 || 490 #endif 491 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS 492 (r = sshbuf_put_cstring(buf, pwent->pw_class)) != 0 || 493 #endif 494 (r = sshbuf_put_cstring(buf, pwent->pw_dir)) != 0 || 495 (r = sshbuf_put_cstring(buf, pwent->pw_shell)) != 0 || 496 #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE 497 (r = sshbuf_put_time(buf, pwent->pw_expire)) != 0 || 498 #endif 499 (r = sshbuf_put_u32(buf, pwent->pw_fields)) != 0) { 500 return r; 501 } 502 return 0; 503 } 504 505 /* 506 * extract struct pwd 507 */ 508 struct passwd * 509 sshbuf_get_passwd(struct sshbuf *buf) 510 { 511 struct passwd *pw; 512 u_int64_t len; 513 int r; 514 515 /* check if size of struct passwd is as same as sender's size */ 516 r = sshbuf_get_u64(buf, &len); 517 if (r != 0 || len != sizeof(*pw)) 518 return NULL; 519 520 pw = xcalloc(1, sizeof(*pw)); 521 if (sshbuf_get_cstring(buf, &pw->pw_name, NULL) != 0 || 522 sshbuf_get_cstring(buf, &pw->pw_passwd, NULL) != 0 || 523 sshbuf_get_u32(buf, &pw->pw_uid) != 0 || 524 sshbuf_get_u32(buf, &pw->pw_gid) != 0 || 525 #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE 526 sshbuf_get_time(buf, &pw->pw_change) != 0 || 527 #endif 528 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS 529 sshbuf_get_cstring(buf, &pw->pw_gecos, NULL) != 0 || 530 #endif 531 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS 532 sshbuf_get_cstring(buf, &pw->pw_class, NULL) != 0 || 533 #endif 534 sshbuf_get_cstring(buf, &pw->pw_dir, NULL) != 0 || 535 sshbuf_get_cstring(buf, &pw->pw_shell, NULL) != 0 || 536 #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE 537 sshbuf_get_time(buf, &pw->pw_expire) != 0 || 538 #endif 539 sshbuf_get_u32(buf, &pw->pw_fields) != 0) { 540 sshbuf_free_passwd(pw); 541 return NULL; 542 } 543 return pw; 544 } 545 546 /* 547 * free struct passwd obtained from sshbuf_get_passwd. 548 */ 549 void 550 sshbuf_free_passwd(struct passwd *pwent) 551 { 552 if (pwent == NULL) 553 return; 554 free(pwent->pw_shell); 555 free(pwent->pw_dir); 556 #ifdef HAVE_STRUCT_PASSWD_PW_CLASS 557 free(pwent->pw_class); 558 #endif 559 #ifdef HAVE_STRUCT_PASSWD_PW_GECOS 560 free(pwent->pw_gecos); 561 #endif 562 free(pwent->pw_passwd); 563 free(pwent->pw_name); 564 free(pwent); 565 } 566