summaryrefslogtreecommitdiffstats
path: root/daemons
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2011-10-10 15:42:11 -0400
committerSimo Sorce <ssorce@redhat.com>2011-11-07 14:25:07 -0500
commit18537d55a7fd21c81af8b56fde69c895b2fa3597 (patch)
treeac460a482411e88b3b534f8869f78aa31a095738 /daemons
parent97018212279be4ff70816194b1e6392b61da36dc (diff)
downloadfreeipa-18537d55a7fd21c81af8b56fde69c895b2fa3597.tar.gz
freeipa-18537d55a7fd21c81af8b56fde69c895b2fa3597.tar.xz
freeipa-18537d55a7fd21c81af8b56fde69c895b2fa3597.zip
Add support for generating PAC for AS requests for user principals
Diffstat (limited to 'daemons')
-rw-r--r--daemons/configure.ac10
-rw-r--r--daemons/ipa-kdb/Makefile.am3
-rw-r--r--daemons/ipa-kdb/ipa_kdb.c7
-rw-r--r--daemons/ipa-kdb/ipa_kdb.h38
-rw-r--r--daemons/ipa-kdb/ipa_kdb_common.c85
-rw-r--r--daemons/ipa-kdb/ipa_kdb_mspac.c756
-rw-r--r--daemons/ipa-kdb/ipa_kdb_principals.c7
7 files changed, 905 insertions, 1 deletions
diff --git a/daemons/configure.ac b/daemons/configure.ac
index e238d8b15..f89c50d62 100644
--- a/daemons/configure.ac
+++ b/daemons/configure.ac
@@ -227,6 +227,16 @@ if test "x$PYTHON" = "x" ; then
fi
dnl ---------------------------------------------------------------------------
+dnl Check for ndr_krb5pac
+dnl ---------------------------------------------------------------------------
+
+PKG_PROG_PKG_CONFIG()
+PKG_CHECK_MODULES([TALLOC], [talloc])
+PKG_CHECK_MODULES([TEVENT], [tevent])
+PKG_CHECK_MODULES([NDRPAC], [ndr_krb5pac])
+
+
+dnl ---------------------------------------------------------------------------
dnl - Set the data install directory since we don't use pkgdatadir
dnl ---------------------------------------------------------------------------
diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am
index 036074f43..b29f60171 100644
--- a/daemons/ipa-kdb/Makefile.am
+++ b/daemons/ipa-kdb/Makefile.am
@@ -19,6 +19,7 @@ INCLUDES = \
$(KRB5_CFLAGS) \
$(SSL_CFLAGS) \
$(WARN_CFLAGS) \
+ $(NDRPAC_CFLAGS) \
$(NULL)
plugindir = $(libdir)/krb5/plugins/kdb
@@ -33,6 +34,7 @@ ipadb_la_SOURCES = \
ipa_kdb_passwords.c \
ipa_kdb_principals.c \
ipa_kdb_pwdpolicy.c \
+ ipa_kdb_mspac.c \
$(KRB5_UTIL_SRCS) \
$(NULL)
@@ -45,6 +47,7 @@ ipadb_la_LIBADD = \
$(KRB5_LIBS) \
$(SSL_LIBS) \
$(LDAP_LIBS) \
+ $(NDRPAC_LIBS) \
$(NULL)
dist_noinst_DATA = ipa_kdb.exports
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index 481b1f392..05ee18720 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -259,6 +259,11 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
ipactx->supp_encs = kst;
ipactx->n_supp_encs = n_kst;
+ ret = ipadb_reinit_mspac(ipactx);
+ if (ret && ret != ENOENT) {
+ /* TODO: log that there is an issue with adtrust settings */
+ }
+
ret = 0;
done:
@@ -447,7 +452,7 @@ kdb_vftabl kdb_function_table = {
NULL, /* promote_db */
NULL, /* decrypt_key_data */
NULL, /* encrypt_key_data */
- NULL, /* sign_authdata */
+ ipadb_sign_authdata, /* sign_authdata */
NULL, /* check_transited_realms */
NULL, /* check_policy_as */
NULL, /* check_policy_tgs */
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
index cfcaca649..8c907c448 100644
--- a/daemons/ipa-kdb/ipa_kdb.h
+++ b/daemons/ipa-kdb/ipa_kdb.h
@@ -39,10 +39,15 @@
#include <ctype.h>
#include <arpa/inet.h>
#include <endian.h>
+#include <unistd.h>
#include "ipa_krb5.h"
#include "ipa_pwd.h"
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
/* easier to copy the defines here than to mess with kadm5/admin.h
* for now */
#define KMASK_PRINCIPAL 0x000001
@@ -69,6 +74,13 @@
#define IPA_SETUP "ipa-setup-override-restrictions"
+struct ipadb_wincompat {
+ char *flat_domain_name;
+ char *flat_server_name;
+ char *fallback_group;
+ uint32_t fallback_rid;
+};
+
struct ipadb_context {
char *uri;
char *base;
@@ -79,12 +91,14 @@ struct ipadb_context {
bool override_restrictions;
krb5_key_salt_tuple *supp_encs;
int n_supp_encs;
+ struct ipadb_wincompat wc;
};
#define IPA_E_DATA_MAGIC 0x0eda7a
struct ipadb_e_data {
int magic;
bool ipa_user;
+ char *entry_dn;
char *passwd;
time_t last_pwd_change;
char *pw_policy_dn;
@@ -108,6 +122,10 @@ krb5_error_code ipadb_simple_modify(struct ipadb_context *ipactx,
char *dn, LDAPMod **mods);
krb5_error_code ipadb_simple_delete_val(struct ipadb_context *ipactx,
char *dn, char *attr, char *value);
+krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
+ char *entry_dn, char **entry_attrs,
+ char *deref_attr_name, char **deref_attrs,
+ LDAPMessage **res);
int ipadb_ldap_attr_to_int(LDAP *lcontext, LDAPMessage *le,
char *attrname, int *result);
@@ -124,6 +142,8 @@ int ipadb_ldap_attr_to_time_t(LDAP *lcontext, LDAPMessage *le,
int ipadb_ldap_attr_has_value(LDAP *lcontext, LDAPMessage *le,
char *attrname, char *value);
+int ipadb_ldap_deref_results(LDAP *lcontext, LDAPMessage *le,
+ LDAPDerefRes **results);
/* PRINCIPALS FUNCTIONS */
krb5_error_code ipadb_get_principal(krb5_context kcontext,
@@ -182,3 +202,21 @@ krb5_error_code ipadb_get_pwd_expiration(krb5_context context,
krb5_db_entry *entry,
struct ipadb_e_data *ied,
time_t *expire_time);
+
+/* MS-PAC FUNCTIONS */
+
+krb5_error_code ipadb_sign_authdata(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_keyblock *session_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ krb5_authdata ***signed_auth_data);
+
+krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx);
diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c
index 6f67be501..d3e8e9c4c 100644
--- a/daemons/ipa-kdb/ipa_kdb_common.c
+++ b/daemons/ipa-kdb/ipa_kdb_common.c
@@ -264,6 +264,56 @@ done:
return kerr;
}
+krb5_error_code ipadb_deref_search(struct ipadb_context *ipactx,
+ char *entry_dn, char **entry_attrs,
+ char *deref_attr_name, char **deref_attrs,
+ LDAPMessage **res)
+{
+ struct berval derefval = { 0, NULL };
+ LDAPControl *ctrl[2] = { NULL, NULL };
+ LDAPDerefSpec ds[2];
+ krb5_error_code kerr;
+ int times;
+ int ret;
+
+ ds[0].derefAttr = deref_attr_name;
+ ds[0].attributes = deref_attrs;
+ ds[1].derefAttr = NULL;
+
+ ret = ldap_create_deref_control_value(ipactx->lcontext, ds, &derefval);
+ if (ret != LDAP_SUCCESS) {
+ return ENOMEM;
+ }
+
+ ret = ldap_control_create(LDAP_CONTROL_X_DEREF,
+ 1, &derefval, 1, &ctrl[0]);
+ if (ret != LDAP_SUCCESS) {
+ kerr = ENOMEM;
+ goto done;
+ }
+
+ /* retry once if connection errors (tot. max. 2 tries) */
+ times = 2;
+ ret = LDAP_SUCCESS;
+ while (!ipadb_need_retry(ipactx, ret) && times > 0) {
+ times--;
+ ret = ldap_search_ext_s(ipactx->lcontext, entry_dn,
+ LDAP_SCOPE_BASE, "(objectclass=*)",
+ entry_attrs, 0,
+ ctrl, NULL,
+ &std_timeout, LDAP_NO_LIMIT,
+ res);
+ }
+
+ kerr = ipadb_simple_ldap_to_kerr(ret);
+
+done:
+ ldap_memfree(derefval.bv_val);
+ return kerr;
+}
+
+/* result extraction */
+
int ipadb_ldap_attr_to_int(LDAP *lcontext, LDAPMessage *le,
char *attrname, int *result)
{
@@ -430,3 +480,38 @@ int ipadb_ldap_attr_has_value(LDAP *lcontext, LDAPMessage *le,
return ret;
}
+
+int ipadb_ldap_deref_results(LDAP *lcontext, LDAPMessage *le,
+ LDAPDerefRes **results)
+{
+ LDAPControl **ctrls = NULL;
+ LDAPControl *derefctrl = NULL;
+ int ret;
+
+ ret = ldap_get_entry_controls(lcontext, le, &ctrls);
+ if (ret != LDAP_SUCCESS) {
+ return EINVAL;
+ }
+
+ if (!ctrls) {
+ return ENOENT;
+ }
+
+ derefctrl = ldap_control_find(LDAP_CONTROL_X_DEREF, ctrls, NULL);
+ if (!derefctrl) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ ret = ldap_parse_derefresponse_control(lcontext, derefctrl, results);
+ if (ret) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = 0;
+
+done:
+ ldap_controls_free(ctrls);
+ return ret;
+}
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
new file mode 100644
index 000000000..37ec063b5
--- /dev/null
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -0,0 +1,756 @@
+/*
+ * MIT Kerberos KDC database backend for FreeIPA
+ *
+ * Authors: Simo Sorce <ssorce@redhat.com>
+ *
+ * Copyright (C) 2011 Simo Sorce, Red Hat
+ * see file 'COPYING' for use and warranty information
+ *
+ * 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, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ipa_kdb.h"
+#include <talloc.h>
+#include "util/time.h"
+#include "gen_ndr/ndr_krb5pac.h"
+
+#define KRB5INT_PAC_SIGN_AVAILABLE 1
+
+#if KRB5INT_PAC_SIGN_AVAILABLE
+krb5_error_code
+krb5int_pac_sign(krb5_context context,
+ krb5_pac pac,
+ krb5_timestamp authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *privsvr_key,
+ krb5_data *data);
+#define krb5_pac_sign krb5int_pac_sign
+#define KRB5_PAC_LOGON_INFO 1
+#endif
+
+
+
+static char *user_pac_attrs[] = {
+ "objectClass",
+ "uid",
+ "cn",
+ "gidNumber",
+ "krbPrincipalName",
+ "krbCanonicalName",
+ "krbTicketPolicyReference",
+ "krbPrincipalExpiration",
+ "krbPasswordExpiration",
+ "krbPwdPolicyReference",
+ "krbPrincipalType",
+ "krbLastPwdChange",
+ "krbPrincipalAliases",
+ "krbLastSuccessfulAuth",
+ "krbLastFailedAuth",
+ "krbLoginFailedCount",
+ "krbLastAdminUnlock",
+ "krbTicketFlags",
+ "ipaNTSecurityIdentifier",
+ "ipaNTLogonScript",
+ "ipaNTProfilePath",
+ "ipaNTHomeDirectory",
+ "ipaNTHomeDirectoryDrive",
+ NULL
+};
+
+static char *memberof_pac_attrs[] = {
+ "gidNumber",
+ "ipaNTSecurityIdentifier",
+ NULL
+};
+
+#define SID_SUB_AUTHS 15
+
+static int string_to_sid(char *str, struct dom_sid *sid)
+{
+ unsigned long val;
+ char *s, *t;
+ int i;
+
+ memset(sid, '\0', sizeof(struct dom_sid));
+
+ s = str;
+
+ if (strncasecmp(s, "S-", 2) != 0) {
+ return EINVAL;
+ }
+ s += 2;
+
+ val = strtoul(s, &t, 10);
+ if (s == t || !t || *t != '-') {
+ return EINVAL;
+ }
+ s = t + 1;
+ sid->sid_rev_num = val;
+
+ val = strtoul(s, &t, 10);
+ if (s == t || !t) {
+ return EINVAL;
+ }
+ sid->id_auth[2] = (val & 0xff000000) >> 24;
+ sid->id_auth[3] = (val & 0x00ff0000) >> 16;
+ sid->id_auth[4] = (val & 0x0000ff00) >> 8;
+ sid->id_auth[5] = (val & 0x000000ff);
+
+ for (i = 0; i < SID_SUB_AUTHS; i++) {
+ switch (*t) {
+ case '\0':
+ /* no (more) subauths, we are done with it */
+ sid->num_auths = i;
+ return 0;
+ case '-':
+ /* there are (more) subauths */
+ s = t + 1;;
+ break;
+ default:
+ /* garbage */
+ return EINVAL;
+ }
+
+ val = strtoul(s, &t, 10);
+ if (s == t || !t) {
+ return EINVAL;
+ }
+ sid->sub_auths[i] = val;
+ }
+
+ if (*t != '\0') {
+ return EINVAL;
+ }
+
+ sid->num_auths = i;
+ return 0;
+}
+
+/**
+* @brief Takes a user sid and removes the rid.
+* The sid is changed by this function,
+* the removed rid is returned too.
+*
+* @param sid A user/group SID
+* @param rid The actual RID found.
+*
+* @return 0 on success, EINVAL otherwise.
+*/
+static int sid_split_rid(struct dom_sid *sid, uint32_t *rid)
+{
+ if (sid->num_auths == 0) {
+ return EINVAL;
+ }
+
+ sid->num_auths--;
+ *rid = sid->sub_auths[sid->num_auths];
+ sid->sub_auths[sid->num_auths] = 0;
+
+ return 0;
+}
+
+static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
+ LDAPMessage *lentry,
+ TALLOC_CTX *memctx,
+ struct netr_SamInfo3 *info3)
+{
+ LDAP *lcontext = ipactx->lcontext;
+ LDAPDerefRes *deref_results = NULL;
+ struct dom_sid sid;
+ gid_t prigid = -1;
+ time_t timeres;
+ char *strres;
+ int intres;
+ int ret;
+
+ ret = ipadb_ldap_attr_to_int(lcontext, lentry, "gidNumber", &intres);
+ if (ret) {
+ /* gidNumber is mandatory */
+ return ret;
+ }
+ prigid = intres;
+
+
+ info3->base.logon_time = 0; /* do not have this info yet */
+ info3->base.logoff_time = -1; /* do not force logoff */
+
+/* TODO: is krbPrinciplaExpiration what we want to use in kickoff_time ?
+ * Needs more investigation */
+#if 0
+ ret = ipadb_ldap_attr_to_time_t(lcontext, lentry,
+ "krbPrincipalExpiration", &timeres);
+ switch (ret) {
+ case 0:
+ unix_to_nt_time(&info3->base.acct_expiry, timeres);
+ break;
+ case ENOENT:
+ info3->base.acct_expiry = -1;
+ break;
+ default:
+ return ret;
+ }
+#else
+ info3->base.kickoff_time = -1;
+#endif
+
+ ret = ipadb_ldap_attr_to_time_t(lcontext, lentry,
+ "krbLastPwdChange", &timeres);
+ switch (ret) {
+ case 0:
+ unix_to_nt_time(&info3->base.last_password_change, timeres);
+ break;
+ case ENOENT:
+ info3->base.last_password_change = 0;
+ break;
+ default:
+ return ret;
+ }
+
+ /* TODO: from pw policy (ied->pol) */
+ info3->base.allow_password_change = 0;
+ info3->base.force_password_change = -1;
+
+ /* FIXME: handle computer accounts they do not use 'uid' */
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry, "uid", &strres);
+ if (ret) {
+ /* uid is mandatory */
+ return ret;
+ }
+ info3->base.account_name.string = talloc_strdup(memctx, strres);
+ free(strres);
+
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry, "cn", &strres);
+ switch (ret) {
+ case 0:
+ info3->base.full_name.string = talloc_strdup(memctx, strres);
+ free(strres);
+ break;
+ case ENOENT:
+ info3->base.full_name.string = "";
+ break;
+ default:
+ return ret;
+ }
+
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry,
+ "ipaNTLogonScript", &strres);
+ switch (ret) {
+ case 0:
+ info3->base.logon_script.string = talloc_strdup(memctx, strres);
+ free(strres);
+ break;
+ case ENOENT:
+ info3->base.logon_script.string = "";
+ break;
+ default:
+ return ret;
+ }
+
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry,
+ "ipaNTProfilePath", &strres);
+ switch (ret) {
+ case 0:
+ info3->base.profile_path.string = talloc_strdup(memctx, strres);
+ free(strres);
+ break;
+ case ENOENT:
+ info3->base.profile_path.string = "";
+ break;
+ default:
+ return ret;
+ }
+
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry,
+ "ipaNTHomeDirectory", &strres);
+ switch (ret) {
+ case 0:
+ info3->base.home_directory.string = talloc_strdup(memctx, strres);
+ free(strres);
+ break;
+ case ENOENT:
+ info3->base.home_directory.string = "";
+ break;
+ default:
+ return ret;
+ }
+
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry,
+ "ipaNTHomeDirectoryDrive", &strres);
+ switch (ret) {
+ case 0:
+ info3->base.home_drive.string = talloc_strdup(memctx, strres);
+ free(strres);
+ break;
+ case ENOENT:
+ info3->base.home_drive.string = "";
+ break;
+ default:
+ return ret;
+ }
+
+ info3->base.logon_count = 0; /* we do not have this info yet */
+ info3->base.bad_password_count = 0; /* we do not have this info yet */
+
+ ret = ipadb_ldap_attr_to_str(lcontext, lentry,
+ "ipaNTSecurityIdentifier", &strres);
+ if (ret) {
+ /* SID is mandatory */
+ return ret;
+ }
+ ret = string_to_sid(strres, &sid);
+ free(strres);
+ if (ret) {
+ return ret;
+ }
+
+ ret = sid_split_rid(&sid, &info3->base.rid);
+ if (ret) {
+ return ret;
+ }
+
+ ret = ipadb_ldap_deref_results(lcontext, lentry, &deref_results);
+ switch (ret) {
+ LDAPDerefRes *dres;
+ LDAPDerefVal *dval;
+ struct dom_sid gsid;
+ uint32_t trid;
+ gid_t tgid;
+ char *s;
+ int count;
+ case 0:
+ count = 0;
+ for (dres = deref_results; dres; dres = dres->next) {
+ count++; /* count*/
+ }
+ info3->base.groups.rids = talloc_array(memctx,
+ struct samr_RidWithAttribute, count);
+ if (!info3->base.groups.rids) {
+ ldap_derefresponse_free(deref_results);
+ return ENOMEM;
+ }
+
+ count = 0;
+ info3->base.primary_gid = 0;
+ for (dres = deref_results; dres; dres = dres->next) {
+ gsid.sid_rev_num = 0;
+ tgid = 0;
+ for (dval = dres->attrVals; dval; dval = dval->next) {
+ if (strcasecmp(dval->type, "gidNumber") == 0) {
+ tgid = strtoul((char *)dval->vals[0].bv_val, &s, 10);
+ if (tgid == 0) {
+ continue;
+ }
+ }
+ if (strcasecmp(dval->type, "ipaNTSecurityIdentifier") == 0) {
+ ret = string_to_sid((char *)dval->vals[0].bv_val, &gsid);
+ if (ret) {
+ continue;
+ }
+ }
+ }
+ if (tgid && gsid.sid_rev_num) {
+ ret = sid_split_rid(&gsid, &trid);
+ if (ret) {
+ continue;
+ }
+ if (tgid == prigid) {
+ info3->base.primary_gid = trid;
+ continue;
+ }
+ info3->base.groups.rids[count].rid = trid;
+ info3->base.groups.rids[count].attributes =
+ SE_GROUP_ENABLED |
+ SE_GROUP_MANDATORY |
+ SE_GROUP_ENABLED_BY_DEFAULT;
+ count++;
+ }
+ }
+ info3->base.groups.count = count;
+
+ ldap_derefresponse_free(deref_results);
+ break;
+ case ENOENT:
+ info3->base.groups.count = 0;
+ info3->base.groups.rids = NULL;
+ break;
+ default:
+ return ret;
+ }
+
+ if (info3->base.primary_gid == 0) {
+ if (ipactx->wc.fallback_rid) {
+ info3->base.primary_gid = ipactx->wc.fallback_rid;
+ } else {
+ /* can't give a pack without a primary group rid */
+ return ENOENT;
+ }
+ }
+
+ /* always zero out, only valid flags are for extra sids with Krb */
+ info3->base.user_flags = 0; /* netr_UserFlags */
+
+ /* always zero out, not used for Krb, only NTLM */
+ memset(&info3->base.key, '\0', sizeof(info3->base.key));
+
+ if (ipactx->wc.flat_server_name) {
+ info3->base.logon_server.string =
+ talloc_strdup(memctx, ipactx->wc.flat_server_name);
+ if (!info3->base.logon_server.string) {
+ return ENOMEM;
+ }
+ } else {
+ /* can't give a pack without Server NetBIOS Name :-| */
+ return ENOENT;
+ }
+
+ if (ipactx->wc.flat_domain_name) {
+ info3->base.logon_domain.string =
+ talloc_strdup(memctx, ipactx->wc.flat_domain_name);
+ if (!info3->base.logon_domain.string) {
+ return ENOMEM;
+ }
+ } else {
+ /* can't give a pack without Domain NetBIOS Name :-| */
+ return ENOENT;
+ }
+
+ /* we got the domain SID for the user sid */
+ info3->base.domain_sid = &sid;
+
+ /* always zero out, not used for Krb, only NTLM */
+ memset(&info3->base.LMSessKey, '\0', sizeof(info3->base.key));
+
+ /* TODO: fill based on objectclass, user vs computer, etc... */
+ info3->base.acct_flags = ACB_NORMAL; /* samr_AcctFlags */
+
+ info3->base.sub_auth_status = 0;
+ info3->base.last_successful_logon = 0;
+ info3->base.last_failed_logon = 0;
+ info3->base.failed_logon_count = 0; /* We do not have it */
+ info3->base.reserved = 0; /* Reserved */
+
+ return 0;
+}
+
+static krb5_error_code ipadb_get_pac(krb5_context kcontext,
+ krb5_db_entry *client,
+ krb5_pac *pac)
+{
+ TALLOC_CTX *tmpctx;
+ struct ipadb_e_data *ied;
+ struct ipadb_context *ipactx;
+ LDAPMessage *results;
+ LDAPMessage *lentry;
+ DATA_BLOB pac_data;
+ krb5_data data;
+ union PAC_INFO pac_info;
+ krb5_error_code kerr;
+ enum ndr_err_code ndr_err;
+
+ ipactx = ipadb_get_context(kcontext);
+ if (!ipactx) {
+ return KRB5_KDB_DBNOTINITED;
+ }
+
+ tmpctx = talloc_new(NULL);
+ if (!tmpctx) {
+ return ENOMEM;
+ }
+
+ ied = (struct ipadb_e_data *)client->e_data;
+ if (ied->magic != IPA_E_DATA_MAGIC) {
+ return EINVAL;
+ }
+
+ if (!ied->ipa_user) {
+ return 0;
+ }
+
+ memset(&pac_info, 0, sizeof(pac_info));
+ pac_info.logon_info.info = talloc_zero(tmpctx, struct PAC_LOGON_INFO);
+ if (!tmpctx) {
+ kerr = ENOMEM;
+ goto done;
+ }
+
+
+ /* == Search PAC info == */
+ kerr = ipadb_deref_search(ipactx, ied->entry_dn, user_pac_attrs,
+ "memberOf", memberof_pac_attrs, &results);
+ if (kerr) {
+ goto done;
+ }
+
+ lentry = ldap_first_entry(ipactx->lcontext, results);
+ if (!lentry) {
+ kerr = ENOENT;
+ goto done;
+ }
+
+ /* == Fill Info3 == */
+ kerr = ipadb_fill_info3(ipactx, lentry, tmpctx,
+ &pac_info.logon_info.info->info3);
+ if (kerr) {
+ goto done;
+ }
+
+ /* == Package PAC == */
+ ndr_err = ndr_push_union_blob(&pac_data, tmpctx, &pac_info,
+ PAC_TYPE_LOGON_INFO,
+ (ndr_push_flags_fn_t)ndr_push_PAC_INFO);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ kerr = KRB5_KDB_INTERNAL_ERROR;
+ goto done;
+ }
+
+ kerr = krb5_pac_init(kcontext, pac);
+ if (kerr) {
+ goto done;
+ }
+
+ data.magic = KV5M_DATA;
+ data.data = (char *)pac_data.data;
+ data.length = pac_data.length;
+
+ kerr = krb5_pac_add_buffer(kcontext, *pac, KRB5_PAC_LOGON_INFO, &data);
+
+done:
+ talloc_free(tmpctx);
+ return kerr;
+}
+
+
+krb5_error_code ipadb_sign_authdata(krb5_context context,
+ unsigned int flags,
+ krb5_const_principal client_princ,
+ krb5_db_entry *client,
+ krb5_db_entry *server,
+ krb5_db_entry *krbtgt,
+ krb5_keyblock *client_key,
+ krb5_keyblock *server_key,
+ krb5_keyblock *krbtgt_key,
+ krb5_keyblock *session_key,
+ krb5_timestamp authtime,
+ krb5_authdata **tgt_auth_data,
+ krb5_authdata ***signed_auth_data)
+{
+ krb5_const_principal ks_client_princ;
+ krb5_authdata *authdata[2] = { NULL, NULL };
+ krb5_authdata ad;
+ krb5_boolean is_as_req;
+ krb5_error_code kerr;
+ krb5_pac pac = NULL;
+ krb5_data pac_data;
+
+ /* Prefer canonicalised name from client entry */
+ if (client != NULL) {
+ ks_client_princ = client->princ;
+ } else {
+ ks_client_princ = client_princ;
+ }
+
+ is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
+
+ if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) {
+
+ kerr = ipadb_get_pac(context, client, &pac);
+ if (kerr != 0) {
+ goto done;
+ }
+ }
+#if 0
+ if (!is_as_req) {
+ code = ks_verify_pac(context, flags, ks_client_princ, client,
+ server_key, krbtgt_key, authtime,
+ tgt_auth_data, &pac);
+ if (code != 0) {
+ goto done;
+ }
+ }
+
+ if (pac == NULL && client != NULL) {
+
+ code = ks_get_pac(context, client, &pac);
+ if (code != 0) {
+ goto done;
+ }
+ }
+#endif
+ if (pac == NULL) {
+ kerr = KRB5_PLUGIN_OP_NOTSUPP;
+/* kerr = KRB5_KDB_DBTYPE_NOSUP; */
+ goto done;
+ }
+
+ kerr = krb5_pac_sign(context, pac, authtime, ks_client_princ,
+ server_key, krbtgt_key, &pac_data);
+ if (kerr != 0) {
+ goto done;
+ }
+
+ /* put in signed data */
+ ad.magic = KV5M_AUTHDATA;
+ ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC;
+ ad.contents = (krb5_octet *)pac_data.data;
+ ad.length = pac_data.length;
+ authdata[0] = &ad;
+
+ kerr = krb5_encode_authdata_container(context,
+ KRB5_AUTHDATA_IF_RELEVANT,
+ &authdata,
+ signed_auth_data);
+ if (kerr != 0) {
+ goto done;
+ }
+
+ kerr = 0;
+
+done:
+ krb5_pac_free(context, pac);
+ return kerr;
+}
+
+static char *get_server_netbios_name(void)
+{
+ char hostname[MAXHOSTNAMELEN + 1]; /* NOTE: this is 64, too little ? */
+ char *p;
+ int ret;
+
+ ret = gethostname(hostname, MAXHOSTNAMELEN);
+ if (ret) {
+ return NULL;
+ }
+ /* May miss termination */
+ hostname[MAXHOSTNAMELEN] = '\0';
+ for (p = hostname; *p; p++) {
+ if (*p == '.') {
+ *p = 0;
+ break;
+ } else {
+ *p = toupper(*p);
+ }
+ }
+
+ return strdup(hostname);
+}
+
+krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx)
+{
+ char *dom_attrs[] = { "ipaNTFlatName",
+ "ipaNTFallbackPrimaryGroup",
+ NULL };
+ char *grp_attrs[] = { "ipaNTSecurityIdentifier", NULL };
+ krb5_error_code kerr;
+ LDAPMessage *result = NULL;
+ LDAPMessage *lentry;
+ struct dom_sid gsid;
+ char *resstr;
+ int ret;
+
+ /* clean up in case we had old values around */
+ free(ipactx->wc.flat_domain_name);
+ ipactx->wc.flat_domain_name = NULL;
+ free(ipactx->wc.fallback_group);
+ ipactx->wc.fallback_group = NULL;
+ ipactx->wc.fallback_rid = 0;
+
+ kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
+ "(objectclass=ipaNTDomainAttrs)", dom_attrs,
+ &result);
+ if (kerr == KRB5_KDB_NOENTRY) {
+ return ENOENT;
+ } else if (kerr != 0) {
+ return EIO;
+ }
+
+ lentry = ldap_first_entry(ipactx->lcontext, result);
+ if (!lentry) {
+ kerr = ENOENT;
+ goto done;
+ }
+
+ ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
+ "ipaNTFlatName",
+ &ipactx->wc.flat_domain_name);
+ if (ret) {
+ kerr = ret;
+ goto done;
+ }
+
+ free(ipactx->wc.flat_server_name);
+ ipactx->wc.flat_server_name = get_server_netbios_name();
+ if (!ipactx->wc.flat_server_name) {
+ kerr = ENOMEM;
+ goto done;
+ }
+
+ ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
+ "ipaNTFallbackPrimaryGroup",
+ &ipactx->wc.fallback_group);
+ if (ret && ret != ENOENT) {
+ kerr = ret;
+ goto done;
+ }
+
+ /* result and lentry not valid any more from here on */
+ ldap_msgfree(result);
+ result = NULL;
+ lentry = NULL;
+
+ if (ret != ENOENT) {
+ kerr = ipadb_simple_search(ipactx, ipactx->wc.fallback_group,
+ LDAP_SCOPE_BASE,
+ "(objectclass=posixGroup)",
+ grp_attrs, &result);
+ if (kerr && kerr != KRB5_KDB_NOENTRY) {
+ kerr = ret;
+ goto done;
+ }
+
+ lentry = ldap_first_entry(ipactx->lcontext, result);
+ if (!lentry) {
+ kerr = ENOENT;
+ goto done;
+ }
+
+ if (kerr == 0) {
+ ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
+ "ipaNTSecurityIdentifier",
+ &resstr);
+ if (ret && ret != ENOENT) {
+ kerr = ret;
+ goto done;
+ }
+ if (ret == 0) {
+ ret = string_to_sid(resstr, &gsid);
+ if (ret) {
+ kerr = ret;
+ goto done;
+ }
+ ret = sid_split_rid(&gsid, &ipactx->wc.fallback_rid);
+ if (ret) {
+ kerr = ret;
+ goto done;
+ }
+ }
+ }
+ }
+
+ kerr = 0;
+
+done:
+ ldap_msgfree(result);
+ return kerr;
+}
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
index 33ed7b0e1..f5bef8403 100644
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
@@ -556,6 +556,12 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
entry->e_data = (krb5_octet *)ied;
+ ied->entry_dn = ldap_get_dn(lcontext, lentry);
+ if (!ied->entry_dn) {
+ kerr = ENOMEM;
+ goto done;
+ }
+
/* mark this as an ipa_user if it has the posixaccount objectclass */
ret = ipadb_ldap_attr_has_value(lcontext, lentry,
"objectClass", "posixAccount");
@@ -919,6 +925,7 @@ void ipadb_free_principal(krb5_context kcontext, krb5_db_entry *entry)
if (entry->e_data) {
ied = (struct ipadb_e_data *)entry->e_data;
if (ied->magic == IPA_E_DATA_MAGIC) {
+ ldap_memfree(ied->entry_dn);
free(ied->passwd);
free(ied->pw_policy_dn);
for (i = 0; ied->pw_history && ied->pw_history[i]; i++) {