You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
294 lines
12 KiB
294 lines
12 KiB
diff --git a/auth-krb5.c b/auth-krb5.c |
|
index 6c62bdf..11c8562 100644 |
|
--- a/auth-krb5.c |
|
+++ b/auth-krb5.c |
|
@@ -54,6 +54,21 @@ |
|
|
|
extern ServerOptions options; |
|
|
|
+int |
|
+ssh_krb5_kuserok(krb5_context krb5_ctx, krb5_principal krb5_user, const char *client, |
|
+ int k5login_exists) |
|
+{ |
|
+ if (options.use_kuserok || !k5login_exists) |
|
+ return krb5_kuserok(krb5_ctx, krb5_user, client); |
|
+ else { |
|
+ char kuser[65]; |
|
+ |
|
+ if (krb5_aname_to_localname(krb5_ctx, krb5_user, sizeof(kuser), kuser)) |
|
+ return 0; |
|
+ return strcmp(kuser, client) == 0; |
|
+ } |
|
+} |
|
+ |
|
static int |
|
krb5_init(void *context) |
|
{ |
|
@@ -157,8 +172,9 @@ auth_krb5_password(Authctxt *authctxt, const char *password) |
|
if (problem) |
|
goto out; |
|
|
|
- if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, |
|
- authctxt->pw->pw_name)) { |
|
+ /* Use !options.use_kuserok here to make ssh_krb5_kuserok() not |
|
+ * depend on the existance of .k5login */ |
|
+ if (!ssh_krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->pw->pw_name, !options.use_kuserok)) { |
|
problem = -1; |
|
goto out; |
|
} |
|
diff --git a/gss-serv-krb5.c b/gss-serv-krb5.c |
|
index 60de320..0a4930e 100644 |
|
--- a/gss-serv-krb5.c |
|
+++ b/gss-serv-krb5.c |
|
@@ -67,6 +67,7 @@ static int ssh_gssapi_krb5_cmdok(krb5_principal, const char *, const char *, |
|
int); |
|
|
|
static krb5_context krb_context = NULL; |
|
+extern int ssh_krb5_kuserok(krb5_context, krb5_principal, const char *, int); |
|
|
|
/* Initialise the krb5 library, for the stuff that GSSAPI won't do */ |
|
|
|
@@ -92,6 +93,103 @@ ssh_gssapi_krb5_init(void) |
|
* Returns true if the user is OK to log in, otherwise returns 0 |
|
*/ |
|
|
|
+/* The purpose of the function is to find out if a Kerberos principal is |
|
+ * allowed to log in as the given local user. This is a general problem with |
|
+ * Kerberized services because by design the Kerberos principals are |
|
+ * completely independent from the local user names. This is one of the |
|
+ * reasons why Kerberos is working well on different operating systems like |
|
+ * Windows and UNIX/Linux. Nevertheless a relationship between a Kerberos |
|
+ * principal and a local user name must be established because otherwise every |
|
+ * access would be granted for every principal with a valid ticket. |
|
+ * |
|
+ * Since it is a general issue libkrb5 provides some functions for |
|
+ * applications to find out about the relationship between the Kerberos |
|
+ * principal and a local user name. They are krb5_kuserok() and |
|
+ * krb5_aname_to_localname(). |
|
+ * |
|
+ * krb5_kuserok() can be used to "Determine if a principal is authorized to |
|
+ * log in as a local user" (from the MIT Kerberos documentation of this |
|
+ * function). Which is exactly what we are looking for and should be the |
|
+ * preferred choice. It accepts the Kerberos principal and a local user name |
|
+ * and let libkrb5 or its plugins determine if they relate to each other or |
|
+ * not. |
|
+ * |
|
+ * krb5_aname_to_localname() can use used to "Convert a principal name to a |
|
+ * local name" (from the MIT Kerberos documentation of this function). It |
|
+ * accepts a Kerberos principle and returns a local name and it is up to the |
|
+ * application to do any additional checks. There are two issues using |
|
+ * krb5_aname_to_localname(). First, since POSIX user names are case |
|
+ * sensitive, the calling application in general has no other choice than |
|
+ * doing a case-sensitive string comparison between the name returned by |
|
+ * krb5_aname_to_localname() and the name used at the login prompt. When the |
|
+ * users are provided by a case in-sensitive server, e.g. Active Directory, |
|
+ * this might lead to login failures because the user typing the name at the |
|
+ * login prompt might not be aware of the right case. Another issue might be |
|
+ * caused if there are multiple alias names available for a single user. E.g. |
|
+ * the canonical name of a user is user@group.department.example.com but there |
|
+ * exists a shorter login name, e.g. user@example.com, to safe typing at the |
|
+ * login prompt. Here krb5_aname_to_localname() can only return the canonical |
|
+ * name, but if the short alias is used at the login prompt authentication |
|
+ * will fail as well. All this can be avoided by using krb5_kuserok() and |
|
+ * configuring krb5.conf or using a suitable plugin to meet the needs of the |
|
+ * given environment. |
|
+ * |
|
+ * The Fedora and RHEL version of openssh contain two patches which modify the |
|
+ * access control behavior: |
|
+ * - openssh-6.6p1-kuserok.patch |
|
+ * - openssh-6.6p1-force_krb.patch |
|
+ * |
|
+ * openssh-6.6p1-kuserok.patch adds a new option KerberosUseKuserok for |
|
+ * sshd_config which controls if krb5_kuserok() is used to check if the |
|
+ * principle is authorized or if krb5_aname_to_localname() should be used. |
|
+ * The reason to add this patch was that krb5_kuserok() by default checks if |
|
+ * a .k5login file exits in the users home-directory. With this the user can |
|
+ * give access to his account for any given principal which might be |
|
+ * in violation with company policies and it would be useful if this can be |
|
+ * rejected. Nevertheless the patch ignores the fact that krb5_kuserok() does |
|
+ * no only check .k5login but other sources as well and checking .k5login can |
|
+ * be disabled for all applications in krb5.conf as well. With this new |
|
+ * option KerberosUseKuserok set to 'no' (and this is the default for RHEL7 |
|
+ * and Fedora 21) openssh can only use krb5_aname_to_localname() with the |
|
+ * restrictions mentioned above. |
|
+ * |
|
+ * openssh-6.6p1-force_krb.patch adds a ksu like behaviour to ssh, i.e. when |
|
+ * using GSSAPI authentication only commands configured in the .k5user can be |
|
+ * executed. Here the wrong assumption that krb5_kuserok() only checks |
|
+ * .k5login is made as well. In contrast ksu checks .k5login directly and |
|
+ * does not use krb5_kuserok() which might be more useful for the given |
|
+ * purpose. Additionally this patch is not synced with |
|
+ * openssh-6.6p1-kuserok.patch. |
|
+ * |
|
+ * The current patch tries to restore the usage of krb5_kuserok() so that e.g. |
|
+ * localauth plugins can be used. It does so by adding a forth parameter to |
|
+ * ssh_krb5_kuserok() which indicates whether .k5login exists or not. If it |
|
+ * does not exists krb5_kuserok() is called even if KerberosUseKuserok is set |
|
+ * to 'no' because the intent of the option is to not check .k5login and if it |
|
+ * does not exists krb5_kuserok() returns a result without checking .k5login. |
|
+ * If .k5login does exists and KerberosUseKuserok is 'no' we fall back to |
|
+ * krb5_aname_to_localname(). This is in my point of view an acceptable |
|
+ * limitation and does not break the current behaviour. |
|
+ * |
|
+ * Additionally with this patch ssh_krb5_kuserok() is called in |
|
+ * ssh_gssapi_krb5_cmdok() instead of only krb5_aname_to_localname() is |
|
+ * neither .k5login nor .k5users exists to allow plugin evaluation via |
|
+ * krb5_kuserok() as well. |
|
+ * |
|
+ * I tried to keep the patch as minimal as possible, nevertheless I see some |
|
+ * areas for improvement which, if they make sense, have to be evaluated |
|
+ * carefully because they might change existing behaviour and cause breaks |
|
+ * during upgrade: |
|
+ * - I wonder if disabling .k5login usage make sense in sshd or if it should |
|
+ * be better disabled globally in krb5.conf |
|
+ * - if really needed openssh-6.6p1-kuserok.patch should be fixed to really |
|
+ * only disable checking .k5login and maybe .k5users |
|
+ * - the ksu behaviour should be configurable and maybe check the .k5login and |
|
+ * .k5users files directly like ksu itself does |
|
+ * - to make krb5_aname_to_localname() more useful an option for sshd to use |
|
+ * the canonical name (the one returned by getpwnam()) instead of the name |
|
+ * given at the login prompt might be useful */ |
|
+ |
|
static int |
|
ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) |
|
{ |
|
@@ -116,7 +214,8 @@ ssh_gssapi_krb5_userok(ssh_gssapi_client *client, char *name) |
|
/* NOTE: .k5login and .k5users must opened as root, not the user, |
|
* because if they are on a krb5-protected filesystem, user credentials |
|
* to access these files aren't available yet. */ |
|
- if (krb5_kuserok(krb_context, princ, name) && k5login_exists) { |
|
+ if (ssh_krb5_kuserok(krb_context, princ, name, k5login_exists) |
|
+ && k5login_exists) { |
|
retval = 1; |
|
logit("Authorized to %s, krb5 principal %s (krb5_kuserok)", |
|
name, (char *)client->displayname.value); |
|
@@ -171,9 +270,8 @@ ssh_gssapi_krb5_cmdok(krb5_principal principal, const char *name, |
|
snprintf(file, sizeof(file), "%s/.k5users", pw->pw_dir); |
|
/* If both .k5login and .k5users DNE, self-login is ok. */ |
|
if (!k5login_exists && (access(file, F_OK) == -1)) { |
|
- return (krb5_aname_to_localname(krb_context, principal, |
|
- sizeof(kuser), kuser) == 0) && |
|
- (strcmp(kuser, luser) == 0); |
|
+ return ssh_krb5_kuserok(krb_context, principal, luser, |
|
+ k5login_exists); |
|
} |
|
if ((fp = fopen(file, "r")) == NULL) { |
|
int saved_errno = errno; |
|
diff --git a/servconf.c b/servconf.c |
|
index 68fb9ef..904c869 100644 |
|
--- a/servconf.c |
|
+++ b/servconf.c |
|
@@ -157,6 +157,7 @@ initialize_server_options(ServerOptions *options) |
|
options->ip_qos_interactive = -1; |
|
options->ip_qos_bulk = -1; |
|
options->version_addendum = NULL; |
|
+ options->use_kuserok = -1; |
|
} |
|
|
|
void |
|
@@ -312,6 +313,8 @@ fill_default_server_options(ServerOptions *options) |
|
options->version_addendum = xstrdup(""); |
|
if (options->show_patchlevel == -1) |
|
options->show_patchlevel = 0; |
|
+ if (options->use_kuserok == -1) |
|
+ options->use_kuserok = 1; |
|
|
|
/* Turn privilege separation on by default */ |
|
if (use_privsep == -1) |
|
@@ -338,7 +341,7 @@ typedef enum { |
|
sPermitRootLogin, sLogFacility, sLogLevel, |
|
sRhostsRSAAuthentication, sRSAAuthentication, |
|
sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, |
|
- sKerberosGetAFSToken, |
|
+ sKerberosGetAFSToken, sKerberosUseKuserok, |
|
sKerberosTgtPassing, sChallengeResponseAuthentication, |
|
sPasswordAuthentication, sKbdInteractiveAuthentication, |
|
sListenAddress, sAddressFamily, |
|
@@ -410,11 +413,13 @@ static struct { |
|
#else |
|
{ "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, |
|
#endif |
|
+ { "kerberosusekuserok", sKerberosUseKuserok, SSHCFG_ALL }, |
|
#else |
|
{ "kerberosauthentication", sUnsupported, SSHCFG_ALL }, |
|
{ "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, |
|
{ "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, |
|
{ "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, |
|
+ { "kerberosusekuserok", sUnsupported, SSHCFG_ALL }, |
|
#endif |
|
{ "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, |
|
{ "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, |
|
@@ -1526,6 +1531,10 @@ process_server_config_line(ServerOptions *options, char *line, |
|
*activep = value; |
|
break; |
|
|
|
+ case sKerberosUseKuserok: |
|
+ intptr = &options->use_kuserok; |
|
+ goto parse_flag; |
|
+ |
|
case sPermitOpen: |
|
arg = strdelim(&cp); |
|
if (!arg || *arg == '\0') |
|
@@ -1811,6 +1820,7 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) |
|
M_CP_INTOPT(max_authtries); |
|
M_CP_INTOPT(ip_qos_interactive); |
|
M_CP_INTOPT(ip_qos_bulk); |
|
+ M_CP_INTOPT(use_kuserok); |
|
M_CP_INTOPT(rekey_limit); |
|
M_CP_INTOPT(rekey_interval); |
|
|
|
@@ -2062,6 +2072,7 @@ dump_config(ServerOptions *o) |
|
dump_cfg_fmtint(sUseDNS, o->use_dns); |
|
dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); |
|
dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); |
|
+ dump_cfg_fmtint(sKerberosUseKuserok, o->use_kuserok); |
|
|
|
/* string arguments */ |
|
dump_cfg_string(sPidFile, o->pid_file); |
|
diff --git a/servconf.h b/servconf.h |
|
index 37cfa9b..5117dfa 100644 |
|
--- a/servconf.h |
|
+++ b/servconf.h |
|
@@ -173,6 +173,7 @@ typedef struct { |
|
|
|
int num_permitted_opens; |
|
|
|
+ int use_kuserok; |
|
char *chroot_directory; |
|
char *revoked_keys_file; |
|
char *trusted_user_ca_keys; |
|
diff --git a/sshd_config b/sshd_config |
|
index adfd7b1..e772ed5 100644 |
|
--- a/sshd_config |
|
+++ b/sshd_config |
|
@@ -87,6 +87,7 @@ ChallengeResponseAuthentication no |
|
#KerberosOrLocalPasswd yes |
|
#KerberosTicketCleanup yes |
|
#KerberosGetAFSToken no |
|
+#KerberosUseKuserok yes |
|
|
|
# GSSAPI options |
|
GSSAPIAuthentication yes |
|
diff --git a/sshd_config.5 b/sshd_config.5 |
|
index 1fb002d..e0e5fff 100644 |
|
--- a/sshd_config.5 |
|
+++ b/sshd_config.5 |
|
@@ -697,6 +697,10 @@ Specifies whether to automatically destroy the user's ticket cache |
|
file on logout. |
|
The default is |
|
.Dq yes . |
|
+.It Cm KerberosUseKuserok |
|
+Specifies whether to look at .k5login file for user's aliases. |
|
+The default is |
|
+.Dq yes . |
|
.It Cm KexAlgorithms |
|
Specifies the available KEX (Key Exchange) algorithms. |
|
Multiple algorithms must be comma-separated. |
|
@@ -862,6 +866,7 @@ Available keywords are |
|
.Cm HostbasedUsesNameFromPacketOnly , |
|
.Cm KbdInteractiveAuthentication , |
|
.Cm KerberosAuthentication , |
|
+.Cm KerberosUseKuserok , |
|
.Cm MaxAuthTries , |
|
.Cm MaxSessions , |
|
.Cm PasswordAuthentication ,
|
|
|