From b6741d81e187fc84177c12ef8ad900d3b5cda6a4 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Fri, 2 Dec 2016 06:48:35 -0500 Subject: Use Anonymous user to obtain FAST armor ccache The anonymous user allows the framework to obtain an armor ccache without relying on usable credentials, either via a keytab or a pkinit and public certificates. This will be needed once the HTTP keytab is moved away for privilege separation. https://fedorahosted.org/freeipa/ticket/5959 Signed-off-by: Simo Sorce Reviewed-By: Jan Cholasta --- ipaserver/install/httpinstance.py | 13 +++++++++++++ ipaserver/install/krbinstance.py | 5 +++-- ipaserver/install/server/upgrade.py | 1 + ipaserver/install/service.py | 16 +++++++++------- ipaserver/plugins/pkinit.py | 3 ++- ipaserver/rpcserver.py | 29 +++++++++++++---------------- 6 files changed, 41 insertions(+), 26 deletions(-) (limited to 'ipaserver') diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py index a4e895cb4..d07b32253 100644 --- a/ipaserver/install/httpinstance.py +++ b/ipaserver/install/httpinstance.py @@ -43,6 +43,7 @@ import ipapython.errors from ipaserver.install import sysupgrade from ipalib import api from ipalib import errors +from ipalib.constants import ANON_USER from ipaplatform.constants import constants from ipaplatform.tasks import tasks from ipaplatform.paths import paths @@ -167,6 +168,7 @@ class HTTPInstance(service.Service): self.step("adding URL rewriting rules", self.__add_include) self.step("configuring httpd", self.__configure_http) self.step("setting up httpd keytab", self._request_service_keytab) + self.step("retrieving anonymous keytab", self.request_anon_keytab) self.step("setting up ssl", self.__setup_ssl) if self.ca_is_configured: self.step("configure certmonger for renewals", @@ -333,6 +335,17 @@ class HTTPInstance(service.Service): os.chown(nss_path, 0, pent.pw_gid) tasks.restore_context(nss_path) + def request_anon_keytab(self): + parent = os.path.dirname(paths.ANON_KEYTAB) + if not os.path.exists(parent): + os.makedirs(parent, 0o755) + self.run_getkeytab(self.api.env.ldap_uri, paths.ANON_KEYTAB, ANON_USER) + + pent = pwd.getpwnam(self.service_user) + os.chmod(parent, 0o700) + os.chown(parent, pent.pw_uid, pent.pw_gid) + os.chown(paths.ANON_KEYTAB, pent.pw_uid, pent.pw_gid) + def __setup_ssl(self): db = certs.CertDB(self.realm, subject_base=self.subject_base) if self.pkcs12_info: diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py index b52b0c3f9..44b382126 100644 --- a/ipaserver/install/krbinstance.py +++ b/ipaserver/install/krbinstance.py @@ -33,6 +33,7 @@ from ipaserver.install import installutils from ipapython import ipautil from ipapython import kernel_keyring from ipalib import api +from ipalib.constants import ANON_USER from ipalib.install import certmonger from ipapython.ipa_log_manager import root_logger from ipapython.dn import DN @@ -381,13 +382,13 @@ class KrbInstance(service.Service): shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) def get_anonymous_principal_name(self): - princ = "WELLKNOWN/ANONYMOUS" - return "%s@%s" % (princ, self.realm) + return "%s@%s" % (ANON_USER, self.realm) def add_anonymous_principal(self): # Create the special anonymous principal princ_realm = self.get_anonymous_principal_name() installutils.kadmin_addprinc(princ_realm) + self._ldap_mod("anon-princ-aci.ldif", self.sub_dict) def __convert_to_gssapi_replication(self): repl = replication.ReplicationManager(self.realm, diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index c7f0f9f44..80abeba53 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -1757,6 +1757,7 @@ def upgrade_configuration(): krb.stop() krb.start() enable_anonymous_principal(krb) + http.request_anon_keytab() if not ds_running: ds.stop(ds_serverid) diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py index b80044f4b..fe6defc9c 100644 --- a/ipaserver/install/service.py +++ b/ipaserver/install/service.py @@ -539,7 +539,7 @@ class Service(object): except errors.DuplicateEntry: pass - def _run_getkeytab(self): + def run_getkeytab(self, ldap_uri, keytab, principal, retrieve=False): """ backup and remove old service keytab (if present) and fetch a new one using ipa-getkeytab. This assumes that the service principal is already @@ -549,16 +549,15 @@ class Service(object): * self.dm_password is not none, then DM credentials are used to fetch keytab """ - self.fstore.backup_file(self.keytab) + self.fstore.backup_file(keytab) try: - os.unlink(self.keytab) + os.unlink(keytab) except OSError: pass - ldap_uri = self.api.env.ldap_uri args = [paths.IPA_GETKEYTAB, - '-k', self.keytab, - '-p', self.principal, + '-k', keytab, + '-p', principal, '-H', ldap_uri] nolog = tuple() @@ -570,6 +569,9 @@ class Service(object): '-w', self.dm_password]) nolog += (self.dm_password,) + if retrieve: + args.extend(['-r']) + ipautil.run(args, nolog=nolog) def _request_service_keytab(self): @@ -580,7 +582,7 @@ class Service(object): "name, keytab, and username") self._add_service_principal() - self._run_getkeytab() + self.run_getkeytab(self.api.env.ldap_uri, self.keytab, self.principal) pent = pwd.getpwnam(self.service_user) os.chown(self.keytab, pent.pw_uid, pent.pw_gid) diff --git a/ipaserver/plugins/pkinit.py b/ipaserver/plugins/pkinit.py index 0ad4b8571..b6b3f3882 100644 --- a/ipaserver/plugins/pkinit.py +++ b/ipaserver/plugins/pkinit.py @@ -22,6 +22,7 @@ from ipalib import Str from ipalib import Object, Command from ipalib import _ from ipalib.plugable import Registry +from ipalib.constants import ANON_USER from ipapython.dn import DN __doc__ = _(""" @@ -71,7 +72,7 @@ def valid_arg(ugettext, action): class pkinit_anonymous(Command): __doc__ = _('Enable or Disable Anonymous PKINIT.') - princ_name = 'WELLKNOWN/ANONYMOUS@%s' % api.env.realm + princ_name = '%s@%s' % (ANON_USER, api.env.realm) default_dn = DN(('krbprincipalname', princ_name), ('cn', api.env.realm), ('cn', 'kerberos'), api.env.basedn) takes_args = ( diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index 34106ee86..357e836f9 100644 --- a/ipaserver/rpcserver.py +++ b/ipaserver/rpcserver.py @@ -42,7 +42,7 @@ from six.moves.xmlrpc_client import Fault from ipalib import plugable, errors from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES from ipalib.frontend import Local -from ipalib.install.kinit import kinit_keytab, kinit_password +from ipalib.install.kinit import kinit_armor, kinit_password from ipalib.backend import Executioner from ipalib.errors import (PublicError, InternalError, JSONError, CCacheError, RefererError, InvalidSessionPassword, NotFound, ACIError, @@ -56,7 +56,7 @@ from ipaserver.plugins.ldap2 import ldap2 from ipalib.backend import Backend from ipalib.krb_utils import ( krb5_format_principal_name, - krb5_format_service_principal_name, get_credentials_if_valid) + get_credentials_if_valid) from ipapython import ipautil from ipaplatform.paths import paths from ipapython.version import VERSION @@ -945,20 +945,18 @@ class login_password(Backend, KerberosSession): return result def kinit(self, user, realm, 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)) - keytab = paths.IPA_KEYTAB + # get anonymous ccache as an armor for FAST to enable OTP auth armor_path = os.path.join(paths.IPA_CCACHES, "armor_{}".format(os.getpid())) - self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s', - armor_principal, keytab, armor_path) + self.debug('Obtaining armor in ccache %s', armor_path) try: - kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path) - except gssapi.exceptions.GSSError as e: - raise CCacheError(message=unicode(e)) + kinit_armor(armor_path) + except RuntimeError as e: + self.error("Failed to obtain armor cache") + # We try to continue w/o armor, 2FA will be impacted + armor_path = None # Format the user as a kerberos principal principal = krb5_format_principal_name(user, realm) @@ -967,11 +965,10 @@ class login_password(Backend, KerberosSession): kinit_password(principal, password, ccache_name, armor_ccache_name=armor_path) - self.debug('Cleanup the armor ccache') - ipautil.run( - [paths.KDESTROY, '-A', '-c', armor_path], - env={'KRB5CCNAME': armor_path}, - raiseonerr=False) + if armor_path: + self.debug('Cleanup the armor ccache') + ipautil.run([paths.KDESTROY, '-A', '-c', armor_path], + env={'KRB5CCNAME': armor_path}, raiseonerr=False) except RuntimeError as e: if ('kinit: Cannot read password while ' 'getting initial credentials') in str(e): -- cgit