Add sasl support

This commit is contained in:
David Bartley 2007-11-27 19:20:37 -05:00
parent a2f7888d5d
commit 7ce6543ce6
3 changed files with 61 additions and 27 deletions

View File

@ -4,7 +4,7 @@ UNIX Accounts Administration
This module contains functions for creating, deleting, and manipulating
UNIX user accounts and account groups in the CSC LDAP directory.
"""
import re, pwd, grp, os
import re, pwd, grp, os, pwd
from csc.common import conf
from csc.common.excep import InvalidArgument
from csc.backends import ldapi, krb
@ -18,14 +18,14 @@ cfg = {}
def configure():
"""Helper to load the accounts configuration. You need not call this."""
string_fields = [ 'member_shell', 'member_home', 'member_desc',
'member_group', 'club_shell', 'club_home', 'club_desc',
'club_group', 'admin_shell', 'admin_home', 'admin_desc',
'admin_group', 'group_desc', 'username_regex', 'groupname_regex',
'shells_file', 'server_url', 'users_base', 'groups_base',
'admin_bind_dn', 'admin_bind_pw', 'realm', 'admin_principal',
'admin_keytab' ]
'sasl_mech', 'sasl_realm', 'admin_bind_keytab', 'admin_bind_dn',
'admin_bind_userid', 'realm', 'admin_principal', 'admin_keytab' ]
numeric_fields = [ 'member_min_id', 'member_max_id', 'club_min_id',
'club_max_id', 'admin_min_id', 'admin_max_id', 'group_min_id',
'group_max_id', 'min_password_length' ]
@ -78,7 +78,7 @@ class NoSuchGroup(AccountException):
self.account, self.source = account, source
def __str__(self):
return 'Account "%s" not found in %s' % (self.account, self.source)
### Connection Management ###
@ -86,13 +86,16 @@ class NoSuchGroup(AccountException):
ldap_connection = ldapi.LDAPConnection()
krb_connection = krb.KrbConnection()
def connect():
def connect(auth_callback):
"""Connect to LDAP and Kerberos and load configuration. You must call before anything else."""
configure()
# connect to the LDAP server
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
ldap_connection.connect_sasl(cfg['server_url'], cfg['admin_bind_dn'],
cfg['sasl_mech'], cfg['sasl_realm'], cfg['admin_bind_userid'],
('keytab', cfg['admin_bind_keytab']), cfg['users_base'],
cfg['groups_base'])
# connect to the Kerberos master server
krb_connection.connect(cfg['admin_principal'], cfg['admin_keytab'])

View File

@ -9,7 +9,7 @@ Transactions are used in each method that modifies the database.
Future changes to the members database that need to be atomic
must also be moved into this module.
"""
import re, ldap
import re, ldap, os, pwd
from csc.adm import terms
from csc.backends import ldapi
from csc.common import conf
@ -26,7 +26,8 @@ def load_configuration():
"""Load Members Configuration"""
string_fields = [ 'realname_regex', 'server_url', 'users_base',
'groups_base', 'admin_bind_dn', 'admin_bind_pw' ]
'groups_base', 'sasl_mech', 'sasl_realm', 'admin_bind_dn',
'admin_bind_keytab', 'admin_bind_userid' ]
# read configuration file
cfg_tmp = conf.read(CONFIG_FILE)
@ -74,12 +75,14 @@ class NoSuchMember(MemberException):
# global directory connection
ldap_connection = ldapi.LDAPConnection()
def connect():
def connect(auth_callback):
"""Connect to LDAP."""
load_configuration()
ldap_connection.connect(cfg['server_url'], cfg['admin_bind_dn'], cfg['admin_bind_pw'], cfg['users_base'], cfg['groups_base'])
ldap_connection.connect_sasl(cfg['server_url'], cfg['admin_bind_dn'],
cfg['sasl_mech'], cfg['sasl_realm'], cfg['admin_bind_userid'],
('keytab', cfg['admin_bind_keytab']), cfg['users_base'],
cfg['groups_base'])
def disconnect():
"""Disconnect from LDAP."""

View File

@ -13,7 +13,7 @@ have an LDAP entry, even if the account does not log in directly.
This module makes use of python-ldap, a Python module with bindings
to libldap, OpenLDAP's native C client library.
"""
import ldap.modlist
import ldap.modlist, ipc, os
class LDAPException(Exception):
@ -41,36 +41,39 @@ class LDAPConnection(object):
def __init__(self):
self.ldap = None
def connect(self, server, bind_dn, bind_pw, user_base, group_base):
def connect_anon(self, uri, user_base, group_base):
"""
Establish a connection to the LDAP Server.
Parameters:
server - connection string (e.g. ldap://foo.com, ldaps://bar.com)
bind_dn - distinguished name to bind to
bind_pw - password of bind_dn
uri - connection string (e.g. ldap://foo.com, ldaps://bar.com)
user_base - base of the users subtree
group_base - baes of the group subtree
Example: connect('ldaps:///', 'cn=ceo,dc=csclub,dc=uwaterloo,dc=ca',
'secret', 'ou=People,dc=csclub,dc=uwaterloo,dc=ca',
'ou=Group,dc=csclub,dc=uwaterloo,dc=ca')
"""
if bind_pw is None: bind_pw = ''
# open the connection
self.ldap = ldap.initialize(uri)
try:
# authenticate
self.ldap.simple_bind_s('', '')
# open the connection
self.ldap = ldap.initialize(server)
self.user_base = user_base
self.group_base = group_base
# authenticate
self.ldap.simple_bind_s(bind_dn, bind_pw)
def connect_sasl(self, uri, bind_dn, mech, realm, userid, password, user_base, group_base):
except ldap.LDAPError, e:
raise LDAPException("unable to connect: %s" % e)
# open the connection
self.ldap = ldap.initialize(uri)
# authenticate
sasl = Sasl(mech, realm, userid, password)
self.ldap.sasl_interactive_bind_s(bind_dn, sasl)
self.user_base = user_base
self.group_base = group_base
@ -659,6 +662,31 @@ class LDAPConnection(object):
return mlist
class Sasl:
CB_USER = 0x4001
bind_dn = 'dn:uid=%s,cn=%s,cn=%s,cn=auth'
def __init__(self, mech, realm, userid, password):
self.mech = mech
self.bind_dn = self.bind_dn % (userid, realm, mech)
if mech == 'GSSAPI':
type, arg = password
kinit = '/usr/bin/kinit'
kinit_args = [ 'kinit', '%s@%s' % (userid, realm) ]
if type == 'keytab':
kinit_args += [ '-k', '-t', arg ]
pid, kinit_out, kinit_in = ipc.popeni(kinit, kinit_args)
os.waitpid(pid, 0)
def callback(self, id, challenge, prompt, defresult):
if id == self.CB_USER:
return self.bind_dn
else:
return None
### Tests ###
if __name__ == '__main__':