diff options
-rw-r--r-- | ipa-admintools/ipa-adduser | 13 | ||||
-rw-r--r-- | ipa-python/ipaclient.py | 13 | ||||
-rw-r--r-- | ipa-python/rpcclient.py | 16 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/controllers.py | 15 | ||||
-rw-r--r-- | ipa-server/ipa-gui/ipagui/proxyprovider.py | 7 | ||||
-rw-r--r-- | ipa-server/ipaserver/ipaldap.py | 24 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/funcs.py | 60 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/ipa.conf | 1 | ||||
-rw-r--r-- | ipa-server/xmlrpc-server/ipaxmlrpc.py | 3 |
9 files changed, 118 insertions, 34 deletions
diff --git a/ipa-admintools/ipa-adduser b/ipa-admintools/ipa-adduser index 99aadee4..dd99e1e4 100644 --- a/ipa-admintools/ipa-adduser +++ b/ipa-admintools/ipa-adduser @@ -43,6 +43,8 @@ def parse_options(): help="User's first name") parser.add_option("-l", "--lastname", dest="sn", help="User's last name") + parser.add_option("-p", "--password", dest="password", + help="Set user's password") parser.add_option("-s", "--shell", dest="shell", help="Set user's login shell to shell") parser.add_option("--usage", action="store_true", @@ -75,10 +77,11 @@ def main(): else: user.setValue('loginshell', "/bin/bash") + username = args[1] + try: client = ipaclient.IPAClient() client.add_user(user) - print args[1] + " successfully added" except xmlrpclib.Fault, f: print f.faultString return 1 @@ -92,6 +95,14 @@ def main(): print "%s" % (e.message) return 1 + if options.password is not None: + try: + client.modifyPassword(username, None, options.password) + except ipa.ipaerror.IPAError, e: + print "%s" % (e.message) + return 1 + + print username + " successfully added" return 0 main() diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py index 63537f26..880b4785 100644 --- a/ipa-python/ipaclient.py +++ b/ipa-python/ipaclient.py @@ -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 @@ -123,6 +129,13 @@ class IPAClient: result = self.transport.delete_user(uid) return result + def modifyPassword(self,uid,oldpass,newpass): + """Modify a user's password""" + + result = self.transport.modifyPassword(uid,oldpass,newpass) + + return result + def mark_user_deleted(self,uid): """Set a user as inactive by uid.""" diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py index 9e51e981..21ea68bc 100644 --- a/ipa-python/rpcclient.py +++ b/ipa-python/rpcclient.py @@ -195,6 +195,22 @@ class RPCClient: return result + def modifyPassword(self,uid,oldpass,newpass): + """Modify a user's password""" + server = self.setup_server() + + if oldpass is None: + oldpass = "__NONE__" + + try: + result = server.modifyPassword(uid,oldpass,newpass) + except xmlrpclib.Fault, fault: + raise ipaerror.gen_exception(fault.faultCode, fault.faultString) + except socket.error, (value, msg): + raise xmlrpclib.Fault(value, msg) + + return result + def mark_user_deleted(self,uid): """Mark a user as deleted/inactive""" server = self.setup_server() diff --git a/ipa-server/ipa-gui/ipagui/controllers.py b/ipa-server/ipa-gui/ipagui/controllers.py index 60921122..c16f04c6 100644 --- a/ipa-server/ipa-gui/ipagui/controllers.py +++ b/ipa-server/ipa-gui/ipagui/controllers.py @@ -2,6 +2,7 @@ import random from pickle import dumps, loads from base64 import b64encode, b64decode +import os import cherrypy import turbogears from turbogears import controllers, expose, flash @@ -82,7 +83,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') @@ -121,7 +122,7 @@ class Root(controllers.RootController): turbogears.flash("There was a problem with the form!") try: - 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. @@ -141,7 +142,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')) @@ -204,7 +205,7 @@ class Root(controllers.RootController): @identity.require(identity.not_anonymous()) def userlist(self, **kw): """Searches for users and displays list of results""" - client.set_principal(identity.current.user_name) + client.set_krbccache(os.environ["KRB5CCNAME"]) users = None counter = 0 uid = kw.get('uid') @@ -247,7 +248,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()) @@ -285,7 +286,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() @@ -373,7 +374,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() @expose("ipagui.templates.groupnew") diff --git a/ipa-server/ipa-gui/ipagui/proxyprovider.py b/ipa-server/ipa-gui/ipagui/proxyprovider.py index 539d53ef..4499e797 100644 --- a/ipa-server/ipa-gui/ipagui/proxyprovider.py +++ b/ipa-server/ipa-gui/ipagui/proxyprovider.py @@ -1,6 +1,7 @@ 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(SqlObjectIdentityProvider): 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 --git a/ipa-server/ipaserver/ipaldap.py b/ipa-server/ipaserver/ipaldap.py index c0452b05..69d90922 100644 --- a/ipa-server/ipaserver/ipaldap.py +++ b/ipa-server/ipaserver/ipaldap.py @@ -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 @@ -469,6 +469,24 @@ class IPAdmin(SimpleLDAPObject): raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e) return "Success" + def modifyPassword(self,dn,oldpass,newpass): + """Set the user password using RFC 3062, LDAP Password Modify Extended + Operation. This ends up calling the IPA password slapi plugin + handler so the Kerberos password gets set properly. + + oldpass is not mandatory + """ + + sctrl = self.__get_server_controls__() + + try: + if sctrl is not None: + self.set_option(ldap.OPT_SERVER_CONTROLS, sctrl) + self.passwd_s(dn, oldpass, newpass) + except ldap.LDAPError, e: + raise ipaerror.gen_exception(ipaerror.LDAP_DATABASE_ERROR, None, e) + return "Success" + def __wrapmethods(self): """This wraps all methods of SimpleLDAPObject, so that we can intercept the methods that deal with entries. Instead of using a raw list of tuples diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py index 52376f1c..2b93b6b8 100644 --- a/ipa-server/xmlrpc-server/funcs.py +++ b/ipa-server/xmlrpc-server/funcs.py @@ -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) + (proxy_dn, krbccache) = self.__setup_connection(opts) - if keytab is not None: + 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 @@ -524,6 +526,24 @@ class IPAServer: self.releaseConnection(conn) return res + def modifyPassword (self, uid, oldpass, newpass, opts=None): + """Set/Reset a user's password + + uid tells us who's password to change + oldpass is the old password (if available) + newpass is the new password + """ + user_dn = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts) + if user_dn is None: + raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND) + + conn = self.getConnection(opts) + try: + res = conn.modifyPassword(user_dn['dn'], oldpass, newpass) + finally: + self.releaseConnection(conn) + return res + # Group support def __is_group_unique(self, cn, opts): diff --git a/ipa-server/xmlrpc-server/ipa.conf b/ipa-server/xmlrpc-server/ipa.conf index 30a7655a..784f2617 100644 --- a/ipa-server/xmlrpc-server/ipa.conf +++ b/ipa-server/xmlrpc-server/ipa.conf @@ -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 </Proxy> diff --git a/ipa-server/xmlrpc-server/ipaxmlrpc.py b/ipa-server/xmlrpc-server/ipaxmlrpc.py index f2ddd35e..861de8e5 100644 --- a/ipa-server/xmlrpc-server/ipaxmlrpc.py +++ b/ipa-server/xmlrpc-server/ipaxmlrpc.py @@ -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 @@ -308,6 +308,7 @@ def handler(req, profiling=False): h.register_function(f.update_user) h.register_function(f.delete_user) h.register_function(f.mark_user_deleted) + h.register_function(f.modifyPassword) h.register_function(f.get_group_by_cn) h.register_function(f.get_group_by_dn) h.register_function(f.add_group) |