1 /* $OpenBSD: auth-options.c,v 1.91 2020/02/26 13:40:09 jsg Exp $ */ 2 /* 3 * Copyright (c) 2018 Damien Miller <djm@mindrot.org> 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 <sys/queue.h> 20 21 #include <stdlib.h> 22 #include <netdb.h> 23 #include <pwd.h> 24 #include <string.h> 25 #include <stdio.h> 26 #include <stdarg.h> 27 #include <ctype.h> 28 #include <limits.h> 29 30 #include "xmalloc.h" 31 #include "ssherr.h" 32 #include "log.h" 33 #include "sshbuf.h" 34 #include "misc.h" 35 #include "sshkey.h" 36 #include "match.h" 37 #include "ssh2.h" 38 #include "auth-options.h" 39 40 static int 41 dup_strings(char ***dstp, size_t *ndstp, char **src, size_t nsrc) 42 { 43 char **dst; 44 size_t i, j; 45 46 *dstp = NULL; 47 *ndstp = 0; 48 if (nsrc == 0) 49 return 0; 50 51 if ((dst = calloc(nsrc, sizeof(*src))) == NULL) 52 return -1; 53 for (i = 0; i < nsrc; i++) { 54 if ((dst[i] = strdup(src[i])) == NULL) { 55 for (j = 0; j < i; j++) 56 free(dst[j]); 57 free(dst); 58 return -1; 59 } 60 } 61 /* success */ 62 *dstp = dst; 63 *ndstp = nsrc; 64 return 0; 65 } 66 67 #define OPTIONS_CRITICAL 1 68 #define OPTIONS_EXTENSIONS 2 69 static int 70 cert_option_list(struct sshauthopt *opts, struct sshbuf *oblob, 71 u_int which, int crit) 72 { 73 char *command, *allowed; 74 char *name = NULL; 75 struct sshbuf *c = NULL, *data = NULL; 76 int r, ret = -1, found; 77 78 if ((c = sshbuf_fromb(oblob)) == NULL) { 79 error("%s: sshbuf_fromb failed", __func__); 80 goto out; 81 } 82 83 while (sshbuf_len(c) > 0) { 84 sshbuf_free(data); 85 data = NULL; 86 if ((r = sshbuf_get_cstring(c, &name, NULL)) != 0 || 87 (r = sshbuf_froms(c, &data)) != 0) { 88 error("Unable to parse certificate options: %s", 89 ssh_err(r)); 90 goto out; 91 } 92 debug3("found certificate option \"%.100s\" len %zu", 93 name, sshbuf_len(data)); 94 found = 0; 95 if ((which & OPTIONS_EXTENSIONS) != 0) { 96 if (strcmp(name, "no-touch-required") == 0) { 97 opts->no_require_user_presence = 1; 98 found = 1; 99 } else if (strcmp(name, "permit-X11-forwarding") == 0) { 100 opts->permit_x11_forwarding_flag = 1; 101 found = 1; 102 } else if (strcmp(name, 103 "permit-agent-forwarding") == 0) { 104 opts->permit_agent_forwarding_flag = 1; 105 found = 1; 106 } else if (strcmp(name, 107 "permit-port-forwarding") == 0) { 108 opts->permit_port_forwarding_flag = 1; 109 found = 1; 110 } else if (strcmp(name, "permit-pty") == 0) { 111 opts->permit_pty_flag = 1; 112 found = 1; 113 } else if (strcmp(name, "permit-user-rc") == 0) { 114 opts->permit_user_rc = 1; 115 found = 1; 116 } 117 } 118 if (!found && (which & OPTIONS_CRITICAL) != 0) { 119 if (strcmp(name, "force-command") == 0) { 120 if ((r = sshbuf_get_cstring(data, &command, 121 NULL)) != 0) { 122 error("Unable to parse \"%s\" " 123 "section: %s", name, ssh_err(r)); 124 goto out; 125 } 126 if (opts->force_command != NULL) { 127 error("Certificate has multiple " 128 "force-command options"); 129 free(command); 130 goto out; 131 } 132 opts->force_command = command; 133 found = 1; 134 } 135 if (strcmp(name, "source-address") == 0) { 136 if ((r = sshbuf_get_cstring(data, &allowed, 137 NULL)) != 0) { 138 error("Unable to parse \"%s\" " 139 "section: %s", name, ssh_err(r)); 140 goto out; 141 } 142 if (opts->required_from_host_cert != NULL) { 143 error("Certificate has multiple " 144 "source-address options"); 145 free(allowed); 146 goto out; 147 } 148 /* Check syntax */ 149 if (addr_match_cidr_list(NULL, allowed) == -1) { 150 error("Certificate source-address " 151 "contents invalid"); 152 goto out; 153 } 154 opts->required_from_host_cert = allowed; 155 found = 1; 156 } 157 } 158 159 if (!found) { 160 if (crit) { 161 error("Certificate critical option \"%s\" " 162 "is not supported", name); 163 goto out; 164 } else { 165 logit("Certificate extension \"%s\" " 166 "is not supported", name); 167 } 168 } else if (sshbuf_len(data) != 0) { 169 error("Certificate option \"%s\" corrupt " 170 "(extra data)", name); 171 goto out; 172 } 173 free(name); 174 name = NULL; 175 } 176 /* successfully parsed all options */ 177 ret = 0; 178 179 out: 180 free(name); 181 sshbuf_free(data); 182 sshbuf_free(c); 183 return ret; 184 } 185 186 struct sshauthopt * 187 sshauthopt_new(void) 188 { 189 struct sshauthopt *ret; 190 191 if ((ret = calloc(1, sizeof(*ret))) == NULL) 192 return NULL; 193 ret->force_tun_device = -1; 194 return ret; 195 } 196 197 void 198 sshauthopt_free(struct sshauthopt *opts) 199 { 200 size_t i; 201 202 if (opts == NULL) 203 return; 204 205 free(opts->cert_principals); 206 free(opts->force_command); 207 free(opts->required_from_host_cert); 208 free(opts->required_from_host_keys); 209 210 for (i = 0; i < opts->nenv; i++) 211 free(opts->env[i]); 212 free(opts->env); 213 214 for (i = 0; i < opts->npermitopen; i++) 215 free(opts->permitopen[i]); 216 free(opts->permitopen); 217 218 for (i = 0; i < opts->npermitlisten; i++) 219 free(opts->permitlisten[i]); 220 free(opts->permitlisten); 221 222 freezero(opts, sizeof(*opts)); 223 } 224 225 struct sshauthopt * 226 sshauthopt_new_with_keys_defaults(void) 227 { 228 struct sshauthopt *ret = NULL; 229 230 if ((ret = sshauthopt_new()) == NULL) 231 return NULL; 232 233 /* Defaults for authorized_keys flags */ 234 ret->permit_port_forwarding_flag = 1; 235 ret->permit_agent_forwarding_flag = 1; 236 ret->permit_x11_forwarding_flag = 1; 237 ret->permit_pty_flag = 1; 238 ret->permit_user_rc = 1; 239 return ret; 240 } 241 242 /* 243 * Parse and record a permitopen/permitlisten directive. 244 * Return 0 on success. Return -1 on failure and sets *errstrp to error reason. 245 */ 246 static int 247 handle_permit(const char **optsp, int allow_bare_port, 248 char ***permitsp, size_t *npermitsp, const char **errstrp) 249 { 250 char *opt, *tmp, *cp, *host, **permits = *permitsp; 251 size_t npermits = *npermitsp; 252 const char *errstr = "unknown error"; 253 254 if (npermits > SSH_AUTHOPT_PERMIT_MAX) { 255 *errstrp = "too many permission directives"; 256 return -1; 257 } 258 if ((opt = opt_dequote(optsp, &errstr)) == NULL) { 259 return -1; 260 } 261 if (allow_bare_port && strchr(opt, ':') == NULL) { 262 /* 263 * Allow a bare port number in permitlisten to indicate a 264 * listen_host wildcard. 265 */ 266 if (asprintf(&tmp, "*:%s", opt) == -1) { 267 free(opt); 268 *errstrp = "memory allocation failed"; 269 return -1; 270 } 271 free(opt); 272 opt = tmp; 273 } 274 if ((tmp = strdup(opt)) == NULL) { 275 free(opt); 276 *errstrp = "memory allocation failed"; 277 return -1; 278 } 279 cp = tmp; 280 /* validate syntax before recording it. */ 281 host = hpdelim(&cp); 282 if (host == NULL || strlen(host) >= NI_MAXHOST) { 283 free(tmp); 284 free(opt); 285 *errstrp = "invalid permission hostname"; 286 return -1; 287 } 288 /* 289 * don't want to use permitopen_port to avoid 290 * dependency on channels.[ch] here. 291 */ 292 if (cp == NULL || 293 (strcmp(cp, "*") != 0 && a2port(cp) <= 0)) { 294 free(tmp); 295 free(opt); 296 *errstrp = "invalid permission port"; 297 return -1; 298 } 299 /* XXX - add streamlocal support */ 300 free(tmp); 301 /* Record it */ 302 if ((permits = recallocarray(permits, npermits, npermits + 1, 303 sizeof(*permits))) == NULL) { 304 free(opt); 305 /* NB. don't update *permitsp if alloc fails */ 306 *errstrp = "memory allocation failed"; 307 return -1; 308 } 309 permits[npermits++] = opt; 310 *permitsp = permits; 311 *npermitsp = npermits; 312 return 0; 313 } 314 315 struct sshauthopt * 316 sshauthopt_parse(const char *opts, const char **errstrp) 317 { 318 char **oarray, *opt, *cp, *tmp; 319 int r; 320 struct sshauthopt *ret = NULL; 321 const char *errstr = "unknown error"; 322 uint64_t valid_before; 323 324 if (errstrp != NULL) 325 *errstrp = NULL; 326 if ((ret = sshauthopt_new_with_keys_defaults()) == NULL) 327 goto alloc_fail; 328 329 if (opts == NULL) 330 return ret; 331 332 while (*opts && *opts != ' ' && *opts != '\t') { 333 /* flag options */ 334 if ((r = opt_flag("restrict", 0, &opts)) != -1) { 335 ret->restricted = 1; 336 ret->permit_port_forwarding_flag = 0; 337 ret->permit_agent_forwarding_flag = 0; 338 ret->permit_x11_forwarding_flag = 0; 339 ret->permit_pty_flag = 0; 340 ret->permit_user_rc = 0; 341 } else if ((r = opt_flag("cert-authority", 0, &opts)) != -1) { 342 ret->cert_authority = r; 343 } else if ((r = opt_flag("port-forwarding", 1, &opts)) != -1) { 344 ret->permit_port_forwarding_flag = r == 1; 345 } else if ((r = opt_flag("agent-forwarding", 1, &opts)) != -1) { 346 ret->permit_agent_forwarding_flag = r == 1; 347 } else if ((r = opt_flag("x11-forwarding", 1, &opts)) != -1) { 348 ret->permit_x11_forwarding_flag = r == 1; 349 } else if ((r = opt_flag("touch-required", 1, &opts)) != -1) { 350 ret->no_require_user_presence = r != 1; /* NB. flip */ 351 } else if ((r = opt_flag("pty", 1, &opts)) != -1) { 352 ret->permit_pty_flag = r == 1; 353 } else if ((r = opt_flag("user-rc", 1, &opts)) != -1) { 354 ret->permit_user_rc = r == 1; 355 } else if (opt_match(&opts, "command")) { 356 if (ret->force_command != NULL) { 357 errstr = "multiple \"command\" clauses"; 358 goto fail; 359 } 360 ret->force_command = opt_dequote(&opts, &errstr); 361 if (ret->force_command == NULL) 362 goto fail; 363 } else if (opt_match(&opts, "principals")) { 364 if (ret->cert_principals != NULL) { 365 errstr = "multiple \"principals\" clauses"; 366 goto fail; 367 } 368 ret->cert_principals = opt_dequote(&opts, &errstr); 369 if (ret->cert_principals == NULL) 370 goto fail; 371 } else if (opt_match(&opts, "from")) { 372 if (ret->required_from_host_keys != NULL) { 373 errstr = "multiple \"from\" clauses"; 374 goto fail; 375 } 376 ret->required_from_host_keys = opt_dequote(&opts, 377 &errstr); 378 if (ret->required_from_host_keys == NULL) 379 goto fail; 380 } else if (opt_match(&opts, "expiry-time")) { 381 if ((opt = opt_dequote(&opts, &errstr)) == NULL) 382 goto fail; 383 if (parse_absolute_time(opt, &valid_before) != 0 || 384 valid_before == 0) { 385 free(opt); 386 errstr = "invalid expires time"; 387 goto fail; 388 } 389 free(opt); 390 if (ret->valid_before == 0 || 391 valid_before < ret->valid_before) 392 ret->valid_before = valid_before; 393 } else if (opt_match(&opts, "environment")) { 394 if (ret->nenv > INT_MAX) { 395 errstr = "too many environment strings"; 396 goto fail; 397 } 398 if ((opt = opt_dequote(&opts, &errstr)) == NULL) 399 goto fail; 400 /* env name must be alphanumeric and followed by '=' */ 401 if ((tmp = strchr(opt, '=')) == NULL) { 402 free(opt); 403 errstr = "invalid environment string"; 404 goto fail; 405 } 406 if ((cp = strdup(opt)) == NULL) 407 goto alloc_fail; 408 cp[tmp - opt] = '\0'; /* truncate at '=' */ 409 if (!valid_env_name(cp)) { 410 free(cp); 411 free(opt); 412 errstr = "invalid environment string"; 413 goto fail; 414 } 415 free(cp); 416 /* Append it. */ 417 oarray = ret->env; 418 if ((ret->env = recallocarray(ret->env, ret->nenv, 419 ret->nenv + 1, sizeof(*ret->env))) == NULL) { 420 free(opt); 421 ret->env = oarray; /* put it back for cleanup */ 422 goto alloc_fail; 423 } 424 ret->env[ret->nenv++] = opt; 425 } else if (opt_match(&opts, "permitopen")) { 426 if (handle_permit(&opts, 0, &ret->permitopen, 427 &ret->npermitopen, &errstr) != 0) 428 goto fail; 429 } else if (opt_match(&opts, "permitlisten")) { 430 if (handle_permit(&opts, 1, &ret->permitlisten, 431 &ret->npermitlisten, &errstr) != 0) 432 goto fail; 433 } else if (opt_match(&opts, "tunnel")) { 434 if ((opt = opt_dequote(&opts, &errstr)) == NULL) 435 goto fail; 436 ret->force_tun_device = a2tun(opt, NULL); 437 free(opt); 438 if (ret->force_tun_device == SSH_TUNID_ERR) { 439 errstr = "invalid tun device"; 440 goto fail; 441 } 442 } 443 /* 444 * Skip the comma, and move to the next option 445 * (or break out if there are no more). 446 */ 447 if (*opts == '\0' || *opts == ' ' || *opts == '\t') 448 break; /* End of options. */ 449 /* Anything other than a comma is an unknown option */ 450 if (*opts != ',') { 451 errstr = "unknown key option"; 452 goto fail; 453 } 454 opts++; 455 if (*opts == '\0') { 456 errstr = "unexpected end-of-options"; 457 goto fail; 458 } 459 } 460 461 /* success */ 462 if (errstrp != NULL) 463 *errstrp = NULL; 464 return ret; 465 466 alloc_fail: 467 errstr = "memory allocation failed"; 468 fail: 469 sshauthopt_free(ret); 470 if (errstrp != NULL) 471 *errstrp = errstr; 472 return NULL; 473 } 474 475 struct sshauthopt * 476 sshauthopt_from_cert(struct sshkey *k) 477 { 478 struct sshauthopt *ret; 479 480 if (k == NULL || !sshkey_type_is_cert(k->type) || k->cert == NULL || 481 k->cert->type != SSH2_CERT_TYPE_USER) 482 return NULL; 483 484 if ((ret = sshauthopt_new()) == NULL) 485 return NULL; 486 487 /* Handle options and critical extensions separately */ 488 if (cert_option_list(ret, k->cert->critical, 489 OPTIONS_CRITICAL, 1) == -1) { 490 sshauthopt_free(ret); 491 return NULL; 492 } 493 if (cert_option_list(ret, k->cert->extensions, 494 OPTIONS_EXTENSIONS, 0) == -1) { 495 sshauthopt_free(ret); 496 return NULL; 497 } 498 /* success */ 499 return ret; 500 } 501 502 /* 503 * Merges "additional" options to "primary" and returns the result. 504 * NB. Some options from primary have primacy. 505 */ 506 struct sshauthopt * 507 sshauthopt_merge(const struct sshauthopt *primary, 508 const struct sshauthopt *additional, const char **errstrp) 509 { 510 struct sshauthopt *ret; 511 const char *errstr = "internal error"; 512 const char *tmp; 513 514 if (errstrp != NULL) 515 *errstrp = NULL; 516 517 if ((ret = sshauthopt_new()) == NULL) 518 goto alloc_fail; 519 520 /* cert_authority and cert_principals are cleared in result */ 521 522 /* Prefer access lists from primary. */ 523 /* XXX err is both set and mismatch? */ 524 tmp = primary->required_from_host_cert; 525 if (tmp == NULL) 526 tmp = additional->required_from_host_cert; 527 if (tmp != NULL && (ret->required_from_host_cert = strdup(tmp)) == NULL) 528 goto alloc_fail; 529 tmp = primary->required_from_host_keys; 530 if (tmp == NULL) 531 tmp = additional->required_from_host_keys; 532 if (tmp != NULL && (ret->required_from_host_keys = strdup(tmp)) == NULL) 533 goto alloc_fail; 534 535 /* 536 * force_tun_device, permitopen/permitlisten and environment all 537 * prefer the primary. 538 */ 539 ret->force_tun_device = primary->force_tun_device; 540 if (ret->force_tun_device == -1) 541 ret->force_tun_device = additional->force_tun_device; 542 if (primary->nenv > 0) { 543 if (dup_strings(&ret->env, &ret->nenv, 544 primary->env, primary->nenv) != 0) 545 goto alloc_fail; 546 } else if (additional->nenv) { 547 if (dup_strings(&ret->env, &ret->nenv, 548 additional->env, additional->nenv) != 0) 549 goto alloc_fail; 550 } 551 if (primary->npermitopen > 0) { 552 if (dup_strings(&ret->permitopen, &ret->npermitopen, 553 primary->permitopen, primary->npermitopen) != 0) 554 goto alloc_fail; 555 } else if (additional->npermitopen > 0) { 556 if (dup_strings(&ret->permitopen, &ret->npermitopen, 557 additional->permitopen, additional->npermitopen) != 0) 558 goto alloc_fail; 559 } 560 561 if (primary->npermitlisten > 0) { 562 if (dup_strings(&ret->permitlisten, &ret->npermitlisten, 563 primary->permitlisten, primary->npermitlisten) != 0) 564 goto alloc_fail; 565 } else if (additional->npermitlisten > 0) { 566 if (dup_strings(&ret->permitlisten, &ret->npermitlisten, 567 additional->permitlisten, additional->npermitlisten) != 0) 568 goto alloc_fail; 569 } 570 571 #define OPTFLAG_AND(x) ret->x = (primary->x == 1) && (additional->x == 1) 572 /* Permissive flags are logical-AND (i.e. must be set in both) */ 573 OPTFLAG_AND(permit_port_forwarding_flag); 574 OPTFLAG_AND(permit_agent_forwarding_flag); 575 OPTFLAG_AND(permit_x11_forwarding_flag); 576 OPTFLAG_AND(permit_pty_flag); 577 OPTFLAG_AND(permit_user_rc); 578 OPTFLAG_AND(no_require_user_presence); 579 #undef OPTFLAG_AND 580 581 /* Earliest expiry time should win */ 582 if (primary->valid_before != 0) 583 ret->valid_before = primary->valid_before; 584 if (additional->valid_before != 0 && 585 additional->valid_before < ret->valid_before) 586 ret->valid_before = additional->valid_before; 587 588 /* 589 * When both multiple forced-command are specified, only 590 * proceed if they are identical, otherwise fail. 591 */ 592 if (primary->force_command != NULL && 593 additional->force_command != NULL) { 594 if (strcmp(primary->force_command, 595 additional->force_command) == 0) { 596 /* ok */ 597 ret->force_command = strdup(primary->force_command); 598 if (ret->force_command == NULL) 599 goto alloc_fail; 600 } else { 601 errstr = "forced command options do not match"; 602 goto fail; 603 } 604 } else if (primary->force_command != NULL) { 605 if ((ret->force_command = strdup( 606 primary->force_command)) == NULL) 607 goto alloc_fail; 608 } else if (additional->force_command != NULL) { 609 if ((ret->force_command = strdup( 610 additional->force_command)) == NULL) 611 goto alloc_fail; 612 } 613 /* success */ 614 if (errstrp != NULL) 615 *errstrp = NULL; 616 return ret; 617 618 alloc_fail: 619 errstr = "memory allocation failed"; 620 fail: 621 if (errstrp != NULL) 622 *errstrp = errstr; 623 sshauthopt_free(ret); 624 return NULL; 625 } 626 627 /* 628 * Copy options 629 */ 630 struct sshauthopt * 631 sshauthopt_copy(const struct sshauthopt *orig) 632 { 633 struct sshauthopt *ret; 634 635 if ((ret = sshauthopt_new()) == NULL) 636 return NULL; 637 638 #define OPTSCALAR(x) ret->x = orig->x 639 OPTSCALAR(permit_port_forwarding_flag); 640 OPTSCALAR(permit_agent_forwarding_flag); 641 OPTSCALAR(permit_x11_forwarding_flag); 642 OPTSCALAR(permit_pty_flag); 643 OPTSCALAR(permit_user_rc); 644 OPTSCALAR(restricted); 645 OPTSCALAR(cert_authority); 646 OPTSCALAR(force_tun_device); 647 OPTSCALAR(valid_before); 648 OPTSCALAR(no_require_user_presence); 649 #undef OPTSCALAR 650 #define OPTSTRING(x) \ 651 do { \ 652 if (orig->x != NULL && (ret->x = strdup(orig->x)) == NULL) { \ 653 sshauthopt_free(ret); \ 654 return NULL; \ 655 } \ 656 } while (0) 657 OPTSTRING(cert_principals); 658 OPTSTRING(force_command); 659 OPTSTRING(required_from_host_cert); 660 OPTSTRING(required_from_host_keys); 661 #undef OPTSTRING 662 663 if (dup_strings(&ret->env, &ret->nenv, orig->env, orig->nenv) != 0 || 664 dup_strings(&ret->permitopen, &ret->npermitopen, 665 orig->permitopen, orig->npermitopen) != 0 || 666 dup_strings(&ret->permitlisten, &ret->npermitlisten, 667 orig->permitlisten, orig->npermitlisten) != 0) { 668 sshauthopt_free(ret); 669 return NULL; 670 } 671 return ret; 672 } 673 674 static int 675 serialise_array(struct sshbuf *m, char **a, size_t n) 676 { 677 struct sshbuf *b; 678 size_t i; 679 int r; 680 681 if (n > INT_MAX) 682 return SSH_ERR_INTERNAL_ERROR; 683 684 if ((b = sshbuf_new()) == NULL) { 685 return SSH_ERR_ALLOC_FAIL; 686 } 687 for (i = 0; i < n; i++) { 688 if ((r = sshbuf_put_cstring(b, a[i])) != 0) { 689 sshbuf_free(b); 690 return r; 691 } 692 } 693 if ((r = sshbuf_put_u32(m, n)) != 0 || 694 (r = sshbuf_put_stringb(m, b)) != 0) { 695 sshbuf_free(b); 696 return r; 697 } 698 /* success */ 699 return 0; 700 } 701 702 static int 703 deserialise_array(struct sshbuf *m, char ***ap, size_t *np) 704 { 705 char **a = NULL; 706 size_t i, n = 0; 707 struct sshbuf *b = NULL; 708 u_int tmp; 709 int r = SSH_ERR_INTERNAL_ERROR; 710 711 if ((r = sshbuf_get_u32(m, &tmp)) != 0 || 712 (r = sshbuf_froms(m, &b)) != 0) 713 goto out; 714 if (tmp > INT_MAX) { 715 r = SSH_ERR_INVALID_FORMAT; 716 goto out; 717 } 718 n = tmp; 719 if (n > 0 && (a = calloc(n, sizeof(*a))) == NULL) { 720 r = SSH_ERR_ALLOC_FAIL; 721 goto out; 722 } 723 for (i = 0; i < n; i++) { 724 if ((r = sshbuf_get_cstring(b, &a[i], NULL)) != 0) 725 goto out; 726 } 727 /* success */ 728 r = 0; 729 *ap = a; 730 a = NULL; 731 *np = n; 732 n = 0; 733 out: 734 for (i = 0; i < n; i++) 735 free(a[i]); 736 free(a); 737 sshbuf_free(b); 738 return r; 739 } 740 741 static int 742 serialise_nullable_string(struct sshbuf *m, const char *s) 743 { 744 int r; 745 746 if ((r = sshbuf_put_u8(m, s == NULL)) != 0 || 747 (r = sshbuf_put_cstring(m, s)) != 0) 748 return r; 749 return 0; 750 } 751 752 static int 753 deserialise_nullable_string(struct sshbuf *m, char **sp) 754 { 755 int r; 756 u_char flag; 757 758 *sp = NULL; 759 if ((r = sshbuf_get_u8(m, &flag)) != 0 || 760 (r = sshbuf_get_cstring(m, flag ? NULL : sp, NULL)) != 0) 761 return r; 762 return 0; 763 } 764 765 int 766 sshauthopt_serialise(const struct sshauthopt *opts, struct sshbuf *m, 767 int untrusted) 768 { 769 int r = SSH_ERR_INTERNAL_ERROR; 770 771 /* Flag options */ 772 if ((r = sshbuf_put_u8(m, opts->permit_port_forwarding_flag)) != 0 || 773 (r = sshbuf_put_u8(m, opts->permit_agent_forwarding_flag)) != 0 || 774 (r = sshbuf_put_u8(m, opts->permit_x11_forwarding_flag)) != 0 || 775 (r = sshbuf_put_u8(m, opts->permit_pty_flag)) != 0 || 776 (r = sshbuf_put_u8(m, opts->permit_user_rc)) != 0 || 777 (r = sshbuf_put_u8(m, opts->restricted)) != 0 || 778 (r = sshbuf_put_u8(m, opts->cert_authority)) != 0 || 779 (r = sshbuf_put_u8(m, opts->no_require_user_presence)) != 0) 780 return r; 781 782 /* Simple integer options */ 783 if ((r = sshbuf_put_u64(m, opts->valid_before)) != 0) 784 return r; 785 786 /* tunnel number can be negative to indicate "unset" */ 787 if ((r = sshbuf_put_u8(m, opts->force_tun_device == -1)) != 0 || 788 (r = sshbuf_put_u32(m, (opts->force_tun_device < 0) ? 789 0 : (u_int)opts->force_tun_device)) != 0) 790 return r; 791 792 /* String options; these may be NULL */ 793 if ((r = serialise_nullable_string(m, 794 untrusted ? "yes" : opts->cert_principals)) != 0 || 795 (r = serialise_nullable_string(m, 796 untrusted ? "true" : opts->force_command)) != 0 || 797 (r = serialise_nullable_string(m, 798 untrusted ? NULL : opts->required_from_host_cert)) != 0 || 799 (r = serialise_nullable_string(m, 800 untrusted ? NULL : opts->required_from_host_keys)) != 0) 801 return r; 802 803 /* Array options */ 804 if ((r = serialise_array(m, opts->env, 805 untrusted ? 0 : opts->nenv)) != 0 || 806 (r = serialise_array(m, opts->permitopen, 807 untrusted ? 0 : opts->npermitopen)) != 0 || 808 (r = serialise_array(m, opts->permitlisten, 809 untrusted ? 0 : opts->npermitlisten)) != 0) 810 return r; 811 812 /* success */ 813 return 0; 814 } 815 816 int 817 sshauthopt_deserialise(struct sshbuf *m, struct sshauthopt **optsp) 818 { 819 struct sshauthopt *opts = NULL; 820 int r = SSH_ERR_INTERNAL_ERROR; 821 u_char f; 822 u_int tmp; 823 824 if ((opts = calloc(1, sizeof(*opts))) == NULL) 825 return SSH_ERR_ALLOC_FAIL; 826 827 /* Flag options */ 828 #define OPT_FLAG(x) \ 829 do { \ 830 if ((r = sshbuf_get_u8(m, &f)) != 0) \ 831 goto out; \ 832 opts->x = f; \ 833 } while (0) 834 OPT_FLAG(permit_port_forwarding_flag); 835 OPT_FLAG(permit_agent_forwarding_flag); 836 OPT_FLAG(permit_x11_forwarding_flag); 837 OPT_FLAG(permit_pty_flag); 838 OPT_FLAG(permit_user_rc); 839 OPT_FLAG(restricted); 840 OPT_FLAG(cert_authority); 841 OPT_FLAG(no_require_user_presence); 842 #undef OPT_FLAG 843 844 /* Simple integer options */ 845 if ((r = sshbuf_get_u64(m, &opts->valid_before)) != 0) 846 goto out; 847 848 /* tunnel number can be negative to indicate "unset" */ 849 if ((r = sshbuf_get_u8(m, &f)) != 0 || 850 (r = sshbuf_get_u32(m, &tmp)) != 0) 851 goto out; 852 opts->force_tun_device = f ? -1 : (int)tmp; 853 854 /* String options may be NULL */ 855 if ((r = deserialise_nullable_string(m, &opts->cert_principals)) != 0 || 856 (r = deserialise_nullable_string(m, &opts->force_command)) != 0 || 857 (r = deserialise_nullable_string(m, 858 &opts->required_from_host_cert)) != 0 || 859 (r = deserialise_nullable_string(m, 860 &opts->required_from_host_keys)) != 0) 861 goto out; 862 863 /* Array options */ 864 if ((r = deserialise_array(m, &opts->env, &opts->nenv)) != 0 || 865 (r = deserialise_array(m, 866 &opts->permitopen, &opts->npermitopen)) != 0 || 867 (r = deserialise_array(m, 868 &opts->permitlisten, &opts->npermitlisten)) != 0) 869 goto out; 870 871 /* success */ 872 r = 0; 873 *optsp = opts; 874 opts = NULL; 875 out: 876 sshauthopt_free(opts); 877 return r; 878 } 879