# HG changeset patch # User rcritten@redhat.com # Date 1189804742 14400 # Node ID 5d72ce870f29bf9ca9a460b25a748860b65c121d # Parent 9a6e0aad232139a9f1d476e8d8a4613872ebb8fb Use ticket forwarding with TurboGears. mod_proxy forwards the principal name and location of the keytab. In order for this keytab to be usable TurboGears and Apache will need to run as the same user. We will also need to listen only on localhost in TG. diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-python/ipaclient.py --- a/ipa-python/ipaclient.py Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-python/ipaclient.py Fri Sep 14 17:19:02 2007 -0400 @@ -47,6 +47,12 @@ class IPAClient: if self.local: self.transport.set_principal(princ) + def set_krbccache(self,krbccache): + """Set the file location of the Kerberos credentials cache to be used + for LDAP authentication""" + if self.local: + self.transport.set_krbccache(krbccache) + # User support def get_user_by_uid(self,uid,sattrs=None): """Get a specific user by uid. If sattrs is set then only those diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-server/ipa-gui/ipagui/controllers.py --- a/ipa-server/ipa-gui/ipagui/controllers.py Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-server/ipa-gui/ipagui/controllers.py Fri Sep 14 17:19:02 2007 -0400 @@ -2,6 +2,7 @@ from pickle import dumps, loads from pickle import dumps, loads from base64 import b64encode, b64decode +import os import cherrypy import turbogears from turbogears import controllers, expose, flash @@ -77,7 +78,7 @@ class Root(controllers.RootController): def usercreate(self, **kw): """Creates a new user""" restrict_post() - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) if kw.get('submit') == 'Cancel': turbogears.flash("Add user cancelled") raise turbogears.redirect('/userlist') @@ -115,7 +116,7 @@ class Root(controllers.RootController): if tg_errors: turbogears.flash("There was a problem with the form!") - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) user = client.get_user_by_uid(uid, user_fields) user_dict = user.toDict() # Edit shouldn't fill in the password field. @@ -132,7 +133,7 @@ class Root(controllers.RootController): def userupdate(self, **kw): """Updates an existing user""" restrict_post() - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) if kw.get('submit') == 'Cancel Edit': turbogears.flash("Edit user cancelled") raise turbogears.redirect('/usershow', uid=kw.get('uid')) @@ -181,7 +182,7 @@ class Root(controllers.RootController): @identity.require(identity.not_anonymous()) def userlist(self, **kw): """Retrieve a list of all users and display them in one huge list""" - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) users = None counter = 0 uid = kw.get('uid') @@ -204,7 +205,7 @@ class Root(controllers.RootController): @identity.require(identity.not_anonymous()) def usershow(self, uid): """Retrieve a single user for display""" - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) try: user = client.get_user_by_uid(uid, user_fields) return dict(user=user.toDict(), fields=forms.user.UserFields()) @@ -242,7 +243,7 @@ class Root(controllers.RootController): if (len(givenname) == 0) or (len(sn) == 0): return "" - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) givenname = givenname.lower() sn = sn.lower() @@ -328,7 +329,7 @@ class Root(controllers.RootController): @expose("ipagui.templates.groupindex") @identity.require(identity.not_anonymous()) def groupindex(self, tg_errors=None): - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) return dict() @@ -339,5 +340,5 @@ class Root(controllers.RootController): @expose("ipagui.templates.resindex") @identity.require(identity.not_anonymous()) def resindex(self, tg_errors=None): - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) return dict() diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-server/ipa-gui/ipagui/proxyprovider.py --- a/ipa-server/ipa-gui/ipagui/proxyprovider.py Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-server/ipa-gui/ipagui/proxyprovider.py Fri Sep 14 17:19:02 2007 -0400 @@ -1,6 +1,7 @@ from turbogears.identity.soprovider impo from turbogears.identity.soprovider import * from turbogears.identity.visitor import * import logging +import os log = logging.getLogger("turbogears.identity") @@ -97,8 +98,10 @@ class ProxyIdentityProvider(SqlObjectIde def load_identity(self, visit_key): try: -# user_name= cherrypy.request.headers['X-FORWARDED-USER'] - user_name= "test@FREEIPA.ORG" + user_name= cherrypy.request.headers['X-FORWARDED-USER'] + os.environ["KRB5CCNAME"] = cherrypy.request.headers['X-FORWARDED-KEYTAB'] +# user_name = "test@FREEIPA.ORG" +# os.environ["KRB5CCNAME"] = "FILE:/tmp/krb5cc_500" except KeyError: return None set_login_attempted( True ) diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-server/ipaserver/ipaldap.py --- a/ipa-server/ipaserver/ipaldap.py Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-server/ipaserver/ipaldap.py Fri Sep 14 17:19:02 2007 -0400 @@ -264,9 +264,9 @@ class IPAdmin(SimpleLDAPObject): def set_proxydn(self, proxydn): self.proxydn = proxydn - def set_keytab(self, keytab): - if keytab is not None: - os.environ["KRB5CCNAME"] = keytab + def set_krbccache(self, krbccache): + if krbccache is not None: + os.environ["KRB5CCNAME"] = krbccache self.sasl_interactive_bind_s("", sasl_auth) self.proxydn = None diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-server/xmlrpc-server/funcs.py --- a/ipa-server/xmlrpc-server/funcs.py Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-server/xmlrpc-server/funcs.py Fri Sep 14 17:19:02 2007 -0400 @@ -49,7 +49,7 @@ class IPAConnPool: def __init__(self): self.freelist = [] - def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None, keytab=None): + def getConn(self, host, port, bindca, bindcert, bindkey, proxydn=None, krbccache=None): conn = None if len(self.freelist) > 0: for i in range(len(self.freelist)): @@ -62,12 +62,12 @@ class IPAConnPool: if proxydn is not None: conn.set_proxydn(proxydn) else: - conn.set_keytab(keytab) + conn.set_krbccache(krbccache) return conn def releaseConn(self, conn): # We can't re-use SASL connections. If proxydn is None it means - # we have a keytab set. See ipaldap.set_keytab + # we have a Kerberos credentails cache set. See ipaldap.set_krbccache if conn.proxydn is None: conn.unbind_s() else: @@ -91,13 +91,13 @@ class IPAServer: self.basedn = ipa.ipautil.realm_to_suffix(ipa.config.config.get_realm()) self.scope = ldap.SCOPE_SUBTREE self.princ = None - self.keytab = None + self.krbccache = None def set_principal(self, princ): self.princ = princ - def set_keytab(self, keytab): - self.keytab = keytab + def set_krbccache(self, krbccache): + self.krbccache = krbccache def get_dn_from_principal(self, princ): """Given a kerberos principal get the LDAP uid""" @@ -115,43 +115,45 @@ class IPAServer: def __setup_connection(self, opts): """Set up common things done in the connection. - If there is a keytab then return None as the proxy dn and the keytab - otherwise return the proxy dn and None as the keytab. + If there is a Kerberos credentials cache then return None as the + proxy dn and the ccache otherwise return the proxy dn and None as + the ccache. We only want one or the other used at one time and we prefer - the keytab. So if there is a keytab, return that and None for - proxy dn to make calling getConn() easier. + the Kerberos credentials cache. So if there is a ccache, return + that and None for proxy dn to make calling getConn() easier. """ if opts: - if opts.get('keytab'): - self.set_keytab(opts['keytab']) + if opts.get('krbccache'): + self.set_krbccache(opts['krbccache']) self.set_principal(None) else: - self.set_keytab(None) + self.set_krbccache(None) self.set_principal(opts['remoteuser']) else: - self.set_keytab(None) - # The caller should have already set the principal + # The caller should have already set the principal or the + # krbccache. If not they'll get an authentication error later. + pass if self.princ is not None: return self.get_dn_from_principal(self.princ), None else: - return None, self.keytab + return None, self.krbccache def getConnection(self, opts): """Wrapper around IPAConnPool.getConn() so we don't have to pass around self.* every time a connection is needed. - For SASL connections (where we have a keytab) we can't set + For SASL connections (where we have a krbccache) we can't set the SSL variables for certificates. It confuses the ldap module. """ global _LDAPPool - (proxy_dn, keytab) = self.__setup_connection(opts) - - if keytab is not None: + (proxy_dn, krbccache) = self.__setup_connection(opts) + + if krbccache is not None: bindca = None bindcert = None bindkey = None @@ -162,7 +164,7 @@ class IPAServer: bindkey = self.bindkey port = self.sslport - return _LDAPPool.getConn(self.host,port,bindca,bindcert,bindkey,proxy_dn,keytab) + return _LDAPPool.getConn(self.host,port,bindca,bindcert,bindkey,proxy_dn,krbccache) def releaseConnection(self, conn): global _LDAPPool diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-server/xmlrpc-server/ipa.conf --- a/ipa-server/xmlrpc-server/ipa.conf Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-server/xmlrpc-server/ipa.conf Fri Sep 14 17:19:02 2007 -0400 @@ -27,6 +27,7 @@ ProxyRequests Off RewriteCond %{IS_SUBREQ}% false RewriteRule .* - [E=RU:%{LA-U:REMOTE_USER}] RequestHeader set X-Forwarded-User %{RU}e + RequestHeader set X-Forwarded-Keytab %{KRB5CCNAME}e # RequestHeader unset Authorization diff -r 9a6e0aad2321 -r 5d72ce870f29 ipa-server/xmlrpc-server/ipaxmlrpc.py --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py Wed Sep 12 18:58:56 2007 -0400 +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py Fri Sep 14 17:19:02 2007 -0400 @@ -138,7 +138,7 @@ class ModXMLRPCRequestHandler(object): opts['remoteuser'] = req.user if req.subprocess_env.get("KRB5CCNAME") is not None: - opts['keytab'] = req.subprocess_env.get("KRB5CCNAME") + opts['krbccache'] = req.subprocess_env.get("KRB5CCNAME") # Tack onto the end of the passed-in arguments any options we also # need