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