diff --git a/PyKAdminCommon.c b/PyKAdminCommon.c index 638a508..b0f8a53 100644 --- a/PyKAdminCommon.c +++ b/PyKAdminCommon.c @@ -11,6 +11,91 @@ kadm5_get_principal(void *server_handle, krb5_principal principal, */ #include "PyKAdminCommon.h" +#include + +#define TIME_NONE ((time_t) -1) + +int pykadmin_policy_exists(void *server_handle, char *name) { + + kadm5_ret_t retval = KADM5_OK; + kadm5_policy_ent_rec *policy = NULL; + + retval = kadm5_get_policy(server_handle, name, policy); + if (retval == KADM5_OK) + kadm5_free_policy_ent(server_handle, policy); + + return (retval == KADM5_OK); +} + + +inline PyObject *pykadmin_pydatetime_from_timestamp(time_t timestamp) { + + PyDateTime_IMPORT; + + if (timestamp) { + PyObject *datetime = NULL; + PyObject *args = NULL; + + args = Py_BuildValue("(i)", timestamp); + + if (args) { + datetime = PyDateTime_FromTimestamp(args); + Py_DECREF(args); + } + + if (!datetime) + PyErr_SetString(PyExc_AttributeError, NULL); + + return datetime; + } else { + Py_RETURN_NONE; + } +} + +int pykadmin_timestamp_from_pydatetime(PyObject *datetime) { + + PyDateTime_IMPORT; + + time_t timestamp = 0; + struct tm *timeinfo; + + if (datetime) { + + timeinfo = localtime ( ×tamp ); + + timeinfo->tm_year = PyDateTime_GET_YEAR(datetime) - 1900; + timeinfo->tm_mon = PyDateTime_GET_MONTH(datetime) - 1; + timeinfo->tm_mday = PyDateTime_GET_DAY(datetime); + + if (PyDateTime_Check(datetime)) { + timeinfo->tm_hour = PyDateTime_DATE_GET_HOUR(datetime) - 1 ; + timeinfo->tm_min = PyDateTime_DATE_GET_MINUTE(datetime); + timeinfo->tm_sec = PyDateTime_DATE_GET_SECOND(datetime); + } + + timestamp = mktime(timeinfo); + } else { + timestamp = TIME_NONE; + } + + return timestamp; +} + +int pykadmin_seconds_from_pydatetime(PyObject *delta) { + + PyDateTime_IMPORT; + + time_t seconds = 0; + + if (delta) { + seconds += PyDateTime_DELTA_GET_SECONDS(delta); + seconds += PyDateTime_DELTA_GET_DAYS(delta) * 24 * 3600; + } + + return seconds; + +} + krb5_error_code pykadmin_unpack_xdr_osa_princ_ent_rec(PyKAdminObject *kadmin, krb5_db_entry *kdb, osa_princ_ent_rec *adb) { @@ -101,7 +186,6 @@ static krb5_tl_data *dup_tl_data(krb5_tl_data *tl) return n; } - krb5_error_code pykadmin_kadm_from_kdb(PyKAdminObject *kadmin, krb5_db_entry *kdb, kadm5_principal_ent_rec *entry, long mask) { krb5_error_code retval = 0; @@ -265,6 +349,68 @@ krb5_error_code pykadmin_kadm_from_kdb(PyKAdminObject *kadmin, krb5_db_entry *kd } + +/* +typedef struct _kadm5_policy_ent_t { + char *policy; + long pw_min_life; + long pw_max_life; + long ; + long ; + long ; + long ; + + // version 3 fields + int32 krb5_kvno pw_max_fail; + int32 krb5_deltat pw_failcnt_interval; + int32 krb5_deltat pw_lockout_duration; +} kadm5_policy_ent_rec, *kadm5_policy_ent_t; + +typedef struct _osa_policy_ent_t { + int version; + char *name; + krb5_ui_4 pw_min_life; + krb5_ui_4 pw_max_life; + krb5_ui_4 pw_min_length; + krb5_ui_4 pw_min_classes; + krb5_ui_4 pw_history_num; + krb5_ui_4 policy_refcnt; + + // Only valid if version > 1 + krb5_ui_4 pw_max_fail; // pwdMaxFailure + krb5_ui_4 pw_failcnt_interval; // pwdFailureCountInterval + krb5_ui_4 pw_lockout_duration; // pwdLockoutDuration +} osa_policy_ent_rec, *osa_policy_ent_t; + + +*/ + + +krb5_error_code pykadmin_policy_kadm_from_osa(krb5_context ctx, osa_policy_ent_rec *osa, kadm5_policy_ent_rec *entry, long mask) { + + krb5_error_code retval = 0; + + memset(entry, 0, sizeof(kadm5_policy_ent_rec)); + + entry->policy = strdup(osa->name); + entry->pw_min_life = osa->pw_min_life; + entry->pw_max_life = osa->pw_max_life; + entry->pw_min_length = osa->pw_min_length; + entry->pw_min_classes = osa->pw_min_classes; + entry->pw_history_num = osa->pw_history_num; + entry->policy_refcnt = osa->policy_refcnt; + + if (osa->version > 1) { + entry->pw_max_fail = osa->pw_max_fail; + entry->pw_failcnt_interval = osa->pw_failcnt_interval; + entry->pw_lockout_duration = osa->pw_lockout_duration; + } + + return retval; +} + + + int pykadmin_compare_tl_data(krb5_context ctx, krb5_tl_data *a, krb5_tl_data *b) { int result = 1; @@ -354,6 +500,24 @@ int pykadmin_principal_ent_rec_compare(krb5_context ctx, kadm5_principal_ent_rec return result; } +int pykadmin_policy_ent_rec_compare(krb5_context ctx, kadm5_policy_ent_rec *a, kadm5_policy_ent_rec *b) { + + int result = 1; + + result &= (strcmp(a->policy, b->policy) == 0); + + result &= (a->pw_min_life == b->pw_min_life); + result &= (a->pw_max_life == b->pw_max_life); + result &= (a->pw_min_length == b->pw_min_length); + result &= (a->pw_min_classes == b->pw_min_classes); + result &= (a->pw_history_num == b->pw_history_num); + result &= (a->policy_refcnt == b->policy_refcnt); + result &= (a->pw_max_fail == b->pw_max_fail); + result &= (a->pw_failcnt_interval == b->pw_failcnt_interval); + result &= (a->pw_lockout_duration == b->pw_lockout_duration); + + return result; +} /* krb5_error_code pykadmin_copy_kadm_ent_rec(PyKAdminObject *kadmin, kadm5_principal_ent_rec *src, kadm5_principal_ent_rec *dst) { diff --git a/PyKAdminCommon.h b/PyKAdminCommon.h index 35dd6d9..6071851 100644 --- a/PyKAdminCommon.h +++ b/PyKAdminCommon.h @@ -3,17 +3,35 @@ #define PYKADMINCOMMON_H #include + #include #include #include #include -#include "PyKadminXDR.h" +#include "PyKAdminXDR.h" #include "PyKAdminObject.h" + +#ifndef PYTHON3 +#define PyDateTime_DELTA_GET_DAYS(o) (((PyDateTime_Delta*)o)->days) +#define PyDateTime_DELTA_GET_SECONDS(o) (((PyDateTime_Delta*)o)->seconds) +#define PyDateTime_DELTA_GET_MICROSECONDS(o) (((PyDateTime_Delta*)o)->microseconds) +#endif + +int pykadmin_policy_exists(void *server_handle, char *name); + +inline PyObject *pykadmin_pydatetime_from_timestamp(time_t timestamp); +int pykadmin_timestamp_from_pydatetime(PyObject *datetime); + +int pykadmin_seconds_from_pydatetime(PyObject *delta); + krb5_error_code pykadmin_kadm_from_kdb(PyKAdminObject *kadmin, krb5_db_entry *kdb, kadm5_principal_ent_rec *entry, long mask); +krb5_error_code pykadmin_policy_kadm_from_osa(krb5_context ctx, osa_policy_ent_rec *osa, kadm5_policy_ent_rec *entry, long mask); + int pykadmin_principal_ent_rec_compare(krb5_context ctx, kadm5_principal_ent_rec *a, kadm5_principal_ent_rec *b); +int pykadmin_policy_ent_rec_compare(krb5_context ctx, kadm5_policy_ent_rec *a, kadm5_policy_ent_rec *b); // TODO diff --git a/PyKAdminErrors.c b/PyKAdminErrors.c index 6fef596..39dbaaa 100644 --- a/PyKAdminErrors.c +++ b/PyKAdminErrors.c @@ -3,12 +3,12 @@ void PyKAdminError_insert(PyObject *module, kadm5_ret_t retval, char *error_name, char *error_string) { - PyObject *error_number = PyLong_FromUnsignedLong(retval); - PyObject *exception = NULL; - PyObject *error_tuple = NULL; - uint32_t length = strlen(error_name) + 0xF; + PyObject *error_number = PyLong_FromUnsignedLong(retval); + PyObject *exception = NULL; + PyObject *error_tuple = NULL; + uint32_t length = strlen(error_name) + 0xF; - char *real_name = malloc(length); + char *real_name = malloc(length); snprintf(real_name, length, "kadmin.%s", error_name); diff --git a/PyKAdminObject.c b/PyKAdminObject.c index a531a62..b978f52 100644 --- a/PyKAdminObject.c +++ b/PyKAdminObject.c @@ -58,6 +58,10 @@ static PyObject *PyKAdminObject_new(PyTypeObject *type, PyObject *args, PyObject //if (!self->realm) { // // todo : fail //} + + self->_storage = PyDict_New(); + + } return (PyObject *)self; @@ -145,7 +149,7 @@ static PyKAdminPrincipalObject *PyKAdminObject_get_principal(PyKAdminObject *sel return NULL; } - if (self->server_handle) { + if (self) { principal = PyKAdminPrincipalObject_principal_with_name(self, client_name); } @@ -153,6 +157,21 @@ static PyKAdminPrincipalObject *PyKAdminObject_get_principal(PyKAdminObject *sel return principal; } +static PyKAdminPolicyObject *PyKAdminObject_get_policy(PyKAdminObject *self, PyObject *args, PyObject *kwds) { + + PyKAdminPolicyObject *policy = NULL; + char *policy_name = NULL; + + if (!PyArg_ParseTuple(args, "s", &policy_name)) + return NULL; + + if (self->server_handle) { + policy = PyKAdminPolicyObject_policy_with_name(self, policy_name); + } + + return policy; +} + static PyKAdminIterator *PyKAdminObject_principal_iter(PyKAdminObject *self, PyObject *args, PyObject *kwds) { @@ -183,26 +202,35 @@ static PyKAdminIterator *PyKAdminObject_policy_iter(PyKAdminObject *self, PyObje return PyKAdminIterator_create(self, mode, match); } + +#ifdef KADMIN_LOCAL + static int kdb_iter_princs(void *data, krb5_db_entry *kdb) { PyKAdminObject *self = (PyKAdminObject *)data; PyObject *result = NULL; + //PyObject *args = NULL; - PyKAdminPrincipalObject *principal = PyKadminPrincipalObject_principal_with_db_entry(self, kdb); + PyKAdminPrincipalObject *principal = PyKAdminPrincipalObject_principal_with_db_entry(self, kdb); if (principal) { if (self->each_principal.callback) { + + //args = PyTuple_Pack(2, principal, self->each_principal.data); + //result = PyObject_Call(self->each_principal.callback, args, NULL); result = PyObject_CallFunctionObjArgs(self->each_principal.callback, principal, self->each_principal.data, NULL); + //Py_DECREF(args); + if (!result) { // use self to hold exception } } - KAdminPrincipal_destroy(principal); + PyKAdminPrincipalObject_destroy(principal); } return 0; @@ -220,15 +248,19 @@ static PyObject *PyKAdminObject_each_principal(PyKAdminObject *self, PyObject *a static char *kwlist[] = {"", "data", "match", NULL}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|Oz", kwlist, &PyFunction_Type, &self->each_principal.callback, &self->each_principal.data, &match)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, /*&PyFunction_Type,*/ &self->each_principal.callback, &self->each_principal.data, &match)) return NULL; if (!self->each_principal.data) self->each_principal.data = Py_None; + + Py_XINCREF(self->each_principal.callback); Py_XINCREF(self->each_principal.data); + + lock = kadm5_lock(self->server_handle); if (!lock || (lock == KRB5_PLUGIN_OP_NOTSUPP)) { @@ -260,33 +292,96 @@ static void kdb_iter_pols(void *data, osa_policy_ent_rec *entry) { PyKAdminObject *self = (PyKAdminObject *)data; + PyObject *result = NULL; + + PyKAdminPolicyObject *policy = PyKAdminPolicyObject_policy_with_osa_entry(self, entry); + + if (policy) { + + if (self->each_policy.callback) { + + result = PyObject_CallFunctionObjArgs(self->each_policy.callback, policy, self->each_policy.data, NULL); + + if (!result) { + // use self to hold exception + } + + } + PyKAdminPolicyObject_destroy(policy); + } } static PyObject *PyKAdminObject_each_policy(PyKAdminObject *self, PyObject *args, PyObject *kwds) { - return NULL; -} + + char *match = NULL; + krb5_error_code retval = 0; + kadm5_ret_t lock = 0; + static char *kwlist[] = {"", "data", "match", NULL}; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|Oz", kwlist, &PyFunction_Type, &self->each_policy.callback, &self->each_policy.data, &match)) + return NULL; + + if (!self->each_policy.data) + self->each_policy.data = Py_None; + + Py_XINCREF(self->each_policy.callback); + Py_XINCREF(self->each_policy.data); + + lock = kadm5_lock(self->server_handle); + + if (!lock || (lock == KRB5_PLUGIN_OP_NOTSUPP)) { + + krb5_clear_error_message(self->context); + + retval = krb5_db_iter_policy(self->context, match, kdb_iter_pols, (void *)self); + + if (lock != KRB5_PLUGIN_OP_NOTSUPP) { + lock = kadm5_unlock(self->server_handle); + } + } + + Py_XDECREF(self->each_policy.callback); + Py_XDECREF(self->each_policy.data); + + if (retval) { + // TODO raise proper exception + return NULL; + } + + Py_RETURN_TRUE; -static PyKAdminPrincipalObject *PyKAdminObject_list_principals(PyKAdminObject *self, PyObject *args, PyObject *kwds) { - return NULL; } +#endif static PyMethodDef PyKAdminObject_methods[] = { - {"getprinc", (PyCFunction)PyKAdminObject_get_principal, METH_VARARGS, ""}, - {"get_principal", (PyCFunction)PyKAdminObject_get_principal, METH_VARARGS, ""}, + + {"ank", (PyCFunction)PyKAdminObject_create_principal, METH_VARARGS, ""}, + {"addprinc", (PyCFunction)PyKAdminObject_create_principal, METH_VARARGS, ""}, + {"add_principal", (PyCFunction)PyKAdminObject_create_principal, METH_VARARGS, ""}, {"delprinc", (PyCFunction)PyKAdminObject_delete_principal, METH_VARARGS, ""}, {"delete_principal", (PyCFunction)PyKAdminObject_delete_principal, METH_VARARGS, ""}, - {"ank", (PyCFunction)PyKAdminObject_create_principal, METH_VARARGS, ""}, - {"create_princ", (PyCFunction)PyKAdminObject_create_principal, METH_VARARGS, ""}, - {"create_principal", (PyCFunction)PyKAdminObject_create_principal, METH_VARARGS, ""}, - {"list_principals", (PyCFunction)PyKAdminObject_list_principals, METH_VARARGS, ""}, + // kadmin modify princ, rename princ + + {"getprinc", (PyCFunction)PyKAdminObject_get_principal, METH_VARARGS, ""}, + {"get_principal", (PyCFunction)PyKAdminObject_get_principal, METH_VARARGS, ""}, + + + + {"getpol", (PyCFunction)PyKAdminObject_get_policy, METH_VARARGS, ""}, + {"get_policy", (PyCFunction)PyKAdminObject_get_policy, METH_VARARGS, ""}, + {"principals", (PyCFunction)PyKAdminObject_principal_iter, (METH_VARARGS | METH_KEYWORDS), ""}, {"policies", (PyCFunction)PyKAdminObject_policy_iter, (METH_VARARGS | METH_KEYWORDS), ""}, - + + // todo implement + {"lock", (PyCFunction)NULL, METH_NOARGS, ""}, + {"unlock", (PyCFunction)NULL, METH_NOARGS, ""}, + #ifdef KADMIN_LOCAL /* due to the nature of how the kadm5clnt library interfaces with the kerberos database over the rpc layer diff --git a/PyKAdminObject.h b/PyKAdminObject.h index b548fe0..2c724d5 100644 --- a/PyKAdminObject.h +++ b/PyKAdminObject.h @@ -23,10 +23,13 @@ typedef struct { each_iteration_t each_principal; each_iteration_t each_policy; + + PyObject *_storage; } PyKAdminObject; PyTypeObject PyKAdminObject_Type; + PyKAdminObject *PyKAdminObject_create(void); void PyKAdminObject_destroy(PyKAdminObject *self); diff --git a/PyKAdminPolicyObject.c b/PyKAdminPolicyObject.c index 7dce287..5b3e04f 100644 --- a/PyKAdminPolicyObject.c +++ b/PyKAdminPolicyObject.c @@ -3,11 +3,20 @@ #include "PyKAdminErrors.h" #include "PyKAdminIterator.h" #include "PyKAdminPrincipalObject.h" + #include "PyKAdminPolicyObject.h" static void PyKAdminPolicyObject_dealloc(PyKAdminPolicyObject *self) { - self->ob_type->tp_free((PyObject*)self); + if (self) { + kadm5_free_policy_ent(self->kadmin->server_handle, &self->entry); + + Py_XDECREF(self->kadmin); + + PyObject_Del((PyObject*)self); + + //self->ob_type->tp_free((PyObject*)self); + } } static PyObject *PyKAdminPolicyObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { @@ -16,13 +25,26 @@ static PyObject *PyKAdminPolicyObject_new(PyTypeObject *type, PyObject *args, Py self = (PyKAdminPolicyObject *)type->tp_alloc(type, 0); - if (self != NULL) { - } + if (!self) + return NULL; + + memset(&self->entry, 0, sizeof(kadm5_policy_ent_rec)); return (PyObject *)self; } +static kadm5_ret_t _PyKAdminPolicyObject_load(PyKAdminPolicyObject *self, char *policy_name) { + + kadm5_ret_t retval = 0; + + retval = kadm5_get_policy(self->kadmin->server_handle, policy_name, &self->entry); + + return retval; +} + + + static int PyKAdminPolicyObject_init(PyKAdminPolicyObject *self, PyObject *args, PyObject *kwds) { return 0; } @@ -32,9 +54,68 @@ static PyMethodDef PyKAdminPolicyObject_methods[] = { {NULL, NULL, 0, NULL} }; +/* +Policy: test_policy +Maximum password life: 0 +Minimum password life: 864000 +Minimum password length: 1 +Minimum number of password character classes: 1 +Number of old keys kept: 1 +Reference count: 0 +Maximum password failures before lockout: 10 +Password failure count reset interval: 0 days 00:00:00 +Password lockout duration: 0 days 00:00:00 +*/ + +static int KAdminPolicyObject_print(PyKAdminPolicyObject *self, FILE *file, int flags){ + // TODO + + return 0; +} + + +PyObject *PyKAdminPolicy_RichCompare(PyObject *o1, PyObject *o2, int opid) { + + PyKAdminPolicyObject *a = (PyKAdminPolicyObject *)o1; + PyKAdminPolicyObject *b = (PyKAdminPolicyObject *)o2; + + PyObject *result = NULL; + + int equal = pykadmin_policy_ent_rec_compare(a->kadmin->context, &a->entry, &b->entry); + + switch (opid) { + + case Py_EQ: + result = ((a == b) || equal) ? Py_True : Py_False; + break; + case Py_NE: + result = ((a != b) && !equal) ? Py_True : Py_False; + break; + case Py_LT: + case Py_LE: + case Py_GT: + case Py_GE: + default: + result = Py_NotImplemented; + } + + Py_XINCREF(result); + return result; +} + +char *PyKAdminPolicyObject_policy_name(PyKAdminPolicyObject *self) { + + char *name = NULL; + + if (self) + name = self->entry.policy; + + return name; +} + PyTypeObject PyKAdminPolicyObject_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) +// 0, /*ob_size*/ "kadmin.KAdminPolicy", /*tp_name*/ sizeof(PyKAdminPolicyObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ @@ -54,10 +135,10 @@ PyTypeObject PyKAdminPolicyObject_Type = { 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ - "KAdmin objects", /* tp_doc */ + "Python KAdmin Policy Object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ - 0, /* tp_richcompare */ + PyKAdminPolicy_RichCompare, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ @@ -70,29 +151,57 @@ PyTypeObject PyKAdminPolicyObject_Type = { 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)PyKAdminPolicyObject_init, /* tp_init */ - 0, /* tp_alloc */ + PyType_GenericAlloc, /* tp_alloc */ PyKAdminPolicyObject_new, /* tp_new */ }; +PyKAdminPolicyObject *PyKAdminPolicyObject_policy_with_name(PyKAdminObject *kadmin, char *name) { + + kadm5_ret_t retval = 0; + PyKAdminPolicyObject *policy = NULL; + + policy = (PyKAdminPolicyObject *)PyKAdminPolicyObject_new(&PyKAdminPolicyObject_Type, NULL, NULL); + + if (policy) { + Py_XINCREF(kadmin); + policy->kadmin = kadmin; + + retval = _PyKAdminPolicyObject_load(policy, name); + if (retval) { + PyKAdminPolicyObject_dealloc(policy); + } -PyKAdminPolicyObject *PyKAdminPolicyObject_create(PyKAdminObject *kadmin, char *name) { + } + + return policy; +} +PyKAdminPolicyObject *PyKAdminPolicyObject_policy_with_osa_entry(PyKAdminObject *kadmin, osa_policy_ent_rec *entry) { + PyKAdminPolicyObject *policy = NULL; + krb5_error_code retval = 0; + policy = (PyKAdminPolicyObject *)PyKAdminPolicyObject_new(&PyKAdminPolicyObject_Type, NULL, NULL); if (policy) { Py_XINCREF(kadmin); policy->kadmin = kadmin; - } - //_KAdminPolicy_load_principal(policy, client_name); + retval = pykadmin_policy_kadm_from_osa(kadmin->context, entry, &policy->entry, 0); + + if (retval) { + // this will never happen for while the above is called. + } + } return policy; } + + void PyKAdminPolicyObject_destroy(PyKAdminPolicyObject *self) { PyKAdminPolicyObject_dealloc(self); } diff --git a/PyKAdminPolicyObject.h b/PyKAdminPolicyObject.h index 18611cf..00d19a1 100644 --- a/PyKAdminPolicyObject.h +++ b/PyKAdminPolicyObject.h @@ -6,20 +6,30 @@ #include #include #include +#include #include #include +#include "PyKAdminCommon.h" + extern time_t get_date(char *); typedef struct { PyObject_HEAD PyKAdminObject *kadmin; - kadm5_policy_ent_rec policy; + kadm5_policy_ent_rec entry; } PyKAdminPolicyObject; PyTypeObject PyKAdminPolicyObject_Type; -PyKAdminPolicyObject *PyKAdminPolicyObject_create(PyKAdminObject *kadmin, char *name); +//#define PyKAdminPolicy_Check(policy) PyObject_TypeCheck(policy, &PyKAdminPolicyObject_Type) +#define PyKAdminPolicyObject_CheckExact(obj) (Py_TYPE(obj) == &PyKAdminPolicyObject_Type) + +char *PyKAdminPolicyObject_policy_name(PyKAdminPolicyObject *self); + +PyKAdminPolicyObject *PyKAdminPolicyObject_policy_with_name(PyKAdminObject *kadmin, char *name); +PyKAdminPolicyObject *PyKAdminPolicyObject_policy_with_osa_entry(PyKAdminObject *kadmin, osa_policy_ent_rec *entry); + void PyKAdminPolicyObject_destroy(PyKAdminPolicyObject *self); #endif diff --git a/PyKAdminPrincipalObject.c b/PyKAdminPrincipalObject.c index bcaed3a..f7e4aa5 100644 --- a/PyKAdminPrincipalObject.c +++ b/PyKAdminPrincipalObject.c @@ -7,10 +7,32 @@ #include "PyKAdminCommon.h" -#define Principal_Check(princ) PyObject_TypeCheck(princ, &PyKAdminPrincipalObject_Type) - -static void KAdminPrincipal_dealloc(PyKAdminPrincipalObject *self) { - +#include + +#define TIME_NONE ((time_t) -1) + +static const unsigned int kFLAG_MAX = + ( KRB5_KDB_DISALLOW_POSTDATED + | KRB5_KDB_DISALLOW_FORWARDABLE + | KRB5_KDB_DISALLOW_TGT_BASED + | KRB5_KDB_DISALLOW_RENEWABLE + | KRB5_KDB_DISALLOW_PROXIABLE + | KRB5_KDB_DISALLOW_DUP_SKEY + | KRB5_KDB_DISALLOW_ALL_TIX + | KRB5_KDB_REQUIRES_PRE_AUTH + | KRB5_KDB_REQUIRES_HW_AUTH + | KRB5_KDB_REQUIRES_PWCHANGE + | KRB5_KDB_DISALLOW_SVR + | KRB5_KDB_PWCHANGE_SERVICE + | KRB5_KDB_SUPPORT_DESMD5 + | KRB5_KDB_NEW_PRINC + | KRB5_KDB_OK_AS_DELEGATE + | KRB5_KDB_OK_TO_AUTH_AS_DELEGATE + | KRB5_KDB_NO_AUTH_DATA_REQUIRED ); + + +static void PyKAdminPrincipal_dealloc(PyKAdminPrincipalObject *self) { + kadm5_free_principal_ent(self->kadmin->server_handle, &self->entry); Py_XDECREF(self->kadmin); @@ -18,36 +40,48 @@ static void KAdminPrincipal_dealloc(PyKAdminPrincipalObject *self) { self->ob_type->tp_free((PyObject*)self); } -static PyObject *KAdminPrincipal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { +static PyObject *PyKAdminPrincipal_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { PyKAdminPrincipalObject *self; self = (PyKAdminPrincipalObject *)type->tp_alloc(type, 0); - if (!self) - return NULL; - - memset(&self->entry, 0, sizeof(kadm5_principal_ent_rec)); - return (PyObject *)self; + if (self) { + memset(&self->entry, 0, sizeof(kadm5_principal_ent_rec)); + } + + return (PyObject *)self; + } -static int KAdminPrincipal_init(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { +static int PyKAdminPrincipal_init(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { return 0; } -static int KAdminPrincipal_print(PyKAdminPrincipalObject *self, FILE *file, int flags){ +static int PyKAdminPrincipal_print(PyKAdminPrincipalObject *self, FILE *file, int flags){ - krb5_error_code retval = 0; - char *client_name = NULL; + static const char *kPRINT_FORMAT = "%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s\n%s: %s"; - if (self->kadmin) { - - retval = krb5_unparse_name(self->kadmin->context, self->entry.principal, &client_name); - /*if (retval) { - printf("%d\n", retval); - }*/ + krb5_error_code errno; + char *client_name = NULL; - printf(" %s", client_name); + if (self && self->kadmin) { + + errno = krb5_unparse_name(self->kadmin->context, self->entry.principal, &client_name); + + fprintf(file, kPRINT_FORMAT, + "Principal", client_name, + "Expiration date", NULL, + "Last password change", NULL, + "Password expiration date", NULL, + "Maximum ticket life", NULL, + "Maximum renewable life", NULL, + "Last modified", NULL, + "Last successful authentication", NULL, + "Last failed authentication", NULL, + "Failed password attempts", NULL, + "Number of keys", NULL + ); } if (client_name) @@ -58,204 +92,124 @@ static int KAdminPrincipal_print(PyKAdminPrincipalObject *self, FILE *file, int -static PyMemberDef KAdminPrincipal_members[] = { +static PyObject *PyKAdminPrincipal_set_attributes(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - {"last_password_change", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, last_pwd_change), READONLY, ""}, - {"expire_time", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, princ_expire_time), READONLY, ""}, - {"password_expiration", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, pw_expiration), READONLY, ""}, - {"modified_time", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, mod_date), READONLY, ""}, - {"max_life", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, max_life), READONLY, ""}, - - {"max_renewable_life", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, max_renewable_life), READONLY, ""}, - {"last_success", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, last_success), READONLY, ""}, - {"last_failed", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, last_failed), READONLY, ""}, - {"failed_auth_count", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, fail_auth_count), READONLY, ""}, - - {"key_version_number", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, kvno), READONLY, ""}, - {"master_key_version_number", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, mkvno), READONLY, ""}, - - {"policy", T_STRING, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, policy), READONLY, ""}, - - {NULL} -}; + //kadm5_ret_t retval = KADM5_OK; + unsigned int flag = 0; + if (!PyArg_ParseTuple(args, "i", &flag)) + return NULL; -static PyObject *PyKAdminPrincipal_get_principal(PyKAdminPrincipalObject *self, void *closure) { - - char *client_name = NULL; - - krb5_unparse_name(self->kadmin->context, self->entry.principal, &client_name); + if (flag <= kFLAG_MAX) { - PyObject *principal = Py_BuildValue("s", client_name); + self->entry.attributes |= flag; + self->mask |= KADM5_ATTRIBUTES; - free(client_name); + //retval = kadm5_modify_principal(self->kadmin->server_handle, &self->entry, KADM5_ATTRIBUTES); + //if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_modify_principal"); return NULL; } + } - return principal; + Py_RETURN_TRUE; } -static PyObject *KAdminPrincipal_set_expire(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - - kadm5_ret_t retval; - time_t date = 0; - char *expire = NULL; +static PyObject *PyKAdminPrincipal_unset_attributes(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - if (!PyArg_ParseTuple(args, "s", &expire)) + //kadm5_ret_t retval = KADM5_OK; + unsigned int flag = 0; + + if (!PyArg_ParseTuple(args, "(i)", &flag)) return NULL; - - date = get_date(expire); - self->entry.princ_expire_time = date; + if (flag <= kFLAG_MAX) { - retval = kadm5_modify_principal(self->kadmin->server_handle, &self->entry, KADM5_PRINC_EXPIRE_TIME); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_modify_principal"); return NULL; } + self->entry.attributes &= ~flag; + self->mask |= KADM5_ATTRIBUTES; + + //retval = kadm5_modify_principal(self->kadmin->server_handle, &self->entry, KADM5_ATTRIBUTES); + //if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_modify_principal"); return NULL; } + } Py_RETURN_TRUE; } -static PyObject *KAdminPrincipal_set_policy(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - - kadm5_ret_t retval; - char *policy = NULL; - if (!PyArg_ParseTuple(args, "s", &policy)) - return NULL; - - strcpy(self->entry.policy, policy); - retval = kadm5_modify_principal(self->kadmin->server_handle, &self->entry, KADM5_POLICY); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_modify_principal"); return NULL; } +static PyObject *PyKAdminPrincipal_commit(PyKAdminPrincipalObject *self) { - Py_RETURN_TRUE; -} + kadm5_ret_t retval = KADM5_OK; -static PyObject *KAdminPrincipal_clear_policy(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - - kadm5_ret_t retval; + if (self && self->mask) { + + retval = kadm5_modify_principal(self->kadmin->server_handle, &self->entry, self->mask); + if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_modify_principal"); } - retval = kadm5_modify_principal(self->kadmin->server_handle, &self->entry, KADM5_POLICY_CLR); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_modify_principal"); return NULL; } + self->mask = 0; + } Py_RETURN_TRUE; } +static PyObject *PyKAdminPrincipal_reload(PyKAdminPrincipalObject *self) { -static PyGetSetDef KAdminPrincipal_getters_setters[] = { + krb5_error_code ret = 0; + kadm5_ret_t retval = KADM5_OK; - // {"policy", (getter)PyKAdminPrincipal_get_policy, (setter)PyKAdminPrincipal_set_policy, "Kerberos Policy"}, - // {"principal", (getter)PyKAdminPrincipal_get_principal, (setter)PyKAdminPrincipal_set_principal, "Kerberos Principal"}, - // {"policy", (getter)PyKAdminPrincipal_get_policy, NULL, "Kerberos Policy"}, - - {"principal", (getter)PyKAdminPrincipal_get_principal, NULL, "Kerberos Principal"}, - {NULL} -}; - -static PyObject *_KAdminPrincipal_load_principal(PyKAdminPrincipalObject *self, char *client_name) { + krb5_principal temp = NULL; - kadm5_ret_t retval; - krb5_error_code errno; - krb5_principal parsed_name; + if (self) { - if (client_name) { + // we need to free prior to fetching otherwise we leak memory since principal and policy are pointers, alternitively we could manually free those + ret = krb5_copy_principal(self->kadmin->context, self->entry.principal, &temp); + if (ret) {} - errno = krb5_parse_name(self->kadmin->context, client_name, &parsed_name); + retval = kadm5_free_principal_ent(self->kadmin->server_handle, &self->entry); + if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_free_principal_ent"); } - if (errno) { - printf("Failed to parse princ name %d\n", errno); + if (retval == KADM5_OK) { + retval = kadm5_get_principal(self->kadmin->server_handle, temp, &self->entry, KADM5_PRINCIPAL_NORMAL_MASK); + if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_get_principal"); } } - - retval = kadm5_get_principal(self->kadmin->server_handle, parsed_name, &self->entry, KADM5_PRINCIPAL_NORMAL_MASK); - krb5_free_principal(self->kadmin->context, parsed_name); - - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_get_principal"); return NULL; } + krb5_free_principal(self->kadmin->context, temp); - Py_RETURN_TRUE; + if (retval != KADM5_OK) { return NULL; } } - // TODO: raise exception - return NULL; - //Py_RETURN_FALSE; + Py_RETURN_TRUE; } -static PyObject *_KAdminPrincipal_refresh_principal(PyKAdminPrincipalObject *self) { - - kadm5_ret_t retval; - - retval = kadm5_get_principal(self->kadmin->server_handle, self->entry.principal, &self->entry, KADM5_PRINCIPAL_NORMAL_MASK); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_get_principal"); return NULL; } - - Py_RETURN_NONE; +static PyObject *PyKAdminPrincipal_unlock(PyKAdminPrincipalObject *self) { + return NULL; } -static PyObject *KAdminPrincipal_change_password(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - kadm5_ret_t retval; - char *password = NULL; - char *canon = NULL; +static PyObject *PyKAdminPrincipal_change_password(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { + + kadm5_ret_t retval = KADM5_OK; + char *password = NULL; if (!PyArg_ParseTuple(args, "s", &password)) return NULL; - if (password) { + retval = kadm5_chpass_principal(self->kadmin->server_handle, self->entry.principal, password); + if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_chpass_principal"); return NULL; } - retval = krb5_unparse_name(self->kadmin->context, self->entry.principal, &canon); - - if (retval) { - printf("krb5_unparse_name failure: %ld\n", retval); - } - - retval = kadm5_chpass_principal(self->kadmin->server_handle, self->entry.principal, password); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_chpass_principal"); return NULL; } - - _KAdminPrincipal_refresh_principal(self); - - Py_RETURN_TRUE; - - } else { - Py_RETURN_FALSE; - } + Py_RETURN_TRUE; } -static PyObject *KAdminPrincipal_randomize_key(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { +static PyObject *PyKAdminPrincipal_randomize_key(PyKAdminPrincipalObject *self) { - kadm5_ret_t retval; - char *canon = NULL; - - retval = krb5_unparse_name(self->kadmin->context, self->entry.principal, &canon); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "krb5_unparse_name"); return NULL; } + kadm5_ret_t retval = KADM5_OK; retval = kadm5_randkey_principal(self->kadmin->server_handle, self->entry.principal, NULL, NULL); - if (retval != 0x0) { PyKAdmin_RaiseKAdminError(retval, "kadm5_randkey_principal"); return NULL; } - - _KAdminPrincipal_refresh_principal(self); - - Py_XINCREF(Py_True); - return Py_True; -} - -static PyObject *KAdminPrincipal_get_name(PyKAdminPrincipalObject *self, PyObject *args, PyObject *kwds) { - - krb5_error_code retval = 0; - char *client_name = NULL; - PyObject *name = NULL; - - if (self->kadmin) { - - retval = krb5_unparse_name(self->kadmin->context, self->entry.principal, &client_name); - if (retval) {} - - name = Py_BuildValue("z", client_name); + if (retval != KADM5_OK) { PyKAdmin_RaiseKAdminError(retval, "kadm5_randkey_principal"); return NULL; } - } - - return name; + Py_RETURN_TRUE; } - PyObject *PyKAdminPrincipal_RichCompare(PyObject *o1, PyObject *o2, int opid) { -//int PyKAdminPrincipal_compare(PyObject *o1, PyObject *o2) { PyKAdminPrincipalObject *a = (PyKAdminPrincipalObject *)o1; PyKAdminPrincipalObject *b = (PyKAdminPrincipalObject *)o2; @@ -285,32 +239,448 @@ PyObject *PyKAdminPrincipal_RichCompare(PyObject *o1, PyObject *o2, int opid) { done: Py_XINCREF(result); return result; + + +} + +/* + * GETTERS + */ + +static PyObject *PyKAdminPrincipal_get_principal(PyKAdminPrincipalObject *self, void *closure) { + + krb5_error_code ret = 0; + PyObject *principal = NULL; + char *client_name = NULL; + + // todo: handle error + ret = krb5_unparse_name(self->kadmin->context, self->entry.principal, &client_name); + + if (client_name) { + principal = PyString_FromString(client_name); + free(client_name); + } + + return principal; } -static PyMethodDef KAdminPrincipal_methods[] = { - {"cpw", (PyCFunction)KAdminPrincipal_change_password, METH_VARARGS, ""}, - {"change_password", (PyCFunction)KAdminPrincipal_change_password, METH_VARARGS, ""}, - {"randkey", (PyCFunction)KAdminPrincipal_randomize_key, METH_VARARGS, ""}, - {"randomize_key", (PyCFunction)KAdminPrincipal_randomize_key, METH_VARARGS, ""}, + +static PyObject *PyKAdminPrincipal_get_mod_name(PyKAdminPrincipalObject *self, void *closure) { + + krb5_error_code ret = 0; + PyObject *principal = NULL; + char *client_name = NULL; - {"expire", (PyCFunction)KAdminPrincipal_set_expire, METH_VARARGS, ""}, - {"set_policy", (PyCFunction)KAdminPrincipal_set_policy, METH_VARARGS, ""}, - {"clear_policy", (PyCFunction)KAdminPrincipal_clear_policy, METH_VARARGS, ""}, + // todo: handle error + ret = krb5_unparse_name(self->kadmin->context, self->entry.mod_name, &client_name); + + if (client_name) { + principal = PyString_FromString(client_name); + free(client_name); + } + + return principal; +} - {"name", (PyCFunction)KAdminPrincipal_get_name, METH_VARARGS, ""}, +static PyObject *PyKAdminPrincipal_get_last_pwd_change(PyKAdminPrincipalObject *self, void *closure) { + return pykadmin_pydatetime_from_timestamp(self->entry.last_pwd_change); +} + +static PyObject *PyKAdminPrincipal_get_expire(PyKAdminPrincipalObject *self, void *closure) { + return pykadmin_pydatetime_from_timestamp(self->entry.princ_expire_time); +} + +static PyObject *PyKAdminPrincipal_get_pwexpire(PyKAdminPrincipalObject *self, void *closure) { + return pykadmin_pydatetime_from_timestamp(self->entry.pw_expiration); +} + +static PyObject *PyKAdminPrincipal_get_mod_date(PyKAdminPrincipalObject *self, void *closure) { + return pykadmin_pydatetime_from_timestamp(self->entry.mod_date); +} + +static PyObject *PyKAdminPrincipal_get_last_success(PyKAdminPrincipalObject *self, void *closure) { + return pykadmin_pydatetime_from_timestamp(self->entry.last_success); +} + +static PyObject *PyKAdminPrincipal_get_last_failed(PyKAdminPrincipalObject *self, void *closure) { + return pykadmin_pydatetime_from_timestamp(self->entry.last_failed); +} + +static PyObject *PyKAdminPrincipal_get_maxrenewlife(PyKAdminPrincipalObject *self, void *closure) { + + PyDateTime_IMPORT; + + PyObject *delta = PyDelta_FromDSU(0, self->entry.max_renewable_life, 0); + if (!delta) { PyErr_SetString(PyExc_AttributeError, NULL); } + + return delta; +} + + +static PyObject *PyKAdminPrincipal_get_maxlife(PyKAdminPrincipalObject *self, void *closure) { + + PyDateTime_IMPORT; + + PyObject *delta = PyDelta_FromDSU(0, self->entry.max_life, 0); + if (!delta) { PyErr_SetString(PyExc_AttributeError, NULL); } + + return delta; +} + +static PyObject *PyKAdminPrincipal_get_attributes(PyKAdminPrincipalObject *self, void *closure) { + + + unsigned int mask = 1; + PyObject *attrs = PyList_New(0); + + while (mask < kFLAG_MAX) { + + if (mask & self->entry.attributes) { + PyList_Append(attrs, PyInt_FromLong(mask)); + } + + mask = mask << 1; + } + + return attrs; + +} + +static PyObject *PyKAdminPrincipal_get_policy(PyKAdminPrincipalObject *self, void *closure) { + + PyObject *result = Py_None; + + if (self) { + + if (self->entry.policy) { + result = PyString_FromString(self->entry.policy); + } + } + + Py_XINCREF(result); + return result; +} + +static PyObject *PyKAdminPrincipal_get_kvno(PyKAdminPrincipalObject *self, void *closure) { + + PyObject *result = NULL; + + if (self) { +#if PY_MAJOR_VERSION > 2 + result = PyLong_FromLong((long) self->entry.kvno); +#else + result = PyInt_FromLong((long) self->entry.kvno); +#endif + } + + Py_XINCREF(result); + return result; +} + + +/* + * SETTERS + */ + +static krb5_deltat _decode_timedelta_input(PyObject *timedelta) { + + PyDateTime_IMPORT; + + time_t now = 0; + krb5_deltat delta = TIME_NONE; + + if (timedelta) { + + char *date_string = NULL; + + if (PyDelta_CheckExact(timedelta)) { + delta = pykadmin_seconds_from_pydatetime(timedelta); + } else if (PyUnicode_CheckExact(timedelta)) { + // TODO: unicode + } else if (PyString_CheckExact(timedelta)) { + date_string = PyString_AsString(timedelta); + + } else if (timedelta == Py_None) { + date_string = "never"; + } + + if (date_string) { + delta = get_date(date_string); + } + + } + + if (delta == TIME_NONE) { + PyErr_SetString(PyExc_ValueError, "Invalid input"); + } else if (delta != 0) { + time(&now); + delta -= now; + } + + return delta; + +} + +static krb5_timestamp _decode_timestamp_input(PyObject *date) { + + PyDateTime_IMPORT; + + krb5_timestamp timestamp = TIME_NONE; + + if (date) { + + char *date_string = NULL; + + if (PyDate_CheckExact(date) || PyDateTime_CheckExact(date)) { + timestamp = pykadmin_timestamp_from_pydatetime(date); + + } else if (PyUnicode_CheckExact(date)) { + // TODO: unicode + } else if (PyString_CheckExact(date)) { + date_string = PyString_AsString(date); + + } else if (date == Py_None) { + date_string = "never"; + + } + + if (date_string) { + timestamp = get_date(date_string); + } + + } + + if (timestamp == TIME_NONE) + PyErr_SetString(PyExc_ValueError, "Invalid input"); + + return timestamp; +} + + +int PyKAdminPrincipal_set_expire(PyKAdminPrincipalObject *self, PyObject *value, void *closure) { + + krb5_timestamp timestamp = _decode_timestamp_input(value); + + if (timestamp == TIME_NONE) { + return 1; + } + + self->entry.princ_expire_time = timestamp; + self->mask |= KADM5_PRINC_EXPIRE_TIME; + + return 0; +} + +int PyKAdminPrincipal_set_pwexpire(PyKAdminPrincipalObject *self, PyObject *value, void *closure) { + + krb5_timestamp timestamp = _decode_timestamp_input(value); + + if (timestamp == TIME_NONE) { + return 1; + } + + self->entry.princ_expire_time = timestamp; + self->mask |= KADM5_PW_EXPIRATION; + + return 0; + +} + +int PyKAdminPrincipal_set_maxlife(PyKAdminPrincipalObject *self, PyObject *value, void *closure) { + + krb5_timestamp timestamp = _decode_timedelta_input(value); + + if (timestamp == TIME_NONE) { + return 1; + } + + self->entry.max_life = timestamp; + self->mask |= KADM5_MAX_LIFE; + + return 0; + +} + +int PyKAdminPrincipal_set_maxrenewlife(PyKAdminPrincipalObject *self, PyObject *value, void *closure) { + + krb5_timestamp timestamp = _decode_timedelta_input(value); + + if (timestamp == TIME_NONE) { + return 1; + } + + self->entry.max_renewable_life = timestamp; + self->mask |= KADM5_MAX_RLIFE; + + return 0; + +} + + +int PyKAdminPrincipal_set_kvno(PyKAdminPrincipalObject *self, PyObject *value, void *closure) { + + unsigned long kvno = 0; + + if (self) { +#if PY_MAJOR_VERSION > 2 + kvno = PyLong_AsUnsignedLong(value); +#else + kvno = PyInt_AsUnsignedLongMask(value); +#endif + } + + if (!PyErr_Occurred()) { + self->entry.kvno = (unsigned int)kvno; + self->mask |= KADM5_KVNO; + } + + return 0; + +} + +int PyKAdminPrincipal_set_policy(PyKAdminPrincipalObject *self, PyObject *value, void *closure) { + + char *policy_string = NULL; + + if (self) { + + if (value) { + + if (value == Py_None) { + self->mask &= ~KADM5_POLICY; + self->mask |= KADM5_POLICY_CLR; + } + + if (PyUnicode_CheckExact(value)) { + // TODO + } else if (PyString_CheckExact(value)) { + + policy_string = PyString_AsString(value); + + } else if (PyKAdminPolicyObject_CheckExact(value)) { + + policy_string = PyKAdminPolicyObject_policy_name((PyKAdminPolicyObject *)value); + + } + + if (policy_string) { + + if (pykadmin_policy_exists(self->kadmin->server_handle, policy_string)) { + + if (self->entry.policy) { + free(self->entry.policy); + } + + self->entry.policy = policy_string; + // set policy flag and remove policy clear flag if set. + self->mask |= KADM5_POLICY; + self->mask &= ~KADM5_POLICY_CLR; + } + } + + } + } + + return 0; + +} + +/* + * Documentation Strings + */ + +static char kDOCSTRING_COMMIT[] = "commit()\n\tCommit all staged changes to the kerberos database."; +static char kDOCSTRING_CPW[] = "change_password(str)\n\tChange the password for the given principal."; +static char kDOCSTRING_RANDKEY[] = "randkey()\n\tRandomize the key for the given principal."; +static char kDOCSTRING_RELOAD[] = "reload()\n\tReload the local entry from the kerberos database."; +static char kDOCSTRING_UNLOCK[] = "unlock()\n\tUnlock the principal."; +static char kDOCSTRING_SET_FLAGS[] = "set_flags(KADM5_ATTRIBUTES)\n\tSet attributes to their enabled (1) state."; +static char kDOCSTRING_UNSET_FLAGS[] = "unset_flags(KADM5_ATTRIBUTES)\n\tSet attributes to their diabled (0) state."; +static char kDOCSTRING_NAME[] = "str\n\tPrincipal name as a string."; +static char kDOCSTRING_MOD_NAME[] = "str\n\tThe account which last modified the principal as a string."; +static char kDOCSTRING_MOD_DATE[] = "datetime.datetime\n\tThe datetime at which the principal was last modified."; +static char kDOCSTRING_LAST_PWD_CHANGE[] = "datetime.datetime\n\tThe datetime at which the password was last changed."; +static char kDOCSTRING_LAST_SUCCESS[] = "datetime.datetime\n\tThe last successful authentication."; +static char kDOCSTRING_LAST_FAILURE[] = "datetime.datetime\n\tThe last failed authentication."; +static char kDOCSTRING_ATTRIBUTES[] = "list\n\tlist of all attributes which have been set."; +static char kDOCSTRING_EXPIRE[] = "getter: datetime.datetime\n\tsetter: [datetime.date|datetime.datetime|str|None]\n\twhen the account expires."; +static char kDOCSTRING_PWEXPIRE[] = "getter: datetime.datetime\n\tsetter: [datetime.date|datetime.datetime|str|None]\n\twhen the current password expires."; +static char kDOCSTRING_MAXLIFE[] = "getter: datetime.timedelta\n\tsetter: [datetime.timedelta|str|None]\n\tthe maximum ticket life."; +static char kDOCSTRING_MAXRENEWLIFE[] = "getter: datetime.timedelta\n\tsetter: [datetime.timedelta|str|None]\n\tthe maximum renewable life."; +static char kDOCSTRING_POLICY[] = "getter: [str|None]\n\tsetter: [str|kadmin.Policy|None]\n\tpolicy enabled for the principal."; +static char kDOCSTRING_KVNO[] = "getter: int\n\tsetter: [int]\n\tcurrent key version number."; +static char kDOCSTRING_FAILURES[] = "failed authentication count."; +static char kDOCSTRING_MKVNO[] = "master key version number."; + +static PyMethodDef PyKAdminPrincipal_methods[] = { + + {"cpw", (PyCFunction)PyKAdminPrincipal_change_password, METH_VARARGS, kDOCSTRING_CPW}, + {"change_password", (PyCFunction)PyKAdminPrincipal_change_password, METH_VARARGS, kDOCSTRING_CPW}, + {"randkey", (PyCFunction)PyKAdminPrincipal_randomize_key, METH_NOARGS, kDOCSTRING_RANDKEY}, + {"randomize_key", (PyCFunction)PyKAdminPrincipal_randomize_key, METH_NOARGS, kDOCSTRING_RANDKEY}, + + // TODO: principal.modify(expire=a, pwexpire=b, maxlife=c, maxrenewlife=d, attributes=e, policy=f, kvno=g) + //{"modify" (PyCFunction)NULL, METH_KEYWORDS, "doc string"} + + {"commit", (PyCFunction)PyKAdminPrincipal_commit, METH_NOARGS, kDOCSTRING_COMMIT}, + {"reload", (PyCFunction)PyKAdminPrincipal_reload, METH_NOARGS, kDOCSTRING_RELOAD}, + {"unlock", (PyCFunction)PyKAdminPrincipal_unlock, METH_NOARGS, kDOCSTRING_UNLOCK}, + + {"set_flags", (PyCFunction)PyKAdminPrincipal_set_attributes, METH_VARARGS, kDOCSTRING_SET_FLAGS}, + {"unset_flags", (PyCFunction)PyKAdminPrincipal_unset_attributes, METH_VARARGS, kDOCSTRING_UNSET_FLAGS}, + {NULL, NULL, 0, NULL} }; +static PyGetSetDef PyKAdminPrincipal_getters_setters[] = { + + {"principal", (getter)PyKAdminPrincipal_get_principal, NULL, kDOCSTRING_NAME, NULL}, + {"name", (getter)PyKAdminPrincipal_get_principal, NULL, kDOCSTRING_NAME, NULL}, + + {"mod_name", (getter)PyKAdminPrincipal_get_mod_name, NULL, kDOCSTRING_MOD_NAME, NULL}, + {"mod_date", (getter)PyKAdminPrincipal_get_mod_date, NULL, kDOCSTRING_MOD_DATE, NULL}, + + {"last_pwd_change", (getter)PyKAdminPrincipal_get_last_pwd_change, NULL, kDOCSTRING_LAST_PWD_CHANGE, NULL}, + {"last_success", (getter)PyKAdminPrincipal_get_last_success, NULL, kDOCSTRING_LAST_SUCCESS, NULL}, + {"last_failure", (getter)PyKAdminPrincipal_get_last_failed, NULL, kDOCSTRING_LAST_FAILURE, NULL}, + + {"attributes", (getter)PyKAdminPrincipal_get_attributes, NULL, kDOCSTRING_ATTRIBUTES, NULL}, + + + // setter attributes + + {"expire", (getter)PyKAdminPrincipal_get_expire, (setter)PyKAdminPrincipal_set_expire, kDOCSTRING_EXPIRE, NULL}, + {"pwexpire", (getter)PyKAdminPrincipal_get_pwexpire, (setter)PyKAdminPrincipal_set_pwexpire, kDOCSTRING_PWEXPIRE, NULL}, + + {"maxlife", (getter)PyKAdminPrincipal_get_maxlife, (setter)PyKAdminPrincipal_set_maxlife, kDOCSTRING_MAXLIFE, NULL}, + {"maxrenewlife", (getter)PyKAdminPrincipal_get_maxrenewlife, (setter)PyKAdminPrincipal_set_maxrenewlife, kDOCSTRING_MAXRENEWLIFE, NULL}, + + {"policy", (getter)PyKAdminPrincipal_get_policy, (setter)PyKAdminPrincipal_set_policy, kDOCSTRING_POLICY, NULL}, + {"kvno", (getter)PyKAdminPrincipal_get_kvno, (setter)PyKAdminPrincipal_set_kvno, kDOCSTRING_KVNO, NULL}, + + {NULL, NULL, NULL, NULL, NULL} +}; + +static PyMemberDef PyKAdminPrincipal_members[] = { + + {"failures", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, fail_auth_count), READONLY, kDOCSTRING_FAILURES}, + {"mkvno", T_INT, offsetof(PyKAdminPrincipalObject, entry) + offsetof(kadm5_principal_ent_rec, mkvno), READONLY, kDOCSTRING_MKVNO}, + + {NULL} +}; + + PyTypeObject PyKAdminPrincipalObject_Type = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size*/ + PyVarObject_HEAD_INIT(NULL, 0) + //PyObject_HEAD_INIT(NULL) + //0, /*ob_size*/ "kadmin.Principal", /*tp_name*/ sizeof(PyKAdminPrincipalObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ - (destructor)KAdminPrincipal_dealloc, /*tp_dealloc*/ - (printfunc)KAdminPrincipal_print, /*tp_print*/ + (destructor)PyKAdminPrincipal_dealloc, /*tp_dealloc*/ + (printfunc)PyKAdminPrincipal_print, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ @@ -332,49 +702,59 @@ PyTypeObject PyKAdminPrincipalObject_Type = { 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ - KAdminPrincipal_methods, /* tp_methods */ - KAdminPrincipal_members, /* tp_members */ - KAdminPrincipal_getters_setters, /* tp_getset */ + PyKAdminPrincipal_methods, /* tp_methods */ + PyKAdminPrincipal_members, /* tp_members */ + PyKAdminPrincipal_getters_setters, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ - (initproc)KAdminPrincipal_init, /* tp_init */ + (initproc)PyKAdminPrincipal_init, /* tp_init */ 0, /* tp_alloc */ - KAdminPrincipal_new, /* tp_new */ + PyKAdminPrincipal_new, /* tp_new */ }; PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_name(PyKAdminObject *kadmin, char *client_name) { + + krb5_error_code errno; + kadm5_ret_t retval = KADM5_OK; - PyKAdminPrincipalObject *principal = (PyKAdminPrincipalObject *)KAdminPrincipal_new(&PyKAdminPrincipalObject_Type, NULL, NULL); + PyKAdminPrincipalObject *principal = (PyKAdminPrincipalObject *)Py_None; + krb5_principal temp = NULL; - if (principal) { + if (client_name) { - Py_XINCREF(kadmin); - principal->kadmin = kadmin; + principal = (PyKAdminPrincipalObject *)PyKAdminPrincipal_new(&PyKAdminPrincipalObject_Type, NULL, NULL); - /* todo : fetch kadmin entry */ - PyObject *result = _KAdminPrincipal_load_principal(principal, client_name); + if (principal) { - if (!result) { - //Py_XDECREF(kadmin); // this is redundant as dealloc decrementes the reference for us - Py_XINCREF(Py_None); - KAdminPrincipal_dealloc(principal); - principal = (PyKAdminPrincipalObject *)Py_None; - } + Py_INCREF(kadmin); + principal->kadmin = kadmin; + errno = krb5_parse_name(kadmin->context, client_name, &temp); + retval = kadm5_get_principal(kadmin->server_handle, temp, &principal->entry, KADM5_PRINCIPAL_NORMAL_MASK); + + krb5_free_principal(kadmin->context, temp); + + if ((retval != KADM5_OK) || errno) { + PyKAdminPrincipal_dealloc(principal); + Py_INCREF(Py_None); + principal = (PyKAdminPrincipalObject *)Py_None; + } + + } } return principal; } -PyKAdminPrincipalObject *PyKadminPrincipalObject_principal_with_db_entry(PyKAdminObject *kadmin, krb5_db_entry *kdb) { +PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_db_entry(PyKAdminObject *kadmin, krb5_db_entry *kdb) { - krb5_error_code retval; + kadm5_ret_t retval = KADM5_OK; - PyKAdminPrincipalObject *principal = (PyKAdminPrincipalObject *)KAdminPrincipal_new(&PyKAdminPrincipalObject_Type, NULL, NULL); + PyKAdminPrincipalObject *principal = (PyKAdminPrincipalObject *)PyKAdminPrincipal_new(&PyKAdminPrincipalObject_Type, NULL, NULL); if (kdb) { @@ -385,7 +765,7 @@ PyKAdminPrincipalObject *PyKadminPrincipalObject_principal_with_db_entry(PyKAdmi if (retval) { - KAdminPrincipal_dealloc(principal); + PyKAdminPrincipal_dealloc(principal); // todo: set exception principal = NULL; @@ -396,8 +776,8 @@ PyKAdminPrincipalObject *PyKadminPrincipalObject_principal_with_db_entry(PyKAdmi return principal; } -void KAdminPrincipal_destroy(PyKAdminPrincipalObject *self) { - KAdminPrincipal_dealloc(self); +void PyKAdminPrincipalObject_destroy(PyKAdminPrincipalObject *self) { + PyKAdminPrincipal_dealloc(self); } diff --git a/PyKAdminPrincipalObject.h b/PyKAdminPrincipalObject.h index df172af..c77dba3 100644 --- a/PyKAdminPrincipalObject.h +++ b/PyKAdminPrincipalObject.h @@ -3,6 +3,7 @@ #define PYKADMINPRINCIPALOBJECT_H #include + #include #include #include @@ -15,16 +16,21 @@ typedef struct { PyObject_HEAD PyKAdminObject *kadmin; kadm5_principal_ent_rec entry; + + unsigned int mask; + } PyKAdminPrincipalObject; PyTypeObject PyKAdminPrincipalObject_Type; -PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_name(PyKAdminObject *kadmin, char *client_name); -PyKAdminPrincipalObject *PyKadminPrincipalObject_principal_with_db_entry(PyKAdminObject *kadmin, krb5_db_entry *kdb); -PyKAdminPrincipalObject *PyKadminPrincipalObject_principal_with_kadm_entry(PyKAdminObject *kadmin, kadm5_principal_ent_rec *entry); -// create will be replaced with load_princ_w_name +//#define PyKAdminPrincipalObject_Check(principal) PyObject_TypeCheck(principal, &PyKAdminPrincipalObject_Type) +#define PyKAdminPrincipalObject_CheckExact(obj) (Py_TYPE(obj) == &PyKAdminPrincipalObject_Type) + PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_name(PyKAdminObject *kadmin, char *client_name); -void KAdminPrincipal_destroy(PyKAdminPrincipalObject *self); +PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_db_entry(PyKAdminObject *kadmin, krb5_db_entry *kdb); +PyKAdminPrincipalObject *PyKAdminPrincipalObject_principal_with_kadm_entry(PyKAdminObject *kadmin, kadm5_principal_ent_rec *entry); + +void PyKAdminPrincipalObject_destroy(PyKAdminPrincipalObject *self); #endif \ No newline at end of file diff --git a/kadmin.c b/kadmin.c index 9b71ed1..553e64e 100644 --- a/kadmin.c +++ b/kadmin.c @@ -41,6 +41,52 @@ static struct PyMethodDef module_methods[] = { {NULL, NULL, 0, NULL} }; +/* +#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001 +#define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002 +#define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004 +#define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008 +#define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010 +#define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020 +#define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040 +#define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080 +#define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100 +#define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200 +#define KRB5_KDB_DISALLOW_SVR 0x00001000 +#define KRB5_KDB_PWCHANGE_SERVICE 0x00002000 +#define KRB5_KDB_SUPPORT_DESMD5 0x00004000 +#define KRB5_KDB_NEW_PRINC 0x00008000 +#define KRB5_KDB_OK_AS_DELEGATE 0x00100000 +#define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE 0x00200000 +#define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x00400000 + + +*/ + +void PyKAdminConstant_init(PyObject *module) { + + PyModule_AddIntConstant(module, "DISALLOW_POSTDATED", KRB5_KDB_DISALLOW_POSTDATED); + PyModule_AddIntConstant(module, "DISALLOW_FORWARDABLE", KRB5_KDB_DISALLOW_FORWARDABLE); + PyModule_AddIntConstant(module, "DISALLOW_TGT_BASED", KRB5_KDB_DISALLOW_TGT_BASED); + PyModule_AddIntConstant(module, "DISALLOW_RENEWABLE", KRB5_KDB_DISALLOW_RENEWABLE); + PyModule_AddIntConstant(module, "DISALLOW_PROXIABLE", KRB5_KDB_DISALLOW_PROXIABLE); + PyModule_AddIntConstant(module, "DISALLOW_DUP_SKEY", KRB5_KDB_DISALLOW_DUP_SKEY); + PyModule_AddIntConstant(module, "DISALLOW_ALL_TIX", KRB5_KDB_DISALLOW_ALL_TIX); + PyModule_AddIntConstant(module, "REQUIRES_PRE_AUTH", KRB5_KDB_REQUIRES_PRE_AUTH); + PyModule_AddIntConstant(module, "REQUIRES_HW_AUTH", KRB5_KDB_REQUIRES_HW_AUTH); + PyModule_AddIntConstant(module, "REQUIRES_PWCHANGE", KRB5_KDB_REQUIRES_PWCHANGE); + PyModule_AddIntConstant(module, "DISALLOW_SVR", KRB5_KDB_DISALLOW_SVR); + PyModule_AddIntConstant(module, "PWCHANGE_SERVICE", KRB5_KDB_PWCHANGE_SERVICE); + PyModule_AddIntConstant(module, "SUPPORT_DESMD5", KRB5_KDB_SUPPORT_DESMD5); + PyModule_AddIntConstant(module, "NEW_PRINC", KRB5_KDB_NEW_PRINC); + PyModule_AddIntConstant(module, "OK_AS_DELEGATE", KRB5_KDB_OK_AS_DELEGATE); + PyModule_AddIntConstant(module, "OK_TO_AUTH_AS_DELEGATE", KRB5_KDB_OK_TO_AUTH_AS_DELEGATE); + PyModule_AddIntConstant(module, "NO_AUTH_DATA_REQUIRED", KRB5_KDB_NO_AUTH_DATA_REQUIRED); + +} + + + PyMODINIT_FUNC #ifdef KADMIN_LOCAL initkadmin_local(void) @@ -78,6 +124,7 @@ PyMODINIT_FUNC PyModule_AddObject(module, "KAdminError", KAdminError); PyKAdminError_init(module); + PyKAdminConstant_init(module); } diff --git a/test/unittests.py b/test/unittests.py index 040c856..b5b84c0 100644 --- a/test/unittests.py +++ b/test/unittests.py @@ -86,6 +86,8 @@ def setUp(self): self.kadm = kadm + self.logger = logging.getLogger('python-kadmin') + def test_init_with_keytab(self): try: @@ -184,7 +186,7 @@ def test_double_delete(self): account = TEST_ACCOUNTS[0] self.assertRaises(kadmin.KAdminError, kadm.delprinc, account) - + def test_iteration(self): kadm = self.kadm @@ -196,7 +198,7 @@ def test_iteration(self): self.assertEqual(count, size) - + def test_not_exists(self): kadm = self.kadm @@ -239,11 +241,12 @@ def test_princ_compare_ne(self): b = kadm.getprinc(account) self.assertNotEqual(a, b) - + class KAdminLocalUnitTests(unittest.TestCase): - +#class KAdminLocalUnitTests(): + ''' Missing in 2.6 ''' def assertIsNotNone(self, expr, msg=None): self.assertFalse((expr is None), msg) @@ -261,6 +264,8 @@ def setUp(self): self.stop() self.kadm = kadm + + self.logger = logging.getLogger('python-kadmin') def test_local(self): @@ -388,13 +393,23 @@ def test_each_iteration(self): kadm = self.kadm count = [0] + delta = 0 + size = database_size() + start = time.time() + def fxn(princ, data): data[0] += 1 kadm.each_principal(fxn, count) + end = time.time() + + delta = end - start + + self.logger.info("each iteration {0} principals in {1} seconds. [{2} principals/second]".format(count[0], delta, (count[0]/delta))) + self.assertEqual(count[0], size) def test_not_exists(self):