Skip to content

Commit

Permalink
Implement loading of credentials from the OS.
Browse files Browse the repository at this point in the history
  • Loading branch information
geertj committed Dec 7, 2007
1 parent 02c3fab commit d1e4835
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 11 deletions.
12 changes: 12 additions & 0 deletions doc/reference.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@
Kerberos server is autodetected.
</para>

<programlisting>

def load(self):
"""Load credentials from the OS."""
</programlisting>

<para>
The function <function>load()</function> loads credentials from the default
operating system credentials store. It raises an exception in case no
credentials are available.
</para>

<programlisting>

def principal(self):
Expand Down
23 changes: 15 additions & 8 deletions lib/ad/core/creds.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Creds(object):
c_config_stack = {}
c_ccache_stack = {}

def __init__(self, domain, use_system_ccache=False, use_system_config=False):
def __init__(self, domain, use_system_config=False):
"""Constructor.
The `domain' parameter specifies the default domain. The default
Expand All @@ -35,7 +35,6 @@ def __init__(self, domain, use_system_ccache=False, use_system_config=False):
self.m_principal = None
self.m_ccache = None
self.m_config = None
self.m_use_system_ccache = use_system_ccache
self.m_use_system_config = use_system_config
self.m_config_cleanup = []

Expand All @@ -44,6 +43,17 @@ def __del__(self):
up temporary files."""
self.release()

def load(self):
"""Load credentials from the OS."""
ccache = krb5.cc_default()
if not os.access(ccache, os.R_OK):
raise Error, 'No ccache found'
self.m_principal = krb5.cc_get_principal(ccache)
self._init_ccache()
krb5.cc_copy_creds(ccache, self.m_ccache)
self._activate_ccache()
self._resolve_servers_for_domain(self.m_domain)

