summaryrefslogtreecommitdiff
path: root/auth2-pubkey.c
diff options
context:
space:
mode:
authordjm@openbsd.org <djm@openbsd.org>2015-05-21 06:43:30 +0000
committerDamien Miller <djm@mindrot.org>2015-05-21 16:45:46 +1000
commitbcc50d816187fa9a03907ac1f3a52f04a52e10d1 (patch)
tree7fee32fe8c063a24674a37aad34e4b381d995ae5 /auth2-pubkey.c
parent24232a3e5ab467678a86aa67968bbb915caffed4 (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.c150
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
556static int 556static int
557match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) 557process_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
604static int
605match_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 */
626static int
627match_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)