diff options
author | djm@openbsd.org <djm@openbsd.org> | 2015-05-21 06:43:30 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2015-05-21 16:45:46 +1000 |
commit | bcc50d816187fa9a03907ac1f3a52f04a52e10d1 (patch) | |
tree | 7fee32fe8c063a24674a37aad34e4b381d995ae5 /auth2-pubkey.c | |
parent | 24232a3e5ab467678a86aa67968bbb915caffed4 (diff) |
upstream commit
add AuthorizedPrincipalsCommand that allows getting
authorized_principals from a subprocess rather than a file, which is quite
useful in deployments with large userbases
feedback and ok markus@
Upstream-ID: aa1bdac7b16fc6d2fa3524ef08f04c7258d247f6
Diffstat (limited to 'auth2-pubkey.c')
-rw-r--r-- | auth2-pubkey.c | 150 |
1 files changed, 127 insertions, 23 deletions
diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 553144c7b..c4e80b01b 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth2-pubkey.c,v 1.50 2015/05/21 06:38:35 djm Exp $ */ | 1 | /* $OpenBSD: auth2-pubkey.c,v 1.51 2015/05/21 06:43:30 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -554,19 +554,13 @@ match_principals_option(const char *principal_list, struct sshkey_cert *cert) | |||
554 | } | 554 | } |
555 | 555 | ||
556 | static int | 556 | static int |
557 | match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) | 557 | process_principals(FILE *f, char *file, struct passwd *pw, |
558 | struct sshkey_cert *cert) | ||
558 | { | 559 | { |
559 | FILE *f; | ||
560 | char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; | 560 | char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; |
561 | u_long linenum = 0; | 561 | u_long linenum = 0; |
562 | u_int i; | 562 | u_int i; |
563 | 563 | ||
564 | temporarily_use_uid(pw); | ||
565 | debug("trying authorized principals file %s", file); | ||
566 | if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { | ||
567 | restore_uid(); | ||
568 | return 0; | ||
569 | } | ||
570 | while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { | 564 | while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { |
571 | /* Skip leading whitespace. */ | 565 | /* Skip leading whitespace. */ |
572 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | 566 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) |
@@ -594,24 +588,128 @@ match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) | |||
594 | } | 588 | } |
595 | for (i = 0; i < cert->nprincipals; i++) { | 589 | for (i = 0; i < cert->nprincipals; i++) { |
596 | if (strcmp(cp, cert->principals[i]) == 0) { | 590 | if (strcmp(cp, cert->principals[i]) == 0) { |
597 | debug3("matched principal \"%.100s\" " | 591 | debug3("%s:%lu: matched principal \"%.100s\"", |
598 | "from file \"%s\" on line %lu", | 592 | file == NULL ? "(command)" : file, |
599 | cert->principals[i], file, linenum); | 593 | linenum, cert->principals[i]); |
600 | if (auth_parse_options(pw, line_opts, | 594 | if (auth_parse_options(pw, line_opts, |
601 | file, linenum) != 1) | 595 | file, linenum) != 1) |
602 | continue; | 596 | continue; |
603 | fclose(f); | ||
604 | restore_uid(); | ||
605 | return 1; | 597 | return 1; |
606 | } | 598 | } |
607 | } | 599 | } |
608 | } | 600 | } |
601 | return 0; | ||
602 | } | ||
603 | |||
604 | static int | ||
605 | match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) | ||
606 | { | ||
607 | FILE *f; | ||
608 | int success; | ||
609 | |||
610 | temporarily_use_uid(pw); | ||
611 | debug("trying authorized principals file %s", file); | ||
612 | if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { | ||
613 | restore_uid(); | ||
614 | return 0; | ||
615 | } | ||
616 | success = process_principals(f, file, pw, cert); | ||
609 | fclose(f); | 617 | fclose(f); |
610 | restore_uid(); | 618 | restore_uid(); |
611 | return 0; | 619 | return success; |
612 | } | 620 | } |
613 | 621 | ||
614 | /* | 622 | /* |
623 | * Checks whether principal is allowed in output of command. | ||
624 | * returns 1 if the principal is allowed or 0 otherwise. | ||
625 | */ | ||
626 | static int | ||
627 | match_principals_command(struct passwd *user_pw, struct sshkey *key) | ||
628 | { | ||
629 | FILE *f = NULL; | ||
630 | int ok, found_principal = 0; | ||
631 | struct passwd *pw; | ||
632 | int i, ac = 0, uid_swapped = 0; | ||
633 | pid_t pid; | ||
634 | char *tmp, *username = NULL, *command = NULL, **av = NULL; | ||
635 | void (*osigchld)(int); | ||
636 | |||
637 | if (options.authorized_principals_command == NULL) | ||
638 | return 0; | ||
639 | if (options.authorized_principals_command_user == NULL) { | ||
640 | error("No user for AuthorizedPrincipalsCommand specified, " | ||
641 | "skipping"); | ||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | /* | ||
646 | * NB. all returns later this function should go via "out" to | ||
647 | * ensure the original SIGCHLD handler is restored properly. | ||
648 | */ | ||
649 | osigchld = signal(SIGCHLD, SIG_DFL); | ||
650 | |||
651 | /* Prepare and verify the user for the command */ | ||
652 | username = percent_expand(options.authorized_principals_command_user, | ||
653 | "u", user_pw->pw_name, (char *)NULL); | ||
654 | pw = getpwnam(username); | ||
655 | if (pw == NULL) { | ||
656 | error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", | ||
657 | username, strerror(errno)); | ||
658 | goto out; | ||
659 | } | ||
660 | |||
661 | /* Turn the command into an argument vector */ | ||
662 | if (split_argv(options.authorized_principals_command, &ac, &av) != 0) { | ||
663 | error("AuthorizedPrincipalsCommand \"%s\" contains " | ||
664 | "invalid quotes", command); | ||
665 | goto out; | ||
666 | } | ||
667 | if (ac == 0) { | ||
668 | error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments", | ||
669 | command); | ||
670 | goto out; | ||
671 | } | ||
672 | for (i = 1; i < ac; i++) { | ||
673 | tmp = percent_expand(av[i], | ||
674 | "u", user_pw->pw_name, | ||
675 | "h", user_pw->pw_dir, | ||
676 | (char *)NULL); | ||
677 | if (tmp == NULL) | ||
678 | fatal("%s: percent_expand failed", __func__); | ||
679 | free(av[i]); | ||
680 | av[i] = tmp; | ||
681 | } | ||
682 | /* Prepare a printable command for logs, etc. */ | ||
683 | command = assemble_argv(ac, av); | ||
684 | |||
685 | if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command, | ||
686 | ac, av, &f)) == 0) | ||
687 | goto out; | ||
688 | |||
689 | uid_swapped = 1; | ||
690 | temporarily_use_uid(pw); | ||
691 | |||
692 | ok = process_principals(f, NULL, pw, key->cert); | ||
693 | |||
694 | if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0) | ||
695 | goto out; | ||
696 | |||
697 | /* Read completed successfully */ | ||
698 | found_principal = ok; | ||
699 | out: | ||
700 | if (f != NULL) | ||
701 | fclose(f); | ||
702 | signal(SIGCHLD, osigchld); | ||
703 | for (i = 0; i < ac; i++) | ||
704 | free(av[i]); | ||
705 | free(av); | ||
706 | if (uid_swapped) | ||
707 | restore_uid(); | ||
708 | free(command); | ||
709 | free(username); | ||
710 | return found_principal; | ||
711 | } | ||
712 | /* | ||
615 | * Checks whether key is allowed in authorized_keys-format file, | 713 | * Checks whether key is allowed in authorized_keys-format file, |
616 | * returns 1 if the key is allowed or 0 otherwise. | 714 | * returns 1 if the key is allowed or 0 otherwise. |
617 | */ | 715 | */ |
@@ -733,7 +831,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) | |||
733 | { | 831 | { |
734 | char *ca_fp, *principals_file = NULL; | 832 | char *ca_fp, *principals_file = NULL; |
735 | const char *reason; | 833 | const char *reason; |
736 | int ret = 0; | 834 | int ret = 0, found_principal = 0; |
737 | 835 | ||
738 | if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) | 836 | if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) |
739 | return 0; | 837 | return 0; |
@@ -755,14 +853,20 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) | |||
755 | * against the username. | 853 | * against the username. |
756 | */ | 854 | */ |
757 | if ((principals_file = authorized_principals_file(pw)) != NULL) { | 855 | if ((principals_file = authorized_principals_file(pw)) != NULL) { |
758 | if (!match_principals_file(principals_file, pw, key->cert)) { | 856 | if (match_principals_file(principals_file, pw, key->cert)) |
759 | reason = "Certificate does not contain an " | 857 | found_principal = 1; |
760 | "authorized principal"; | 858 | } |
859 | /* Try querying command if specified */ | ||
860 | if (!found_principal && match_principals_command(pw, key)) | ||
861 | found_principal = 1; | ||
862 | /* If principals file or command specify, then require a match here */ | ||
863 | if (!found_principal && (principals_file != NULL || | ||
864 | options.authorized_principals_command != NULL)) { | ||
865 | reason = "Certificate does not contain an authorized principal"; | ||
761 | fail_reason: | 866 | fail_reason: |
762 | error("%s", reason); | 867 | error("%s", reason); |
763 | auth_debug_add("%s", reason); | 868 | auth_debug_add("%s", reason); |
764 | goto out; | 869 | goto out; |
765 | } | ||
766 | } | 870 | } |
767 | if (key_cert_check_authority(key, 0, 1, | 871 | if (key_cert_check_authority(key, 0, 1, |
768 | principals_file == NULL ? pw->pw_name : NULL, &reason) != 0) | 872 | principals_file == NULL ? pw->pw_name : NULL, &reason) != 0) |