def acquire(self, principal, password=None, keytab=None, server=None):
"""Acquire credentials for `principal'.
Expand All @@ -62,9 +72,8 @@ def acquire(self, principal, password=None, keytab=None, server=None):
else:
domain = self.m_domain
principal = '%s@%s' % (principal, domain)
if not self.m_use_system_ccache:
self._init_ccache()
self._activate_ccache()
self._init_ccache()
self._activate_ccache()
if not self.m_use_system_config:
if server is None:
self._resolve_servers_for_domain(domain)
Expand Down Expand Up @@ -107,8 +116,6 @@ def _init_ccache(self):

def _activate_ccache(self):
"""Active our private credential cache."""
if self.m_use_system_ccache:
return
assert self.m_ccache is not None
orig = self._environ('KRB5CCNAME')
if orig != self.m_ccache:
Expand All @@ -117,7 +124,7 @@ def _activate_ccache(self):

def _release_ccache(self):
"""Release the current Kerberos configuration."""
if self.m_use_system_ccache or not self.m_ccache:
if not self.m_ccache:
return
# Things are complicated by the fact that multiple instances of this
# class my exist. Therefore we need to keep track whether we have set
Expand Down
13 changes: 13 additions & 0 deletions lib/ad/core/test/test_creds.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,19 @@ def test_acquire_keytab(self):
pattern = '.*krbtgt/%s@%s' % (domain.upper(), domain.upper())
assert child.expect([pattern]) == 0

def test_load(self):
self.require(ad_user=True)
domain = self.domain().upper()
principal = '%s@%s' % (self.ad_user_account(), domain)
self.acquire_credentials(principal, self.ad_user_password())
creds = ADCreds(domain)
creds.load()
assert creds.principal().lower() == principal.lower()
ccache, princ, creds = self.list_credentials()
assert princ.lower() == principal.lower()
assert len(creds) > 0
assert creds[0] == 'krbtgt/%s@%s' % (domain, domain)

def test_acquire_multi(self):
self.require(ad_user=True)
domain = self.domain()
Expand Down
111 changes: 111 additions & 0 deletions lib/ad/protocol/krb5.c
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,111 @@ k5_change_password(PyObject *self, PyObject *args)
}


static PyObject *
k5_cc_default(PyObject *self, PyObject *args)
{
krb5_context ctx;
krb5_error_code code;
krb5_ccache ccache;
const char *name;
PyObject *ret;

code = krb5_init_context(&ctx);
RETURN_ON_ERROR("krb5_init_context()", code);
code = krb5_cc_default(ctx, &ccache);
RETURN_ON_ERROR("krb5_cc_default()", code);
name = krb5_cc_get_name(ctx, ccache);
if (name == NULL)
{
PyErr_Format(k5_error, "krb5_cc_default() returned NULL");
return NULL;
}

ret = PyString_FromString(name);
if (ret == NULL)
return ret;

code = krb5_cc_close(ctx, ccache);
RETURN_ON_ERROR("krb5_cc_close()", code);
krb5_free_context(ctx);

return ret;
}

static PyObject *
k5_cc_copy_creds(PyObject *self, PyObject *args)
{
krb5_context ctx;
char *namein, *nameout;
krb5_error_code code;
krb5_ccache ccin, ccout;
krb5_principal principal;

if (!PyArg_ParseTuple( args, "ss", &namein, &nameout))
return NULL;

code = krb5_init_context(&ctx);
RETURN_ON_ERROR("krb5_init_context()", code);
code = krb5_cc_resolve(ctx, namein, &ccin);
RETURN_ON_ERROR("krb5_cc_resolve()", code);
code = krb5_cc_get_principal(ctx, ccin, &principal);
RETURN_ON_ERROR("krb5_cc_get_principal()", code);

code = krb5_cc_resolve(ctx, nameout, &ccout);
RETURN_ON_ERROR("krb5_cc_resolve()", code);
code = krb5_cc_initialize(ctx, ccout, principal);
RETURN_ON_ERROR("krb5_cc_get_initialize()", code);
code = krb5_cc_copy_creds(ctx, ccin, ccout);
RETURN_ON_ERROR("krb5_cc_copy_creds()", code);

code = krb5_cc_close(ctx, ccin);
RETURN_ON_ERROR("krb5_cc_close()", code);
code = krb5_cc_close(ctx, ccout);
RETURN_ON_ERROR("krb5_cc_close()", code);
krb5_free_principal(ctx, principal);
krb5_free_context(ctx);

Py_INCREF(Py_None);
return Py_None;
}


static PyObject *
k5_cc_get_principal(PyObject *self, PyObject *args)
{
krb5_context ctx;
char *ccname, *name;
krb5_error_code code;
krb5_ccache ccache;
krb5_principal principal;
PyObject *ret;

if (!PyArg_ParseTuple( args, "s", &ccname))
return NULL;

code = krb5_init_context(&ctx);
RETURN_ON_ERROR("krb5_init_context()", code);
code = krb5_cc_resolve(ctx, ccname, &ccache);
RETURN_ON_ERROR("krb5_cc_resolve()", code);
code = krb5_cc_get_principal(ctx, ccache, &principal);
RETURN_ON_ERROR("krb5_cc_get_principal()", code);
code = krb5_unparse_name(ctx, principal, &name);
RETURN_ON_ERROR("krb5_unparse_name()", code);

ret = PyString_FromString(name);
if (ret == NULL)
return ret;

code = krb5_cc_close(ctx, ccache);
RETURN_ON_ERROR("krb5_cc_close()", code);
krb5_free_unparsed_name(ctx, name);
krb5_free_principal(ctx, principal);
krb5_free_context(ctx);

return ret;
}


static PyMethodDef k5_methods[] =
{
{ "get_init_creds_password",
Expand All @@ -267,6 +372,12 @@ static PyMethodDef k5_methods[] =
(PyCFunction) k5_set_password, METH_VARARGS },
{ "change_password",
(PyCFunction) k5_change_password, METH_VARARGS },
{ "cc_default",
(PyCFunction) k5_cc_default, METH_VARARGS },
{ "cc_copy_creds",
(PyCFunction) k5_cc_copy_creds, METH_VARARGS },
{ "cc_get_principal",
(PyCFunction) k5_cc_get_principal, METH_VARARGS },
{ NULL, NULL }
};

Expand Down
39 changes: 39 additions & 0 deletions lib/ad/test/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,45 @@ def execute_as_root(self, command):
raise Error, m
return child.before

def acquire_credentials(self, principal, password, ccache=None):
if ccache is None:
ccache = ''
else:
ccache = '-c %s' % ccache
child = pexpect.spawn('kinit %s %s' % (principal, ccache))
child.expect(':')
child.sendline(password)
child.expect(pexpect.EOF)
assert not child.isalive()
if child.exitstatus != 0:
m = 'Command kinit exited with status %s' % child.exitstatus
raise Error, m

def list_credentials(self, ccache=None):
if ccache is None:
ccache = ''
child = pexpect.spawn('klist %s' % ccache)
try:
child.expect('Ticket cache: ([a-zA-Z0-9_/.:-]+)\r\n')
except pexpect.EOF:
m = 'Command klist exited with status %s' % child.exitstatus
raise Error, m
ccache = child.match.group(1)
child.expect('Default principal: ([a-zA-Z0-9_/.:@-]+)\r\n')
principal = child.match.group(1)
creds = []
while True:
i = child.expect(['\r\n', pexpect.EOF,
'\d\d/\d\d/\d\d \d\d:\d\d:\d\d\s+' \
'\d\d/\d\d/\d\d \d\d:\d\d:\d\d\s+' \
'([a-zA-Z0-9_/.:@-]+)\r\n'])
if i == 0:
continue
elif i == 1:
break
creds.append(child.match.group(1))
return ccache, principal, creds

def _iptables_supported(self):
if self.c_iptables is None:
try:
Expand Down
4 changes: 1 addition & 3 deletions tut/tutorial2.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from ad import Client, Creds, activate

domain = 'freeadi.org'
user = 'Administrator'
password = 'Pass123'

creds = Creds(domain)
creds.acquire(user, password)
creds.load()
activate(creds)

client = Client(domain)
Expand Down

0 comments on commit d1e4835

Please sign in to comment.