The attached patches are for master and concern the effort of creating
trust relationships between IPA and AD domains.

With these patches if you have run ipa-adtrust-install the IPA kdc will
be able to create a MS-PAC if the user has the right attributes
ipaNTSecurityIdentifier on the user entry and on the primary group entry
are required (or a fallback primary group).
If the objects are not in place the MS-PAC generation is silently
skipped and no MS-PAC will be attached to the tickets.

The MS-PAC is always generated if all data is available, in future we
may think of making this conditional, but that is not in the scope of
this patches. 

In order to apply these patches you need the coverity fix patches #2036
#2037 I sent yesterday.

In order to build this code you need samba 4 experimental packages with
the libndr_krb5pac.so librray, header files and pkgconfig configuration
files.

Simo.

-- 
Simo Sorce * Red Hat, Inc * New York
>From 02cd0398a18ae489dec5ea83bdad55dbb0ab5587 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Mon, 10 Oct 2011 15:42:11 -0400
Subject: [PATCH 1/2] 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 +
 7 files changed, 905 insertions(+), 1 deletions(-)
 create mode 100644 daemons/ipa-kdb/ipa_kdb_mspac.c

diff --git a/daemons/configure.ac b/daemons/configure.ac
index e238d8b15901e7b46882cddd7d8621969d794039..f89c50d62a3d59c3dddd3439f285fe6e5d9b89ee 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 036074f437bdf8e177cd26018c7f611cf553c505..b29f60171116640d0f2b350111017fd8d6bbce59 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 481b1f392766498c5d7c6333fe73bafefde87dae..05ee18720a11fc6b8579fd00206d1cbb9d5a1a34 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 cfcaca6493fd3f4657fd9f1839b6f3ac9f22546d..8c907c448d0f497786f7b66fb4e17e6590d4cc29 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 6f67be50123c9de3cecdef231c59b797f314158c..d3e8e9c4cb5438cd9c918daf5a19fc736fc698cb 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 0000000000000000000000000000000000000000..37ec063b594efeda797154d55b9325521705ebea
--- /dev/null
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -0,0 +1,756 @@
+/*
+ * MIT Kerberos KDC database backend for FreeIPA
+ *
+ * Authors: Simo Sorce <sso...@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 33ed7b0e15ff1fa29150d1c2695c5a3b0c4c5f03..f5bef84030f5cc3f3381d78b03d5c521a81445f1 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++) {
-- 
1.7.7

>From 1e9299965071e816573e5273de99ba478c2f7e46 Mon Sep 17 00:00:00 2001
From: Simo Sorce <sso...@redhat.com>
Date: Mon, 17 Oct 2011 17:18:43 -0400
Subject: [PATCH 2/2] MS-PAC: Add support for verifying PAC in TGS requests

Fake code for now, to be rebased later
---
 daemons/ipa-kdb/ipa_kdb_mspac.c |   69 +++++++++++++++++++++++++++++++++++----
 1 files changed, 62 insertions(+), 7 deletions(-)

diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
index 37ec063b594efeda797154d55b9325521705ebea..3d4975e73e2402bda3065f6f90bf8bf7c2e4f9c5 100644
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
@@ -26,6 +26,7 @@
 #include "gen_ndr/ndr_krb5pac.h"
 
 #define KRB5INT_PAC_SIGN_AVAILABLE 1
+#define KRB5INT_FIND_AUTHDATA_AVAILABLE 1
 
 #if KRB5INT_PAC_SIGN_AVAILABLE
 krb5_error_code
@@ -40,6 +41,14 @@ krb5int_pac_sign(krb5_context context,
 #define KRB5_PAC_LOGON_INFO 1
 #endif
 
+#if KRB5INT_FIND_AUTHDATA_AVAILABLE
+krb5_error_code
+krb5int_find_authdata(krb5_context context,
+                      krb5_authdata *const *ticket_authdata,
+                      krb5_authdata *const *ap_req_authdata,
+                      krb5_authdatatype ad_type, krb5_authdata ***results);
+#define krb5_find_authdata krb5int_find_authdata
+#endif
 
 
 static char *user_pac_attrs[] = {
@@ -531,6 +540,51 @@ done:
     return kerr;
 }
 
+static krb5_error_code ipadb_verify_pac(krb5_context context,
+                                        unsigned int flags,
+                                        krb5_const_principal client_princ,
+                                        krb5_db_entry *client,
+                                        krb5_keyblock *server_key,
+                                        krb5_keyblock *krbtgt_key,
+                                        krb5_timestamp authtime,
+                                        krb5_authdata **tgt_auth_data,
+                                        krb5_pac *pac)
+{
+    krb5_authdata **authdata = NULL;
+    krb5_error_code kerr;
+
+    /* find the existing PAC, if present */
+    kerr = krb5_find_authdata(context, tgt_auth_data, NULL,
+                              KRB5_AUTHDATA_WIN2K_PAC, &authdata);
+    if (kerr != 0) {
+        return kerr;
+    }
+
+    /* check pac data */
+    if (authdata == NULL || authdata[0] == NULL) {
+        kerr = 0; /* none */
+        goto done;
+    }
+    if (authdata[1] != NULL) {
+        kerr = KRB5KDC_ERR_BADOPTION; /* FIXME: right error ? */
+        goto done;
+    }
+
+    kerr = krb5_pac_parse(context,
+                          authdata[0]->contents,
+                          authdata[0]->length,
+                          pac);
+    if (kerr) {
+        goto done;
+    }
+
+    kerr = krb5_pac_verify(context, *pac, authtime,
+                            client_princ, krbtgt_key, NULL);
+
+done:
+    krb5_free_authdata(context, authdata);
+    return kerr;
+}
 
 krb5_error_code ipadb_sign_authdata(krb5_context context,
                                     unsigned int flags,
@@ -566,20 +620,21 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
     if (is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) {
 
         kerr = ipadb_get_pac(context, client, &pac);
-        if (kerr != 0) {
+        if (kerr != 0 && kerr != ENOENT) {
             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) {
+        kerr = ipadb_verify_pac(context, flags,
+                                ks_client_princ, client,
+                                server_key, krbtgt_key,
+                                authtime, tgt_auth_data, &pac);
+        if (kerr != 0) {
             goto done;
         }
     }
-
+#if 0
     if (pac == NULL && client != NULL) {
 
         code = ks_get_pac(context, client, &pac);
-- 
1.7.7

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to