summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ipa-admintools/Makefile2
-rw-r--r--ipa-admintools/ipa-addservice (renamed from ipa-admintools/ipa-getkeytab)19
-rw-r--r--ipa-client/Makefile.am30
-rw-r--r--ipa-client/configure.ac125
-rwxr-xr-xipa-client/ipa-client.spec1
-rw-r--r--ipa-client/ipa-client.spec.in1
-rw-r--r--ipa-client/ipa-getkeytab.c495
-rw-r--r--ipa-server/ipa-install/share/Makefile.am1
-rw-r--r--ipa-server/ipa-install/share/default-aci.ldif5
-rw-r--r--ipa-server/ipa-install/share/default-keytypes.ldif20
-rw-r--r--ipa-server/ipa-install/share/kdc.conf.template2
-rw-r--r--ipa-server/ipa-install/share/kerberos.ldif1
-rw-r--r--ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c1666
-rw-r--r--ipa-server/ipaserver/krbinstance.py5
14 files changed, 1852 insertions, 521 deletions
diff --git a/ipa-admintools/Makefile b/ipa-admintools/Makefile
index 9586e71c..36d5d224 100644
--- a/ipa-admintools/Makefile
+++ b/ipa-admintools/Makefile
@@ -18,11 +18,11 @@ install:
install -m 755 ipa-findgroup $(SBINDIR)
install -m 755 ipa-groupmod $(SBINDIR)
install -m 755 ipa-passwd $(SBINDIR)
+ install -m 755 ipa-addservice $(SBINDIR)
install -m 755 ipa-adddelegation $(SBINDIR)
install -m 755 ipa-deldelegation $(SBINDIR)
install -m 755 ipa-listdelegation $(SBINDIR)
install -m 755 ipa-moddelegation $(SBINDIR)
- install -m 755 ipa-getkeytab $(SBINDIR)
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
diff --git a/ipa-admintools/ipa-getkeytab b/ipa-admintools/ipa-addservice
index 5ecb7e4a..fd94038f 100644
--- a/ipa-admintools/ipa-getkeytab
+++ b/ipa-admintools/ipa-addservice
@@ -36,13 +36,11 @@ import getpass
import errno
def usage():
- print "ipa-getkeytab [-a] principal filename"
+ print "ipa-addservice principal"
sys.exit(1)
def parse_options():
parser = OptionParser()
- parser.add_option("-a", "--add", dest="add_princ", action="store_true",
- help="add the principal")
args = ipa.config.init_config(sys.argv)
options, args = parser.parse_args(args)
@@ -55,25 +53,14 @@ def main():
options, args = parse_options()
- if len(args) != 3:
+ if len(args) != 2:
usage()
princ_name = args[1]
- file_name = args[2]
client = ipaclient.IPAClient()
try:
- if options.add_princ:
- client.add_service_principal(princ_name)
-
- princs = client.get_keytab(princ_name)
-
- if princs is None:
- print "could not generate keytab"
- sys.exit(1)
-
- fd = open(file_name, "w")
- fd.write(princs)
+ client.add_service_principal(princ_name)
except Exception, e:
print str(e)
diff --git a/ipa-client/Makefile.am b/ipa-client/Makefile.am
index caabffe1..95c2cc3e 100644
--- a/ipa-client/Makefile.am
+++ b/ipa-client/Makefile.am
@@ -4,6 +4,36 @@ AUTOMAKE_OPTIONS = 1.7
NULL =
+INCLUDES = \
+ -I. \
+ -I$(srcdir) \
+ -DPREFIX=\""$(prefix)"\" \
+ -DBINDIR=\""$(bindir)"\" \
+ -DLIBDIR=\""$(libdir)"\" \
+ -DLIBEXECDIR=\""$(libexecdir)"\" \
+ -DDATADIR=\""$(datadir)"\" \
+ $(KRB5_CFLAGS) \
+ $(LDAP_CFLAGS) \
+ $(SASL_CFLAGS) \
+ $(POPT_CFLAGS) \
+ $(WARN_CFLAGS) \
+ $(NULL)
+
+sbin_PROGRAMS = \
+ ipa-getkeytab \
+ $(NULL)
+
+ipa_getkeytab_SOURCES = \
+ ipa-getkeytab.c \
+ $(NULL)
+
+ipa_getkeytab_LDADD = \
+ $(KRB5_LIBS) \
+ $(LDAP_LIBS) \
+ $(SASL_LIBS) \
+ $(POPT_LIBS) \
+ $(NULL)
+
SUBDIRS = \
firefox \
ipaclient \
diff --git a/ipa-client/configure.ac b/ipa-client/configure.ac
index b50928da..655c0cc1 100644
--- a/ipa-client/configure.ac
+++ b/ipa-client/configure.ac
@@ -10,9 +10,134 @@ AM_INIT_AUTOMAKE
AM_MAINTAINER_MODE
+AC_PROG_CC
+AC_STDC_HEADERS
+AC_DISABLE_STATIC
+AC_PROG_LIBTOOL
+
+AC_HEADER_STDC
+
AC_SUBST(VERSION)
dnl ---------------------------------------------------------------------------
+dnl - Check for KRB5
+dnl ---------------------------------------------------------------------------
+
+KRB5_LIBS=
+AC_CHECK_HEADER(krb5.h)
+
+krb5_impl=mit
+
+if test "x$ac_cv_header_krb5_h" = "xyes" ; then
+ dnl lazy check for Heimdal Kerberos
+ AC_CHECK_HEADERS(heim_err.h)
+ if test $ac_cv_header_heim_err_h = yes ; then
+ krb5_impl=heimdal
+ else
+ krb5_impl=mit
+ fi
+
+ if test "x$krb5_impl" = "xmit"; then
+ AC_CHECK_LIB(k5crypto, main,
+ [krb5crypto=k5crypto],
+ [krb5crypto=crypto])
+
+ AC_CHECK_LIB(krb5, main,
+ [have_krb5=yes
+ KRB5_LIBS="-lkrb5 -l$krb5crypto -lcom_err"],
+ [have_krb5=no],
+ [-l$krb5crypto -lcom_err])
+
+ elif test "x$krb5_impl" = "xheimdal"; then
+ AC_CHECK_LIB(des, main,
+ [krb5crypto=des],
+ [krb5crypto=crypto])
+
+ AC_CHECK_LIB(krb5, main,
+ [have_krb5=yes
+ KRB5_LIBS="-lkrb5 -l$krb5crypto -lasn1 -lroken -lcom_err"],
+ [have_krb5=no],
+ [-l$krb5crypto -lasn1 -lroken -lcom_err])
+
+ AC_DEFINE(HAVE_HEIMDAL_KERBEROS, 1,
+ [define if you have HEIMDAL Kerberos])
+
+ else
+ have_krb5=no
+ AC_MSG_WARN([Unrecognized Kerberos5 Implementation])
+ fi
+
+ if test "x$have_krb5" = "xyes" ; then
+ ol_link_krb5=yes
+
+ AC_DEFINE(HAVE_KRB5, 1,
+ [define if you have Kerberos V])
+
+ else
+ AC_MSG_ERROR([Required Kerberos 5 support not available])
+ fi
+
+fi
+
+AC_SUBST(KRB5_LIBS)
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for LDAP
+dnl ---------------------------------------------------------------------------
+
+LDAP_LIBS=
+AC_CHECK_HEADER(ldap.h)
+AC_CHECK_HEADER(lber.h)
+
+AC_CHECK_LIB(ldap, ldap_search, with_ldap=yes)
+dnl Check for other libraries we need to link with to get the main routines.
+test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes], , -llber) }
+test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes with_ldap_krb=yes], , -llber -lkrb) }
+test "$with_ldap" != "yes" && { AC_CHECK_LIB(ldap, ldap_open, [with_ldap=yes with_ldap_lber=yes with_ldap_krb=yes with_ldap_des=yes], , -llber -lkrb -ldes) }
+dnl Recently, we need -lber even though the main routines are elsewhere,
+dnl because otherwise be get link errors w.r.t. ber_pvt_opt_on. So just
+dnl check for that (it's a variable not a fun but that doesn't seem to
+dnl matter in these checks) and stick in -lber if so. Can't hurt (even to
+dnl stick it in always shouldn't hurt, I don't think) ... #### Someone who
+dnl #### understands LDAP needs to fix this properly.
+test "$with_ldap_lber" != "yes" && { AC_CHECK_LIB(lber, ber_pvt_opt_on, with_ldap_lber=yes) }
+
+if test "$with_ldap" = "yes"; then
+ if test "$with_ldap_des" = "yes" ; then
+ LDAP_LIBS="${LDAP_LIBS} -ldes"
+ fi
+ if test "$with_ldap_krb" = "yes" ; then
+ LDAP_LIBS="${LDAP_LIBS} -lkrb"
+ fi
+ if test "$with_ldap_lber" = "yes" ; then
+ LDAP_LIBS="${LDAP_LIBS} -llber"
+ fi
+ LDAP_LIBS="${LDAP_LIBS} -lldap"
+else
+ AC_MSG_ERROR([LDAP not found])
+fi
+
+AC_SUBST(LDAP_LIBS)
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for POPT
+dnl ---------------------------------------------------------------------------
+
+POPT_LIBS=
+AC_CHECK_HEADER(popt.h)
+AC_CHECK_LIB(popt, poptGetContext, [POPT_LIBS="-lpopt"])
+AC_SUBST(POPT_LIBS)
+
+dnl ---------------------------------------------------------------------------
+dnl - Check for SASL
+dnl ---------------------------------------------------------------------------
+
+SASL_LIBS=
+AC_CHECK_HEADER(sasl/sasl.h)
+AC_CHECK_LIB(sasl2, sasl_client_init, [SASL_LIBS="-lsasl2"])
+AC_SUBST(SASL_LIBS)
+
+dnl ---------------------------------------------------------------------------
dnl - Check for Python
dnl ---------------------------------------------------------------------------
diff --git a/ipa-client/ipa-client.spec b/ipa-client/ipa-client.spec
index c184600b..9c7f41b5 100755
--- a/ipa-client/ipa-client.spec
+++ b/ipa-client/ipa-client.spec
@@ -36,6 +36,7 @@ rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa-client-install
+%{_sbindir}/ipa-getkeytab
%dir %{_usr}/share/ipa
%{_usr}/share/ipa/*
diff --git a/ipa-client/ipa-client.spec.in b/ipa-client/ipa-client.spec.in
index 5dbd90f3..47964a4c 100644
--- a/ipa-client/ipa-client.spec.in
+++ b/ipa-client/ipa-client.spec.in
@@ -36,6 +36,7 @@ rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa-client-install
+%{_sbindir}/ipa-getkeytab
%dir %{_usr}/share/ipa
%{_usr}/share/ipa/*
diff --git a/ipa-client/ipa-getkeytab.c b/ipa-client/ipa-getkeytab.c
new file mode 100644
index 00000000..c032aa1d
--- /dev/null
+++ b/ipa-client/ipa-getkeytab.c
@@ -0,0 +1,495 @@
+/* (C) Simo Sorce */
+#define _GNU_SOURCE
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <krb5.h>
+#include <ldap.h>
+#include <sasl/sasl.h>
+#include <popt.h>
+
+static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *sit)
+{
+ sasl_interact_t *in = NULL;
+ int ret = LDAP_OTHER;
+ krb5_principal princ = (krb5_principal)priv_data;
+
+ if (!ld) return LDAP_PARAM_ERROR;
+
+ for (in = sit; in && in->id != SASL_CB_LIST_END; in++) {
+ switch(in->id) {
+ case SASL_CB_USER:
+ in->result = princ->data[0].data;
+ in->len = princ->data[0].length;
+ ret = LDAP_SUCCESS;
+ break;
+ case SASL_CB_GETREALM:
+ in->result = princ->realm.data;
+ in->len = princ->realm.length;
+ ret = LDAP_SUCCESS;
+ break;
+ default:
+ in->result = NULL;
+ in->len = 0;
+ ret = LDAP_OTHER;
+ }
+ }
+ return ret;
+}
+
+#define KEYTAB_SET_OID "2.16.840.1.113730.3.8.3.1"
+#define KEYTAB_RET_OID "2.16.840.1.113730.3.8.3.2"
+
+static void free_keys(krb5_context krbctx, krb5_keyblock *keys, int num_keys)
+{
+ int i;
+
+ for (i = 0; i < num_keys; i++) {
+ krb5_free_keyblock_contents(krbctx, &keys[i]);
+ }
+ free(keys);
+}
+
+static int create_keys(krb5_context krbctx, krb5_keyblock **keys)
+{
+ krb5_error_code krberr;
+ krb5_enctype *ktypes;
+ krb5_keyblock *key;
+ int i, j, k, max_keys;
+
+ krberr = krb5_get_permitted_enctypes(krbctx, &ktypes);
+ if (krberr) {
+ fprintf(stderr, "No preferred enctypes ?!\n");
+ return 0;
+ }
+
+ for (i = 0; ktypes[i]; i++) /* count max encodings */ ;
+ max_keys = i;
+ if (!max_keys) {
+ krb5_free_ktypes(krbctx, ktypes);
+ fprintf(stderr, "No preferred enctypes ?!\n");
+ return 0;
+ }
+
+ key = calloc(max_keys, sizeof(krb5_keyblock));
+ if (!key) {
+ krb5_free_ktypes(krbctx, ktypes);
+ fprintf(stderr, "Out of Memory!\n");
+ return 0;
+ }
+
+ k = 0; /* effective number of keys */
+
+ for (i = 0; i < max_keys; i++) {
+ krb5_boolean similar;
+
+ /* Check we don't already have a key with a similar encoding,
+ * it would just produce redundant data and this is what the
+ * kerberos libs do anyway */
+ similar = 0;
+ for (j = 0; j < i; j++) {
+ krberr = krb5_c_enctype_compare(krbctx, ktypes[i],
+ ktypes[j], &similar);
+ if (krberr) {
+ krb5_free_ktypes(krbctx, ktypes);
+ free_keys(krbctx, key, i);
+ fprintf(stderr, "Enctype comparison failed!\n");
+ return 0;
+ }
+ if (similar) break;
+ }
+ if (similar) continue;
+
+ krberr = krb5_c_make_random_key(krbctx, ktypes[i], &key[k]);
+ if (krberr) {
+ krb5_free_ktypes(krbctx, ktypes);
+ free_keys(krbctx, key, k);
+ fprintf(stderr, "Making random key failed!\n");
+ return 0;
+ }
+ k++;
+ }
+
+ krb5_free_ktypes(krbctx, ktypes);
+
+ *keys = key;
+ return k;
+}
+
+static struct berval *create_key_control(krb5_keyblock *keys, int num_keys, const char *principalName)
+{
+ struct berval *bval;
+ BerElement *be;
+ int ret, i;
+
+ be = ber_alloc_t(LBER_USE_DER);
+ if (!be) {
+ return NULL;
+ }
+
+ ret = ber_printf(be, "{s{", principalName);
+ if (ret == -1) {
+ ber_free(be, 1);
+ return NULL;
+ }
+
+ for (i = 0; i < num_keys; i++) {
+
+ /* we set only the EncryptionKey, no salt or s2kparams */
+ ret = ber_printf(be, "{t[{t[i]t[o]}]}",
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
+ (ber_int_t)keys[i].enctype,
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
+ (char *)keys[i].contents, (ber_len_t)keys[i].length);
+
+ if (ret == -1) {
+ ber_free(be, 1);
+ return NULL;
+ }
+ }
+
+ ret = ber_printf(be, "}}");
+ if (ret == -1) {
+ ber_free(be, 1);
+ return NULL;
+ }
+
+ ret = ber_flatten(be, &bval);
+ if (ret == -1) {
+ ber_free(be, 1);
+ return NULL;
+ }
+
+ ber_free(be, 1);
+ return bval;
+}
+
+int filter_keys(krb5_context krbctx, krb5_keyblock *keys, int *num_keys, ber_int_t *enctypes)
+{
+ int ret, i, j, k;
+
+ k = *num_keys;
+
+ for (i = 0; i < k; i++) {
+ for (j = 0; enctypes[j]; j++) {
+ if (keys[i].enctype == enctypes[j]) break;
+ }
+ if (enctypes[j] == 0) { /* unsupported one */
+ krb5_free_keyblock_contents(krbctx, &keys[i]);
+ /* remove unsupported one */
+ k--;
+ for (j = i; j < k; j++) {
+ keys[j] = keys[j + 1];
+ }
+ /* new key has been moved to this position, make sure
+ * we do not skip it, by neutralizing next i increment */
+ i--;
+ }
+ }
+
+ if (k == 0) {
+ return -1;
+ }
+
+ *num_keys = k;
+ return 0;
+}
+
+static int ldap_set_keytab(const char *servername,
+ const char *principal_name,
+ krb5_principal princ,
+ krb5_keyblock *keys,
+ int num_keys,
+ ber_int_t **enctypes)
+{
+ int version;
+ LDAP *ld = NULL;
+ BerElement *ctrl = NULL;
+ BerElement *sctrl = NULL;
+ struct berval *control = NULL;
+ char *ldap_uri = NULL;
+ struct berval **ncvals;
+ char *ldap_base = NULL;
+ char *retoid = NULL;
+ struct berval *retdata = NULL;
+ struct timeval tv;
+ LDAPMessage *entry, *res = NULL;
+ LDAPControl **srvctrl = NULL;
+ LDAPControl *pprc = NULL;
+ char *err = NULL;
+ int msgid;
+ int ret, rc;
+ int kvno, i;
+ ber_tag_t rtag;
+ struct berval bv;
+ ber_int_t *encs = NULL;
+
+ /* cant' return more than num_keys, sometimes less */
+ encs = calloc(num_keys + 1, sizeof(ber_int_t));
+ if (!encs) {
+ fprintf(stderr, "Out of Memory!\n");
+ return 0;
+ }
+
+ /* build password change control */
+ control = create_key_control(keys, num_keys, principal_name);
+ if (!control) {
+ fprintf(stderr, "Failed to create control!\n");
+ goto error_out;
+ }
+
+ /* connect to ldap server */
+ ret = asprintf(&ldap_uri, "ldap://%s:389", servername);
+ if (ret == -1) {
+ fprintf(stderr, "Unable to determine server URI!\n");
+ goto error_out;
+ }
+
+ /* TODO: support referrals ? */
+ ret = ldap_initialize(&ld, ldap_uri);
+ if(ret != LDAP_SUCCESS) {
+ fprintf(stderr, "Unable to initialize ldap library!\n");
+ goto error_out;
+ }
+
+ version = LDAP_VERSION3;
+ ret = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version);
+ if (ret != LDAP_OPT_SUCCESS) {
+ fprintf(stderr, "Unable to set ldap options!\n");
+ goto error_out;
+ }
+
+ ret = ldap_sasl_interactive_bind_s(ld,
+ NULL, "GSSAPI",
+ NULL, NULL,
+ LDAP_SASL_AUTOMATIC,
+ ldap_sasl_interact, princ);
+ if (ret != LDAP_SUCCESS) {
+ fprintf(stderr, "SASL Bind failed!\n");
+ goto error_out;
+ }
+
+ /* find base dn */
+ /* TODO: address the case where we have multiple naming contexts */
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ /* perform password change */
+ ret = ldap_extended_operation(ld,
+ KEYTAB_SET_OID,
+ control, NULL, NULL,
+ &msgid);
+ if (ret != LDAP_SUCCESS) {
+ fprintf(stderr, "Operation failed! %s\n", ldap_err2string(ret));
+ goto error_out;
+ }
+
+ ber_bvfree(control);
+
+ tv.tv_sec = 10;
+ tv.tv_usec = 0;
+
+ ret = ldap_result(ld, msgid, 1, &tv, &res);
+ if (ret == -1) {
+ fprintf(stderr, "Operation failed! %s\n", ldap_err2string(ret));
+ goto error_out;
+ }
+
+ ret = ldap_parse_extended_result(ld, res, &retoid, &retdata, 0);
+ if(ret != LDAP_SUCCESS) {
+ fprintf(stderr, "Operation failed! %s\n", ldap_err2string(ret));
+ goto error_out;
+ }
+
+ ret = ldap_parse_result(ld, res, &rc, NULL, &err, NULL, &srvctrl, 0);
+ if(ret != LDAP_SUCCESS || rc != LDAP_SUCCESS) {
+ fprintf(stderr, "Operation failed! %s\n", err?err:ldap_err2string(ret));
+ goto error_out;
+ }
+
+ if (!srvctrl) {
+ fprintf(stderr, "Missing reply control!\n");
+ goto error_out;
+ }
+
+ for (i = 0; srvctrl[i]; i++) {
+ if (0 == strcmp(srvctrl[i]->ldctl_oid, KEYTAB_RET_OID)) {
+ pprc = srvctrl[i];
+ }
+ }
+ if (!pprc) {
+ fprintf(stderr, "Missing reply control!\n");
+ goto error_out;
+ }
+
+ sctrl = ber_init(&pprc->ldctl_value);
+
+ if (!sctrl) {
+ fprintf(stderr, "ber_init() failed, Invalid control ?!\n");
+ goto error_out;
+ }
+
+ /* Format of response
+ *
+ * KeytabGetRequest ::= SEQUENCE {
+ * new_kvno Int32
+ * SEQUENCE OF KeyTypes
+ * }
+ *
+ * * List of accepted enctypes *
+ * KeyTypes ::= SEQUENCE {
+ * enctype Int32
+ * }
+ */
+
+ rtag = ber_scanf(sctrl, "{i{", &kvno);
+ if (rtag == LBER_ERROR) {
+ fprintf(stderr, "ber_scanf() failed, Invalid control ?!\n");
+ goto error_out;
+ }
+
+ for (i = 0; i < num_keys; i++) {
+ ret = ber_scanf(sctrl, "{i}", &encs[i]);
+ if (ret == LBER_ERROR) break;
+ }
+ *enctypes = encs;
+
+ if (err) ldap_memfree(err);
+ ber_free(sctrl, 1);
+ ldap_controls_free(srvctrl);
+ ldap_msgfree(res);
+ ldap_unbind_ext_s(ld, NULL, NULL);
+ free(ldap_uri);
+ return kvno;
+
+error_out:
+ if (sctrl) ber_free(sctrl, 1);
+ if (srvctrl) ldap_controls_free(srvctrl);
+ if (err) ldap_memfree(err);
+ if (res) ldap_msgfree(res);
+ if (ld) ldap_unbind_ext_s(ld, NULL, NULL);
+ if (ldap_uri) free(ldap_uri);
+ if (control) ber_bvfree(control);
+ if (encs) free(encs);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char *server = NULL;
+ static const char *principal = NULL;
+ static const char *keytab = NULL;
+ struct poptOption options[] = {
+ { "server", 's', POPT_ARG_STRING, &server, 0, "Contact this specific KDC Server", "Server Name" },
+ { "principal", 'p', POPT_ARG_STRING, &principal, 0, "The principal to get a keytab for (ex: ftp/ftp.example.com@EXAMPLE.COM)", "Kerberos Service Principal Name" },
+ { "keytab", 'k', POPT_ARG_STRING, &keytab, 0, "File were to store the keytab information", "Keytab File Name" },
+ { NULL, 0, POPT_ARG_NONE, NULL, 0, NULL, NULL }
+ };
+ poptContext pc;
+ char *ktname;
+ krb5_context krbctx;
+ krb5_ccache ccache;
+ krb5_principal uprinc;
+ krb5_principal sprinc;
+ krb5_error_code krberr;
+ krb5_keyblock *keys = NULL;
+ int num_keys = 0;
+ ber_int_t *enctypes;
+ krb5_keytab kt;
+ int kvno;
+ int i, ret;
+
+ pc = poptGetContext("ipa-getkeytab", argc, (const char **)argv, options, 0);
+ ret = poptGetNextOpt(pc);
+ if (ret != -1 || !server || !principal || !keytab) {
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+
+ ret = asprintf(&ktname, "WRFILE:%s", keytab);
+ if (ret == -1) {
+ exit(2);
+ }
+
+ krberr = krb5_init_context(&krbctx);
+ if (krberr) {
+ fprintf(stderr, "Kerberos context initialization failed\n");
+ exit(3);
+ }
+
+ krberr = krb5_parse_name(krbctx, principal, &sprinc);
+ if (krberr) {
+ fprintf(stderr, "Invalid Service Principal Name\n");
+ exit(4);
+ }
+
+ krberr = krb5_cc_default(krbctx, &ccache);
+ if (krberr) {
+ fprintf(stderr, "Kerberos Credential Cache not found\nDo you have a Kerberos Ticket?\n");
+ exit(5);
+ }
+
+ krberr = krb5_cc_get_principal(krbctx, ccache, &uprinc);
+ if (krberr) {
+ fprintf(stderr, "Kerberos User Principal not found\nDo you have a valid Credential Cache?\n");
+ exit(6);
+ }
+
+ krberr = krb5_kt_resolve(krbctx, ktname, &kt);
+ if (krberr) {
+ fprintf(stderr, "Failed to open Keytab\n");
+ exit(7);
+ }
+
+ /* create key material */
+ num_keys = create_keys(krbctx, &keys);
+ if (!num_keys) {
+ fprintf(stderr, "Failed to create random key material\n");
+ exit(8);
+ }
+
+ kvno = ldap_set_keytab(server, principal, uprinc, keys, num_keys, &enctypes);
+ if (!kvno) {
+ exit(9);
+ }
+
+ ret = filter_keys(krbctx, keys, &num_keys, enctypes);
+ if (ret == -1) {
+ fprintf(stderr, "No keys accepted by the KDC!\n");
+ exit(10);
+ }
+
+ for (i = 0; i < num_keys; i++) {
+ krb5_keytab_entry kt_entry;
+ memset((char *)&kt_entry, 0, sizeof(kt_entry));
+ kt_entry.principal = sprinc;
+ kt_entry.key = keys[i];
+ kt_entry.vno = kvno;
+
+ krberr = krb5_kt_add_entry(krbctx, kt, &kt_entry);
+ if (krberr) {
+ fprintf(stderr, "Failed to add key to the keytab\n");
+ exit (11);
+ }
+ }
+
+ free_keys(krbctx, keys, num_keys);
+
+ krberr = krb5_kt_close(krbctx, kt);
+ if (krberr) {
+ fprintf(stderr, "Failed to close the keytab\n");
+ exit (12);
+ }
+
+ exit(0);
+}
diff --git a/ipa-server/ipa-install/share/Makefile.am b/ipa-server/ipa-install/share/Makefile.am
index 5d117dec..4908d35a 100644
--- a/ipa-server/ipa-install/share/Makefile.am
+++ b/ipa-server/ipa-install/share/Makefile.am
@@ -8,6 +8,7 @@ app_DATA = \
60ipaconfig.ldif \
bootstrap-template.ldif \
default-aci.ldif \
+ default-keytypes.ldif \
kerberos.ldif \
indeces.ldif \
bind.named.conf.template \
diff --git a/ipa-server/ipa-install/share/default-aci.ldif b/ipa-server/ipa-install/share/default-aci.ldif
index 5715259a..1f1409e1 100644
--- a/ipa-server/ipa-install/share/default-aci.ldif
+++ b/ipa-server/ipa-install/share/default-aci.ldif
@@ -3,8 +3,9 @@
dn: $SUFFIX
changetype: modify
replace: aci
-aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMkey")(version 3.0; acl "Enable Anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";)
-aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory || krbMkey")(version 3.0; acl "Admin can manage any entry"; allow (all) userdn = "ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX";)
+aci: (targetattr = "krbMKey")(version 3.0; acl "Only the kerberos account can access this one"; deny (read, search, compare, write) userdn != "ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
+aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Enable Anonymous access"; allow (read, search, compare) userdn = "ldap:///anyone";)
+aci: (targetattr != "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Admin can manage any entry"; allow (all) userdn = "ldap:///uid=admin,cn=sysaccounts,cn=etc,$SUFFIX";)
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Admins can write passwords"; allow (write) groupdn="ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";)
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "Password change service can read/write passwords"; allow (read, write) userdn="ldap:///krbprincipalname=kadmin/changepw@$REALM,cn=$REALM,cn=kerberos,$SUFFIX";)
aci: (targetattr = "userPassword || krbPrincipalKey || sambaLMPassword || sambaNTPassword || passwordHistory")(version 3.0; acl "KDC System Account can access passwords"; allow (all) userdn="ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
diff --git a/ipa-server/ipa-install/share/default-keytypes.ldif b/ipa-server/ipa-install/share/default-keytypes.ldif
new file mode 100644
index 00000000..9cafacd3
--- /dev/null
+++ b/ipa-server/ipa-install/share/default-keytypes.ldif
@@ -0,0 +1,20 @@
+#kerberos keytypes
+dn: cn=$REALM,cn=kerberos,$SUFFIX
+changetype: modify
+add: krbSupportedEncSaltTypes
+krbSupportedEncSaltTypes: aes256-cts:normal
+krbSupportedEncSaltTypes: aes128-cts:normal
+krbSupportedEncSaltTypes: des3-hmac-sha1:normal
+krbSupportedEncSaltTypes: arcfour-hmac:normal
+krbSupportedEncSaltTypes: des-hmac-sha1:normal
+krbSupportedEncSaltTypes: des-cbc-md5:normal
+krbSupportedEncSaltTypes: des-cbc-crc:normal
+krbSupportedEncSaltTypes: des-cbc-crc:v4
+krbSupportedEncSaltTypes: des-cbc-crc:afs3
+krbDefaultEncSaltTypes: aes256-cts:normal
+krbDefaultEncSaltTypes: aes128-cts:normal
+krbDefaultEncSaltTypes: des3-hmac-sha1:normal
+krbDefaultEncSaltTypes: arcfour-hmac:normal
+krbDefaultEncSaltTypes: des-hmac-sha1:normal
+krbDefaultEncSaltTypes: des-cbc-md5:normal
+
diff --git a/ipa-server/ipa-install/share/kdc.conf.template b/ipa-server/ipa-install/share/kdc.conf.template
index 69e769e3..0ac4d467 100644
--- a/ipa-server/ipa-install/share/kdc.conf.template
+++ b/ipa-server/ipa-install/share/kdc.conf.template
@@ -4,7 +4,7 @@
[realms]
$REALM = {
master_key_type = des3-hmac-sha1
- supported_enctypes = des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal des-cbc-crc:v4 des-cbc-crc:afs3
+ supported_enctypes = aes256-cts:normal aes128-cts:normal des3-hmac-sha1:normal arcfour-hmac:normal des-hmac-sha1:normal des-cbc-md5:normal des-cbc-crc:normal des-cbc-crc:v4 des-cbc-crc:afs3
max_life = 7d
max_renewable_life = 14d
acl_file = /var/kerberos/krb5kdc/kadm5.acl
diff --git a/ipa-server/ipa-install/share/kerberos.ldif b/ipa-server/ipa-install/share/kerberos.ldif
index 75057aa3..f1b651d5 100644
--- a/ipa-server/ipa-install/share/kerberos.ldif
+++ b/ipa-server/ipa-install/share/kerberos.ldif
@@ -14,4 +14,3 @@ objectClass: top
cn: kerberos
aci: (targetattr="*")(version 3.0; acl "KDC System Account"; allow (all) userdn= "ldap:///uid=kdc,cn=sysaccounts,cn=etc,$SUFFIX";)
-
diff --git a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
index 0d87b2ba..782eee76 100644
--- a/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
+++ b/ipa-server/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
@@ -84,15 +84,13 @@
/* ber tags for the PasswdModifyResponseValue sequence */
#define LDAP_EXTOP_PASSMOD_TAG_GENPWD 0x80U
-/* number of bytes used for random password generation */
-#define LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN 8
-
-/* number of random bytes needed to generate password */
-#define LDAP_EXTOP_PASSMOD_RANDOM_BYTES 6
-
/* OID of the extended operation handled by this plug-in */
#define EXOP_PASSWD_OID "1.3.6.1.4.1.4203.1.11.1"
+/* OID to retrieve keytabs */
+#define KEYTAB_SET_OID "2.16.840.1.113730.3.8.3.1"
+#define KEYTAB_RET_OID "2.16.840.1.113730.3.8.3.2"
+
/* krbTicketFlags */
#define KTF_DISALLOW_POSTDATED 0x00000001
#define KTF_DISALLOW_FORWARDABLE 0x00000002
@@ -122,23 +120,150 @@
#define KRB5P_SALT_SIZE 16
-struct krb5p_keysalt {
+static const char *ipapwd_def_encsalts[] = {
+ "des3-hmac-sha1:normal",
+/* "arcfour-hmac:normal",
+ "des-hmac-sha1:normal",
+ "des-cbc-md5:normal", */
+ "des-cbc-crc:normal",
+/* "des-cbc-crc:v4",
+ "des-cbc-crc:afs3", */
+ NULL
+};
+
+struct ipapwd_encsalt {
krb5_int32 enc_type;
krb5_int32 salt_type;
};
+struct ipapwd_config {
+ char *realm;
+ krb5_keyblock *kmkey;
+ int num_supp_encsalts;
+ struct ipapwd_encsalt *supp_encsalts;
+ int num_pref_encsalts;
+ struct ipapwd_encsalt *pref_encsalts;
+};
+
+static const char *ipa_realm_dn = NULL;
+
+Slapi_Mutex *ipa_globals = NULL;
+
+static struct ipapwd_config *ipapwd_config = NULL;
+
static void *ipapwd_plugin_id;
+#define IPA_CHANGETYPE_NORMAL 0
+#define IPA_CHANGETYPE_ADMIN 1
+#define IPA_CHANGETYPE_DSMGR 2
-const char *ipa_realm_dn = NULL;
-const char *ipa_realm = NULL;
-struct krb5p_keysalt *ipa_keysalts = NULL;
-int ipa_n_keysalts = 0;
+struct ipapwd_data {
+ Slapi_Entry *target;
+ const char *dn;
+ const char *password;
+ time_t timeNow;
+ time_t lastPwChange;
+ time_t expireTime;
+ int changetype;
+ int pwHistoryLen;
+};
-Slapi_Mutex *ipa_globals = NULL;
-krb5_keyblock *ipa_kmkey = NULL;
+struct ipapwd_krbkeydata {
+ int32_t type;
+ struct berval value;
+};
+
+struct ipapwd_krbkey {
+ struct ipapwd_krbkeydata *salt;
+ struct ipapwd_krbkeydata *ekey;
+ struct berval s2kparams;
+};
+
+struct ipapwd_keyset {
+ uint16_t major_vno;
+ uint16_t minor_vno;
+ uint32_t kvno;
+ uint32_t mkvno;
+ struct ipapwd_krbkey *keys;
+ int num_keys;
+};
+
+static void ipapwd_keyset_free(struct ipapwd_keyset **pkset)
+{
+ struct ipapwd_keyset *kset = *pkset;
+ int i;
-static int ipapwd_getMasterKey(const char *realm_dn);
+ if (!kset) return;
+
+ for (i = 0; i < kset->num_keys; i++) {
+ if (kset->keys[i].salt) {
+ if (kset->keys[i].salt->value.bv_val) {
+ free(kset->keys[i].salt->value.bv_val);
+ }
+ free(kset->keys[i].salt);
+ }
+ if (kset->keys[i].ekey) {
+ if (kset->keys[i].ekey->value.bv_val) {
+ free(kset->keys[i].ekey->value.bv_val);
+ }
+ free(kset->keys[i].ekey);
+ }
+ if (kset->keys[i].s2kparams.bv_val) {
+ free(kset->keys[i].s2kparams.bv_val);
+ }
+ }
+ free(kset->keys);
+ free(kset);
+ *pkset = NULL;
+}
+
+static int filter_keys(struct ipapwd_keyset *kset)
+{
+ struct ipapwd_config *config;
+ int i, j;
+
+ slapi_lock_mutex(ipa_globals);
+ config = ipapwd_config;
+ slapi_unlock_mutex(ipa_globals);
+
+ for (i = 0; i < kset->num_keys; i++) {
+ for (j = 0; j < config->num_supp_encsalts; j++) {
+ if (kset->keys[i].ekey->type == config->supp_encsalts[j].enc_type) break;
+ }
+ if (j == config->num_supp_encsalts) { /* not valid */
+
+ /* free key */
+ if (kset->keys[i].ekey) {
+ if (kset->keys[i].ekey->value.bv_val) {
+ free(kset->keys[i].ekey->value.bv_val);
+ }
+ free(kset->keys[i].ekey);
+ }
+ if (kset->keys[i].salt) {
+ if (kset->keys[i].salt->value.bv_val) {
+ free(kset->keys[i].salt->value.bv_val);
+ }
+ free(kset->keys[i].salt);
+ }
+ if (kset->keys[i].s2kparams.bv_val) {
+ free(kset->keys[i].s2kparams.bv_val);
+ }
+
+ /* move all remaining keys up by one */
+ kset->num_keys -= 1;
+
+ for (j = i; j < kset->num_keys; j++) {
+ kset->keys[j] = kset->keys[j + 1];
+ }
+
+ /* new key has been moved to this position, make sure
+ * we do not skip it, by neutralizing next i increment */
+ i--;
+ }
+ }
+
+ return 0;
+}
/* Novell key-format scheme:
@@ -170,187 +295,197 @@ static int ipapwd_getMasterKey(const char *realm_dn);
*/
-struct kbvals {
- ber_int_t kvno;
- const struct berval *bval;
-};
-
-static int cmpkbvals(const void *p1, const void *p2)
-{
- const struct kbvals *k1, *k2;
-
- k1 = (struct kbvals *)p1;
- k2 = (struct kbvals *)p2;
-
- return (((int)k1->kvno) - ((int)k2->kvno));
-}
-
-static inline void encode_int16(unsigned int val, unsigned char *p)
-{
- p[1] = (val >> 8) & 0xff;
- p[0] = (val ) & 0xff;
-}
-
-#define IPA_CHANGETYPE_NORMAL 0
-#define IPA_CHANGETYPE_ADMIN 1
-#define IPA_CHANGETYPE_DSMGR 2
-
-struct ipapwd_data {
- Slapi_Entry *target;
- const char *dn;
- const char *password;
- time_t timeNow;
- time_t lastPwChange;
- time_t expireTime;
- int changetype;
- int pwHistoryLen;
-};
-
-static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data *data)
+static struct berval *encode_keys(struct ipapwd_keyset *kset)
{
- krb5_keyblock *kmkey;
- const char *krbPrincipalName;
- uint32_t krbMaxTicketLife;
- Slapi_Attr *krbPrincipalKey = NULL;
- struct kbvals *kbvals = NULL;
- time_t time_now;
- int kvno;
- int num_versions, num_keys;
- int krbTicketFlags;
BerElement *be = NULL;
struct berval *bval = NULL;
- Slapi_Value **svals = NULL;
- int svals_no;
- krb5_principal princ;
- krb5_error_code krberr;
- krb5_data pwd;
int ret, i;
- slapi_lock_mutex(ipa_globals);
- kmkey = ipa_kmkey;
- slapi_unlock_mutex(ipa_globals);
-
- time_now = time(NULL);
+ be = ber_alloc_t(LBER_USE_DER);
- krbPrincipalName = slapi_entry_attr_get_charptr(data->target, "krbPrincipalName");
- if (!krbPrincipalName) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "no krbPrincipalName present in this entry\n");
+ if (!be) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "memory allocation failed\n");
return NULL;
}
- krbMaxTicketLife = slapi_entry_attr_get_uint(data->target, "krbMaxTicketLife");
- if (krbMaxTicketLife == 0) {
- /* FIXME: retrieve the default from config (max_life from kdc.conf) */
- krbMaxTicketLife = 86400; /* just set the default 24h for now */
+ ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{",
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), kset->major_vno,
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), kset->minor_vno,
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2), kset->kvno,
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3), kset->mkvno,
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4));
+ if (ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "encoding asn1 vno info failed\n");
+ goto done;
}
- kvno = 0;
- num_keys = 0;
- num_versions = 1;
+ for (i = 0; i < kset->num_keys; i++) {
- /* retrieve current kvno and and keys */
- ret = slapi_entry_attr_find(data->target, "krbPrincipalKey", &krbPrincipalKey);
- if (ret == 0) {
- int i, n, count, idx;
- ber_int_t tkvno;
- Slapi_ValueSet *svs;
- Slapi_Value *sv;
- ber_tag_t tag, tmp;
- const struct berval *cbval;
-
- slapi_attr_get_valueset(krbPrincipalKey, &svs);
- count = slapi_valueset_count(svs);
- if (count > 0) {
- kbvals = (struct kbvals *)calloc(count, sizeof(struct kbvals));
+ ret = ber_printf(be, "{");
+ if (ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "encoding asn1 EncryptionKey failed\n");
+ goto done;
}
- n = 0;
- for (i = 0, idx = 0; count > 0 && i < count; i++) {
- if (i == 0) {
- idx = slapi_valueset_first_value(svs, &sv);
- } else {
- idx = slapi_valueset_next_value(svs, idx, &sv);
- }
- if (idx == -1) {
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
- "Array of stored keys shorter than expected\n");
- break;
- }
- cbval = slapi_value_get_berval(sv);
- if (!cbval) {
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
- "Error retrieving berval from Slapi_Value\n");
- continue;
+
+ if (kset->keys[i].salt) {
+ ret = ber_printf(be, "t[{t[i]",
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
+ kset->keys[i].salt->type);
+ if ((ret != -1) && kset->keys[i].salt->value.bv_len) {
+ ret = ber_printf(be, "t[o]",
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
+ kset->keys[i].salt->value.bv_val,
+ kset->keys[i].salt->value.bv_len);
+ }
+ if (ret != -1) {
+ ret = ber_printf(be, "}]");
}
- be = ber_init(cbval);
- if (!be) {
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
- "ber_init() failed!\n");
- continue;
+ if (ret == -1) {
+ goto done;
}
+ }
- tag = ber_scanf(be, "{xxt[i]", &tmp, &tkvno);
- if (tag == LBER_ERROR) {
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
- "Bad OLD key encoding ?!\n");
- ber_free(be, 1);
- continue;
- }
+ ret = ber_printf(be, "t[{t[i]t[o]}]",
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
+ kset->keys[i].ekey->type,
+ (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
+ kset->keys[i].ekey->value.bv_val,
+ kset->keys[i].ekey->value.bv_len);
+ if (ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "encoding asn1 EncryptionKey failed\n");
+ goto done;
+ }
- kbvals[n].kvno = tkvno;
- kbvals[n].bval = cbval;
- n++;
+ /* FIXME: s2kparams not supported yet */
- if (tkvno > kvno) {
- kvno = tkvno;
- }
+ ret = ber_printf(be, "}");
+ if (ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "encoding asn1 EncryptionKey failed\n");
+ goto done;
+ }
+ }
- ber_free(be, 1);
+ ret = ber_printf(be, "}]}");
+ if (ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "encoding asn1 end of sequences failed\n");
+ goto done;
+ }
+
+ ret = ber_flatten(be, &bval);
+ if (ret == -1) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "flattening asn1 failed\n");
+ goto done;
+ }
+done:
+ ber_free(be, 1);
+
+ return bval;
+}
+
+static int ipapwd_get_cur_kvno(Slapi_Entry *target)
+{
+ Slapi_Attr *krbPrincipalKey = NULL;
+ Slapi_ValueSet *svs;
+ Slapi_Value *sv;
+ BerElement *be = NULL;
+ const struct berval *cbval;
+ ber_tag_t tag, tmp;
+ ber_int_t tkvno;
+ int hint;
+ int kvno;
+ int ret;
+
+ /* retrieve current kvno and and keys */
+ ret = slapi_entry_attr_find(target, "krbPrincipalKey", &krbPrincipalKey);
+ if (ret != 0) {
+ return 0;
+ }
+
+ kvno = 0;
+
+ slapi_attr_get_valueset(krbPrincipalKey, &svs);
+ hint = slapi_valueset_first_value(svs, &sv);
+ while (hint != -1) {
+ cbval = slapi_value_get_berval(sv);
+ if (!cbval) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "Error retrieving berval from Slapi_Value\n");
+ goto next;
+ }
+ be = ber_init(cbval);
+ if (!be) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "ber_init() failed!\n");
+ goto next;
}
- num_keys = n;
- /* now verify how many keys we need to keep around */
- if (num_keys) {
- if (time_now > data->lastPwChange + krbMaxTicketLife) {
- /* the last password change was long ago,
- * at most only the last entry need to be kept */
- num_versions = 2;
- } else {
- /* we don't know how many changes have happened since
- * the oldest krbtgt was release, keep all + new */
- num_versions = num_keys + 1;
- }
+ tag = ber_scanf(be, "{xxt[i]", &tmp, &tkvno);
+ if (tag == LBER_ERROR) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "Bad OLD key encoding ?!\n");
+ ber_free(be, 1);
+ goto next;
+ }
- /* now reorder by kvno */
- if (num_keys > 1) {
- qsort(kbvals, num_keys, sizeof(struct kbvals), cmpkbvals);
- }
+ if (tkvno > kvno) {
+ kvno = tkvno;
}
+
+ ber_free(be, 1);
+next:
+ hint = slapi_valueset_next_value(svs, hint, &sv);
}
- /* increment kvno (will be 1 if this is a new entry) */
- kvno++;
+ return kvno;
+}
+
+static inline void encode_int16(unsigned int val, unsigned char *p)
+{
+ p[1] = (val >> 8) & 0xff;
+ p[0] = (val ) & 0xff;
+}
+
+static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data *data)
+{
+ struct ipapwd_config *config;
+ const char *krbPrincipalName;
+ uint32_t krbMaxTicketLife;
+ int kvno, i;
+ int krbTicketFlags;
+ struct berval *bval = NULL;
+ Slapi_Value **svals = NULL;
+ krb5_principal princ;
+ krb5_error_code krberr;
+ krb5_data pwd;
+ struct ipapwd_keyset *kset = NULL;
+
+ slapi_lock_mutex(ipa_globals);
+ config = ipapwd_config;
+ slapi_unlock_mutex(ipa_globals);
- svals = (Slapi_Value **)calloc(num_versions + 1, sizeof(Slapi_Value *));
+ svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *));
if (!svals) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "memory allocation failed\n");
return NULL;
}
- /* set all old keys to save */
- for (svals_no = 0; svals_no < (num_versions - 1); svals_no++) {
- int idx;
+ kvno = ipapwd_get_cur_kvno(data->target);
- idx = num_keys - (num_versions - 1) + svals_no;
- svals[svals_no] = slapi_value_new_berval(kbvals[idx].bval);
- if (!svals[svals_no]) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "Converting berval to Slapi_Value\n");
- goto enc_error;
- }
+ krbPrincipalName = slapi_entry_attr_get_charptr(data->target, "krbPrincipalName");
+ if (!krbPrincipalName) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "no krbPrincipalName present in this entry\n");
+ return NULL;
}
- if (kbvals) free(kbvals);
-
krberr = krb5_parse_name(krbctx, krbPrincipalName, &princ);
if (krberr) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
@@ -359,35 +494,40 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
goto enc_error;
}
+ krbMaxTicketLife = slapi_entry_attr_get_uint(data->target, "krbMaxTicketLife");
+ if (krbMaxTicketLife == 0) {
+ /* FIXME: retrieve the default from config (max_life from kdc.conf) */
+ krbMaxTicketLife = 86400; /* just set the default 24h for now */
+ }
+
krbTicketFlags = slapi_entry_attr_get_int(data->target, "krbTicketFlags");
pwd.data = (char *)data->password;
pwd.length = strlen(data->password);
- be = ber_alloc_t( LBER_USE_DER );
-
- if (!be) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "memory allocation failed\n");
+ kset = malloc(sizeof(struct ipapwd_keyset));
+ if (!kset) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
goto enc_error;
}
- /* major-vno = 1 and minor-vno = 1 */
/* this encoding assumes all keys have the same kvno */
+ /* major-vno = 1 and minor-vno = 1 */
+ kset->major_vno = 1;
+ kset->minor_vno = 1;
+ /* increment kvno (will be 1 if this is a new entry) */
+ kset->kvno = kvno + 1;
/* we also assum mkvno is 0 */
- ret = ber_printf(be, "{t[i]t[i]t[i]t[i]t[{",
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), 1,
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), 1,
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2), kvno,
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 3), 0,
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 4));
- if (ret == -1) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "encoding asn1 vno info failed\n");
+ kset->mkvno = 0;
+
+ kset->num_keys = config->num_pref_encsalts;
+ kset->keys = calloc(kset->num_keys, sizeof(struct ipapwd_krbkey));
+ if (!kset->keys) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
goto enc_error;
}
- for (i = 0; i < ipa_n_keysalts; i++) {
+ for (i = 0; i < kset->num_keys; i++) {
krb5_keyblock key;
krb5_data salt;
krb5_octet *ptr;
@@ -398,7 +538,7 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
salt.data = NULL;
- switch (ipa_keysalts[i].salt_type) {
+ switch (config->pref_encsalts[i].salt_type) {
case KRB5_KDB_SALTTYPE_ONLYREALM:
@@ -437,8 +577,9 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
* name without any separator) */
if (krbTicketFlags & KTF_REQUIRES_PRE_AUTH) {
salt.length = KRB5P_SALT_SIZE;
+ salt.data = malloc(KRB5P_SALT_SIZE);
krberr = krb5_c_random_make_octets(krbctx, &salt);
- if (!krberr) {
+ if (krberr) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
"krb5_c_random_make_octets failed [%s]\n",
krb5_get_error_message(krbctx, krberr));
@@ -479,12 +620,12 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
default:
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "Invalid salt type [%d]\n", ipa_keysalts[i].salt_type);
+ "Invalid salt type [%d]\n", config->pref_encsalts[i].salt_type);
goto enc_error;
}
/* need to build the key now to manage the AFS salt.length special case */
- krberr = krb5_c_string_to_key(krbctx, ipa_keysalts[i].enc_type, &pwd, &salt, &key);
+ krberr = krb5_c_string_to_key(krbctx, config->pref_encsalts[i].enc_type, &pwd, &salt, &key);
if (krberr) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
"krb5_c_string_to_key failed [%s]\n",
@@ -496,7 +637,7 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
salt.length = strlen(salt.data);
}
- krberr = krb5_c_encrypt_length(krbctx, kmkey->enctype, key.length, &len);
+ krberr = krb5_c_encrypt_length(krbctx, config->kmkey->enctype, key.length, &len);
if (krberr) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
"krb5_c_string_to_key failed [%s]\n",
@@ -522,7 +663,7 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
cipher.ciphertext.length = len;
cipher.ciphertext.data = (char *)ptr+2;
- krberr = krb5_c_encrypt(krbctx, kmkey, 0, 0, &plain, &cipher);
+ krberr = krb5_c_encrypt(krbctx, config->kmkey, 0, 0, &plain, &cipher);
if (krberr) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
"krb5_c_encrypt failed [%s]\n",
@@ -534,79 +675,69 @@ static Slapi_Value **encrypt_encode_key(krb5_context krbctx, struct ipapwd_data
}
/* KrbSalt */
- if (salt.length) {
- ret = ber_printf(be, "{t[{t[i]t[o]}]",
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), ipa_keysalts[i].salt_type,
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), salt.data, salt.length);
- } else {
- ret = ber_printf(be, "{t[{t[i]}]",
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0),
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), ipa_keysalts[i].salt_type);
- }
- if (ret == -1) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "encoding asn1 KrbSalt failed\n");
+ kset->keys[i].salt = malloc(sizeof(struct ipapwd_krbkeydata));
+ if (!kset->keys[i].salt) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
krb5int_c_free_keyblock_contents(krbctx, &key);
- krb5_free_data_contents(krbctx, &salt);
free(ptr);
goto enc_error;
}
+
+ kset->keys[i].salt->type = config->pref_encsalts[i].salt_type;
+
+ if (salt.length) {
+ kset->keys[i].salt->value.bv_len = salt.length;
+ kset->keys[i].salt->value.bv_val = salt.data;
+ }
/* EncryptionKey */
- ret = ber_printf(be, "t[{t[i]t[o]}]}",
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1),
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 0), key.enctype,
- (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1), ptr, len+2);
- if (ret == -1) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "encoding asn1 EncryptionKey failed\n");
+ kset->keys[i].ekey = malloc(sizeof(struct ipapwd_krbkeydata));
+ if (!kset->keys[i].ekey) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
krb5int_c_free_keyblock_contents(krbctx, &key);
- krb5_free_data_contents(krbctx, &salt);
free(ptr);
goto enc_error;
}
-
+ kset->keys[i].ekey->type = key.enctype;
+ kset->keys[i].ekey->value.bv_len = len+2;
+ kset->keys[i].ekey->value.bv_val = malloc(len+2);
+ if (!kset->keys[i].ekey->value.bv_val) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ krb5int_c_free_keyblock_contents(krbctx, &key);
+ free(ptr);
+ goto enc_error;
+ }
+ memcpy(kset->keys[i].ekey->value.bv_val, ptr, len+2);
+
/* make sure we free the memory used now that we are done with it */
krb5int_c_free_keyblock_contents(krbctx, &key);
- krb5_free_data_contents(krbctx, &salt);
free(ptr);
}
- ret = ber_printf(be, "}]}");
- if (ret == -1) {
- slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "encoding asn1 end of sequences failed\n");
- goto enc_error;
- }
-
- ret = ber_flatten(be, &bval);
- if (ret == -1) {
+ bval = encode_keys(kset);
+ if (!bval) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
- "flattening asn1 failed\n");
+ "encoding asn1 KrbSalt failed\n");
goto enc_error;
}
- svals[svals_no] = slapi_value_new_berval(bval);
- if (!svals[svals_no]) {
+ svals[0] = slapi_value_new_berval(bval);
+ if (!svals[0]) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
"Converting berval to Slapi_Value\n");
goto enc_error;
}
- svals_no++;
- svals[svals_no] = NULL;
-
+ ipapwd_keyset_free(&kset);
krb5_free_principal(krbctx, princ);
ber_bvfree(bval);
- ber_free(be, 1);
return svals;
enc_error:
+ if (kset) ipapwd_keyset_free(&kset);
krb5_free_principal(krbctx, princ);
if (bval) ber_bvfree(bval);
if (svals) free(svals);
- if (be) ber_free(be, 1);
return NULL;
}
@@ -1038,7 +1169,7 @@ static Slapi_Value **ipapwd_setPasswordHistory(Slapi_Mods *smods, struct ipapwd_
static Slapi_Value *ipapwd_strip_pw_date(Slapi_Value *pw)
{
- char *pwstr;
+ const char *pwstr;
pwstr = slapi_value_get_string(pw);
if (strlen(pwstr) <= GENERALIZED_TIME_LENGTH) {
@@ -1462,7 +1593,8 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
if (!svals) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "key encryption/encoding failed\n");
krb5_free_context(krbctx);
- return LDAP_OPERATIONS_ERROR;
+ ret = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
}
/* done with it */
krb5_free_context(krbctx);
@@ -1472,8 +1604,8 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
/* change Last Password Change field with the current date */
if (!gmtime_r(&(data->timeNow), &utctime)) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n");
- free(svals);
- return LDAP_OPERATIONS_ERROR;
+ ret = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
}
strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime);
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastPwdChange", timestr);
@@ -1481,8 +1613,8 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
/* set Password Expiration date */
if (!gmtime_r(&(data->expireTime), &utctime)) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to convert expiration date\n");
- free(svals);
- return LDAP_OPERATIONS_ERROR;
+ ret = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
}
strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime);
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbPasswordExpiration", timestr);
@@ -1497,9 +1629,9 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
if (ntlm_flags) {
char *password = strdup(data->password);
if (encode_ntlm_keys(password, ntlm_flags, &ntlm) != 0) {
- free(svals);
free(password);
- return LDAP_OPERATIONS_ERROR;
+ ret = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
}
if (ntlm_flags & KTF_LM_HASH) {
hexbuf(lm, ntlm.lm);
@@ -1518,8 +1650,8 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
userpwd = slapi_encode(data->password, NULL);
if (!userpwd) {
slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to make userPassword hash\n");
- free(svals);
- return LDAP_OPERATIONS_ERROR;
+ ret = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
}
slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "userPassword", userpwd);
@@ -1538,14 +1670,18 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
/* commit changes */
ret = ipapwd_apply_mods(data->dn, smods);
- slapi_mods_free(&smods);
-
slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "<= ipapwd_SetPassword: %d\n", ret);
- for (i = 0; svals[i]; i++) {
- slapi_value_free(&svals[i]);
+free_and_return:
+ slapi_mods_free(&smods);
+
+ if (svals) {
+ for (i = 0; svals[i]; i++) {
+ slapi_value_free(&svals[i]);
+ }
+ free(svals);
}
- free(svals);
+
if (pwvals) {
for (i = 0; pwvals[i]; i++) {
slapi_value_free(&pwvals[i]);
@@ -1555,60 +1691,15 @@ static int ipapwd_SetPassword(struct ipapwd_data *data)
return ret;
}
-#if 0 /* Not used right now */
-
-/* Generate a new, basic random password */
-static int ipapwd_generate_basic_passwd( int passlen, char **genpasswd )
+static int ipapwd_chpwop(Slapi_PBlock *pb)
{
- unsigned char *data = NULL;
- char *enc = NULL;
- int datalen = LDAP_EXTOP_PASSMOD_RANDOM_BYTES;
- int enclen = LDAP_EXTOP_PASSMOD_GEN_PASSWD_LEN + 1;
-
- if ( genpasswd == NULL ) {
- return LDAP_OPERATIONS_ERROR;
- }
-
- if ( passlen > 0 ) {
- datalen = passlen * 3 / 4 + 1;
- enclen = datalen * 4; /* allocate the large enough space */
- }
-
- data = (unsigned char *)slapi_ch_calloc( datalen, 1 );
- enc = (char *)slapi_ch_calloc( enclen, 1 );
-
- /* get random bytes from NSS */
- PK11_GenerateRandom( data, datalen );
-
- /* b64 encode the random bytes to get a password made up
- * of printable characters. ldif_base64_encode() will
- * zero-terminate the string */
- (void)ldif_base64_encode( data, enc, passlen, -1 );
-
- /* This will get freed by the caller */
- *genpasswd = slapi_ch_malloc( 1 + passlen );
-
- /* trim the password to the proper length */
- PL_strncpyz( *genpasswd, enc, passlen + 1 );
-
- slapi_ch_free( (void **)&data );
- slapi_ch_free_string( &enc );
-
- return LDAP_SUCCESS;
-}
-#endif
-
-/* Password Modify Extended operation plugin function */
-static int ipapwd_extop(Slapi_PBlock *pb)
-{
- char *oid = NULL;
char *bindDN = NULL;
char *authmethod = NULL;
char *dn = NULL;
char *oldPasswd = NULL;
char *newPasswd = NULL;
char *errMesg = NULL;
- int ret=0, rc=0, sasl_ssf=0, is_ssl=0, is_root=0;
+ int ret=0, rc=0, is_root=0;
ber_tag_t tag=0;
ber_len_t len=-1;
struct berval *extop_value = NULL;
@@ -1616,69 +1707,11 @@ static int ipapwd_extop(Slapi_PBlock *pb)
Slapi_Entry *targetEntry=NULL;
char *attrlist[] = {"*", "passwordHistory", NULL };
struct ipapwd_data pwdata;
+ struct ipapwd_config *config;
- slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_extop\n");
-
- /* Before going any further, we'll make sure that the right extended operation plugin
- * has been called: i.e., the OID shipped whithin the extended operation request must
- * match this very plugin's OID: EXOP_PASSWD_OID. */
- if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ) {
- errMesg = "Could not get OID value from request.\n";
- rc = LDAP_OPERATIONS_ERROR;
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
- errMesg );
- goto free_and_return;
- } else {
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
- "Received extended operation request with OID %s\n", oid );
- }
-
- if ( strcasecmp( oid, EXOP_PASSWD_OID ) != 0) {
- errMesg = "Request OID does not match Passwd OID.\n";
- rc = LDAP_OPERATIONS_ERROR;
- goto free_and_return;
- } else {
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
- "Password Modify extended operation request confirmed.\n" );
- }
- /* Now , at least we know that the request was indeed a Password Modify one. */
-
- /* make sure we have the master key */
- if (ipa_kmkey == NULL) {
- ret = ipapwd_getMasterKey(ipa_realm_dn);
- if (ret != LDAP_SUCCESS) {
- errMesg = "Fatal Internal Error Retrieving Master Key";
- rc = LDAP_OPERATIONS_ERROR;
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg );
- goto free_and_return;
- }
- }
-
-#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
- /* Allow password modify only for SSL/TLS established connections and
- * connections using SASL privacy layers */
- if ( slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) {
- errMesg = "Could not get SASL SSF from connection\n";
- rc = LDAP_OPERATIONS_ERROR;
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
- errMesg );
- goto free_and_return;
- }
-
- if (slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl) != 0) {
- errMesg = "Could not get IS SSL from connection\n";
- rc = LDAP_OPERATIONS_ERROR;
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
- errMesg );
- goto free_and_return;
- }
-
- if ( (is_ssl == 0) && (sasl_ssf <= 1) ) {
- errMesg = "Operation requires a secure connection.\n";
- rc = LDAP_CONFIDENTIALITY_REQUIRED;
- goto free_and_return;
- }
-#endif
+ slapi_lock_mutex(ipa_globals);
+ config = ipapwd_config;
+ slapi_unlock_mutex(ipa_globals);
/* Get the ber value of the extended operation */
slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
@@ -1805,7 +1838,7 @@ parse_req_done:
if (ret) {
/* Couldn't find the entry, fail */
errMesg = "No such Entry exists.\n" ;
- rc = LDAP_NO_SUCH_OBJECT ;
+ rc = LDAP_NO_SUCH_OBJECT;
goto free_and_return;
}
@@ -1868,7 +1901,7 @@ parse_req_done:
if (bindexp) {
/* special case kpasswd and Directory Manager */
if ((strncasecmp(bindexp[0], "krbprincipalname=kadmin/changepw@", 33) == 0) &&
- (strcasecmp(&(bindexp[0][33]), ipa_realm) == 0)) {
+ (strcasecmp(&(bindexp[0][33]), config->realm) == 0)) {
pwdata.changetype = IPA_CHANGETYPE_NORMAL;
}
if ((strcasecmp(bindexp[0], "cn=Directory Manager") == 0) &&
@@ -1928,83 +1961,660 @@ free_and_return:
return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
-} /* ipapwd_extop */
+}
-/* Novell key-format scheme:
- KrbMKey ::= SEQUENCE {
- kvno [0] UInt32,
- key [1] MasterKey
- }
-
- MasterKey ::= SEQUENCE {
- keytype [0] Int32,
- keyvalue [1] OCTET STRING
- }
-*/
+/* Password Modify Extended operation plugin function */
+static int ipapwd_setkeytab(Slapi_PBlock *pb)
+{
+ struct ipapwd_config *config;
+ char *bindDN = NULL;
+ char *serviceName = NULL;
+ char *errMesg = NULL;
+ int ret=0, rc=0, is_root=0;
+ struct berval *extop_value = NULL;
+ BerElement *ber = NULL;
+ Slapi_PBlock *pbte = NULL;
+ Slapi_Entry *targetEntry=NULL;
+ struct berval *bval = NULL;
+ Slapi_Value **svals = NULL;
+ const char *bdn;
+ const Slapi_DN *bsdn;
+ Slapi_DN *sdn;
+ Slapi_Backend *be;
+ Slapi_Entry **es = NULL;
+ int scope, res;
+ char *filter;
+ char *attrlist[] = {"krbPrincipalKey", "krbLastPwdChange", NULL };
+ krb5_context krbctx = NULL;
+ krb5_principal krbname = NULL;
+ krb5_error_code krberr;
+ int i, kvno;
+ Slapi_Mods *smods;
+ ber_tag_t rtag, ttmp;
+ ber_int_t tint;
+ ber_len_t tlen;
+ struct ipapwd_keyset *kset = NULL;
+ struct tm utctime;
+ char timestr[GENERALIZED_TIME_LENGTH+1];
+ time_t time_now = time(NULL);
+
+ slapi_lock_mutex(ipa_globals);
+ config = ipapwd_config;
+ slapi_unlock_mutex(ipa_globals);
+
+ svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *));
+ if (!svals) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "memory allocation failed\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+
+ krberr = krb5_init_context(&krbctx);
+ if (krberr) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb5_init_context failed\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+
+ /* Get Bind DN */
+ slapi_pblock_get(pb, SLAPI_CONN_DN, &bindDN);
+
+ /* If the connection is bound anonymously, we must refuse to process this operation. */
+ if (bindDN == NULL || *bindDN == '\0') {
+ /* Refuse the operation because they're bound anonymously */
+ errMesg = "Anonymous Binds are not allowed.\n";
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto free_and_return;
+ }
+
+ /* Get the ber value of the extended operation */
+ slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_VALUE, &extop_value);
+
+ if ((ber = ber_init(extop_value)) == NULL)
+ {
+ errMesg = "KeytabGet Request decode failed.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto free_and_return;
+ }
+
+ /* Format of request to parse
+ *
+ * KeytabGetRequest ::= SEQUENCE {
+ * serviceIdentity OCTET STRING
+ * keys SEQUENCE OF KrbKey,
+ * ...
+ * }
+ *
+ * KrbKey ::= SEQUENCE {
+ * key [0] EncryptionKey,
+ * salt [1] KrbSalt OPTIONAL,
+ * s2kparams [2] OCTET STRING OPTIONAL,
+ * ...
+ * }
+ *
+ * EncryptionKey ::= SEQUENCE {
+ * keytype [0] Int32,
+ * keyvalue [1] OCTET STRING
+ * }
+ *
+ * KrbSalt ::= SEQUENCE {
+ * type [0] Int32,
+ * salt [1] OCTET STRING OPTIONAL
+ * }
+ */
+
+ /* ber parse code */
+ rtag = ber_scanf(ber, "{a{", &serviceName);
+ if (rtag == LBER_ERROR) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n");
+ errMesg = "Invalid payload, failed to decode.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto free_and_return;
+ }
+
+ krberr = krb5_parse_name(krbctx, serviceName, &krbname);
+ if (krberr) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb5_parse_name failed\n");
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
-static int ipapwd_getMasterKey(const char *realm_dn)
+ /* check entry before doing any other decoding */
+
+
+ /* Find ancestor base DN */
+ sdn = slapi_sdn_new_dn_byval(ipa_realm_dn);
+ be = slapi_be_select(sdn);
+ slapi_sdn_free(&sdn);
+ bsdn = slapi_be_getsuffix(be, 0);
+ bdn = slapi_sdn_get_dn(bsdn);
+ scope = LDAP_SCOPE_SUBTREE;
+
+ /* get Entry by krbPrincipalName */
+ filter = slapi_ch_smprintf("(krbPrincipalName=%s)", serviceName);
+
+ pbte = slapi_pblock_new();
+ slapi_search_internal_set_pb(pbte,
+ bdn, scope, filter, attrlist, 0,
+ NULL, /* Controls */
+ NULL, /* UniqueID */
+ ipapwd_plugin_id,
+ 0); /* Flags */
+
+ /* do search the tree */
+ ret = slapi_search_internal_pb(pbte);
+ slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_RESULT, &res);
+ if (ret == -1 || res != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "Search for Principal failed, err (%d)\n",
+ res?res:ret);
+ errMesg = "PrincipalName not found.\n";
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto free_and_return;
+ }
+
+ /* get entries */
+ slapi_pblock_get(pbte, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &es);
+ if (!es) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "No entries ?!");
+ errMesg = "PrincipalName not found.\n";
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto free_and_return;
+ }
+
+ /* count entries */
+ for (i = 0; es[i]; i++) /* count */ ;
+
+ /* if there is none or more than one, freak out */
+ if (i != 1) {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop",
+ "Too many entries, or entry no found (%d)", i);
+ errMesg = "PrincipalName not found.\n";
+ rc = LDAP_NO_SUCH_OBJECT;
+ goto free_and_return;
+ }
+ targetEntry = es[0];
+
+ /* First thing to do is to ask access control if the bound identity has
+ rights to modify the userpassword attribute on this entry. If not, then
+ we fail immediately with insufficient access. This means that we don't
+ leak any useful information to the client such as current password
+ wrong, etc.
+ */
+
+ is_root = slapi_dn_isroot(bindDN);
+ slapi_pblock_set(pb, SLAPI_REQUESTOR_ISROOT, &is_root);
+
+ /* In order to perform the access control check , we need to select a backend (even though
+ * we don't actually need it otherwise).
+ */
+ slapi_pblock_set(pb, SLAPI_BACKEND, be);
+
+ /* Access Strategy:
+ * If the user has WRITE-ONLY access, a new keytab is set on the entry.
+ */
+
+ ret = slapi_access_allowed(pb, targetEntry, "krbPrincipalKey", NULL, SLAPI_ACL_WRITE);
+ if (ret != LDAP_SUCCESS) {
+ errMesg = "Insufficient access rights\n";
+ rc = LDAP_INSUFFICIENT_ACCESS;
+ goto free_and_return;
+ }
+
+ /* increment kvno (will be 1 if this is a new entry) */
+ kvno = ipapwd_get_cur_kvno(targetEntry) + 1;
+
+ /* ok access allowed, init kset and continue to parse ber buffer */
+
+ errMesg = "Unable to set key\n";
+ rc = LDAP_OPERATIONS_ERROR;
+
+ kset = malloc(sizeof(struct ipapwd_keyset));
+ if (!kset) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ goto free_and_return;
+ }
+
+ /* this encoding assumes all keys have the same kvno */
+ /* major-vno = 1 and minor-vno = 1 */
+ kset->major_vno = 1;
+ kset->minor_vno = 1;
+ kset->kvno = kvno;
+ /* we also assum mkvno is 0 */
+ kset->mkvno = 0;
+
+ kset->keys = NULL;
+ kset->num_keys = 0;
+
+ rtag = ber_peek_tag(ber, &tlen);
+ while (rtag == LBER_SEQUENCE) {
+ krb5_data plain;
+ krb5_enc_data cipher;
+ struct berval tval;
+ krb5_octet *kdata;
+ size_t klen;
+
+ i = kset->num_keys;
+
+ if (kset->keys) {
+ struct ipapwd_krbkey *newset;
+
+ newset = realloc(kset->keys, sizeof(struct ipapwd_krbkey) * (i + 1));
+ if (!newset) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ goto free_and_return;
+ }
+ kset->keys = newset;
+ } else {
+ kset->keys = malloc(sizeof(struct ipapwd_krbkey));
+ if (!kset->keys) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ goto free_and_return;
+ }
+ }
+ kset->num_keys += 1;
+
+ kset->keys[i].salt = NULL;
+ kset->keys[i].ekey = NULL;
+ kset->keys[i].s2kparams.bv_len = 0;
+ kset->keys[i].s2kparams.bv_val = NULL;
+
+ /* EncryptionKey */
+ rtag = ber_scanf(ber, "{t[{t[i]t[o]}]", &ttmp, &ttmp, &tint, &ttmp, &tval);
+ if (rtag == LBER_ERROR) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n");
+ errMesg = "Invalid payload, failed to decode.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto free_and_return;
+ }
+
+ kset->keys[i].ekey = calloc(1, sizeof(struct ipapwd_krbkeydata));
+ if (!kset->keys[i].ekey) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ goto free_and_return;
+ }
+
+ kset->keys[i].ekey->type = tint;
+
+ plain.length = tval.bv_len;
+ plain.data = tval.bv_val;
+
+ krberr = krb5_c_encrypt_length(krbctx, config->kmkey->enctype, plain.length, &klen);
+ if (krberr) {
+ free(tval.bv_val);
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb encryption failed!\n");
+ goto free_and_return;
+ }
+
+ kdata = malloc(2 + klen);
+ if (!kdata) {
+ free(tval.bv_val);
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ goto free_and_return;
+ }
+ encode_int16(plain.length, kdata);
+
+ kset->keys[i].ekey->value.bv_len = 2 + klen;
+ kset->keys[i].ekey->value.bv_val = (char *)kdata;
+
+ cipher.ciphertext.length = klen;
+ cipher.ciphertext.data = (char *)kdata + 2;
+
+ krberr = krb5_c_encrypt(krbctx, config->kmkey, 0, 0, &plain, &cipher);
+ if (krberr) {
+ free(tval.bv_val);
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "krb encryption failed!\n");
+ goto free_and_return;
+ }
+
+ free(tval.bv_val);
+
+ rtag = ber_peek_tag(ber, &tlen);
+
+ /* KrbSalt */
+ if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
+
+ rtag = ber_scanf(ber, "t[{t[i]", &ttmp, &ttmp, &tint);
+ if (rtag == LBER_ERROR) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n");
+ errMesg = "Invalid payload, failed to decode.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto free_and_return;
+ }
+
+ kset->keys[i].salt = calloc(1, sizeof(struct ipapwd_krbkeydata));
+ if (!kset->keys[i].salt) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "malloc failed!\n");
+ goto free_and_return;
+ }
+
+ kset->keys[i].salt->type = tint;
+
+ rtag = ber_peek_tag(ber, &tlen);
+ if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 1)) {
+
+ rtag = ber_scanf(ber, "t[o]}]", &ttmp, &tval);
+ if (rtag == LBER_ERROR) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n");
+ errMesg = "Invalid payload, failed to decode.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto free_and_return;
+ }
+
+ kset->keys[i].salt->value = tval;
+
+ rtag = ber_peek_tag(ber, &tlen);
+ }
+ }
+
+ /* FIXME: s2kparams - NOT implemented yet */
+ if (rtag == (ber_tag_t)(LBER_CONSTRUCTED | LBER_CLASS_CONTEXT | 2)) {
+ rtag = ber_scanf(ber, "t[x]}", &ttmp);
+ } else {
+ rtag = ber_scanf(ber, "}", &ttmp);
+ }
+ if (rtag == LBER_ERROR) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "ber_scanf failed\n");
+ errMesg = "Invalid payload, failed to decode.\n";
+ rc = LDAP_PROTOCOL_ERROR;
+ goto free_and_return;
+ }
+
+ rtag = ber_peek_tag(ber, &tlen);
+ }
+
+ ber_free(ber, 1);
+ ber = NULL;
+
+ /* filter un-supported encodings */
+ ret = filter_keys(kset);
+ if (ret) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "keyset filtering failed\n");
+ goto free_and_return;
+ }
+
+ /* check if we have any left */
+ if (kset->num_keys == 0) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "keyset filtering rejected all proposed keys\n");
+ errMesg = "All enctypes provided are unsupported";
+ rc = LDAP_UNWILLING_TO_PERFORM;
+ goto free_and_return;
+ }
+
+ smods = slapi_mods_new();
+
+ /* change Last Password Change field with the current date */
+ if (!gmtime_r(&(time_now), &utctime)) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to retrieve current date (buggy gmtime_r ?)\n");
+ slapi_mods_free(&smods);
+ goto free_and_return;
+ }
+ strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime);
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbLastPwdChange", timestr);
+
+ /* FIXME: set Password Expiration date ? */
+#if 0
+ if (!gmtime_r(&(data->expireTime), &utctime)) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop", "failed to convert expiration date\n");
+ slapi_ch_free_string(&randPasswd);
+ slapi_mods_free(&smods);
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+ strftime(timestr, GENERALIZED_TIME_LENGTH+1, "%Y%m%d%H%M%SZ", &utctime);
+ slapi_mods_add_string(smods, LDAP_MOD_REPLACE, "krbPasswordExpiration", timestr);
+#endif
+
+ bval = encode_keys(kset);
+ if (!bval) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "encoding asn1 KrbSalt failed\n");
+ slapi_mods_free(&smods);
+ goto free_and_return;
+ }
+
+ svals[0] = slapi_value_new_berval(bval);
+ if (!svals[0]) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipa_pwd_extop",
+ "Converting berval to Slapi_Value\n");
+ slapi_mods_free(&smods);
+ goto free_and_return;
+ }
+
+ slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals);
+
+ /* commit changes */
+ ret = ipapwd_apply_mods(slapi_entry_get_dn_const(targetEntry), smods);
+
+ if (ret != LDAP_SUCCESS) {
+ slapi_mods_free(&smods);
+ goto free_and_return;
+
+ }
+ slapi_mods_free(&smods);
+
+ /* Format of response
+ *
+ * KeytabGetRequest ::= SEQUENCE {
+ * new_kvno Int32
+ * SEQUENCE OF KeyTypes
+ * }
+ *
+ * * List of accepted enctypes *
+ * KeyTypes ::= SEQUENCE {
+ * enctype Int32
+ * }
+ */
+
+ errMesg = "Internal Error\n";
+ rc = LDAP_OPERATIONS_ERROR;
+
+ ber = ber_alloc();
+ if (!ber) {
+ goto free_and_return;
+ }
+
+ ret = ber_printf(ber, "{i{", (ber_int_t)kvno);
+ if (ret == -1) {
+ goto free_and_return;
+ }
+
+ for (i = 0; i < kset->num_keys; i++) {
+ ret = ber_printf(ber, "{i}", (ber_int_t)kset->keys[i].ekey->type);
+ if (ret == -1) {
+ goto free_and_return;
+ }
+ }
+ ret = ber_printf(ber, "}}");
+ if (ret == -1) {
+ goto free_and_return;
+ }
+
+ if (ret != -1) {
+ struct berval *bvp;
+ LDAPControl new_ctrl = {0};
+
+ ret = ber_flatten(ber, &bvp);
+ if (ret == -1) {
+ goto free_and_return;
+ }
+
+ new_ctrl.ldctl_oid = KEYTAB_RET_OID;
+ new_ctrl.ldctl_value = *bvp;
+ new_ctrl.ldctl_iscritical = 0;
+ rc= slapi_pblock_set(pb, SLAPI_ADD_RESCONTROL, &new_ctrl);
+ ber_bvfree(bvp);
+ }
+
+ /* Free anything that we allocated above */
+free_and_return:
+ slapi_ch_free_string(&serviceName);
+ if (kset) ipapwd_keyset_free(&kset);
+
+ if (bval) ber_bvfree(bval);
+ if (ber) ber_free(ber, 1);
+
+ if (pbte) {
+ slapi_free_search_results_internal(pbte);
+ slapi_pblock_destroy(pbte);
+ }
+ if (svals) {
+ for (i = 0; svals[i]; i++) {
+ slapi_value_free(&svals[i]);
+ }
+ free(svals);
+ }
+
+ if (krbname) krb5_free_principal(krbctx, krbname);
+ if (krbctx) krb5_free_context(krbctx);
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg ? errMesg : "success");
+ slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
+
+ return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+}
+
+static int new_ipapwd_encsalt(krb5_context krbctx, const char * const *encsalts,
+ struct ipapwd_encsalt **es_types, int *num_es_types)
{
- krb5_keyblock *kmkey;
+ struct ipapwd_encsalt *es;
+ int nes, i;
+
+ for (i = 0; encsalts[i]; i++) /* count */ ;
+ es = calloc(i + 1, sizeof(struct ipapwd_encsalt));
+ if (!es) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory!\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+
+ for (i = 0, nes = 0; encsalts[i]; i++) {
+ char *enc, *salt;
+ krb5_int32 tmpsalt;
+ krb5_enctype tmpenc;
+ krb5_boolean similar;
+ krb5_error_code krberr;
+ int j;
+
+ enc = strdup(encsalts[i]);
+ if (!enc) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Allocation error\n");
+ return LDAP_OPERATIONS_ERROR;
+ }
+ salt = strchr(enc, ':');
+ if (!salt) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Invalid krb5 enc string\n");
+ free(enc);
+ continue;
+ }
+ *salt = '\0'; /* null terminate the enc type */
+ salt++; /* skip : */
+
+ krberr = krb5_string_to_enctype(enc, &tmpenc);
+ if (krberr) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Invalid krb5 enctype\n");
+ free(enc);
+ continue;
+ }
+
+ krberr = krb5_string_to_salttype(salt, &tmpsalt);
+ for (j = 0; j < nes; j++) {
+ krb5_c_enctype_compare(krbctx, es[j].enc_type, tmpenc, &similar);
+ if (similar && (es[j].salt_type == tmpsalt)) {
+ break;
+ }
+ }
+
+ if (j == nes) {
+ /* not found */
+ es[j].enc_type = tmpenc;
+ es[j].salt_type = tmpsalt;
+ nes++;
+ }
+
+ free(enc);
+ }
+
+ *es_types = es;
+ *num_es_types = nes;
+
+ return LDAP_SUCCESS;
+}
+
+static int ipapwd_getConfig(krb5_context krbctx, const char *realm_dn)
+{
+ struct ipapwd_config *config = NULL;
+ krb5_keyblock *kmkey = NULL;
+ Slapi_Entry *realm_entry = NULL;
Slapi_Attr *a;
Slapi_Value *v;
- Slapi_Entry *realm_entry;
- BerElement *be;
+ BerElement *be = NULL;
ber_tag_t tag, tmp;
ber_int_t ttype;
const struct berval *bval;
- struct berval *mkey;
+ struct berval *mkey = NULL;
+ char **encsalts;
+ int ret;
+ config = malloc(sizeof(struct ipapwd_config));
+ if (!config) {
+ slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory!\n");
+ goto free_and_error;
+ }
kmkey = malloc(sizeof(krb5_keyblock));
if (!kmkey) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory!\n");
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
+ }
+ config->kmkey = kmkey;
+
+ ret = krb5_get_default_realm(krbctx, &config->realm);
+ if (ret) {
+ slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Failed to get default realm?!\n");
+ goto free_and_error;
}
- if (ipapwd_getEntry(realm_dn, &realm_entry, NULL) != LDAP_SUCCESS) {
+
+ /* get the Realm Container entry */
+ ret = ipapwd_getEntry(realm_dn, &realm_entry, NULL);
+ if (ret != LDAP_SUCCESS) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No realm Entry?\n");
- free(kmkey);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
- if (slapi_entry_attr_find(realm_entry, "krbMKey", &a) == -1) {
+ /*** get the Kerberos Master Key ***/
+
+ ret = slapi_entry_attr_find(realm_entry, "krbMKey", &a);
+ if (ret == -1) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No master key??\n");
- free(kmkey);
- slapi_entry_free(realm_entry);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
/* there should be only one value here */
- if (slapi_attr_first_value(a, &v) == -1) {
+ ret = slapi_attr_first_value(a, &v);
+ if (ret == -1) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No master key values??\n");
- free(kmkey);
- slapi_entry_free(realm_entry);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
bval = slapi_value_get_berval(v);
if (!bval) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Error retrieving master key berval\n");
- free(kmkey);
- slapi_entry_free(realm_entry);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
be = ber_init(bval);
if (!bval) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "ber_init() failed!\n");
- free(kmkey);
- slapi_entry_free(realm_entry);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
tag = ber_scanf(be, "{i{iO}}", &tmp, &ttype, &mkey);
if (tag == LBER_ERROR) {
slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_start",
"Bad Master key encoding ?!\n");
- free(kmkey);
- ber_free(be, 1);
- slapi_entry_free(realm_entry);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
kmkey->magic = KV5M_KEYBLOCK;
@@ -2013,70 +2623,172 @@ static int ipapwd_getMasterKey(const char *realm_dn)
kmkey->contents = malloc(mkey->bv_len);
if (!kmkey->contents) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory!\n");
- ber_bvfree(mkey);
- ber_free(be, 1);
- free(kmkey);
- slapi_entry_free(realm_entry);
- return LDAP_OPERATIONS_ERROR;
+ goto free_and_error;
}
memcpy(kmkey->contents, mkey->bv_val, mkey->bv_len);
+ ber_bvfree(mkey);
+ ber_free(be, 1);
+ /*** get the Supported Enc/Salt types ***/
+
+ encsalts = slapi_entry_attr_get_charray(realm_entry, "krbSupportedEncSaltTypes");
+ if (encsalts) {
+ ret = new_ipapwd_encsalt(krbctx, (const char * const *)encsalts,
+ &config->supp_encsalts,
+ &config->num_supp_encsalts);
+ slapi_ch_array_free(encsalts);
+ } else {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_start", "No configured salt types use defaults\n");
+ ret = new_ipapwd_encsalt(krbctx, ipapwd_def_encsalts,
+ &config->supp_encsalts,
+ &config->num_supp_encsalts);
+ }
+ if (ret) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start",
+ "Can't get Supported EncSalt Types\n");
+ goto free_and_error;
+ }
+
+ /*** get the Preferred Enc/Salt types ***/
+
+ encsalts = slapi_entry_attr_get_charray(realm_entry, "krbDefaultEncSaltTypes");
+ if (encsalts) {
+ ret = new_ipapwd_encsalt(krbctx, (const char * const *)encsalts,
+ &config->pref_encsalts,
+ &config->num_pref_encsalts);
+ slapi_ch_array_free(encsalts);
+ } else {
+ slapi_log_error(SLAPI_LOG_TRACE, "ipapwd_start", "No configured salt types use defaults\n");
+ ret = new_ipapwd_encsalt(krbctx, ipapwd_def_encsalts,
+ &config->pref_encsalts,
+ &config->num_pref_encsalts);
+ }
+ if (ret) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start",
+ "Can't get Preferred EncSalt Types\n");
+ goto free_and_error;
+ }
+
+ /*** set config/replace old config ***/
+
+ /* FIXME: free old one in a safe way, use read locks ? */
slapi_lock_mutex(ipa_globals);
- ipa_kmkey = kmkey;
+ ipapwd_config = config;
slapi_unlock_mutex(ipa_globals);
slapi_entry_free(realm_entry);
- ber_bvfree(mkey);
- ber_free(be, 1);
return LDAP_SUCCESS;
+
+free_and_error:
+ if (mkey) ber_bvfree(mkey);
+ if (be) ber_free(be, 1);
+ if (config->pref_encsalts) free(config->pref_encsalts);
+ if (config->supp_encsalts) free(config->supp_encsalts);
+ if (config->kmkey) free(config->kmkey);
+ if (config) free(config);
+ if (realm_entry) slapi_entry_free(realm_entry);
+ return LDAP_OPERATIONS_ERROR;
}
-static char *ipapwd_oid_list[] = {
- EXOP_PASSWD_OID,
- NULL
-};
+static int ipapwd_extop(Slapi_PBlock *pb)
+{
+ char *oid = NULL;
+ char *errMesg = NULL;
+ int ret=0, rc=0, sasl_ssf=0, is_ssl=0;
+ slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", "=> ipapwd_extop\n");
-static char *ipapwd_name_list[] = {
- "ipapwd_extop",
- NULL
-};
+#ifdef LDAP_EXTOP_PASSMOD_CONN_SECURE
+ /* Allow password modify only for SSL/TLS established connections and
+ * connections using SASL privacy layers */
+ if (slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) {
+ errMesg = "Could not get SASL SSF from connection\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
+ errMesg );
+ goto free_and_return;
+ }
-/* will read this from the krbSupportedEncSaltTypes in the krbRealmContainer later on */
-const char *krb_sup_encs[] = {
- "des3-hmac-sha1:normal",
- "arcfour-hmac:normal",
- "des-hmac-sha1:normal",
- "des-cbc-md5:normal",
- "des-cbc-crc:normal",
- "des-cbc-crc:v4",
- "des-cbc-crc:afs3",
- NULL
-};
+ if (slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl) != 0) {
+ errMesg = "Could not get IS SSL from connection\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ slapi_log_error( SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
+ errMesg );
+ goto free_and_return;
+ }
+
+ if ((is_ssl == 0) && (sasl_ssf <= 1)) {
+ errMesg = "Operation requires a secure connection.\n";
+ rc = LDAP_CONFIDENTIALITY_REQUIRED;
+ goto free_and_return;
+ }
+#endif
+
+ /* make sure we have the master key */
+ if (ipapwd_config == NULL) {
+ krb5_context krbctx;
+ krb5_error_code krberr;
+
+ krberr = krb5_init_context(&krbctx);
+ if (krberr) {
+ slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "krb5_init_context failed\n");
+ errMesg = "Fatal Internal Error";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+ ret = ipapwd_getConfig(krbctx, ipa_realm_dn);
+ if (ret != LDAP_SUCCESS) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", "Error Retrieving Master Key");
+ errMesg = "Fatal Internal Error";
+ rc = LDAP_OPERATIONS_ERROR;
+ goto free_and_return;
+ }
+ krb5_free_context(krbctx);
+ }
+
+ /* Before going any further, we'll make sure that the right extended operation plugin
+ * has been called: i.e., the OID shipped whithin the extended operation request must
+ * match this very plugin's OIDs: EXOP_PASSWD_OID or KEYTAB_SET_OID. */
+ if (slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &oid) != 0) {
+ errMesg = "Could not get OID value from request.\n";
+ rc = LDAP_OPERATIONS_ERROR;
+ slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg);
+ goto free_and_return;
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop",
+ "Received extended operation request with OID %s\n", oid);
+ }
+
+ if (strcasecmp(oid, EXOP_PASSWD_OID) == 0) {
+ return ipapwd_chpwop(pb);
+ }
+ if (strcasecmp(oid, KEYTAB_SET_OID) == 0) {
+ return ipapwd_setkeytab(pb);
+ }
+
+ errMesg = "Request OID does not match supported OIDs.\n";
+ rc = LDAP_OPERATIONS_ERROR;
+
+free_and_return:
+ slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg);
+ slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
+
+ return SLAPI_PLUGIN_EXTENDED_SENT_RESULT;
+}
-#define KRBCHECK(ctx, err, fname) do { \
- if (err) { \
- slapi_log_error(SLAPI_LOG_PLUGIN, "ipapwd_start", \
- "%s failed [%s]\n", fname, \
- krb5_get_error_message(ctx, err)); \
- return LDAP_OPERATIONS_ERROR; \
- } } while(0)
/* Init data structs */
-/* TODO: read input from tree */
static int ipapwd_start( Slapi_PBlock *pb )
{
- int krberr, i;
krb5_context krbctx;
+ krb5_error_code krberr;
char *realm;
char *realm_dn;
char *config_dn;
char *partition_dn;
Slapi_Entry *config_entry;
int ret;
- struct krb5p_keysalt *keysalts;
- int n_keysalts;
ipa_globals = slapi_new_mutex();
@@ -2085,80 +2797,17 @@ static int ipapwd_start( Slapi_PBlock *pb )
slapi_log_error(SLAPI_LOG_FATAL, "ipapwd_start", "krb5_init_context failed\n");
return LDAP_OPERATIONS_ERROR;
}
- if (krb5_get_default_realm(krbctx, &realm)) {
- krb5_free_context(krbctx);
- return LDAP_OPERATIONS_ERROR;
- }
- for (i = 0; krb_sup_encs[i]; i++) /* count */ ;
- keysalts = (struct krb5p_keysalt *)malloc(sizeof(struct krb5p_keysalt) * (i + 1));
- if (!keysalts) {
- krb5_free_context(krbctx);
- free(realm);
- return LDAP_OPERATIONS_ERROR;
- }
-
- for (i = 0, n_keysalts = 0; krb_sup_encs[i]; i++) {
- char *enc, *salt;
- krb5_int32 tmpenc;
- krb5_int32 tmpsalt;
- krb5_boolean similar;
- int j;
-
- enc = strdup(krb_sup_encs[i]);
- if (!enc) {
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Allocation error\n");
- krb5_free_context(krbctx);
- free(realm);
- return LDAP_OPERATIONS_ERROR;
- }
- salt = strchr(enc, ':');
- if (!salt) {
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Invalid krb5 enc string\n");
- free(enc);
- continue;
- }
- *salt = '\0'; /* null terminate the enc type */
- salt++; /* skip : */
-
- krberr = krb5_string_to_enctype(enc, &tmpenc);
- if (krberr) {
- slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Invalid krb5 enctype\n");
- free(enc);
- continue;
- }
-
- krberr = krb5_string_to_salttype(salt, &tmpsalt);
- for (j = 0; j < n_keysalts; j++) {
- krb5_c_enctype_compare(krbctx, keysalts[j].enc_type, tmpenc, &similar);
- if (similar && (keysalts[j].salt_type == tmpsalt)) {
- break;
- }
- }
-
- if (j == n_keysalts) {
- /* not found */
- keysalts[j].enc_type = tmpenc;
- keysalts[j].salt_type = tmpsalt;
- n_keysalts++;
- }
-
- free(enc);
- }
-
- krb5_free_context(krbctx);
/*retrieve the master key from the stash file */
if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &config_dn) != 0) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No config DN?\n");
- free(keysalts);
- free(realm);
+ krb5_free_context(krbctx);
return LDAP_OPERATIONS_ERROR;
}
if (ipapwd_getEntry(config_dn, &config_entry, NULL) != LDAP_SUCCESS) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "No config Entry?\n");
- free(keysalts);
- free(realm);
+ krb5_free_context(krbctx);
return LDAP_OPERATIONS_ERROR;
}
@@ -2167,34 +2816,53 @@ static int ipapwd_start( Slapi_PBlock *pb )
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Missing partition configuration entry (nsslapd-targetSubtree)!\n");
krb5_free_context(krbctx);
slapi_entry_free(config_entry);
- free(keysalts);
- free(realm);
return LDAP_OPERATIONS_ERROR;
}
+ ret = krb5_get_default_realm(krbctx, &realm);
+ if (ret) {
+ slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Failed to get default realm?!\n");
+ krb5_free_context(krbctx);
+ return LDAP_OPERATIONS_ERROR;
+ }
realm_dn = slapi_ch_smprintf("cn=%s,cn=kerberos,%s", realm, partition_dn);
if (!realm_dn) {
slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory ?\n");
- free(keysalts);
free(realm);
+ krb5_free_context(krbctx);
+ slapi_entry_free(config_entry);
return LDAP_OPERATIONS_ERROR;
}
+ free(realm);
- ipa_realm = realm;
+ slapi_lock_mutex(ipa_globals);
ipa_realm_dn = realm_dn;
- ipa_keysalts = keysalts;
- ipa_n_keysalts = n_keysalts;
+ slapi_unlock_mutex(ipa_globals);
- ret = ipapwd_getMasterKey(ipa_realm_dn);
+ ret = ipapwd_getConfig(krbctx, ipa_realm_dn);
if (ret) {
slapi_log_error( SLAPI_LOG_PLUGIN, "ipapwd_start", "Couldn't init master key at start delaying ...");
ret = LDAP_SUCCESS;
}
+ krb5_free_context(krbctx);
slapi_entry_free(config_entry);
return ret;
}
+static char *ipapwd_oid_list[] = {
+ EXOP_PASSWD_OID,
+ KEYTAB_SET_OID,
+ NULL
+};
+
+
+static char *ipapwd_name_list[] = {
+ "Password Change Extended Operation",
+ "Keytab Retrieval Extended Operation",
+ NULL
+};
+
/* Initialization function */
int ipapwd_init( Slapi_PBlock *pb )
{
diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py
index 438bfb7d..358e47cc 100644
--- a/ipa-server/ipaserver/krbinstance.py
+++ b/ipa-server/ipaserver/krbinstance.py
@@ -128,6 +128,7 @@ class KrbInstance(service.Service):
self.step("adding kerberos entries to the DS", self.__add_krb_entries)
self.step("adding defalt ACIs", self.__add_default_acis)
self.step("configuring KDC", self.__create_instance)
+ self.step("adding defalt keytypes", self.__add_default_keytypes)
self.step("creating a keytab for the directory", self.__create_ds_keytab)
self.step("creating a keytab for the machine", self.__create_host_keytab)
self.step("exporting the kadmin keytab", self.__export_kadmin_changepw_keytab)
@@ -253,9 +254,11 @@ class KrbInstance(service.Service):
self.__ldap_mod("kerberos.ldif")
def __add_default_acis(self):
- #Change the default ACL to avoid anonimous access to kerberos keys and othe hashes
self.__ldap_mod("default-aci.ldif")
+ def __add_default_keytypes(self):
+ self.__ldap_mod("default-keytypes.ldif")
+
def __create_replica_instance(self):
self.__create_instance(replica=True)