summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--auth2-pubkey.c150
-rw-r--r--servconf.c37
-rw-r--r--servconf.h10
-rw-r--r--sshd.c7
-rw-r--r--sshd_config.538
5 files changed, 213 insertions, 29 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)
diff --git a/servconf.c b/servconf.c
index 9257a174b..5acaf61b1 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.c,v 1.269 2015/05/04 06:10:48 djm Exp $ */ 1/* $OpenBSD: servconf.c,v 1.270 2015/05/21 06:43:30 djm Exp $ */
2/* 2/*
3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 3 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
4 * All rights reserved 4 * All rights reserved
@@ -160,6 +160,8 @@ initialize_server_options(ServerOptions *options)
160 options->revoked_keys_file = NULL; 160 options->revoked_keys_file = NULL;
161 options->trusted_user_ca_keys = NULL; 161 options->trusted_user_ca_keys = NULL;
162 options->authorized_principals_file = NULL; 162 options->authorized_principals_file = NULL;
163 options->authorized_principals_command = NULL;
164 options->authorized_principals_command_user = NULL;
163 options->ip_qos_interactive = -1; 165 options->ip_qos_interactive = -1;
164 options->ip_qos_bulk = -1; 166 options->ip_qos_bulk = -1;
165 options->version_addendum = NULL; 167 options->version_addendum = NULL;
@@ -400,6 +402,7 @@ typedef enum {
400 sUsePrivilegeSeparation, sAllowAgentForwarding, 402 sUsePrivilegeSeparation, sAllowAgentForwarding,
401 sHostCertificate, 403 sHostCertificate,
402 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, 404 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
405 sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser,
403 sKexAlgorithms, sIPQoS, sVersionAddendum, 406 sKexAlgorithms, sIPQoS, sVersionAddendum,
404 sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, 407 sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
405 sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, 408 sAuthenticationMethods, sHostKeyAgent, sPermitUserRC,
@@ -532,6 +535,8 @@ static struct {
532 { "ipqos", sIPQoS, SSHCFG_ALL }, 535 { "ipqos", sIPQoS, SSHCFG_ALL },
533 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, 536 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
534 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, 537 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
538 { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL },
539 { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL },
535 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, 540 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
536 { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, 541 { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL },
537 { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, 542 { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL },
@@ -1734,6 +1739,34 @@ process_server_config_line(ServerOptions *options, char *line,
1734 *charptr = xstrdup(arg); 1739 *charptr = xstrdup(arg);
1735 break; 1740 break;
1736 1741
1742 case sAuthorizedPrincipalsCommand:
1743 if (cp == NULL)
1744 fatal("%.200s line %d: Missing argument.", filename,
1745 linenum);
1746 len = strspn(cp, WHITESPACE);
1747 if (*activep &&
1748 options->authorized_principals_command == NULL) {
1749 if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0)
1750 fatal("%.200s line %d: "
1751 "AuthorizedPrincipalsCommand must be "
1752 "an absolute path", filename, linenum);
1753 options->authorized_principals_command =
1754 xstrdup(cp + len);
1755 }
1756 return 0;
1757
1758 case sAuthorizedPrincipalsCommandUser:
1759 charptr = &options->authorized_principals_command_user;
1760
1761 arg = strdelim(&cp);
1762 if (!arg || *arg == '\0')
1763 fatal("%s line %d: missing "
1764 "AuthorizedPrincipalsCommandUser argument.",
1765 filename, linenum);
1766 if (*activep && *charptr == NULL)
1767 *charptr = xstrdup(arg);
1768 break;
1769
1737 case sAuthenticationMethods: 1770 case sAuthenticationMethods:
1738 if (options->num_auth_methods == 0) { 1771 if (options->num_auth_methods == 0) {
1739 while ((arg = strdelim(&cp)) && *arg != '\0') { 1772 while ((arg = strdelim(&cp)) && *arg != '\0') {
@@ -2229,6 +2262,8 @@ dump_config(ServerOptions *o)
2229 ? "none" : o->version_addendum); 2262 ? "none" : o->version_addendum);
2230 dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); 2263 dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
2231 dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); 2264 dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
2265 dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command);
2266 dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user);
2232 dump_cfg_string(sHostKeyAgent, o->host_key_agent); 2267 dump_cfg_string(sHostKeyAgent, o->host_key_agent);
2233 dump_cfg_string(sKexAlgorithms, 2268 dump_cfg_string(sKexAlgorithms,
2234 o->kex_algorithms ? o->kex_algorithms : KEX_SERVER_KEX); 2269 o->kex_algorithms ? o->kex_algorithms : KEX_SERVER_KEX);
diff --git a/servconf.h b/servconf.h
index 38520f476..dc2a5f6a6 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.h,v 1.117 2015/04/29 03:48:56 dtucker Exp $ */ 1/* $OpenBSD: servconf.h,v 1.118 2015/05/21 06:43:31 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -178,9 +178,11 @@ typedef struct {
178 char *chroot_directory; 178 char *chroot_directory;
179 char *revoked_keys_file; 179 char *revoked_keys_file;
180 char *trusted_user_ca_keys; 180 char *trusted_user_ca_keys;
181 char *authorized_principals_file;
182 char *authorized_keys_command; 181 char *authorized_keys_command;
183 char *authorized_keys_command_user; 182 char *authorized_keys_command_user;
183 char *authorized_principals_file;
184 char *authorized_principals_command;
185 char *authorized_principals_command_user;
184 186
185 int64_t rekey_limit; 187 int64_t rekey_limit;
186 int rekey_interval; 188 int rekey_interval;
@@ -216,9 +218,11 @@ struct connection_info {
216 M_CP_STROPT(banner); \ 218 M_CP_STROPT(banner); \
217 M_CP_STROPT(trusted_user_ca_keys); \ 219 M_CP_STROPT(trusted_user_ca_keys); \
218 M_CP_STROPT(revoked_keys_file); \ 220 M_CP_STROPT(revoked_keys_file); \
219 M_CP_STROPT(authorized_principals_file); \
220 M_CP_STROPT(authorized_keys_command); \ 221 M_CP_STROPT(authorized_keys_command); \
221 M_CP_STROPT(authorized_keys_command_user); \ 222 M_CP_STROPT(authorized_keys_command_user); \
223 M_CP_STROPT(authorized_principals_file); \
224 M_CP_STROPT(authorized_principals_command); \
225 M_CP_STROPT(authorized_principals_command_user); \
222 M_CP_STROPT(hostbased_key_types); \ 226 M_CP_STROPT(hostbased_key_types); \
223 M_CP_STROPT(pubkey_key_types); \ 227 M_CP_STROPT(pubkey_key_types); \
224 M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ 228 M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \
diff --git a/sshd.c b/sshd.c
index 78729ddde..eb49b5b84 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshd.c,v 1.448 2015/04/27 00:21:21 djm Exp $ */ 1/* $OpenBSD: sshd.c,v 1.449 2015/05/21 06:43:31 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1698,6 +1698,11 @@ main(int ac, char **av)
1698 strcasecmp(options.authorized_keys_command, "none") != 0)) 1698 strcasecmp(options.authorized_keys_command, "none") != 0))
1699 fatal("AuthorizedKeysCommand set without " 1699 fatal("AuthorizedKeysCommand set without "
1700 "AuthorizedKeysCommandUser"); 1700 "AuthorizedKeysCommandUser");
1701 if (options.authorized_principals_command_user == NULL &&
1702 (options.authorized_principals_command != NULL &&
1703 strcasecmp(options.authorized_principals_command, "none") != 0))
1704 fatal("AuthorizedPrincipalsCommand set without "
1705 "AuthorizedPrincipalsCommandUser");
1701 1706
1702 /* 1707 /*
1703 * Check whether there is any path through configured auth methods. 1708 * Check whether there is any path through configured auth methods.
diff --git a/sshd_config.5 b/sshd_config.5
index e40ecedef..884e767b8 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -33,7 +33,7 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: sshd_config.5,v 1.201 2015/05/21 06:38:35 djm Exp $ 36.\" $OpenBSD: sshd_config.5,v 1.202 2015/05/21 06:43:31 djm Exp $
37.Dd $Mdocdate: May 21 2015 $ 37.Dd $Mdocdate: May 21 2015 $
38.Dt SSHD_CONFIG 5 38.Dt SSHD_CONFIG 5
39.Os 39.Os
@@ -287,6 +287,42 @@ directory.
287Multiple files may be listed, separated by whitespace. 287Multiple files may be listed, separated by whitespace.
288The default is 288The default is
289.Dq .ssh/authorized_keys .ssh/authorized_keys2 . 289.Dq .ssh/authorized_keys .ssh/authorized_keys2 .
290.It Cm AuthorizedPrincipalsCommand
291Specifies a program to be used to generate the list of allowed
292certificate principals as per
293.Cm AuthorizedPrincipalsFile .
294The program must be owned by root, not writable by group or others and
295specified by an absolute path.
296.Pp
297Arguments to
298.Cm AuthorizedPrincipalsCommand
299may be provided using the following tokens, which will be expanded
300at runtime: %% is replaced by a literal '%', %u is replaced by the
301username being authenticated and %h is replaced by the home directory
302of the user being authenticated.
303.Pp
304The program should produce on standard output zero or
305more lines of
306.Cm AuthorizedPrincipalsFile
307output.
308If either
309.Cm AuthorizedPrincipalsCommand
310or
311.Cm AuthorizedPrincipalsFile
312is specified, then certificates offered by the client for authentication
313must contain a principal that is listed.
314By default, no AuthorizedPrincipalsCommand is run.
315.It Cm AuthorizedPrincipalsCommandUser
316Specifies the user under whose account the AuthorizedPrincipalsCommand is run.
317It is recommended to use a dedicated user that has no other role on the host
318than running authorized principals commands.
319If
320.Cm AuthorizedPrincipalsCommand
321is specified but
322.Cm AuthorizedPrincipalsCommandUser
323is not, then
324.Xr sshd 8
325will refuse to start.
290.It Cm AuthorizedPrincipalsFile 326.It Cm AuthorizedPrincipalsFile
291Specifies a file that lists principal names that are accepted for 327Specifies a file that lists principal names that are accepted for
292certificate authentication. 328certificate authentication.