From 18537d55a7fd21c81af8b56fde69c895b2fa3597 Mon Sep 17 00:00:00 2001 From: Simo Sorce Date: Mon, 10 Oct 2011 15:42:11 -0400 Subject: Add support for generating PAC for AS requests for user principals --- daemons/configure.ac | 10 + daemons/ipa-kdb/Makefile.am | 3 + daemons/ipa-kdb/ipa_kdb.c | 7 +- daemons/ipa-kdb/ipa_kdb.h | 38 ++ daemons/ipa-kdb/ipa_kdb_common.c | 85 ++++ daemons/ipa-kdb/ipa_kdb_mspac.c | 756 +++++++++++++++++++++++++++++++++++ daemons/ipa-kdb/ipa_kdb_principals.c | 7 + freeipa.spec.in | 1 + 8 files changed, 906 insertions(+), 1 deletion(-) create mode 100644 daemons/ipa-kdb/ipa_kdb_mspac.c diff --git a/daemons/configure.ac b/daemons/configure.ac index e238d8b15..f89c50d62 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -226,6 +226,16 @@ if test "x$PYTHON" = "x" ; then AC_MSG_ERROR([Python not found]) 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 #include #include +#include #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 + * + * 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 . + */ + +#include "ipa_kdb.h" +#include +#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++) { diff --git a/freeipa.spec.in b/freeipa.spec.in index 33aba87db..828d308e1 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -35,6 +35,7 @@ BuildRequires: policycoreutils >= %{POLICYCOREUTILSVER} %if 0%{?fedora} >= 16 BuildRequires: systemd-units %endif +BuildRequires: samba-4.0-devel %endif BuildRequires: nspr-devel BuildRequires: nss-devel -- cgit