diff options
author | Sumit Bose <sbose@redhat.com> | 2011-10-25 10:33:30 +0200 |
---|---|---|
committer | Simo Sorce <ssorce@redhat.com> | 2011-12-06 08:29:53 -0500 |
commit | edb6ed5007e7b0b4ac118d567b94e04d996d8997 (patch) | |
tree | a4428fd03b1e983193567820e900cdf6541455bb | |
parent | 7c3e5f1be5cad9ee61547883ee1e4f852e14c3a7 (diff) | |
download | freeipa-edb6ed5007e7b0b4ac118d567b94e04d996d8997.tar.gz freeipa-edb6ed5007e7b0b4ac118d567b94e04d996d8997.tar.xz freeipa-edb6ed5007e7b0b4ac118d567b94e04d996d8997.zip |
Add ipasam samba passdb backend
https://fedorahosted.org/freeipa/ticket/1874
-rw-r--r-- | daemons/Makefile.am | 1 | ||||
-rw-r--r-- | daemons/configure.ac | 7 | ||||
-rw-r--r-- | daemons/ipa-sam/Makefile.am | 60 | ||||
-rw-r--r-- | daemons/ipa-sam/README | 1 | ||||
-rw-r--r-- | daemons/ipa-sam/ipa_sam.c | 3235 | ||||
-rw-r--r-- | daemons/ipa-sam/ipa_sam.h | 28 | ||||
-rw-r--r-- | freeipa.spec.in | 2 | ||||
-rw-r--r-- | install/share/smb.conf.template | 2 |
8 files changed, 3334 insertions, 2 deletions
diff --git a/daemons/Makefile.am b/daemons/Makefile.am index 031df16ac..05cd1a768 100644 --- a/daemons/Makefile.am +++ b/daemons/Makefile.am @@ -15,6 +15,7 @@ export AM_CFLAGS SUBDIRS = \ ipa-kdb \ ipa-slapi-plugins \ + ipa-sam \ $(NULL) DISTCLEANFILES = \ diff --git a/daemons/configure.ac b/daemons/configure.ac index 46f476574..bee260dcd 100644 --- a/daemons/configure.ac +++ b/daemons/configure.ac @@ -227,7 +227,7 @@ if test "x$PYTHON" = "x" ; then fi dnl --------------------------------------------------------------------------- -dnl Check for ndr_krb5pac +dnl Check for ndr_krb5pac and other samba libraries dnl --------------------------------------------------------------------------- PKG_PROG_PKG_CONFIG() @@ -235,6 +235,10 @@ PKG_CHECK_MODULES([TALLOC], [talloc]) PKG_CHECK_MODULES([TEVENT], [tevent]) PKG_CHECK_MODULES([NDRPAC], [ndr_krb5pac]) PKG_CHECK_MODULES([NDRNBT], [ndr_nbt]) +PKG_CHECK_MODULES([NDR], [ndr]) +PKG_CHECK_MODULES([SAMBAUTIL], [samba-util]) +SAMBA40EXTRA_LIBPATH="-L`$PKG_CONFIG --variable=libdir samba-util`/samba" +AC_SUBST(SAMBA40EXTRA_LIBPATH) dnl --------------------------------------------------------------------------- @@ -300,6 +304,7 @@ AC_SUBST(LDFLAGS) AC_CONFIG_FILES([ Makefile ipa-kdb/Makefile + ipa-sam/Makefile ipa-slapi-plugins/Makefile ipa-slapi-plugins/ipa-cldap/Makefile ipa-slapi-plugins/ipa-enrollment/Makefile diff --git a/daemons/ipa-sam/Makefile.am b/daemons/ipa-sam/Makefile.am new file mode 100644 index 000000000..002e34eca --- /dev/null +++ b/daemons/ipa-sam/Makefile.am @@ -0,0 +1,60 @@ +NULL = +SAMBA40EXTRA_LIBS = $(SAMBA40EXTRA_LIBPATH) \ + -lsmbldap \ + -lcliauth \ + -lpdb \ + -lsecurity \ + -lutil_str \ + -lsmbconf \ + $(NULL) + +INCLUDES = \ + -I. \ + -I$(srcdir) \ + -I/usr/include/samba-4.0 \ + -DPREFIX=\""$(prefix)"\" \ + -DBINDIR=\""$(bindir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DDATADIR=\""$(datadir)"\" \ + -DLDAPIDIR=\""$(localstatedir)/run"\" \ + -DHAVE_LDAP \ + $(AM_CFLAGS) \ + $(LDAP_CFLAGS) \ + $(KRB5_CFLAGS) \ + $(WARN_CFLAGS) \ + $(TALLOC_CFLAGS) \ + $(SAMBAUTIL_CFLAGS) \ + $(NDR_CFLAGS) \ + $(NULL) + +plugindir = $(libdir)/samba/pdb +plugin_LTLIBRARIES = \ + ipasam.la \ + $(NULL) + +ipasam_la_SOURCES = \ + ipa_sam.c \ + $(NULL) + +ipasam_la_LDFLAGS = \ + -avoid-version \ + -module \ + $(NULL) + +ipasam_la_LIBADD = \ + $(LDAP_LIBS) \ + $(TALLOC_LIBS) \ + $(SAMBAUTIL_LIBS) \ + $(NDR_LIBS) \ + $(SAMBA40EXTRA_LIBS) \ + $(NULL) + +EXTRA_DIST = \ + README \ + ipa_sam.h \ + $(NULL) + +MAINTAINERCLEANFILES = \ + *~ \ + Makefile.in diff --git a/daemons/ipa-sam/README b/daemons/ipa-sam/README new file mode 100644 index 000000000..f53ab1ccf --- /dev/null +++ b/daemons/ipa-sam/README @@ -0,0 +1 @@ +This is the ipa samba passdb backend. diff --git a/daemons/ipa-sam/ipa_sam.c b/daemons/ipa-sam/ipa_sam.c new file mode 100644 index 000000000..684cd37ef --- /dev/null +++ b/daemons/ipa-sam/ipa_sam.c @@ -0,0 +1,3235 @@ +#define HAVE_IMMEDIATE_STRUCTURES 1 +#define LDAP_DEPRECATED 1 + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <sys/types.h> +#include <pwd.h> +#include <errno.h> +#include <ldap.h> + +#include <talloc.h> + +#include <param.h> +#include <ndr.h> +#include <util/data_blob.h> +#include <util/time.h> +#include <util/debug.h> + +#include <core/ntstatus.h> +#include <gen_ndr/security.h> +#include <smbldap.h> + +#include <gen_ndr/samr.h> + +#include <passdb.h> + +/* TODO: remove if smbrunsecret() is removed */ +typedef struct connection_structi {} connection_struct; +struct current_user { + connection_struct *conn; + uint16_t vuid; + struct security_unix_token ut; + struct security_token *nt_user_token; +}; +extern struct current_user current_user; + +/* from drsblobs.h */ +struct AuthInfoNone { + uint32_t size;/* [value(0)] */ +}; + +struct AuthInfoNT4Owf { + uint32_t size;/* [value(16)] */ + struct samr_Password password; +}; + +struct AuthInfoClear { + uint32_t size; + uint8_t *password; +}; + +struct AuthInfoVersion { + uint32_t size;/* [value(4)] */ + uint32_t version; +}; + +union AuthInfo { + struct AuthInfoNone none;/* [case(TRUST_AUTH_TYPE_NONE)] */ + struct AuthInfoNT4Owf nt4owf;/* [case(TRUST_AUTH_TYPE_NT4OWF)] */ + struct AuthInfoClear clear;/* [case(TRUST_AUTH_TYPE_CLEAR)] */ + struct AuthInfoVersion version;/* [case(TRUST_AUTH_TYPE_VERSION)] */ +}/* [nodiscriminant] */; + +struct AuthenticationInformation { + NTTIME LastUpdateTime; + enum lsa_TrustAuthType AuthType; + union AuthInfo AuthInfo;/* [switch_is(AuthType)] */ + DATA_BLOB _pad;/* [flag(LIBNDR_FLAG_ALIGN4)] */ +}/* [public] */; + +struct AuthenticationInformationArray { + uint32_t count; + struct AuthenticationInformation *array; +}/* [gensize,nopush,public,nopull] */; + +struct trustAuthInOutBlob { + uint32_t count; + uint32_t current_offset;/* [value((count>0)?12:0)] */ + uint32_t previous_offset;/* [value((count>0)?12+ndr_size_AuthenticationInformationArray(¤t,ndr->flags):0)] */ + struct AuthenticationInformationArray current;/* [subcontext_size((previous_offset)-(current_offset)),subcontext(0)] */ + struct AuthenticationInformationArray previous;/* [subcontext(0),flag(LIBNDR_FLAG_REMAINING)] */ +}/* [gensize,public,nopush] */; + + +enum ndr_err_code ndr_pull_trustAuthInOutBlob(struct ndr_pull *ndr, int ndr_flags, struct trustAuthInOutBlob *r); /*available in libndr-samba.so */ +bool fetch_ldap_pw(char **dn, char** pw); /* available in libpdb.so */ +void nt_lm_owf_gen(const char *pwd, uint8_t nt_p16[16], uint8_t p16[16]); /* available in libcliauth.so */ +bool sid_check_is_builtin(const struct dom_sid *sid); /* available in libpdb.so */ +void strlower_m(char *s); /* available in libutil_str.so */ +char *talloc_asprintf_strupper_m(TALLOC_CTX *t, const char *fmt, ...); /* available in libutil_str.so */ +void sid_copy(struct dom_sid *dst, const struct dom_sid *src); /* available in libsecurity.so */ +bool sid_linearize(char *outbuf, size_t len, const struct dom_sid *sid); /* available in libsmbconf.so */ +bool string_to_sid(struct dom_sid *sidout, const char *sidstr); /* available in libsecurity.so */ +bool sid_compose(struct dom_sid *dst, const struct dom_sid *domain_sid, uint32_t rid); /* available in libsecurity.so */ +bool sid_peek_rid(const struct dom_sid *sid, uint32_t *rid); /* available in libsecurity.so */ +int dom_sid_compare_domain(const struct dom_sid *sid1, const struct dom_sid *sid2); /* available in libsecurity.so */ +char *sid_string_talloc(TALLOC_CTX *mem_ctx, const struct dom_sid *sid); /* available in libsmbconf.so */ +char *sid_string_dbg(const struct dom_sid *sid); /* available in libsmbconf.so */ +bool is_null_sid(const struct dom_sid *sid); /* available in libsecurity.so */ +bool strnequal(const char *s1,const char *s2,size_t n); /* available in libutil_str.so */ +int smbrunsecret(const char *cmd, const char *secret); /* available in libsmbconf.so */ +bool trim_char(char *s,char cfront,char cback); /* available in libutil_str.so */ +bool sid_peek_check_rid(const struct dom_sid *exp_dom_sid, const struct dom_sid *sid, uint32_t *rid); /* available in libsecurity.so */ +char *escape_ldap_string(TALLOC_CTX *mem_ctx, const char *s); /* available in libsmbconf.so */ +extern const struct dom_sid global_sid_Builtin; /* available in libsecurity.so */ +bool secrets_store(const char *key, const void *data, size_t size); /* available in libpdb.so */ +/* from smb_macros.h */ +#define SMB_REALLOC_ARRAY(p,type,count) (type *)realloc_array((p),sizeof(type),(count),true) /* Always frees p on error or s == 0 */ +#define ADD_TO_ARRAY(mem_ctx, type, elem, array, num) \ +do { \ + *(array) = ((mem_ctx) != NULL) ? \ + talloc_realloc(mem_ctx, (*(array)), type, (*(num))+1) : \ + SMB_REALLOC_ARRAY((*(array)), type, (*(num))+1); \ + SMB_ASSERT((*(array)) != NULL); \ + (*(array))[*(num)] = (elem); \ + (*(num)) += 1; \ +} while (0) + + +#define LDAP_SUFFIX "dc=ipa,dc=devel" /* FIXME !!! */ +#define LDAP_PAGE_SIZE 1024 +#define LDAP_OBJ_SAMBASAMACCOUNT "ipaNTUserAttrs" +#define LDAP_OBJ_TRUSTED_DOMAIN "ipaNTTrustedDomain" +#define LDAP_ATTRIBUTE_SID "ipaNTSecurityIdentifier" +#define LDAP_OBJ_GROUPMAP "ipaNTGroupAttrs" + +#define IPA_KEYTAB_SET_OID "2.16.840.1.113730.3.8.10.1" +#define IPA_KEYTAB_SET_OID_OLD "2.16.840.1.113730.3.8.3.1" +#define IPA_MAGIC_ID_STR "999" + +#define LDAP_ATTRIBUTE_CN "cn" +#define LDAP_ATTRIBUTE_UID "uid" +#define LDAP_ATTRIBUTE_TRUST_TYPE "ipaNTTrustType" +#define LDAP_ATTRIBUTE_TRUST_ATTRIBUTES "ipaNTTrustAttributes" +#define LDAP_ATTRIBUTE_TRUST_DIRECTION "ipaNTTrustDirection" +#define LDAP_ATTRIBUTE_TRUST_POSIX_OFFSET "ipaNTTrustPosixOffset" +#define LDAP_ATTRIBUTE_SUPPORTED_ENC_TYPE "ipaNTSupportedEncryptionTypes" +#define LDAP_ATTRIBUTE_TRUST_PARTNER "ipaNTTrustPartner" +#define LDAP_ATTRIBUTE_FLAT_NAME "ipaNTFlatName" +#define LDAP_ATTRIBUTE_TRUST_AUTH_OUTGOING "ipaNTTrustAuthOutgoing" +#define LDAP_ATTRIBUTE_TRUST_AUTH_INCOMING "ipaNTTrustAuthIncoming" +#define LDAP_ATTRIBUTE_SECURITY_IDENTIFIER "ipaNTSecurityIdentifier" +#define LDAP_ATTRIBUTE_TRUST_FOREST_TRUST_INFO "ipaNTTrustForestTrustInfo" +#define LDAP_ATTRIBUTE_FALLBACK_PRIMARY_GROUP "ipaNTFallbackPrimaryGroup" +#define LDAP_ATTRIBUTE_OBJECTCLASS "objectClass" +#define LDAP_ATTRIBUTE_HOME_DRIVE "ipaNTHomeDirectoryDrive" +#define LDAP_ATTRIBUTE_HOME_PATH "ipaNTHomeDirectory" +#define LDAP_ATTRIBUTE_LOGON_SCRIPT "ipaNTLogonScript" +#define LDAP_ATTRIBUTE_PROFILE_PATH "ipaNTProfilePath" +#define LDAP_ATTRIBUTE_NTHASH "ipaNTHash" + +#define LDAP_OBJ_KRB_PRINCIPAL "krbPrincipal" +#define LDAP_OBJ_KRB_PRINCIPAL_AUX "krbPrincipalAux" +#define LDAP_OBJ_KRB_TICKET_POLICY_AUX "krbTicketPolicyAux" +#define LDAP_ATTRIBUTE_KRB_PRINCIPAL "krbPrincipalName" + +#define LDAP_OBJ_IPAOBJECT "ipaObject" +#define LDAP_OBJ_IPAHOST "ipaHost" +#define LDAP_OBJ_POSIXACCOUNT "posixAccount" + +#define LDAP_OBJ_GROUPOFNAMES "groupOfNames" +#define LDAP_OBJ_NESTEDGROUP "nestedGroup" +#define LDAP_OBJ_IPAUSERGROUP "ipaUserGroup" +#define LDAP_OBJ_POSIXGROUP "posixGroup" + +#define HAS_KRB_PRINCIPAL (1<<0) +#define HAS_KRB_PRINCIPAL_AUX (1<<1) +#define HAS_IPAOBJECT (1<<2) +#define HAS_IPAHOST (1<<3) +#define HAS_POSIXACCOUNT (1<<4) +#define HAS_GROUPOFNAMES (1<<5) +#define HAS_NESTEDGROUP (1<<6) +#define HAS_IPAUSERGROUP (1<<7) +#define HAS_POSIXGROUP (1<<8) +#define HAS_KRB_TICKET_POLICY_AUX (1<<9) + +struct ipasam_privates { + NTSTATUS (*ldapsam_add_sam_account)(struct pdb_methods *, + struct samu *sampass); + NTSTATUS (*ldapsam_update_sam_account)(struct pdb_methods *, + struct samu *sampass); + NTSTATUS (*ldapsam_create_user)(struct pdb_methods *my_methods, + TALLOC_CTX *tmp_ctx, const char *name, + uint32_t acb_info, uint32_t *rid); + NTSTATUS (*ldapsam_create_dom_group)(struct pdb_methods *my_methods, + TALLOC_CTX *tmp_ctx, + const char *name, + uint32_t *rid); + char *realm; + char *base_dn; + char *trust_dn; + char *flat_name; + char *fallback_primary_group; +}; + +static LDAP *priv2ld(struct ldapsam_privates *priv) +{ + return priv->smbldap_state->ldap_struct; +} + +static char *get_single_attribute(TALLOC_CTX *mem_ctx, LDAP *ldap_struct, + LDAPMessage *entry, const char *attribute) +{ + struct berval **values; + int c; + char *result = NULL; + size_t conv_size; + + if (attribute == NULL || entry == NULL) { + return NULL; + } + + values = ldap_get_values_len(ldap_struct, entry, attribute); + if (values == NULL) { + DEBUG(10, ("Attribute [%s] not found.\n", attribute)); + return NULL; + } + + c = ldap_count_values_len(values); + if (c != 1) { + DEBUG(10, ("Found [%d] values for attribute [%s] but expected only 1.\n", + c, attribute)); + goto done; + } + + if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX, + values[0]->bv_val, values[0]->bv_len, + &result, &conv_size)) { + DEBUG(10, ("Failed to convert value of [%s].\n", attribute)); + result = NULL; + goto done; + } + +done: + ldap_value_free_len(values); + return result; +} + +static char *get_dn(TALLOC_CTX *mem_ctx, LDAP *ld, LDAPMessage *entry) +{ + char *utf8_dn; + char *unix_dn = NULL; + size_t conv_size; + + utf8_dn = ldap_get_dn(ld, entry); + if (utf8_dn == NULL) { + DEBUG (10, ("ldap_get_dn failed\n")); + return NULL; + } + if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX, + utf8_dn, strlen(utf8_dn) + 1, + &unix_dn, &conv_size)) { + DEBUG (10, ("Failed to convert [%s]\n", utf8_dn)); + unix_dn = NULL; + goto done; + } + +done: + ldap_memfree(utf8_dn); + return unix_dn; +} + + + + + +static bool ldapsam_extract_rid_from_entry(LDAP *ldap_struct, + LDAPMessage *entry, + const struct dom_sid *domain_sid, + uint32_t *rid) +{ + char *str; + struct dom_sid sid; + + str = get_single_attribute(NULL, ldap_struct, entry, + LDAP_ATTRIBUTE_SID); + if (str == NULL) { + DEBUG(10, ("Could not find SID attribute\n")); + return false; + } + + if (!string_to_sid(&sid, str)) { + talloc_free(str); + DEBUG(10, ("Could not convert string %s to sid\n", str)); + return false; + } + talloc_free(str); + + if (dom_sid_compare_domain(&sid, domain_sid) != 0) { + DEBUG(10, ("SID %s is not in expected domain %s\n", + str, sid_string_dbg(domain_sid))); + return false; + } + + if (!sid_peek_rid(&sid, rid)) { + DEBUG(10, ("Could not peek into RID\n")); + return false; + } + + return true; +} + +static NTSTATUS ldapsam_lookup_rids(struct pdb_methods *methods, + const struct dom_sid *domain_sid, + int num_rids, + uint32_t *rids, + const char **names, + enum lsa_SidType *attrs) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + LDAPMessage *msg = NULL; + LDAPMessage *entry; + char *allsids = NULL; + int i, rc, num_mapped; + NTSTATUS result = NT_STATUS_NO_MEMORY; + TALLOC_CTX *mem_ctx; + LDAP *ld; + bool is_builtin; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + goto done; + } + + if (!sid_check_is_builtin(domain_sid) && + !sid_check_is_domain(domain_sid)) { + result = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + if (num_rids == 0) { + result = NT_STATUS_NONE_MAPPED; + goto done; + } + + for (i=0; i<num_rids; i++) + attrs[i] = SID_NAME_UNKNOWN; + + allsids = talloc_strdup(mem_ctx, ""); + if (allsids == NULL) { + goto done; + } + + for (i=0; i<num_rids; i++) { + struct dom_sid sid; + sid_compose(&sid, domain_sid, rids[i]); + allsids = talloc_asprintf_append_buffer( + allsids, "(%s=%s)", + LDAP_ATTRIBUTE_SID, + sid_string_talloc(mem_ctx, &sid)); + if (allsids == NULL) { + goto done; + } + } + + /* First look for users */ + + { + char *filter; + const char *ldap_attrs[] = { "uid", LDAP_ATTRIBUTE_SID, NULL }; + + filter = talloc_asprintf( + mem_ctx, ("(&(objectClass=%s)(|%s))"), + LDAP_OBJ_SAMBASAMACCOUNT, allsids); + + if (filter == NULL) { + goto done; + } + + rc = smbldap_search(ldap_state->smbldap_state, + ldap_state->ipasam_privates->base_dn, + LDAP_SCOPE_SUBTREE, filter, ldap_attrs, 0, + &msg); + talloc_autofree_ldapmsg(mem_ctx, msg); + } + + if (rc != LDAP_SUCCESS) + goto done; + + ld = ldap_state->smbldap_state->ldap_struct; + num_mapped = 0; + + for (entry = ldap_first_entry(ld, msg); + entry != NULL; + entry = ldap_next_entry(ld, entry)) { + uint32_t rid; + int rid_index; + const char *name; + + if (!ldapsam_extract_rid_from_entry(ld, entry, domain_sid, + &rid)) { + DEBUG(2, ("Could not find sid from ldap entry\n")); + continue; + } + + name = get_single_attribute(names, ld, entry, "uid"); + if (name == NULL) { + DEBUG(2, ("Could not retrieve uid attribute\n")); + continue; + } + + for (rid_index = 0; rid_index < num_rids; rid_index++) { + if (rid == rids[rid_index]) + break; + } + + if (rid_index == num_rids) { + DEBUG(2, ("Got a RID not asked for: %d\n", rid)); + continue; + } + + attrs[rid_index] = SID_NAME_USER; + names[rid_index] = name; + num_mapped += 1; + } + + if (num_mapped == num_rids) { + /* No need to look for groups anymore -- we're done */ + result = NT_STATUS_OK; + goto done; + } + + /* Same game for groups */ + + { + char *filter; + const char *ldap_attrs[] = { "cn", "displayName", + LDAP_ATTRIBUTE_SID, + NULL }; + + filter = talloc_asprintf( + mem_ctx, "(&(objectClass=%s)(|%s))", + LDAP_OBJ_GROUPMAP, allsids); + if (filter == NULL) { + goto done; + } + + rc = smbldap_search(ldap_state->smbldap_state, + ldap_state->ipasam_privates->base_dn, + LDAP_SCOPE_SUBTREE, filter, ldap_attrs, 0, + &msg); + talloc_autofree_ldapmsg(mem_ctx, msg); + } + + if (rc != LDAP_SUCCESS) + goto done; + + /* ldap_struct might have changed due to a reconnect */ + + ld = ldap_state->smbldap_state->ldap_struct; + + /* For consistency checks, we already checked we're only domain or builtin */ + + is_builtin = sid_check_is_builtin(domain_sid); + + for (entry = ldap_first_entry(ld, msg); + entry != NULL; + entry = ldap_next_entry(ld, entry)) + { + uint32_t rid; + int rid_index; + const char *attr; + enum lsa_SidType type; + const char *dn = get_dn(mem_ctx, ld, entry); + + type = SID_NAME_DOM_GRP; + + /* Consistency checks */ + if ((is_builtin && (type != SID_NAME_ALIAS)) || + (!is_builtin && ((type != SID_NAME_ALIAS) && + (type != SID_NAME_DOM_GRP)))) { + DEBUG(2, ("Rejecting invalid group mapping entry %s\n", dn)); + } + + if (!ldapsam_extract_rid_from_entry(ld, entry, domain_sid, + &rid)) { + DEBUG(2, ("Could not find sid from ldap entry %s\n", dn)); + continue; + } + + attr = get_single_attribute(names, ld, entry, "displayName"); + + if (attr == NULL) { + DEBUG(10, ("Could not retrieve 'displayName' attribute from %s\n", + dn)); + attr = get_single_attribute(names, ld, entry, "cn"); + } + + if (attr == NULL) { + DEBUG(2, ("Could not retrieve naming attribute from %s\n", + dn)); + continue; + } + + for (rid_index = 0; rid_index < num_rids; rid_index++) { + if (rid == rids[rid_index]) + break; + } + + if (rid_index == num_rids) { + DEBUG(2, ("Got a RID not asked for: %d\n", rid)); + continue; + } + + attrs[rid_index] = type; + names[rid_index] = attr; + num_mapped += 1; + } + + result = NT_STATUS_NONE_MAPPED; + + if (num_mapped > 0) + result = (num_mapped == num_rids) ? + NT_STATUS_OK : STATUS_SOME_UNMAPPED; + done: + TALLOC_FREE(mem_ctx); + return result; +} + +static bool ldapsam_sid_to_id(struct pdb_methods *methods, + const struct dom_sid *sid, + uid_t *uid, gid_t *gid, + enum lsa_SidType *type) +{ + struct ldapsam_privates *priv = + (struct ldapsam_privates *)methods->private_data; + char *filter; + const char *attrs[] = { "objectClass", "gidNumber", "uidNumber", + NULL }; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + bool ret = false; + char *value; + struct berval **values; + size_t c; + int rc; + + TALLOC_CTX *mem_ctx; + + mem_ctx = talloc_new(NULL); + if (mem_ctx == NULL) { + DEBUG(0, ("talloc_new failed\n")); + return false; + } + + filter = talloc_asprintf(mem_ctx, + "(&(%s=%s)" + "(|(objectClass=%s)(objectClass=%s)))", + LDAP_ATTRIBUTE_SID, sid_string_talloc(mem_ctx, sid), + LDAP_OBJ_GROUPMAP, LDAP_OBJ_SAMBASAMACCOUNT); + if (filter == NULL) { + DEBUG(5, ("talloc_asprintf failed\n")); + goto done; + } + + rc = smbldap_search_suffix(priv->smbldap_state, filter, + attrs, &result); + if (rc != LDAP_SUCCESS) { + goto done; + } + talloc_autofree_ldapmsg(mem_ctx, result); + + if (ldap_count_entries(priv2ld(priv), result) != 1) { + DEBUG(10, ("Got %d entries, expected one\n", + ldap_count_entries(priv2ld(priv), result))); + goto done; + } + + entry = ldap_first_entry(priv2ld(priv), result); + + values = ldap_get_values_len(priv2ld(priv), entry, "objectClass"); + if (values == NULL) { + DEBUG(10, ("Cannot find any objectclasses.\n")); + goto done; + } + + for (c = 0; values[c] != NULL; c++) { + if (strncmp(LDAP_OBJ_GROUPMAP, values[c]->bv_val, + values[c]->bv_len) == 0) { + break; + } + } + + if (values[c] != NULL) { + const char *gid_str; + /* It's a group */ + + gid_str = get_single_attribute(mem_ctx, priv2ld(priv), entry, + "gidNumber"); + if (gid_str == NULL) { + DEBUG(1, ("%s has no gidNumber\n", + get_dn(mem_ctx, priv2ld(priv), entry))); + goto done; + } + + *gid = strtoul(gid_str, NULL, 10); + *type = SID_NAME_DOM_GRP; + store_gid_sid_cache(sid, *gid); + ret = true; + goto done; + } + + /* It must be a user */ + + value = get_single_attribute(mem_ctx, priv2ld(priv), entry, + "uidNumber"); + if (value == NULL) { + DEBUG(1, ("Could not find uidNumber in %s\n", + get_dn(mem_ctx, priv2ld(priv), entry))); + goto done; + } + + *uid = strtoul(value, NULL, 10); + *type = SID_NAME_USER; + store_uid_sid_cache(sid, *uid); + + ret = true; + done: + TALLOC_FREE(mem_ctx); + return ret; +} + +static bool ldapsam_uid_to_sid(struct pdb_methods *methods, uid_t uid, + struct dom_sid *sid) +{ + struct ldapsam_privates *priv = + (struct ldapsam_privates *)methods->private_data; + char *filter; + const char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL }; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + bool ret = false; + char *user_sid_string; + struct dom_sid user_sid; + int rc; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + filter = talloc_asprintf(tmp_ctx, + "(&(uidNumber=%u)" + "(objectClass=%s)" + "(objectClass=%s))", + (unsigned int)uid, + LDAP_OBJ_POSIXACCOUNT, + LDAP_OBJ_SAMBASAMACCOUNT); + if (filter == NULL) { + DEBUG(3, ("talloc_asprintf failed\n")); + goto done; + } + + rc = smbldap_search_suffix(priv->smbldap_state, filter, attrs, &result); + if (rc != LDAP_SUCCESS) { + goto done; + } + talloc_autofree_ldapmsg(tmp_ctx, result); + + if (ldap_count_entries(priv2ld(priv), result) != 1) { + DEBUG(3, ("ERROR: Got %d entries for uid %u, expected one\n", + ldap_count_entries(priv2ld(priv), result), + (unsigned int)uid)); + goto done; + } + + entry = ldap_first_entry(priv2ld(priv), result); + + user_sid_string = get_single_attribute(tmp_ctx, priv2ld(priv), entry, + LDAP_ATTRIBUTE_SID); + if (user_sid_string == NULL) { + DEBUG(1, ("Could not find SID in object '%s'\n", + get_dn(tmp_ctx, priv2ld(priv), entry))); + goto done; + } + + if (!string_to_sid(&user_sid, user_sid_string)) { + DEBUG(3, ("Error calling sid_string_talloc for sid '%s'\n", + user_sid_string)); + goto done; + } + + sid_copy(sid, &user_sid); + + store_uid_sid_cache(sid, uid); + + ret = true; + + done: + TALLOC_FREE(tmp_ctx); + return ret; +} + +static bool ldapsam_gid_to_sid(struct pdb_methods *methods, gid_t gid, + struct dom_sid *sid) +{ + struct ldapsam_privates *priv = + (struct ldapsam_privates *)methods->private_data; + char *filter; + const char *attrs[] = { LDAP_ATTRIBUTE_SID, NULL }; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + bool ret = false; + char *group_sid_string; + struct dom_sid group_sid; + int rc; + TALLOC_CTX *tmp_ctx = talloc_stackframe(); + + filter = talloc_asprintf(tmp_ctx, + "(&(gidNumber=%u)" + "(objectClass=%s))", + (unsigned int)gid, + LDAP_OBJ_GROUPMAP); + if (filter == NULL) { + DEBUG(3, ("talloc_asprintf failed\n")); + goto done; + } + + rc = smbldap_search_suffix(priv->smbldap_state, filter, attrs, &result); + if (rc != LDAP_SUCCESS) { + goto done; + } + talloc_autofree_ldapmsg(tmp_ctx, result); + + if (ldap_count_entries(priv2ld(priv), result) != 1) { + DEBUG(3, ("ERROR: Got %d entries for gid %u, expected one\n", + ldap_count_entries(priv2ld(priv), result), + (unsigned int)gid)); + goto done; + } + + entry = ldap_first_entry(priv2ld(priv), result); + + group_sid_string = get_single_attribute(tmp_ctx, priv2ld(priv), entry, + LDAP_ATTRIBUTE_SID); + if (group_sid_string == NULL) { + DEBUG(1, ("Could not find SID in object '%s'\n", + get_dn(tmp_ctx, priv2ld(priv), entry))); + goto done; + } + + if (!string_to_sid(&group_sid, group_sid_string)) { + DEBUG(3, ("Error calling sid_string_talloc for sid '%s'\n", + group_sid_string)); + goto done; + } + + sid_copy(sid, &group_sid); + + store_gid_sid_cache(sid, gid); + + ret = true; + + done: + TALLOC_FREE(tmp_ctx); + return ret; +} + + +static char *get_ldap_filter(TALLOC_CTX *mem_ctx, const char *username) +{ + char *escaped = NULL; + char *result = NULL; + + escaped = escape_ldap_string(mem_ctx, username); + if (escaped == NULL) { + return NULL; + } + + result = talloc_asprintf(mem_ctx, "(&(uid=%s)(objectclass=%s))", + escaped, LDAP_OBJ_SAMBASAMACCOUNT); + + TALLOC_FREE(escaped); + + return result; +} + +static const char **talloc_attrs(TALLOC_CTX *mem_ctx, ...) +{ + int i, num = 0; + va_list ap; + const char **result; + + va_start(ap, mem_ctx); + while (va_arg(ap, const char *) != NULL) + num += 1; + va_end(ap); + + if ((result = talloc_array(mem_ctx, const char *, num+1)) == NULL) { + return NULL; + } + + va_start(ap, mem_ctx); + for (i=0; i<num; i++) { + result[i] = talloc_strdup(result, va_arg(ap, const char*)); + if (result[i] == NULL) { + talloc_free(result); + va_end(ap); + return NULL; + } + } + va_end(ap); + + result[num] = NULL; + return result; +} + + +struct ldap_search_state { + struct smbldap_state *connection; + + uint32_t acct_flags; + uint16_t group_type; + + const char *base; + int scope; + const char *filter; + const char **attrs; + int attrsonly; + void *pagedresults_cookie; + + LDAPMessage *entries, *current_entry; + bool (*ldap2displayentry)(struct ldap_search_state *state, + TALLOC_CTX *mem_ctx, + LDAP *ld, LDAPMessage *entry, + struct samr_displayentry *result); +}; + +static bool ldapsam_search_firstpage(struct pdb_search *search) +{ + struct ldap_search_state *state = + (struct ldap_search_state *)search->private_data; + LDAP *ld; + int rc = LDAP_OPERATIONS_ERROR; + + state->entries = NULL; + + if (state->connection->paged_results) { + rc = smbldap_search_paged(state->connection, state->base, + state->scope, state->filter, + state->attrs, state->attrsonly, + LDAP_PAGE_SIZE, &state->entries, + &state->pagedresults_cookie); + } + + if ((rc != LDAP_SUCCESS) || (state->entries == NULL)) { + + if (state->entries != NULL) { + /* Left over from unsuccessful paged attempt */ + ldap_msgfree(state->entries); + state->entries = NULL; + } + + rc = smbldap_search(state->connection, state->base, + state->scope, state->filter, state->attrs, + state->attrsonly, &state->entries); + + if ((rc != LDAP_SUCCESS) || (state->entries == NULL)) + return false; + + /* Ok, the server was lying. It told us it could do paged + * searches when it could not. */ + state->connection->paged_results = false; + } + + ld = state->connection->ldap_struct; + if ( ld == NULL) { + DEBUG(5, ("Don't have an LDAP connection right after a " + "search\n")); + return false; + } + state->current_entry = ldap_first_entry(ld, state->entries); + + return true; +} + +static bool ldapsam_search_nextpage(struct pdb_search *search) +{ + struct ldap_search_state *state = + (struct ldap_search_state *)search->private_data; + int rc; + + if (!state->connection->paged_results) { + /* There is no next page when there are no paged results */ + return false; + } + + rc = smbldap_search_paged(state->connection, state->base, + state->scope, state->filter, state->attrs, + state->attrsonly, LDAP_PAGE_SIZE, + &state->entries, + &state->pagedresults_cookie); + + if ((rc != LDAP_SUCCESS) || (state->entries == NULL)) + return false; + + state->current_entry = ldap_first_entry(state->connection->ldap_struct, state->entries); + + if (state->current_entry == NULL) { + ldap_msgfree(state->entries); + state->entries = NULL; + return false; + } + + return true; +} + +static bool ldapsam_search_next_entry(struct pdb_search *search, + struct samr_displayentry *entry) +{ + struct ldap_search_state *state = + (struct ldap_search_state *)search->private_data; + bool result; + + retry: + if ((state->entries == NULL) && (state->pagedresults_cookie == NULL)) + return false; + + if ((state->entries == NULL) && + !ldapsam_search_nextpage(search)) + return false; + + if (state->current_entry == NULL) { + return false; + } + + result = state->ldap2displayentry(state, search, + state->connection->ldap_struct, + state->current_entry, entry); + + if (!result) { + char *dn; + dn = ldap_get_dn(state->connection->ldap_struct, + state->current_entry); + DEBUG(5, ("Skipping entry %s\n", dn != NULL ? dn : "<NULL>")); + if (dn != NULL) ldap_memfree(dn); + } + + state->current_entry = ldap_next_entry(state->connection->ldap_struct, + state->current_entry); + + if (state->current_entry == NULL) { + ldap_msgfree(state->entries); + state->entries = NULL; + } + + if (!result) goto retry; + + return true; +} + +static void ldapsam_search_end(struct pdb_search *search) +{ + struct ldap_search_state *state = + (struct ldap_search_state *)search->private_data; + int rc; + + if (state->pagedresults_cookie == NULL) + return; + + if (state->entries != NULL) + ldap_msgfree(state->entries); + + state->entries = NULL; + state->current_entry = NULL; + + if (!state->connection->paged_results) + return; + + /* Tell the LDAP server we're not interested in the rest anymore. */ + + rc = smbldap_search_paged(state->connection, state->base, state->scope, + state->filter, state->attrs, + state->attrsonly, 0, &state->entries, + &state->pagedresults_cookie); + + if (rc != LDAP_SUCCESS) + DEBUG(5, ("Could not end search properly\n")); + + return; +} + +static bool ldapuser2displayentry(struct ldap_search_state *state, + TALLOC_CTX *mem_ctx, + LDAP *ld, LDAPMessage *entry, + struct samr_displayentry *result) +{ + char **vals; + size_t converted_size; + struct dom_sid sid; + +/* FIXME: SB try to figure out which flags to set instead of hardcode them */ + result->acct_flags = 66048; + result->account_name = ""; + result->fullname = ""; + result->description = ""; + + vals = ldap_get_values(ld, entry, "uid"); + if ((vals == NULL) || (vals[0] == NULL)) { + DEBUG(5, ("\"uid\" not found\n")); + return false; + } + if (!pull_utf8_talloc(mem_ctx, + discard_const_p(char *, &result->account_name), + vals[0], &converted_size)) + { + DEBUG(0,("ldapuser2displayentry: pull_utf8_talloc failed: %s", + strerror(errno))); + } + + ldap_value_free(vals); + + vals = ldap_get_values(ld, entry, "displayName"); + if ((vals == NULL) || (vals[0] == NULL)) + DEBUG(8, ("\"displayName\" not found\n")); + else if (!pull_utf8_talloc(mem_ctx, + discard_const_p(char *, &result->fullname), + vals[0], &converted_size)) + { + DEBUG(0,("ldapuser2displayentry: pull_utf8_talloc failed: %s", + strerror(errno))); + } + + ldap_value_free(vals); + + vals = ldap_get_values(ld, entry, "description"); + if ((vals == NULL) || (vals[0] == NULL)) + DEBUG(8, ("\"description\" not found\n")); + else if (!pull_utf8_talloc(mem_ctx, + discard_const_p(char *, &result->description), + vals[0], &converted_size)) + { + DEBUG(0,("ldapuser2displayentry: pull_utf8_talloc failed: %s", + strerror(errno))); + } + + ldap_value_free(vals); + + if ((result->account_name == NULL) || + (result->fullname == NULL) || + (result->description == NULL)) { + DEBUG(0, ("talloc failed\n")); + return false; + } + + vals = ldap_get_values(ld, entry, LDAP_ATTRIBUTE_SID); + if ((vals == NULL) || (vals[0] == NULL)) { + DEBUG(0, ("\"objectSid\" not found\n")); + return false; + } + + if (!string_to_sid(&sid, vals[0])) { + DEBUG(0, ("Could not convert %s to SID\n", vals[0])); + ldap_value_free(vals); + return false; + } + ldap_value_free(vals); + + if (!sid_peek_check_rid(get_global_sam_sid(), &sid, &result->rid)) { + DEBUG(0, ("sid does not belong to our domain\n")); + return false; + } + + return true; +} + +static bool ldapsam_search_users(struct pdb_methods *methods, + struct pdb_search *search, + uint32_t acct_flags) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + struct ldap_search_state *state; + + state = talloc(search, struct ldap_search_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + return false; + } + + state->connection = ldap_state->smbldap_state; + + if ((acct_flags != 0) && ((acct_flags & ACB_NORMAL) != 0)) + state->base = LDAP_SUFFIX; + else if ((acct_flags != 0) && + ((acct_flags & (ACB_WSTRUST|ACB_SVRTRUST|ACB_DOMTRUST)) != 0)) + state->base = LDAP_SUFFIX; + else + state->base = LDAP_SUFFIX; + + state->acct_flags = acct_flags; + state->base = talloc_strdup(search, state->base); + state->scope = LDAP_SCOPE_SUBTREE; + state->filter = get_ldap_filter(search, "*"); + state->attrs = talloc_attrs(search, "uid", LDAP_ATTRIBUTE_SID, + "displayName", "description", + NULL); + state->attrsonly = 0; + state->pagedresults_cookie = NULL; + state->entries = NULL; + state->ldap2displayentry = ldapuser2displayentry; + + if ((state->filter == NULL) || (state->attrs == NULL)) { + DEBUG(0, ("talloc failed\n")); + return false; + } + + search->private_data = state; + search->next_entry = ldapsam_search_next_entry; + search->search_end = ldapsam_search_end; + + return ldapsam_search_firstpage(search); +} + +static bool ldapgroup2displayentry(struct ldap_search_state *state, + TALLOC_CTX *mem_ctx, + LDAP *ld, LDAPMessage *entry, + struct samr_displayentry *result) +{ + char **vals = NULL; + size_t converted_size; + struct dom_sid sid; + uint16_t group_type; + + result->account_name = ""; + result->fullname = ""; + result->description = ""; + + group_type = SID_NAME_DOM_GRP; + + if ((state->group_type != 0) && + ((state->group_type != group_type))) { + ldap_value_free(vals); + return false; + } + + ldap_value_free(vals); + + /* display name is the NT group name */ + + vals = ldap_get_values(ld, entry, "displayName"); + if ((vals == NULL) || (vals[0] == NULL)) { + DEBUG(8, ("\"displayName\" not found\n")); + + /* fallback to the 'cn' attribute */ + vals = ldap_get_values(ld, entry, "cn"); + if ((vals == NULL) || (vals[0] == NULL)) { + DEBUG(5, ("\"cn\" not found\n")); + return false; + } + if (!pull_utf8_talloc(mem_ctx, + discard_const_p(char *, + &result->account_name), + vals[0], &converted_size)) + { + DEBUG(0,("ldapgroup2displayentry: pull_utf8_talloc " + "failed: %s", strerror(errno))); + } + } + else if (!pull_utf8_talloc(mem_ctx, + discard_const_p(char *, + &result->account_name), + vals[0], &converted_size)) + { + DEBUG(0,("ldapgroup2displayentry: pull_utf8_talloc failed: %s", + strerror(errno))); + } + + ldap_value_free(vals); + + vals = ldap_get_values(ld, entry, "description"); + if ((vals == NULL) || (vals[0] == NULL)) + DEBUG(8, ("\"description\" not found\n")); + else if (!pull_utf8_talloc(mem_ctx, + discard_const_p(char *, &result->description), + vals[0], &converted_size)) + { + DEBUG(0,("ldapgroup2displayentry: pull_utf8_talloc failed: %s", + strerror(errno))); + } + ldap_value_free(vals); + + if ((result->account_name == NULL) || + (result->fullname == NULL) || + (result->description == NULL)) { + DEBUG(0, ("talloc failed\n")); + return false; + } + + vals = ldap_get_values(ld, entry, LDAP_ATTRIBUTE_SID); + if ((vals == NULL) || (vals[0] == NULL)) { + DEBUG(0, ("\"objectSid\" not found\n")); + if (vals != NULL) { + ldap_value_free(vals); + } + return false; + } + + if (!string_to_sid(&sid, vals[0])) { + DEBUG(0, ("Could not convert %s to SID\n", vals[0])); + return false; + } + + ldap_value_free(vals); + + switch (group_type) { + case SID_NAME_DOM_GRP: + case SID_NAME_ALIAS: + + if (!sid_peek_check_rid(get_global_sam_sid(), &sid, &result->rid) + && !sid_peek_check_rid(&global_sid_Builtin, &sid, &result->rid)) + { + DEBUG(0, ("SID is not in our domain\n")); + return false; + } + break; + + default: + DEBUG(0,("unknown group type: %d\n", group_type)); + return false; + } + + result->acct_flags = 0; + + return true; +} + +static bool ldapsam_search_grouptype(struct pdb_methods *methods, + struct pdb_search *search, + const struct dom_sid *sid, + enum lsa_SidType type) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + struct ldap_search_state *state; + + state = talloc(search, struct ldap_search_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + return false; + } + + state->connection = ldap_state->smbldap_state; + + state->base = talloc_strdup(search, LDAP_SUFFIX); + state->connection = ldap_state->smbldap_state; + state->scope = LDAP_SCOPE_SUBTREE; + state->filter = talloc_asprintf(search, "(&(objectclass=%s)" + "(%s=%s*))", + LDAP_OBJ_GROUPMAP, + LDAP_ATTRIBUTE_SID, + sid_string_talloc(search, sid)); + state->attrs = talloc_attrs(search, "cn", LDAP_ATTRIBUTE_SID, + "displayName", "description", + NULL); + state->attrsonly = 0; + state->pagedresults_cookie = NULL; + state->entries = NULL; + state->group_type = type; + state->ldap2displayentry = ldapgroup2displayentry; + + if ((state->filter == NULL) || (state->attrs == NULL)) { + DEBUG(0, ("talloc failed\n")); + return false; + } + + search->private_data = state; + search->next_entry = ldapsam_search_next_entry; + search->search_end = ldapsam_search_end; + + return ldapsam_search_firstpage(search); +} + +static bool ldapsam_search_groups(struct pdb_methods *methods, + struct pdb_search *search) +{ + return ldapsam_search_grouptype(methods, search, get_global_sam_sid(), SID_NAME_DOM_GRP); +} + +static bool ldapsam_search_aliases(struct pdb_methods *methods, + struct pdb_search *search, + const struct dom_sid *sid) +{ + return ldapsam_search_groups(methods, search); +} + + + + + + + + + + +static char *trusted_domain_dn(TALLOC_CTX *mem_ctx, + struct ldapsam_privates *ldap_state, + const char *domain) +{ + return talloc_asprintf(mem_ctx, "%s=%s,%s", + LDAP_ATTRIBUTE_CN, domain, + ldap_state->ipasam_privates->trust_dn); +} + +static NTSTATUS ipasam_get_objectclasses(struct ldapsam_privates *ldap_state, + const char *dn, LDAPMessage *entry, + uint32_t *has_objectclass) +{ + struct berval **bervals; + size_t c; + + bervals = ldap_get_values_len(priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_OBJECTCLASS); + if (bervals == NULL) { + DEBUG(0, ("Entry [%s] does not have any objectclasses.\n", dn)); + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + + *has_objectclass = 0; + for (c = 0; bervals[c] != NULL; c++) { + if (strnequal(bervals[c]->bv_val, LDAP_OBJ_KRB_PRINCIPAL, bervals[c]->bv_len)) { + *has_objectclass |= HAS_KRB_PRINCIPAL; + } else if (strnequal(bervals[c]->bv_val, + LDAP_OBJ_KRB_PRINCIPAL_AUX, bervals[c]->bv_len)) { + *has_objectclass |= HAS_KRB_PRINCIPAL_AUX; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_IPAOBJECT, bervals[c]->bv_len)) { + *has_objectclass |= HAS_IPAOBJECT; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_IPAHOST, bervals[c]->bv_len)) { + *has_objectclass |= HAS_IPAHOST; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_POSIXACCOUNT, bervals[c]->bv_len)) { + *has_objectclass |= HAS_POSIXACCOUNT; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_GROUPOFNAMES, bervals[c]->bv_len)) { + *has_objectclass |= HAS_GROUPOFNAMES; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_NESTEDGROUP, bervals[c]->bv_len)) { + *has_objectclass |= HAS_NESTEDGROUP; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_IPAUSERGROUP, bervals[c]->bv_len)) { + *has_objectclass |= HAS_IPAUSERGROUP; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_POSIXGROUP, bervals[c]->bv_len)) { + *has_objectclass |= HAS_POSIXGROUP; + } else if (strnequal(bervals[c]->bv_val, LDAP_OBJ_KRB_TICKET_POLICY_AUX, bervals[c]->bv_len)) { + *has_objectclass |= HAS_KRB_TICKET_POLICY_AUX; + } + } + ldap_value_free_len(bervals); + + return NT_STATUS_OK; +} + +static bool search_krb_princ(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char *princ, const char *base_dn, + LDAPMessage **entry) +{ + int rc; + LDAPMessage *result = NULL; + uint32_t num_result; + char *filter; + + filter = talloc_asprintf(mem_ctx, "%s=%s", + LDAP_ATTRIBUTE_KRB_PRINCIPAL, princ); + if (filter == NULL) { + return false; + } + + rc = smbldap_search(ldap_state->smbldap_state, base_dn, + LDAP_SCOPE_SUBTREE, filter, NULL, 0, &result); + + if (result != NULL) { + talloc_autofree_ldapmsg(mem_ctx, result); + } + + if (rc == LDAP_NO_SUCH_OBJECT) { + *entry = NULL; + return true; + } + + if (rc != LDAP_SUCCESS) { + return false; + } + + num_result = ldap_count_entries(priv2ld(ldap_state), result); + + if (num_result > 1) { + DEBUG(1, ("search_krb_princ: more than one object found " + "with filter '%s'?!\n", filter)); + return false; + } + + if (num_result == 0) { + DEBUG(1, ("get_trusted_domain_int: no object found " + "with filter '%s'.\n", filter)); + *entry = NULL; + } else { + *entry = ldap_first_entry(priv2ld(ldap_state), result); + } + + return true; +} + +static bool set_krb_princ(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char *princ, const char *pwd, + const char *base_dn) +{ + LDAPMessage *entry = NULL; + LDAPMod **mods = NULL; + char *dn = NULL; + int ret; + uint32_t has_objectclass = 0; + NTSTATUS status; + char *inp; + + if (!search_krb_princ(ldap_state, mem_ctx, princ, base_dn, &entry)) { + return false; + } + + if (entry) { + dn = get_dn(mem_ctx, priv2ld(ldap_state), entry); + if (!dn) { + return false; + } + + status = ipasam_get_objectclasses(ldap_state, dn, entry, + &has_objectclass); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + } else { + dn = talloc_asprintf(mem_ctx, "%s=%s,%s", + LDAP_ATTRIBUTE_KRB_PRINCIPAL, princ, + base_dn); + if (!dn) { + return false; + } + } + + if (!(has_objectclass & HAS_KRB_PRINCIPAL)) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, + LDAP_ATTRIBUTE_OBJECTCLASS, + LDAP_OBJ_KRB_PRINCIPAL); + } + + if (!(has_objectclass & HAS_KRB_PRINCIPAL_AUX)) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, + LDAP_ATTRIBUTE_OBJECTCLASS, + LDAP_OBJ_KRB_PRINCIPAL_AUX); + } + + if (!(has_objectclass & HAS_KRB_TICKET_POLICY_AUX)) { + smbldap_set_mod(&mods, LDAP_MOD_ADD, + LDAP_ATTRIBUTE_OBJECTCLASS, + LDAP_OBJ_KRB_TICKET_POLICY_AUX); + } + + smbldap_make_mod(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_KRB_PRINCIPAL, princ); + + if (entry == NULL) { + ret = smbldap_add(ldap_state->smbldap_state, dn, mods); + } else { + ret = smbldap_modify(ldap_state->smbldap_state, dn, mods); + } + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("error writing cross realm principal data!\n")); + return false; + } + + /* TODO: Call the appropriate expo if ipasam is part of the FreeIPA + * source tree */ + inp = talloc_asprintf(mem_ctx, "change_password -pw %s %s", pwd, princ); + if (inp == NULL) { + return false; + } + + uid_t save_uid = current_user.ut.uid; + gid_t save_gid = current_user.ut.gid; + current_user.ut.uid = 0; + current_user.ut.gid = 0; + ret = smbrunsecret("kadmin.local", inp); + current_user.ut.uid = save_uid; + current_user.ut.gid = save_gid; + if (ret != 0) { + DEBUG(1, ("calling kadmin.local failed.\n")); + return false; + } + + return true; +} + +static bool del_krb_princ(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char *princ, const char *base_dn) +{ + LDAPMessage *entry = NULL; + char *dn = NULL; + int ret; + + if (!search_krb_princ(ldap_state, mem_ctx, princ, base_dn, &entry)) { + return false; + } + + if (entry) { + dn = get_dn(mem_ctx, priv2ld(ldap_state), entry); + if (!dn) { + return false; + } + + ret = smbldap_delete(ldap_state->smbldap_state, dn); + if (ret != LDAP_SUCCESS) { + return false; + } + } + + return true; +} + +enum princ_mod { + SET_PRINC, + DEL_PRINC +}; + +static bool handle_cross_realm_princs(struct ldapsam_privates *ldap_state, + const char *domain, const char *pwd, + enum princ_mod mod) +{ + char *trusted_dn; + char *princ_l; + char *princ_r; + char *remote_realm; + bool ok; + TALLOC_CTX *tmp_ctx; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return false; + } + + remote_realm = talloc_strdup_upper(tmp_ctx, domain); + if (remote_realm == NULL) { + ok = false; + goto done; + } + + trusted_dn = trusted_domain_dn(tmp_ctx, ldap_state, domain); + + princ_l = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", remote_realm, + ldap_state->ipasam_privates->realm); + princ_r = talloc_asprintf(tmp_ctx, "krbtgt/%s@%s", + ldap_state->ipasam_privates->realm, remote_realm); + + if (trusted_dn == NULL || princ_l == NULL || princ_r == NULL) { + ok = false; + goto done; + } + + switch (mod) { + case SET_PRINC: + if (!set_krb_princ(ldap_state, tmp_ctx, princ_l, pwd, + trusted_dn) || + !set_krb_princ(ldap_state, tmp_ctx, princ_r, pwd, + trusted_dn)) { + ok = false; + goto done; + } + break; + case DEL_PRINC: + if (!del_krb_princ(ldap_state, tmp_ctx, princ_l, + trusted_dn) || + !del_krb_princ(ldap_state, tmp_ctx, princ_r, + trusted_dn)) { + ok = false; + goto done; + } + break; + default: + DEBUG(1, ("unknown operation.\n")); + ok = false; + goto done; + } + + ok = true; +done: + talloc_free(tmp_ctx); + return ok; +} + +static bool set_cross_realm_princs(struct ldapsam_privates *ldap_state, + const char *domain, const char *pwd) +{ + return handle_cross_realm_princs(ldap_state, domain, pwd, SET_PRINC); +} + +static bool del_cross_realm_princs(struct ldapsam_privates *ldap_state, + const char *domain) +{ + return handle_cross_realm_princs(ldap_state, domain, NULL, DEL_PRINC); +} + +static bool get_trusted_domain_int(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char *filter, LDAPMessage **entry) +{ + int rc; + LDAPMessage *result = NULL; + uint32_t num_result; + + rc = smbldap_search(ldap_state->smbldap_state, + ldap_state->ipasam_privates->trust_dn, + LDAP_SCOPE_SUBTREE, filter, NULL, 0, &result); + + if (result != NULL) { + talloc_autofree_ldapmsg(mem_ctx, result); + } + + if (rc == LDAP_NO_SUCH_OBJECT) { + *entry = NULL; + return true; + } + + if (rc != LDAP_SUCCESS) { + return false; + } + + num_result = ldap_count_entries(priv2ld(ldap_state), result); + + if (num_result > 1) { + DEBUG(1, ("get_trusted_domain_int: more than one " + "%s object with filter '%s'?!\n", + LDAP_OBJ_TRUSTED_DOMAIN, filter)); + return false; + } + + if (num_result == 0) { + DEBUG(1, ("get_trusted_domain_int: no " + "%s object with filter '%s'.\n", + LDAP_OBJ_TRUSTED_DOMAIN, filter)); + *entry = NULL; + } else { + *entry = ldap_first_entry(priv2ld(ldap_state), result); + } + + return true; +} + +static bool get_trusted_domain_by_name_int(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char *domain, + LDAPMessage **entry) +{ + char *filter = NULL; + bool ok; + + filter = talloc_asprintf(mem_ctx, + "(&(objectClass=%s)(|(%s=%s)(%s=%s)(cn=%s)))", + LDAP_OBJ_TRUSTED_DOMAIN, + LDAP_ATTRIBUTE_FLAT_NAME, domain, + LDAP_ATTRIBUTE_TRUST_PARTNER, domain, domain); + if (filter == NULL) { + return false; + } + + ok = get_trusted_domain_int(ldap_state, mem_ctx, filter, entry); + talloc_free(filter); + + return ok; +} + +static bool get_trusted_domain_by_sid_int(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char *sid, LDAPMessage **entry) +{ + char *filter = NULL; + bool ok; + + filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(%s=%s))", + LDAP_OBJ_TRUSTED_DOMAIN, + LDAP_ATTRIBUTE_SECURITY_IDENTIFIER, sid); + if (filter == NULL) { + return false; + } + + ok = get_trusted_domain_int(ldap_state, mem_ctx, filter, entry); + talloc_free(filter); + + return ok; +} + +static bool get_uint32_t_from_ldap_msg(struct ldapsam_privates *ldap_state, + LDAPMessage *entry, + const char *attr, + uint32_t *val) +{ + char *dummy; + long int l; + char *endptr; + + dummy = get_single_attribute(NULL, priv2ld(ldap_state), entry, attr); + if (dummy == NULL) { + DEBUG(9, ("Attribute %s not present.\n", attr)); + *val = 0; + return true; + } + + l = strtoul(dummy, &endptr, 10); + TALLOC_FREE(dummy); + + if (l < 0 || l > UINT32_MAX || *endptr != '\0') { + return false; + } + + *val = l; + + return true; +} + +static bool fill_pdb_trusted_domain(TALLOC_CTX *mem_ctx, + struct ldapsam_privates *ldap_state, + LDAPMessage *entry, + struct pdb_trusted_domain **_td) +{ + char *dummy; + bool res; + struct pdb_trusted_domain *td; + + if (entry == NULL) { + return false; + } + + td = talloc_zero(mem_ctx, struct pdb_trusted_domain); + if (td == NULL) { + return false; + } + + /* All attributes are MAY */ + + dummy = get_single_attribute(NULL, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_SECURITY_IDENTIFIER); + if (dummy == NULL) { + DEBUG(9, ("Attribute %s not present.\n", + LDAP_ATTRIBUTE_SECURITY_IDENTIFIER)); + ZERO_STRUCT(td->security_identifier); + } else { + res = string_to_sid(&td->security_identifier, dummy); + TALLOC_FREE(dummy); + if (!res) { + return false; + } + } + + if (!smbldap_talloc_single_blob(td, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_TRUST_AUTH_INCOMING, + &td->trust_auth_incoming)) { + DEBUG(9, ("Failed to set incoming auth info.\n")); + } + + + if (!smbldap_talloc_single_blob(td, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_TRUST_AUTH_OUTGOING, + &td->trust_auth_outgoing)) { + DEBUG(9, ("Failed to set outgoing auth info.\n")); + } + + td->netbios_name = get_single_attribute(td, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_FLAT_NAME); + if (td->netbios_name == NULL) { + DEBUG(9, ("Attribute %s not present.\n", + LDAP_ATTRIBUTE_FLAT_NAME)); + } + + td->domain_name = get_single_attribute(td, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_TRUST_PARTNER); + if (td->domain_name == NULL) { + DEBUG(9, ("Attribute %s not present.\n", + LDAP_ATTRIBUTE_TRUST_PARTNER)); + } + + res = get_uint32_t_from_ldap_msg(ldap_state, entry, + LDAP_ATTRIBUTE_TRUST_DIRECTION, + &td->trust_direction); + if (!res) { + return false; + } + + res = get_uint32_t_from_ldap_msg(ldap_state, entry, + LDAP_ATTRIBUTE_TRUST_ATTRIBUTES, + &td->trust_attributes); + if (!res) { + return false; + } + + res = get_uint32_t_from_ldap_msg(ldap_state, entry, + LDAP_ATTRIBUTE_TRUST_TYPE, + &td->trust_type); + if (!res) { + return false; + } + + td->trust_posix_offset = talloc_zero(td, uint32_t); + if (td->trust_posix_offset == NULL) { + return false; + } + res = get_uint32_t_from_ldap_msg(ldap_state, entry, + LDAP_ATTRIBUTE_TRUST_POSIX_OFFSET, + td->trust_posix_offset); + if (!res) { + return false; + } + + td->supported_enc_type = talloc_zero(td, uint32_t); + if (td->supported_enc_type == NULL) { + return false; + } + res = get_uint32_t_from_ldap_msg(ldap_state, entry, + LDAP_ATTRIBUTE_SUPPORTED_ENC_TYPE, + td->supported_enc_type); + if (!res) { + return false; + } + if (*td->supported_enc_type == 0) { + *td->supported_enc_type = KERB_ENCTYPE_DES_CBC_CRC | + KERB_ENCTYPE_DES_CBC_MD5 | + KERB_ENCTYPE_RC4_HMAC_MD5 | + KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96 | + KERB_ENCTYPE_AES256_CTS_HMAC_SHA1_96; + } + + if (!smbldap_talloc_single_blob(td, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_TRUST_FOREST_TRUST_INFO, + &td->trust_forest_trust_info)) { + DEBUG(9, ("Failed to set forest trust info.\n")); + } + + *_td = td; + + return true; +} + +static NTSTATUS ipasam_get_trusted_domain(struct pdb_methods *methods, + TALLOC_CTX *mem_ctx, + const char *domain, + struct pdb_trusted_domain **td) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + LDAPMessage *entry = NULL; + + DEBUG(10, ("ipasam_get_trusted_domain called for domain %s\n", domain)); + + if (!get_trusted_domain_by_name_int(ldap_state, mem_ctx, domain, + &entry)) { + return NT_STATUS_UNSUCCESSFUL; + } + if (entry == NULL) { + DEBUG(5, ("ipasam_get_trusted_domain: no such trusted domain: " + "%s\n", domain)); + return NT_STATUS_NO_SUCH_DOMAIN; + } + + if (!fill_pdb_trusted_domain(mem_ctx, ldap_state, entry, td)) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +static NTSTATUS ipasam_get_trusted_domain_by_sid(struct pdb_methods *methods, + TALLOC_CTX *mem_ctx, + struct dom_sid *sid, + struct pdb_trusted_domain **td) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + LDAPMessage *entry = NULL; + char *sid_str; + bool ok; + + sid_str = sid_string_talloc(mem_ctx, sid); + if (sid_str == NULL) { + return NT_STATUS_NO_MEMORY; + } + + DEBUG(10, ("ipasam_get_trusted_domain_by_sid called for sid %s\n", + sid_str)); + + ok = get_trusted_domain_by_sid_int(ldap_state, mem_ctx, sid_str, + &entry); + talloc_free(sid_str); + if (!ok) { + return NT_STATUS_UNSUCCESSFUL; + } + if (entry == NULL) { + DEBUG(5, ("ipasam_get_trusted_domain_by_sid: no trusted domain " + "with sid: %s\n", sid_str)); + return NT_STATUS_NO_SUCH_DOMAIN; + } + + ok = fill_pdb_trusted_domain(mem_ctx, ldap_state, entry, td); + if (!ok) { + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} + +static bool smbldap_make_mod_uint32_t(LDAP *ldap_struct, LDAPMessage *entry, + LDAPMod ***mods, const char *attribute, + const uint32_t val) +{ + char *dummy; + + dummy = talloc_asprintf(NULL, "%lu", (unsigned long) val); + if (dummy == NULL) { + return false; + } + smbldap_make_mod(ldap_struct, entry, mods, attribute, dummy); + TALLOC_FREE(dummy); + + return true; +} + +static NTSTATUS get_trust_pwd(TALLOC_CTX *mem_ctx, const DATA_BLOB *auth_blob, + char **pwd, NTTIME *last_update) +{ + NTSTATUS status; + struct trustAuthInOutBlob iopw; + enum ndr_err_code ndr_err; + TALLOC_CTX *tmp_ctx; + char *trustpw; + size_t converted_size; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + ndr_err = ndr_pull_struct_blob(auth_blob, tmp_ctx, &iopw, + (ndr_pull_flags_fn_t)ndr_pull_trustAuthInOutBlob); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + if (iopw.count != 0 && iopw.current.count != 0 && + iopw.current.array[0].AuthType == TRUST_AUTH_TYPE_CLEAR) { + if (pwd != NULL) { + if (!convert_string_talloc(tmp_ctx, CH_UTF16, CH_UNIX, + iopw.current.array[0].AuthInfo.clear.password, + iopw.current.array[0].AuthInfo.clear.size, + &trustpw, &converted_size)) { + + status = NT_STATUS_NO_MEMORY; + goto done; + } + + *pwd = talloc_strndup(mem_ctx, trustpw, converted_size); + if (*pwd == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + } + + if (last_update != NULL) { + *last_update = iopw.current.array[0].LastUpdateTime; + } + } else { + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + status = NT_STATUS_OK; + +done: + talloc_free(tmp_ctx); + return status; +} + +static NTSTATUS ipasam_set_trusted_domain(struct pdb_methods *methods, + const char* domain, + const struct pdb_trusted_domain *td) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + LDAPMessage *entry = NULL; + LDAPMod **mods; + bool res; + char *trusted_dn = NULL; + int ret; + NTSTATUS status; + TALLOC_CTX *tmp_ctx; + char *trustpw; + + DEBUG(10, ("ipasam_set_trusted_domain called for domain %s\n", domain)); + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + res = get_trusted_domain_by_name_int(ldap_state, tmp_ctx, domain, + &entry); + if (!res) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + mods = NULL; + smbldap_make_mod(priv2ld(ldap_state), entry, &mods, "objectClass", + LDAP_OBJ_TRUSTED_DOMAIN); + + if (td->netbios_name != NULL) { + smbldap_make_mod(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_FLAT_NAME, + td->netbios_name); + } + + if (td->domain_name != NULL) { + smbldap_make_mod(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_TRUST_PARTNER, + td->domain_name); + } + + if (!is_null_sid(&td->security_identifier)) { + smbldap_make_mod(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_SECURITY_IDENTIFIER, + sid_string_talloc(tmp_ctx, &td->security_identifier)); + } + + if (td->trust_type != 0) { + res = smbldap_make_mod_uint32_t(priv2ld(ldap_state), entry, + &mods, LDAP_ATTRIBUTE_TRUST_TYPE, + td->trust_type); + if (!res) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + if (td->trust_attributes != 0) { + res = smbldap_make_mod_uint32_t(priv2ld(ldap_state), entry, + &mods, + LDAP_ATTRIBUTE_TRUST_ATTRIBUTES, + td->trust_attributes); + if (!res) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + if (td->trust_direction != 0) { + res = smbldap_make_mod_uint32_t(priv2ld(ldap_state), entry, + &mods, + LDAP_ATTRIBUTE_TRUST_DIRECTION, + td->trust_direction); + if (!res) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + if (td->trust_posix_offset != NULL) { + res = smbldap_make_mod_uint32_t(priv2ld(ldap_state), entry, + &mods, + LDAP_ATTRIBUTE_TRUST_POSIX_OFFSET, + *td->trust_posix_offset); + if (!res) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + if (td->supported_enc_type != NULL) { + res = smbldap_make_mod_uint32_t(priv2ld(ldap_state), entry, + &mods, + LDAP_ATTRIBUTE_SUPPORTED_ENC_TYPE, + *td->supported_enc_type); + if (!res) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + if (td->trust_auth_outgoing.data != NULL) { + smbldap_make_mod_blob(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_TRUST_AUTH_OUTGOING, + &td->trust_auth_outgoing); + } + + if (td->trust_auth_incoming.data != NULL) { + smbldap_make_mod_blob(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_TRUST_AUTH_INCOMING, + &td->trust_auth_incoming); + } + + if (td->trust_forest_trust_info.data != NULL) { + smbldap_make_mod_blob(priv2ld(ldap_state), entry, &mods, + LDAP_ATTRIBUTE_TRUST_FOREST_TRUST_INFO, + &td->trust_forest_trust_info); + } + + talloc_autofree_ldapmod(tmp_ctx, mods); + + trusted_dn = trusted_domain_dn(tmp_ctx, ldap_state, domain); + if (trusted_dn == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + if (entry == NULL) { + ret = smbldap_add(ldap_state->smbldap_state, trusted_dn, mods); + } else { + ret = smbldap_modify(ldap_state->smbldap_state, trusted_dn, mods); + } + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("error writing trusted domain data!\n")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (entry == NULL) { /* FIXME: allow password updates here */ + status = get_trust_pwd(tmp_ctx, &td->trust_auth_incoming, + &trustpw, NULL); + if (!NT_STATUS_IS_OK(status)) { + goto done; + } + res = set_cross_realm_princs(ldap_state, td->domain_name, + trustpw); + memset(trustpw, 0, strlen(trustpw)); + if (!res) { + DEBUG(1, ("error writing cross realm principals!\n")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + } + + status = NT_STATUS_OK; +done: + talloc_free(tmp_ctx); + return status; +} + +static NTSTATUS ipasam_del_trusted_domain(struct pdb_methods *methods, + const char *domain) +{ + int ret; + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + LDAPMessage *entry = NULL; + const char *dn; + const char *domain_name; + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (!get_trusted_domain_by_name_int(ldap_state, tmp_ctx, domain, + &entry)) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (entry == NULL) { + DEBUG(5, ("ipasam_del_trusted_domain: no such trusted domain: " + "%s\n", domain)); + status = NT_STATUS_NO_SUCH_DOMAIN; + goto done; + } + + dn = get_dn(tmp_ctx, priv2ld(ldap_state), entry); + if (dn == NULL) { + DEBUG(0,("ipasam_del_trusted_domain: Out of memory!\n")); + status = NT_STATUS_NO_MEMORY; + goto done; + } + + domain_name = get_single_attribute(tmp_ctx, priv2ld(ldap_state), entry, + LDAP_ATTRIBUTE_TRUST_PARTNER); + if (domain_name == NULL) { + DEBUG(1, ("Attribute %s not present.\n", + LDAP_ATTRIBUTE_TRUST_PARTNER)); + status = NT_STATUS_INVALID_PARAMETER; + goto done; + } + + if (!del_cross_realm_princs(ldap_state, domain_name)) { + DEBUG(1, ("error deleting cross realm principals!\n")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + ret = smbldap_delete(ldap_state->smbldap_state, dn); + if (ret != LDAP_SUCCESS) { + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + status = NT_STATUS_OK; +done: + talloc_free(tmp_ctx); + return status; +} + +static NTSTATUS ipasam_enum_trusted_domains(struct pdb_methods *methods, + TALLOC_CTX *mem_ctx, + uint32_t *num_domains, + struct pdb_trusted_domain ***domains) +{ + int rc; + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)methods->private_data; + char *filter = NULL; + int scope = LDAP_SCOPE_SUBTREE; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + + filter = talloc_asprintf(mem_ctx, "(objectClass=%s)", + LDAP_OBJ_TRUSTED_DOMAIN); + if (filter == NULL) { + return NT_STATUS_NO_MEMORY; + } + + rc = smbldap_search(ldap_state->smbldap_state, + ldap_state->ipasam_privates->trust_dn, + scope, filter, NULL, 0, &result); + TALLOC_FREE(filter); + + if (result != NULL) { + talloc_autofree_ldapmsg(mem_ctx, result); + } + + if (rc == LDAP_NO_SUCH_OBJECT) { + *num_domains = 0; + *domains = NULL; + return NT_STATUS_OK; + } + + if (rc != LDAP_SUCCESS) { + return NT_STATUS_UNSUCCESSFUL; + } + + *num_domains = 0; + if (!(*domains = talloc_array(mem_ctx, struct pdb_trusted_domain *, 1))) { + DEBUG(1, ("talloc failed\n")); + return NT_STATUS_NO_MEMORY; + } + + for (entry = ldap_first_entry(priv2ld(ldap_state), result); + entry != NULL; + entry = ldap_next_entry(priv2ld(ldap_state), entry)) + { + struct pdb_trusted_domain *dom_info; + + if (!fill_pdb_trusted_domain(*domains, ldap_state, entry, + &dom_info)) { + return NT_STATUS_UNSUCCESSFUL; + } + + ADD_TO_ARRAY(*domains, struct pdb_trusted_domain *, dom_info, + domains, num_domains); + + if (*domains == NULL) { + DEBUG(1, ("talloc failed\n")); + return NT_STATUS_NO_MEMORY; + } + } + + DEBUG(5, ("ipasam_enum_trusted_domains: got %d domains\n", *num_domains)); + return NT_STATUS_OK; +} + +static NTSTATUS ipasam_enum_trusteddoms(struct pdb_methods *methods, + TALLOC_CTX *mem_ctx, + uint32_t *num_domains, + struct trustdom_info ***domains) +{ + NTSTATUS status; + struct pdb_trusted_domain **td; + int i; + + status = ipasam_enum_trusted_domains(methods, mem_ctx, + num_domains, &td); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (*num_domains == 0) { + return NT_STATUS_OK; + } + + if (!(*domains = talloc_array(mem_ctx, struct trustdom_info *, + *num_domains))) { + DEBUG(1, ("talloc failed\n")); + goto fail; + } + + for (i = 0; i < *num_domains; i++) { + struct trustdom_info *dom_info; + + dom_info = talloc(*domains, struct trustdom_info); + if (dom_info == NULL) { + DEBUG(1, ("talloc failed\n")); + goto fail; + } + + dom_info->name = talloc_steal(mem_ctx, td[i]->netbios_name); + sid_copy(&dom_info->sid, &td[i]->security_identifier); + + (*domains)[i] = dom_info; + } + + return NT_STATUS_OK; + +fail: + talloc_free(td); + talloc_free(*domains); + + return NT_STATUS_NO_MEMORY; +} + +static uint32_t pdb_ipasam_capabilities(struct pdb_methods *methods) +{ + return PDB_CAP_STORE_RIDS | PDB_CAP_ADS | PDB_CAP_TRUSTED_DOMAINS_EX; +} + +static bool init_sam_from_td(struct samu *user, struct pdb_trusted_domain *td, + struct ldapsam_privates *ldap_state) +{ + NTSTATUS status; + struct dom_sid u_sid; + char *name; + uint8_t smblmpwd[LM_HASH_LEN]; + uint8_t smbntpwd[NT_HASH_LEN]; + char *trustpw; + + if (!pdb_set_acct_ctrl(user, ACB_DOMTRUST | ACB_TRUSTED_FOR_DELEGATION, + PDB_SET)) { + return false; + } + + if (!pdb_set_domain(user, ldap_state->domain_name, PDB_DEFAULT)) { + return false; + } + + name = talloc_asprintf(user, "%s$", td->netbios_name); + if (name == NULL) { + return false; + } + + if (!pdb_set_username(user, name, PDB_SET)) { + return false; + } + + if (!pdb_set_nt_username(user, name, PDB_SET)) { + return false; + } + + /* FIXME: create a proper SID here */ + if (!sid_compose(&u_sid, &ldap_state->domain_sid, 6789)) { + return false; + } + + if (!pdb_set_user_sid(user, &u_sid, PDB_SET)) { + return false; + } + + status = get_trust_pwd(user, &td->trust_auth_incoming, &trustpw, NULL); + if (!NT_STATUS_IS_OK(status)) { + return false; + } + nt_lm_owf_gen(trustpw, smbntpwd, smblmpwd); + memset(trustpw, 0, strlen(trustpw)); + talloc_free(trustpw); + if (!pdb_set_lanman_passwd(user, smblmpwd, PDB_SET)) { + return false; + } + if (!pdb_set_nt_passwd(user, smbntpwd, PDB_SET)) { + return false; + } + + return true; +} + +static bool init_sam_from_ldap(struct ldapsam_privates *ldap_state, + struct samu * sampass, + LDAPMessage * entry) +{ + char *username = NULL; + char *domain = NULL; + char *nt_username = NULL; + char *fullname = NULL; + char *homedir = NULL; + char *dir_drive = NULL; + char *logon_script = NULL; + char *profile_path = NULL; + char *temp = NULL; + bool ret = false; + DATA_BLOB nthash; + + TALLOC_CTX *tmp_ctx = talloc_init("init_sam_from_ldap"); + if (!tmp_ctx) { + return false; + } + if (sampass == NULL || ldap_state == NULL || entry == NULL) { + DEBUG(0, ("init_sam_from_ldap: NULL parameters found!\n")); + goto fn_exit; + } + + if (priv2ld(ldap_state) == NULL) { + DEBUG(0, ("init_sam_from_ldap: ldap_state->smbldap_state->" + "ldap_struct is NULL!\n")); + goto fn_exit; + } + + if (!(username = smbldap_talloc_first_attribute(priv2ld(ldap_state), + entry, LDAP_ATTRIBUTE_UID, tmp_ctx))) { + DEBUG(1, ("init_sam_from_ldap: No uid attribute found for " + "this user!\n")); + goto fn_exit; + } + + DEBUG(2, ("init_sam_from_ldap: Entry found for user: %s\n", username)); + + nt_username = talloc_strdup(tmp_ctx, username); + if (!nt_username) { + goto fn_exit; + } + + domain = talloc_strdup(tmp_ctx, ldap_state->domain_name); + if (!domain) { + goto fn_exit; + } + + pdb_set_username(sampass, username, PDB_SET); + + pdb_set_domain(sampass, domain, PDB_DEFAULT); + pdb_set_nt_username(sampass, nt_username, PDB_SET); + + if ((temp = smbldap_talloc_single_attribute( + ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_SECURITY_IDENTIFIER, + tmp_ctx)) != NULL) { + pdb_set_user_sid_from_string(sampass, temp, PDB_SET); + } else { + goto fn_exit; + } + + fullname = smbldap_talloc_single_attribute( + ldap_state->smbldap_state->ldap_struct, + entry, + LDAP_ATTRIBUTE_CN, + tmp_ctx); + if (fullname) { + pdb_set_fullname(sampass, fullname, PDB_SET); + } + + dir_drive = smbldap_talloc_single_attribute( + ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_HOME_DRIVE, tmp_ctx); + if (dir_drive) { + pdb_set_dir_drive(sampass, dir_drive, PDB_SET); + } + + homedir = smbldap_talloc_single_attribute( + ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_HOME_PATH, tmp_ctx); + if (homedir) { + pdb_set_homedir(sampass, homedir, PDB_SET); + } + + logon_script = smbldap_talloc_single_attribute( + ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_LOGON_SCRIPT, tmp_ctx); + if (logon_script) { + pdb_set_logon_script(sampass, logon_script, PDB_SET); + } + + profile_path = smbldap_talloc_single_attribute( + ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_PROFILE_PATH, tmp_ctx); + if (profile_path) { + pdb_set_profile_path(sampass, profile_path, PDB_SET); + } + + + pdb_set_acct_ctrl(sampass, ACB_NORMAL, PDB_SET); + + if (!smbldap_talloc_single_blob(tmp_ctx, + ldap_state->smbldap_state->ldap_struct, + entry, LDAP_ATTRIBUTE_NTHASH, + &nthash)) { + DEBUG(5, ("Failed to read NT hash form LDAP response.\n")); + } + if (nthash.length != NT_HASH_LEN && nthash.length != 0) { + DEBUG(5, ("NT hash from LDAP has the wrong size.\n")); + } else { + if (!pdb_set_nt_passwd(sampass, nthash.data, PDB_SET)) { + DEBUG(5, ("Failed to set NT hash.\n")); + } + } +/* FIXME: */ + if (!pdb_set_pass_last_set_time(sampass, (time_t) 1, PDB_SET)) { + DEBUG(5, ("Failed to set last time set.\n")); + } + + ret = true; + +fn_exit: + + talloc_free(tmp_ctx); + return ret; +} + +static NTSTATUS getsam_interdom_trust_account(struct pdb_methods *methods, + struct samu *user, + const char *sname) +{ + char *dom_name; + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *) methods->private_data; + int slen; + TALLOC_CTX *tmp_ctx; + struct pdb_trusted_domain *td; + NTSTATUS status; + + slen = strlen(sname); + if (sname[slen - 1] != '.') { + DEBUG(5, ("Requested account [%s] is not a inter domain " + "trust account.\n", sname)); + return NT_STATUS_NO_SUCH_USER; + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + dom_name = talloc_strdup(tmp_ctx, sname); + if (dom_name == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + dom_name[slen - 1] = '\0'; + + status = ipasam_get_trusted_domain(methods, tmp_ctx, dom_name, &td); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(5, ("ipasam_get_trusted_domain failed.\n")); + goto done; + } + + if (!init_sam_from_td(user, td, ldap_state)) { + DEBUG(5, ("init_sam_from_td failed.\n")); + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + status = NT_STATUS_OK; + +done: + talloc_free(tmp_ctx); + return status; +} +static NTSTATUS ldapsam_getsampwnam(struct pdb_methods *methods, + struct samu *user, + const char *sname) +{ + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *) methods->private_data; + int slen; + TALLOC_CTX *tmp_ctx; + NTSTATUS status; + char *filter; + char *escaped_user; + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + int ret; + int count; + + slen = strlen(sname); + if (sname[slen - 1] == '.') { + return getsam_interdom_trust_account(methods, user, sname); + } + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + escaped_user = escape_ldap_string(tmp_ctx, sname); + if (escaped_user == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + filter = talloc_asprintf(tmp_ctx, "(&(%s=%s)(%s=%s))", + LDAP_ATTRIBUTE_OBJECTCLASS, + LDAP_OBJ_SAMBASAMACCOUNT, + LDAP_ATTRIBUTE_UID, escaped_user); + if (filter == NULL) { + status = NT_STATUS_NO_MEMORY; + goto done; + } + + ret = smbldap_search(ldap_state->smbldap_state, + ldap_state->ipasam_privates->base_dn, + LDAP_SCOPE_SUBTREE,filter, NULL, 0, + &result); + if (ret != LDAP_SUCCESS) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + count = ldap_count_entries(ldap_state->smbldap_state->ldap_struct, + result); + if (count != 1) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, result); + if (entry == NULL) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + if (!init_sam_from_ldap(ldap_state, user, entry)) { + status = NT_STATUS_NO_SUCH_USER; + goto done; + } + + status = NT_STATUS_OK; + +done: + ldap_msgfree(result); + talloc_free(tmp_ctx); + return status; +} + +static bool ipasam_get_trusteddom_pw(struct pdb_methods *methods, + const char *domain, + char** pwd, + struct dom_sid *sid, + time_t *pass_last_set_time) +{ + NTSTATUS status; + TALLOC_CTX *tmp_ctx; + struct pdb_trusted_domain *td; + bool ret = false; + char *trustpw; + NTTIME last_update; + + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + return false; + } + + status = ipasam_get_trusted_domain(methods, tmp_ctx, domain, &td); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + + /* trusteddom_pw routines do not use talloc yet... */ + if (pwd != NULL) { + status = get_trust_pwd(tmp_ctx, &td->trust_auth_incoming, + &trustpw, &last_update); + if (!NT_STATUS_IS_OK(status)) { + ret = false; + goto done; + } + *pwd = strdup(trustpw); + memset(trustpw, 0, strlen(trustpw)); + talloc_free(trustpw); + if (*pwd == NULL) { + ret =false; + goto done; + } + } + + if (pass_last_set_time != NULL) { + *pass_last_set_time = nt_time_to_unix(last_update); + } + + if (sid != NULL) { + sid_copy(sid, &td->security_identifier); + } + + ret = true; +done: + talloc_free(tmp_ctx); + return ret; +} + +static bool ipasam_set_trusteddom_pw(struct pdb_methods *methods, + const char* domain, + const char* pwd, + const struct dom_sid *sid) +{ + return false; +} + +static bool ipasam_del_trusteddom_pw(struct pdb_methods *methods, + const char *domain) +{ + return false; +} + +static struct pdb_domain_info *pdb_ipasam_get_domain_info(struct pdb_methods *pdb_methods, + TALLOC_CTX *mem_ctx) +{ + struct pdb_domain_info *info; + struct ldapsam_privates *ldap_state = + (struct ldapsam_privates *)pdb_methods->private_data; + char sid_buf[24]; + DATA_BLOB sid_blob; + NTSTATUS status; + + info = talloc(mem_ctx, struct pdb_domain_info); + if (info == NULL) { + DEBUG(1, ("talloc failed\n")); + return NULL; + } + + info->name = talloc_strdup(info, ldap_state->ipasam_privates->flat_name); + if (info->name == NULL) { + DEBUG(1, ("talloc_strdup domain_name failed\n")); + goto fail; + } + + /* TODO: read dns_domain, dns_forest and guid from LDAP */ + info->dns_domain = talloc_strdup(info, ldap_state->ipasam_privates->realm); + if (info->dns_domain == NULL) { + goto fail; + } + strlower_m(info->dns_domain); + info->dns_forest = talloc_strdup(info, info->dns_domain); + + /* we expect a domain SID to have 4 sub IDs */ + if (ldap_state->domain_sid.num_auths != 4) { + goto fail; + } + + sid_copy(&info->sid, &ldap_state->domain_sid); + + if (!sid_linearize(sid_buf, sizeof(sid_buf), &info->sid)) { + goto fail; + } + + /* the first 8 bytes of the linearized SID are not random, + * so we skip them */ + sid_blob.data = (uint8_t *) sid_buf + 8 ; + sid_blob.length = 16; + + status = GUID_from_ndr_blob(&sid_blob, &info->guid); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + return info; + +fail: + TALLOC_FREE(info); + return NULL; +} + +static void ipasam_free_private_data(void **vp) +{ + struct ldapsam_privates **ldap_state = (struct ldapsam_privates **)vp; + + smbldap_free_struct(&(*ldap_state)->smbldap_state); + + if ((*ldap_state)->result != NULL) { + ldap_msgfree((*ldap_state)->result); + (*ldap_state)->result = NULL; + } + if ((*ldap_state)->domain_dn != NULL) { + SAFE_FREE((*ldap_state)->domain_dn); + } + + *ldap_state = NULL; + + /* No need to free any further, as it is talloc()ed */ +} + +static NTSTATUS ipasam_search_domain_info(struct smbldap_state *ldap_state, + LDAPMessage ** result) +{ + const char *filter = "objectClass=ipaNTDomainAttrs"; + const char *attr_list[] = { + LDAP_ATTRIBUTE_FLAT_NAME, + LDAP_ATTRIBUTE_SID, + LDAP_ATTRIBUTE_FALLBACK_PRIMARY_GROUP, + LDAP_ATTRIBUTE_OBJECTCLASS, + NULL}; + int count; + int ret; + + ret = smbldap_search_suffix(ldap_state, filter, attr_list , result); + + if (ret != LDAP_SUCCESS) { + DEBUG(2,("ipasam_search_domain_info: " + "smbldap_search_suffix failed: %s\n", + ldap_err2string (ret))); + DEBUG(2,("ipasam_search_domain_info: Query was: %s\n", filter)); + return NT_STATUS_UNSUCCESSFUL; + } + + count = ldap_count_entries(ldap_state->ldap_struct, *result); + + if (count == 1) { + return NT_STATUS_OK; + } + + DEBUG(0, ("iapsam_search_domain_info: Got [%d] domain info entries, " + "but expected only 1.\n", count)); + + return NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS ipasam_get_base_dn(struct smbldap_state *smbldap_state, + TALLOC_CTX *mem_ctx, char **base_dn) +{ + int ret; + LDAPMessage *result; + LDAPMessage *entry = NULL; + int count; + char *nc; + const char *attr_list[] = { + "namingContexts", + "defaultNamingContext", + NULL + }; + + ret = smbldap_search(smbldap_state, "", LDAP_SCOPE_BASE, + "(objectclass=*)", attr_list, 0, &result); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("Failed to get base DN from RootDSE: %s\n", + ldap_err2string (ret))); + return NT_STATUS_UNSUCCESSFUL; + } + + count = ldap_count_entries(smbldap_state->ldap_struct, result); + + if (count != 1) { + DEBUG(1, ("Unexpected number of results [%d] for base DN " + "search.\n", count)); + ldap_msgfree(result); + return NT_STATUS_OK; + } + + entry = ldap_first_entry(smbldap_state->ldap_struct, + result); + if (entry == NULL) { + DEBUG(0, ("Could not get RootDSE entry\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + nc = get_single_attribute(mem_ctx, smbldap_state->ldap_struct, entry, + "defaultNamingContext"); + if (nc != NULL) { + *base_dn = nc; + ldap_msgfree(result); + return NT_STATUS_OK; + } + + nc = get_single_attribute(mem_ctx, smbldap_state->ldap_struct, entry, + "namingContexts"); + if (nc != NULL) { + *base_dn = nc; + ldap_msgfree(result); + return NT_STATUS_OK; + } + + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; +} + +static NTSTATUS ipasam_get_domain_name(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + const char **domain_name) +{ + int ret; + LDAPMessage *result; + LDAPMessage *entry = NULL; + int count; + char *cn; + struct smbldap_state *smbldap_state = ldap_state->smbldap_state; + const char *attr_list[] = { + "associatedDomain", + NULL + }; + + ret = smbldap_search(smbldap_state, + ldap_state->ipasam_privates->base_dn, + LDAP_SCOPE_SUBTREE, + "objectclass=domainRelatedObject", attr_list, 0, + &result); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("Failed to get domain name: %s\n", + ldap_err2string (ret))); + return NT_STATUS_UNSUCCESSFUL; + } + + count = ldap_count_entries(smbldap_state->ldap_struct, result); + + if (count != 1) { + DEBUG(1, ("Unexpected number of results [%d] for domain name " + "search.\n", count)); + ldap_msgfree(result); + return NT_STATUS_OK; + } + + entry = ldap_first_entry(smbldap_state->ldap_struct, + result); + if (entry == NULL) { + DEBUG(0, ("Could not get domainRelatedObject entry\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + cn = get_single_attribute(mem_ctx, smbldap_state->ldap_struct, entry, + "associatedDomain"); + if (cn == NULL) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + *domain_name = cn; + ldap_msgfree(result); + return NT_STATUS_OK; +} + +static NTSTATUS ipasam_get_realm(struct ldapsam_privates *ldap_state, + TALLOC_CTX *mem_ctx, + char **realm) +{ + int ret; + LDAPMessage *result; + LDAPMessage *entry = NULL; + int count; + char *cn; + struct smbldap_state *smbldap_state = ldap_state->smbldap_state; + const char *attr_list[] = { + "cn", + NULL + }; + + ret = smbldap_search(smbldap_state, + ldap_state->ipasam_privates->base_dn, + LDAP_SCOPE_SUBTREE, + "objectclass=krbrealmcontainer", attr_list, 0, + &result); + if (ret != LDAP_SUCCESS) { + DEBUG(1, ("Failed to get realm: %s\n", + ldap_err2string (ret))); + return NT_STATUS_UNSUCCESSFUL; + } + + count = ldap_count_entries(smbldap_state->ldap_struct, result); + + if (count != 1) { + DEBUG(1, ("Unexpected number of results [%d] for realm " + "search.\n", count)); + ldap_msgfree(result); + return NT_STATUS_OK; + } + + entry = ldap_first_entry(smbldap_state->ldap_struct, + result); + if (entry == NULL) { + DEBUG(0, ("Could not get krbrealmcontainer entry\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + cn = get_single_attribute(mem_ctx, smbldap_state->ldap_struct, entry, + "cn"); + if (cn == NULL) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + *realm = cn; + ldap_msgfree(result); + return NT_STATUS_OK; +} + +#define SECRETS_DOMAIN_SID "SECRETS/SID" +static char *sec_key(TALLOC_CTX *mem_ctx, const char *d) +{ + return talloc_asprintf_strupper_m(mem_ctx, "%s/%s", + SECRETS_DOMAIN_SID, d); +} + +static NTSTATUS save_sid_to_secret(struct ldapsam_privates *ldap_state) +{ + char hostname[255]; + int ret; + char *p; + TALLOC_CTX *tmp_ctx; + NTSTATUS status = NT_STATUS_UNSUCCESSFUL; + + tmp_ctx =talloc_new(NULL); + if (tmp_ctx == NULL) { + return NT_STATUS_NO_MEMORY; + } + + if (!secrets_store(sec_key(tmp_ctx, ldap_state->domain_name), + &ldap_state->domain_sid, sizeof(struct dom_sid))) { + DEBUG(1, ("Failed to store domain SID")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + if (!secrets_store(sec_key(tmp_ctx, + ldap_state->ipasam_privates->flat_name), + &ldap_state->domain_sid, sizeof(struct dom_sid))) { + DEBUG(1, ("Failed to store domain SID")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + ret = gethostname(hostname, sizeof(hostname)); + if (ret == -1) { + DEBUG(1, ("gethostname failed.\n")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + hostname[sizeof(hostname)-1] = '\0'; + p = strchr(hostname, '.'); + if (p != NULL) { + *p = '\0'; + } + + if (!secrets_store(sec_key(tmp_ctx, hostname), + &ldap_state->domain_sid, sizeof(struct dom_sid))) { + DEBUG(1, ("Failed to store domain SID")); + status = NT_STATUS_UNSUCCESSFUL; + goto done; + } + + status = NT_STATUS_OK; + +done: + talloc_free(tmp_ctx); + return status; +} + +static NTSTATUS pdb_init_ipasam(struct pdb_methods **pdb_method, + const char *location) +{ + struct ldapsam_privates *ldap_state; + + char *uri; + NTSTATUS status; + char *dn = NULL; + char *domain_sid_string = NULL; + struct dom_sid ldap_domain_sid; + char *bind_dn = NULL; + char *bind_secret = NULL; + + LDAPMessage *result = NULL; + LDAPMessage *entry = NULL; + + status = make_pdb_method(pdb_method); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + (*pdb_method)->name = "ipasam"; + + if ( !(ldap_state = talloc_zero(*pdb_method, struct ldapsam_privates)) ) { + DEBUG(0, ("pdb_init_ipasam: talloc() failed for ldapsam private_data!\n")); + return NT_STATUS_NO_MEMORY; + } + + ldap_state->ipasam_privates = talloc_zero(ldap_state, + struct ipasam_privates); + if (ldap_state->ipasam_privates == NULL) { + return NT_STATUS_NO_MEMORY; + } + ldap_state->is_ipa_ldap = true; + + uri = talloc_strdup( NULL, location ); + if (uri == NULL) { + return NT_STATUS_NO_MEMORY; + } + trim_char( uri, '\"', '\"' ); + if (!fetch_ldap_pw(&bind_dn, &bind_secret)) { + DEBUG(0, ("pdb_init_ipasam: Failed to retrieve LDAP password from secrets.tdb\n")); + return NT_STATUS_NO_MEMORY; + } + status = smbldap_init(*pdb_method, pdb_get_tevent_context(), + uri, false, bind_dn, bind_secret, + &ldap_state->smbldap_state); + talloc_free(uri); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + (*pdb_method)->private_data = ldap_state; + (*pdb_method)->free_private_data = ipasam_free_private_data; + + status = ipasam_get_base_dn(ldap_state->smbldap_state, + ldap_state->ipasam_privates, + &ldap_state->ipasam_privates->base_dn); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to get base DN.\n")); + return status; + } + + if (!(smbldap_has_extension(priv2ld(ldap_state), IPA_KEYTAB_SET_OID) || + smbldap_has_extension(priv2ld(ldap_state), IPA_KEYTAB_SET_OID_OLD))) { + DEBUG(0, ("Server is not an IPA server.\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + ldap_state->ipasam_privates->trust_dn = talloc_asprintf( + ldap_state->ipasam_privates, + "cn=ad,cn=trusts,%s", + ldap_state->ipasam_privates->base_dn); + if (ldap_state->ipasam_privates->trust_dn == NULL) { + DEBUG(0, ("Failed to create trsut DN.\n")); + return NT_STATUS_NO_MEMORY; + } + + status = ipasam_get_domain_name(ldap_state, ldap_state, + &ldap_state->domain_name); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to get domain name.\n")); + return status; + } + + status = ipasam_get_realm(ldap_state, ldap_state->ipasam_privates, + &ldap_state->ipasam_privates->realm); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("Failed to get realm.\n")); + return status; + } + + status = ipasam_search_domain_info(ldap_state->smbldap_state, &result); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0, ("pdb_init_ldapsam: WARNING: Could not get domain " + "info, nor add one to the domain. " + "We cannot work reliably without it.\n")); + return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; + } + + entry = ldap_first_entry(ldap_state->smbldap_state->ldap_struct, + result); + if (entry == NULL) { + DEBUG(0, ("pdb_init_ipasam: Could not get domain info " + "entry\n")); + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + dn = get_dn(ldap_state, ldap_state->smbldap_state->ldap_struct, entry); + if (dn == NULL) { + ldap_msgfree(result); + return NT_STATUS_UNSUCCESSFUL; + } + + ldap_state->domain_dn = smb_xstrdup(dn); + talloc_free(dn); + + ldap_state->ipasam_privates->flat_name = get_single_attribute( + ldap_state, + ldap_state->smbldap_state->ldap_struct, + entry, + LDAP_ATTRIBUTE_FLAT_NAME); + if (ldap_state->ipasam_privates->flat_name == NULL) { + DEBUG(0, ("Missing mandatory attribute %s.\n", + LDAP_ATTRIBUTE_FLAT_NAME)); + return NT_STATUS_INVALID_PARAMETER; + } + + domain_sid_string = get_single_attribute( + ldap_state, + ldap_state->smbldap_state->ldap_struct, + entry, + LDAP_ATTRIBUTE_SID); + + if (domain_sid_string) { + if (!string_to_sid(&ldap_domain_sid, domain_sid_string)) { + DEBUG(1, ("pdb_init_ldapsam: SID [%s] could not be " + "read as a valid SID\n", domain_sid_string)); + ldap_msgfree(result); + TALLOC_FREE(domain_sid_string); + return NT_STATUS_INVALID_PARAMETER; + } + sid_copy(&ldap_state->domain_sid, &ldap_domain_sid); + talloc_free(domain_sid_string); + + status = save_sid_to_secret(ldap_state); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + } + + + + (*pdb_method)->getsampwnam = ldapsam_getsampwnam; + (*pdb_method)->search_users = ldapsam_search_users; + (*pdb_method)->search_groups = ldapsam_search_groups; + (*pdb_method)->search_aliases = ldapsam_search_aliases; + (*pdb_method)->lookup_rids = ldapsam_lookup_rids; + (*pdb_method)->sid_to_id = ldapsam_sid_to_id; + (*pdb_method)->uid_to_sid = ldapsam_uid_to_sid; + (*pdb_method)->gid_to_sid = ldapsam_gid_to_sid; + + (*pdb_method)->capabilities = pdb_ipasam_capabilities; + (*pdb_method)->get_domain_info = pdb_ipasam_get_domain_info; + + (*pdb_method)->get_trusteddom_pw = ipasam_get_trusteddom_pw; + (*pdb_method)->set_trusteddom_pw = ipasam_set_trusteddom_pw; + (*pdb_method)->del_trusteddom_pw = ipasam_del_trusteddom_pw; + (*pdb_method)->enum_trusteddoms = ipasam_enum_trusteddoms; + + (*pdb_method)->get_trusted_domain = ipasam_get_trusted_domain; + (*pdb_method)->get_trusted_domain_by_sid = ipasam_get_trusted_domain_by_sid; + (*pdb_method)->set_trusted_domain = ipasam_set_trusted_domain; + (*pdb_method)->del_trusted_domain = ipasam_del_trusted_domain; + (*pdb_method)->enum_trusted_domains = ipasam_enum_trusted_domains; + + return NT_STATUS_OK; +} + +NTSTATUS samba_module_init(void) +{ + return smb_register_passdb(PASSDB_INTERFACE_VERSION, "ipasam", + pdb_init_ipasam); +} diff --git a/daemons/ipa-sam/ipa_sam.h b/daemons/ipa-sam/ipa_sam.h new file mode 100644 index 000000000..46e6ffe1a --- /dev/null +++ b/daemons/ipa-sam/ipa_sam.h @@ -0,0 +1,28 @@ +/* + Unix SMB/CIFS implementation. + IPA helper functions for SAMBA + Copyright (C) Sumit Bose <sbose@redhat.com> 2010 + + 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/>. + +*/ + +#ifndef _PASSDB_PDB_IPA_H_ +#define _PASSDB_PDB_IPA_H_ + +/* The following definitions come from passdb/pdb_ipa.c */ + +NTSTATUS pdb_ipa_init(void); + +#endif /* _PASSDB_PDB_IPA_H_ */ diff --git a/freeipa.spec.in b/freeipa.spec.in index c14da8bb5..4d2340df2 100644 --- a/freeipa.spec.in +++ b/freeipa.spec.in @@ -327,6 +327,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_modrdn.la rm %{buildroot}/%{plugin_dir}/libipa_lockout.la rm %{buildroot}/%{plugin_dir}/libipa_cldap.la rm %{buildroot}/%{_libdir}/krb5/plugins/kdb/ipadb.la +rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la # Some user-modifiable HTML files are provided. Move these to /etc # and link back. @@ -547,6 +548,7 @@ fi %dir %{_localstatedir}/cache/ipa %attr(700,apache,apache) %dir %{_localstatedir}/cache/ipa/sessions %attr(755,root,root) %{_libdir}/krb5/plugins/kdb/ipadb.so +%attr(755,root,root) %{_libdir}/samba/pdb/ipasam.so %{_mandir}/man1/ipa-replica-conncheck.1.gz %{_mandir}/man1/ipa-replica-install.1.gz %{_mandir}/man1/ipa-replica-manage.1.gz diff --git a/install/share/smb.conf.template b/install/share/smb.conf.template index a7fc10691..4ab79daa5 100644 --- a/install/share/smb.conf.template +++ b/install/share/smb.conf.template @@ -9,7 +9,7 @@ domain logons = yes log level = 1 max log size = 100000 log file = /var/log/samba/log.%m -passdb backend = IPA_ldapsam:ldapi://$LDAPI_SOCKET +passdb backend = ipasam:ldapi://$LDAPI_SOCKET ldapsam:trusted=yes ldap ssl = off ldap admin dn = $SMB_DN |