URL: https://github.com/freeipa/freeipa/pull/420 Author: martbab Title: #420: WIP: Allow login to WebUI using Kerberos aliases/enterprise principals Action: opened
PR body: """ The logic of the extraction/validation of principal from the request and subsequent authentication was simplified and most of the guesswork will be done by KDC during kinit. This allows for using principal aliases/enterprise principals to log into WebUI and also lays foundation for enabling logons from users from trusted domains. https://fedorahosted.org/freeipa/ticket/6343 """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/420/head:pr420 git checkout pr420
From ca157deb6206221267f2136d455a9cfae9382599 Mon Sep 17 00:00:00 2001 From: Martin Babinsky <mbabi...@redhat.com> Date: Thu, 22 Sep 2016 09:58:47 +0200 Subject: [PATCH] WIP: Allow login to WebUI using Kerberos aliases/enterprise principals The logic of the extraction/validation of principal from the request and subsequent authentication was simplified and most of the guesswork will be done by KDC during kinit. https://fedorahosted.org/freeipa/ticket/6343 --- ipalib/krb_utils.py | 14 ------------- ipaserver/rpcserver.py | 56 ++++++++++++++++++-------------------------------- 2 files changed, 20 insertions(+), 50 deletions(-) diff --git a/ipalib/krb_utils.py b/ipalib/krb_utils.py index d005a87..14b1ccb 100644 --- a/ipalib/krb_utils.py +++ b/ipalib/krb_utils.py @@ -79,20 +79,6 @@ def krb5_parse_ccache(ccache_name): def krb5_unparse_ccache(scheme, name): return '%s:%s' % (scheme.upper(), name) -def krb5_format_principal_name(user, realm): - ''' - Given a Kerberos user principal name and a Kerberos realm - return the Kerberos V5 user principal name. - - :parameters: - user - User principal name. - realm - The Kerberos realm the user exists in. - :returns: - Kerberos V5 user principal name. - ''' - return '%s@%s' % (user, realm) def krb5_format_service_principal_name(service, host, realm): ''' diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index 1da4ec4..5048f63 100644 --- a/ipaserver/rpcserver.py +++ b/ipaserver/rpcserver.py @@ -51,7 +51,7 @@ from ipalib.request import context, destroy_context from ipalib.rpc import (xml_dumps, xml_loads, json_encode_binary, json_decode_binary) -from ipalib.util import parse_time_duration, normalize_name +from ipalib.util import parse_time_duration from ipapython.dn import DN from ipaserver.plugins.ldap2 import ldap2 from ipaserver.session import ( @@ -60,8 +60,9 @@ default_max_session_duration, krbccache_dir, krbccache_prefix) from ipalib.backend import Backend from ipalib.krb_utils import ( - krb_ticket_expiration_threshold, krb5_format_principal_name, + krb_ticket_expiration_threshold, krb5_format_service_principal_name, get_credentials, get_credentials_if_valid) +from ipapython import kerberos from ipapython import ipautil from ipaplatform.paths import paths from ipapython.version import VERSION @@ -937,33 +938,15 @@ def __call__(self, environ, start_response): return self.bad_request(environ, start_response, "no user specified") # allows login in the form user@SERVER_REALM or user@server_realm - # FIXME: uppercasing may be removed when better handling of UPN - # is introduced - - parts = normalize_name(user) - - if "domain" in parts: - # username is of the form user@SERVER_REALM or user@server_realm - - # check whether the realm is server's realm - # Users from other realms are not supported - # (they do not have necessary LDAP entry, LDAP connect will fail) - - if parts["domain"].upper()==self.api.env.realm: - user=parts["name"] - else: - return self.unauthorized(environ, start_response, '', 'denied') - - elif "flatname" in parts: - # username is of the form NetBIOS\user - return self.unauthorized(environ, start_response, '', 'denied') - - else: + try: + user_principal = kerberos.Principal(user) + except Exception: # username is of the form user or of some wild form, e.g. - # user@REALM1@REALM2 or NetBIOS1\NetBIOS2\user (see normalize_name) + # user@REALM1@REALM2 or NetBIOS1\NetBIOS2\user + return self.unauthorized(environ, start_response, '', 'denied') - # wild form username will fail at kinit, so nothing needs to be done - pass + if not (user_principal.is_user or user_principal.is_enterprise): + return self.unauthorized(environ, start_response, '', 'denied') password = query_dict.get('password', None) if password is not None: @@ -977,7 +960,7 @@ def __call__(self, environ, start_response): # Get the ccache we'll use and attempt to get credentials in it with user,password ipa_ccache_name = get_ipa_ccache_name() try: - self.kinit(user, self.api.env.realm, password, ipa_ccache_name) + self.kinit(unicode(user_principal), password, ipa_ccache_name) except PasswordExpired as e: return self.unauthorized(environ, start_response, str(e), 'password-expired') except InvalidSessionPassword as e: @@ -995,12 +978,12 @@ def __call__(self, environ, start_response): return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response) - def kinit(self, user, realm, password, ccache_name): + def kinit(self, principal, password, ccache_name): # get http service ccache as an armor for FAST to enable OTP authentication armor_principal = str(krb5_format_service_principal_name( - 'HTTP', self.api.env.host, realm)) + 'HTTP', self.api.env.host, self.api.env.realm)) keytab = paths.IPA_KEYTAB - armor_name = "%sA_%s" % (krbccache_prefix, user) + armor_name = "%sA_%s" % (krbccache_prefix, principal) armor_path = os.path.join(krbccache_dir, armor_name) self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s', @@ -1011,12 +994,13 @@ def kinit(self, user, realm, password, ccache_name): except gssapi.exceptions.GSSError as e: raise CCacheError(message=unicode(e)) - # Format the user as a kerberos principal - principal = krb5_format_principal_name(user, realm) - try: - kinit_password(principal, password, ccache_name, - armor_ccache_name=armor_path) + kinit_password( + unicode(principal), + password, + ccache_name, + armor_ccache_name=armor_path, + enterprise=True) self.debug('Cleanup the armor ccache') ipautil.run(
-- Manage your subscription for the Freeipa-devel mailing list: https://www.redhat.com/mailman/listinfo/freeipa-devel Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code