From d0587cbdd5bc5e07a6e8519deb07adaace643740 Mon Sep 17 00:00:00 2001 From: Rob Crittenden Date: Mon, 14 Sep 2009 17:04:08 -0400 Subject: Enrollment for a host in an IPA domain This will create a host service principal and may create a host entry (for admins). A keytab will be generated, by default in /etc/krb5.keytab If no kerberos credentails are available then enrollment over LDAPS is used if a password is provided. This change requires that openldap be used as our C LDAP client. It is much easier to do SSL using openldap than mozldap (no certdb required). Otherwise we'd have to write a slew of extra code to create a temporary cert database, import the CA cert, ... --- daemons/configure.ac | 1 + daemons/ipa-slapi-plugins/Makefile.am | 1 + .../ipa-slapi-plugins/ipa-enrollment/Makefile.am | 42 ++ .../ipa-enrollment/enrollment-conf.ldif | 16 + .../ipa-enrollment/ipa_enrollment.c | 457 +++++++++++++++++++++ .../ipa-pwd-extop/ipa_pwd_extop.c | 40 +- 6 files changed, 556 insertions(+), 1 deletion(-) create mode 100644 daemons/ipa-slapi-plugins/ipa-enrollment/Makefile.am create mode 100644 daemons/ipa-slapi-plugins/ipa-enrollment/enrollment-conf.ldif create mode 100644 daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c (limited to 'daemons') diff --git a/daemons/configure.ac b/daemons/configure.ac index e726bd982..7f0fd680f 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -260,6 +260,7 @@ AC_CONFIG_FILES([ Makefile ipa-kpasswd/Makefile ipa-slapi-plugins/Makefile + ipa-slapi-plugins/ipa-enrollment/Makefile ipa-slapi-plugins/ipa-memberof/Makefile ipa-slapi-plugins/ipa-pwd-extop/Makefile ipa-slapi-plugins/ipa-winsync/Makefile diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am index 3b58ccb14..ac2d26879 100644 --- a/daemons/ipa-slapi-plugins/Makefile.am +++ b/daemons/ipa-slapi-plugins/Makefile.am @@ -1,6 +1,7 @@ NULL = SUBDIRS = \ + ipa-enrollment \ ipa-pwd-extop \ ipa-memberof \ ipa-winsync \ diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/Makefile.am b/daemons/ipa-slapi-plugins/ipa-enrollment/Makefile.am new file mode 100644 index 000000000..8a4ba09c7 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-enrollment/Makefile.am @@ -0,0 +1,42 @@ +NULL = + +INCLUDES = \ + -I. \ + -I$(srcdir) \ + -DPREFIX=\""$(prefix)"\" \ + -DBINDIR=\""$(bindir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + $(MOZLDAP_CFLAGS) \ + $(KRB5_CFLAGS) \ + $(WARN_CFLAGS) \ + $(NULL) + +plugindir = $(libdir)/dirsrv/plugins +plugin_LTLIBRARIES = \ + libipa_enrollment_extop.la \ + $(NULL) + +libipa_enrollment_extop_la_SOURCES = \ + ipa_enrollment.c \ + $(NULL) + +libipa_enrollment_extop_la_LDFLAGS = -avoid-version + +libipa_enrollment_extop_la_LIBADD = \ + $(MOZLDAP_LIBS) \ + $(NULL) + +appdir = $(IPA_DATA_DIR) +app_DATA = \ + enrollment-conf.ldif \ + $(NULL) + +EXTRA_DIST = \ + $(app_DATA) \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/enrollment-conf.ldif b/daemons/ipa-slapi-plugins/ipa-enrollment/enrollment-conf.ldif new file mode 100644 index 000000000..1c98277a8 --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-enrollment/enrollment-conf.ldif @@ -0,0 +1,16 @@ +dn: cn=ipa_enrollment_extop,cn=plugins,cn=config +changetype: add +objectclass: top +objectclass: nsSlapdPlugin +objectclass: extensibleObject +cn: ipa_enrollment_extop +nsslapd-pluginpath: libipa_enrollment_extop +nsslapd-plugininitfunc: ipaenrollment_init +nsslapd-plugintype: extendedop +nsslapd-pluginenabled: on +nsslapd-pluginid: ipa_enrollment_extop +nsslapd-pluginversion: 1.0 +nsslapd-pluginvendor: RedHat +nsslapd-plugindescription: Enroll hosts into the IPA domain +nsslapd-plugin-depends-on-type: database +nsslapd-realmTree: $SUFFIX diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c new file mode 100644 index 000000000..8a9906aab --- /dev/null +++ b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c @@ -0,0 +1,457 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2005 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* + * Enroll a host into the IPA domain. + * + */ + +#include +#include +#include +#include + +/* OID of the extended operation handled by this plug-in */ +#define JOIN_OID "2.16.840.1.113730.3.8.3.53" + +Slapi_PluginDesc pdesc = { + "ipa-enrollment", + "IPA Project", + "IPA/2.0", + "IPA Enrollment Extended Operation plugin" +}; + +static char *ipaenrollment_oid_list[] = { + JOIN_OID, + NULL +}; + +static char *ipaenrollment_name_list[] = { + "Enrollment Extended Operation", + NULL +}; + +static void *ipaenrollment_plugin_id; + +static char *realm; +static const char *ipa_realm_dn; + +static int +ipaenrollement_secure(Slapi_PBlock *pb, char **errMesg) +{ + int sasl_ssf, is_ssl; + int rc = LDAP_SUCCESS; + + slapi_log_error(SLAPI_LOG_TRACE, "ipa_enrollment", "=> ipaenrollment_secure\n"); + + /* Allow enrollment only for SSL/TLS established connections and + * connections using SASL privacy layers */ + if (slapi_pblock_get(pb, SLAPI_CONN_SASL_SSF, &sasl_ssf) != 0) { + slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", + "Could not get SASL SSF from connection\n"); + *errMesg = "Operation requires a secure connection.\n"; + rc = LDAP_OPERATIONS_ERROR; + goto done; + } + + if (slapi_pblock_get(pb, SLAPI_CONN_IS_SSL_SESSION, &is_ssl) != 0) { + slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", + "Could not get IS SSL from connection\n"); + *errMesg = "Operation requires a secure connection.\n"; + rc = LDAP_OPERATIONS_ERROR; + goto done; + } + + if ((0 == is_ssl) && (sasl_ssf <= 1)) { + *errMesg = "Operation requires a secure connection.\n"; + rc = LDAP_CONFIDENTIALITY_REQUIRED; + goto done; + } + +done: + slapi_log_error(SLAPI_LOG_TRACE, "ipa_enrollment", "<= ipaenrollment_secure\n"); + return rc; + +} + +/* The extop call passes in the FQDN of the host to enroll. + * We take that and set the krbPrincipalName and add the appropriate + * objectclasses, then return krbPrincipalName. The caller should take + * this and pass it to ipa-getkeytab to generate the keytab. + * + * The password for the entry is removed by ipa-getkeytab. + */ +static int +ipa_join(Slapi_PBlock *pb) +{ + char *bindDN = NULL; + char *errMesg = NULL; + struct berval *extop_value = NULL; + Slapi_PBlock *pbte = NULL; + Slapi_PBlock *pbtm = NULL; + Slapi_Entry *targetEntry=NULL; + Slapi_DN *sdn; + Slapi_Backend *be; + Slapi_Entry **es = NULL; + int rc=0, ret=0, res, i; + int is_root=0; + char *krbLastPwdChange = NULL; + char *fqdn = NULL; + Slapi_Mods *smods; + char *attrlist[] = {"fqdn", "krbPrincipalKey", "krbLastPwdChange", "krbPrincipalName", NULL }; + char * filter; + + int scope = LDAP_SCOPE_SUBTREE; + char *principal; + struct berval retbval; + + /* 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); + + /* We are passed in the FQDN of the host to enroll. Do an internal + * search and pull that entry. + */ + filter = slapi_ch_smprintf("(fqdn=%s)", extop_value->bv_val); + pbte = slapi_pblock_new(); + slapi_search_internal_set_pb(pbte, + ipa_realm_dn, scope, filter, attrlist, 0, + NULL, /* Controls */ + NULL, /* UniqueID */ + ipaenrollment_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, "ipaenrollment_extop", + "Search for host failed, err (%d)\n", + res?res:ret); + errMesg = "Host 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 = "Host 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, "ipaenrollment_extop", + "Too many entries, or entry no found (%d)", i); + errMesg = "Host not found.\n"; + rc = LDAP_NO_SUCH_OBJECT; + goto free_and_return; + } + targetEntry = es[0]; + + /* Is this host already enrolled? */ + krbLastPwdChange = slapi_entry_attr_get_charptr(targetEntry, "krbLastPwdChange"); + if (NULL != krbLastPwdChange) { + slapi_log_error(SLAPI_LOG_TRACE, "ipaenrollment_extop", + "Host already enrolled"); + errMesg = "Host already enrolled.\n"; + rc = LDAP_OPERATIONS_ERROR; + goto free_and_return; + } + + /* 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). + */ + sdn = slapi_sdn_new_dn_byval(bindDN); + be = slapi_be_select(sdn); + 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; + } + + /* If a principal is already set return the name */ + principal = slapi_entry_attr_get_charptr(targetEntry, "krbPrincipalName"); + if (NULL != principal) + goto done; + + /* Add the elements needed for enrollment */ + smods = slapi_mods_new(); + fqdn = slapi_entry_attr_get_charptr(targetEntry, "fqdn"); + principal = slapi_ch_smprintf("host/%s@%s", fqdn, realm); + slapi_mods_add_string(smods, LDAP_MOD_ADD, "krbPrincipalName", principal); + slapi_mods_add_string(smods, LDAP_MOD_ADD, "objectClass", "krbPrincipalAux"); + + pbtm = slapi_pblock_new(); + slapi_modify_internal_set_pb (pbtm, slapi_entry_get_dn_const(targetEntry), + slapi_mods_get_ldapmods_byref(smods), + NULL, /* Controls */ + NULL, /* UniqueID */ + ipaenrollment_plugin_id, /* PluginID */ + 0); /* Flags */ + + rc = slapi_modify_internal_pb (pbtm); + if (rc) { + slapi_log_error(SLAPI_LOG_TRACE, "ipaenrollment_extop", + "WARNING: modify error %d on entry '%s'\n", + rc, slapi_entry_get_dn_const(targetEntry)); + } else { + slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rc); + + if (rc != LDAP_SUCCESS){ + slapi_log_error(SLAPI_LOG_TRACE, "ipaenrollment_extop", + "WARNING: modify error %d on entry '%s'\n", + rc, slapi_entry_get_dn_const(targetEntry)); + } else { + slapi_log_error(SLAPI_LOG_TRACE, "ipaenrollment_extop", + "<= apply mods: Successful\n"); + } + } + +done: + /* Return the krbprincipalname */ + retbval.bv_val = principal; + retbval.bv_len = strlen(principal); + + ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_OID, JOIN_OID); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_EXT_OP_RET_VALUE, &retbval); + if (ret) { + errMesg = "Could not set return values"; + slapi_log_error(SLAPI_LOG_PLUGIN, "ipaenrollmenti_extop", "%s\n", + errMesg); + rc = SLAPI_PLUGIN_EXTENDED_SENT_RESULT; + } + + /* Free anything that we allocated above */ +free_and_return: + + if (pbte) { + slapi_free_search_results_internal(pbte); + slapi_pblock_destroy(pbte); + } + if (pbtm) { + slapi_pblock_destroy(pbtm); + } + + if (krbLastPwdChange) slapi_ch_free_string(&krbLastPwdChange); + + slapi_log_error(SLAPI_LOG_PLUGIN, "ipaenrollment_extop", errMesg ? errMesg : "success\n"); + slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); + + free(principal); + + return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; +} + +/* Extended operation plug-in */ +static int +ipaenrollment_extop(Slapi_PBlock *pb) +{ + char *oid; + char *errMesg = NULL; + int rc, ret; + + slapi_log_error(SLAPI_LOG_TRACE, "ipa_enrollment", "=> ipaenrollment_extop\n"); + + rc = ipaenrollement_secure(pb, &errMesg); + if (rc) { + goto free_and_return; + } + + /* Get the OID and the value included in the request */ + if (slapi_pblock_get(pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0) { + errMesg = "Could not get OID and value from request.\n"; + rc = LDAP_OPERATIONS_ERROR; + slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg); + goto free_and_return; + } + + if (strcasecmp(oid, JOIN_OID) == 0) { + ret = ipa_join(pb); + return ret; + } + + errMesg = "Request OID does not match supported OIDs.\n"; + rc = LDAP_OPERATIONS_ERROR; + +free_and_return: + slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_enrollment", errMesg); + slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); + + return SLAPI_PLUGIN_EXTENDED_SENT_RESULT; +} + +static int +ipaenrollment_start(Slapi_PBlock *pb) +{ + krb5_error_code krberr; + krb5_context krbctx; + char *config_dn = NULL; + char *partition_dn = NULL; + Slapi_Entry *config_entry = NULL; + int ret = LDAP_SUCCESS; + Slapi_DN *sdn; + int rc = 0; + + krberr = krb5_init_context(&krbctx); + if (krberr) { + slapi_log_error(SLAPI_LOG_FATAL, "ipaenrollment_init", + "krb5_init_context failed\n"); + return LDAP_OPERATIONS_ERROR; + } + + ret = krb5_get_default_realm(krbctx, &realm); + if (ret) { + slapi_log_error(SLAPI_LOG_FATAL, "ipaenrollment_init", + "Failed to get default realm?!\n"); + ret = LDAP_OPERATIONS_ERROR; + } + + if (slapi_pblock_get(pb, SLAPI_TARGET_DN, &config_dn) != 0) { + slapi_log_error( SLAPI_LOG_FATAL, "ipaenrollment_start", "No config DN?\n"); + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + sdn = slapi_sdn_new_dn_byref(config_dn); + if ((rc = slapi_search_internal_get_entry(sdn, NULL, &config_entry, + ipaenrollment_plugin_id)) != LDAP_SUCCESS ){ + slapi_log_error(SLAPI_LOG_TRACE, "ipaenrollment_extop", + "ipaenrollment_start: No such entry-(%s), err (%d)\n", + config_dn, rc); + } + slapi_sdn_free(&sdn); + + partition_dn = slapi_entry_attr_get_charptr(config_entry, "nsslapd-realmtree"); + if (!partition_dn) { + slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Missing partition configuration entry (nsslapd-realmTree)!\n"); + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + + ipa_realm_dn = slapi_ch_smprintf("cn=computers,cn=accounts,%s", partition_dn); + slapi_ch_free_string(&partition_dn); + if (!ipa_realm_dn) { + slapi_log_error( SLAPI_LOG_FATAL, "ipapwd_start", "Out of memory ?\n"); + ret = LDAP_OPERATIONS_ERROR; + goto done; + } + +done: + if (krbctx) krb5_free_context(krbctx); + if (config_entry) slapi_entry_free(config_entry); + + return ret; +} + +int +ipaenrollment_init(Slapi_PBlock *pb) +{ + int ret; + + /* Get the arguments appended to the plugin extendedop directive + * in the plugin entry. The first argument + * (after the standard arguments for the directive) should + * contain the OID of the extended op. + */ + + ret = slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ipaenrollment_plugin_id); + if ((ret != 0) || (NULL == ipaenrollment_plugin_id)) { + slapi_log_error(SLAPI_LOG_PLUGIN, + "ipaenrollment_init", "Could not get identity or identity was NULL\n"); + return -1; + } + + slapi_log_error(SLAPI_LOG_PLUGIN, "ipaenrollment_init", + "Registering plug-in for extended op.\n"); + + /* Register the plug-in function as an extended operation + plug-in function. */ + ret = slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN, (void *)ipaenrollment_start); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&pdesc); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, ipaenrollment_oid_list); + if (!ret) ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, ipaenrollment_name_list); + if (!ret) slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_FN, (void *)ipaenrollment_extop); + + if (ret) { + slapi_log_error(SLAPI_LOG_PLUGIN, "ipaenrollment_init", + "Failed to set plug-in version, function, and OID.\n"); + return -1; + } + + return 0; +} diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c index 24acc8875..744d7dd3a 100644 --- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c +++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c @@ -2088,6 +2088,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) Slapi_Entry *targetEntry=NULL; struct berval *bval = NULL; Slapi_Value **svals = NULL; + Slapi_Value **evals = NULL; const char *bdn; const Slapi_DN *bsdn; Slapi_DN *sdn; @@ -2095,7 +2096,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) Slapi_Entry **es = NULL; int scope, res; char *filter; - char *attrlist[] = {"krbPrincipalKey", "krbLastPwdChange", NULL }; + char *attrlist[] = {"krbPrincipalKey", "krbLastPwdChange", "userPassword", "krbPrincipalName", "enrolledBy", NULL }; krb5_context krbctx = NULL; krb5_principal krbname = NULL; krb5_error_code krberr; @@ -2108,6 +2109,8 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) struct tm utctime; char timestr[GENERALIZED_TIME_LENGTH+1]; time_t time_now = time(NULL); + char *pw = NULL; + char *krbPrincipalName = NULL; svals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *)); if (!svals) { @@ -2522,6 +2525,31 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg) slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE, "krbPrincipalKey", svals); + /* If we are creating a keytab for a host service attempt to remove + * the userPassword attribute if it exists + */ + pw = slapi_entry_attr_get_charptr(targetEntry, "userPassword"); + krbPrincipalName = slapi_entry_attr_get_charptr(targetEntry, "krbPrincipalName"); + if ((strncmp(krbPrincipalName, "host/", 5) == 0)) { + char * krbLastPwdChange = slapi_entry_attr_get_charptr(targetEntry, "krbLastPwdChange"); + char * enrolledBy = slapi_entry_attr_get_charptr(targetEntry, "enrolledBy"); + if (NULL == enrolledBy) { + evals = (Slapi_Value **)calloc(2, sizeof(Slapi_Value *)); + evals[0] = slapi_value_new_string(bindDN); + slapi_mods_add_mod_values(smods, LDAP_MOD_ADD, "enrolledBy", evals); + } else { + slapi_ch_free_string(&enrolledBy); + } + if ((NULL != pw) && (NULL == krbLastPwdChange)) { + slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, "userPassword", NULL); + slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", + "Removing userPassword from host entry\n"); + slapi_ch_free_string(&pw); + } + slapi_ch_free_string(&krbLastPwdChange); + } + slapi_ch_free_string(&krbPrincipalName); + /* commit changes */ ret = ipapwd_apply_mods(slapi_entry_get_dn_const(targetEntry), smods); @@ -2603,10 +2631,18 @@ free_and_return: } free(svals); } + if (evals) { + for (i = 0; evals[i]; i++) { + slapi_value_free(&evals[i]); + } + free(evals); + } if (krbname) krb5_free_principal(krbctx, krbname); if (krbctx) krb5_free_context(krbctx); + if (rc == LDAP_SUCCESS) + errMesg = NULL; slapi_log_error(SLAPI_LOG_PLUGIN, "ipa_pwd_extop", errMesg ? errMesg : "success"); slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL); @@ -2938,6 +2974,8 @@ static int ipapwd_gen_checks(Slapi_PBlock *pb, char **errMesg, } sdn = slapi_sdn_new_dn_byref(dn); if (!sdn) { + slapi_log_error(SLAPI_LOG_TRACE, "ipa_pwd_extop", + "Unable to convert dn to sdn %s", dn?dn:""); *errMesg = "Internal Error"; rc = LDAP_OPERATIONS_ERROR; goto done; -- cgit