Start implementing AD-style trusted domains in Samba4's NETLOGON server
[kai/samba.git] / source4 / rpc_server / netlogon / dcerpc_netlogon.c
index 763e6a327e1f7cfb93cdf265a21ae437c00b6dd6..b948d1210e88483d84be5113900681dde1f3fe0e 100644 (file)
@@ -3,7 +3,7 @@
 
    endpoint server for the netlogon pipe
 
-   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
+   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2008
    Copyright (C) Stefan Metzmacher <metze@samba.org>  2005
    
    This program is free software; you can redistribute it and/or modify
@@ -34,6 +34,9 @@
 #include "auth/gensec/schannel_state.h"
 #include "libcli/security/security.h"
 #include "param/param.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "librpc/gen_ndr/ndr_netlogon.h"
 
 struct server_pipe_state {
        struct netr_Credential client_challenge;
@@ -84,6 +87,9 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
        const char *attrs[] = {"unicodePwd", "userAccountControl", 
                               "objectSid", NULL};
 
+       const char *trust_dom_attrs[] = {"flatname", NULL};
+       const char *account_name;
+
        ZERO_STRUCTP(r->out.credentials);
        *r->out.rid = 0;
        *r->out.negotiate_flags = *r->in.negotiate_flags;
@@ -98,10 +104,54 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
        if (sam_ctx == NULL) {
                return NT_STATUS_INVALID_SYSTEM_SERVICE;
        }
+
+       if (r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
+               char *encoded_account = ldb_binary_encode_string(mem_ctx, r->in.account_name);
+               char *flatname;
+               if (!encoded_account) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               /* Kill the trailing dot */
+               if (encoded_account[strlen(encoded_account)-1] == '.') {
+                       encoded_account[strlen(encoded_account)-1] = '\0';
+               }
+
+               /* pull the user attributes */
+               num_records = gendb_search(sam_ctx, mem_ctx, NULL, &msgs, trust_dom_attrs,
+                                          "(&(trustPartner=%s)(objectclass=trustedDomain))", 
+                                          encoded_account);
+               
+               if (num_records == 0) {
+                       DEBUG(3,("Couldn't find trust [%s] in samdb.\n", 
+                                encoded_account));
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+               
+               if (num_records > 1) {
+                       DEBUG(0,("Found %d records matching user [%s]\n", num_records, r->in.account_name));
+                       return NT_STATUS_INTERNAL_DB_CORRUPTION;
+               }
+               
+               flatname = ldb_msg_find_attr_as_string(msgs[0], "flatname", NULL);
+               if (!flatname) {
+                       /* No flatname for this trust - we can't proceed */
+                       return NT_STATUS_ACCESS_DENIED;
+               }
+               account_name = talloc_asprintf(mem_ctx, "%s$", flatname);
+
+               if (!account_name) {
+                       return NT_STATUS_NO_MEMORY;
+               }
+               
+       } else {
+               account_name = r->in.account_name;
+       }
+       
        /* pull the user attributes */
        num_records = gendb_search(sam_ctx, mem_ctx, NULL, &msgs, attrs,
                                   "(&(sAMAccountName=%s)(objectclass=user))", 
-                                  r->in.account_name);
+                                  ldb_binary_encode_string(mem_ctx, account_name));
 
        if (num_records == 0) {
                DEBUG(3,("Couldn't find user [%s] in samdb.\n", 
@@ -127,7 +177,8 @@ static NTSTATUS dcesrv_netr_ServerAuthenticate3(struct dcesrv_call_state *dce_ca
                        DEBUG(1, ("Client asked for a workstation secure channel, but is not a workstation (member server) acb flags: 0x%x\n", user_account_control));
                        return NT_STATUS_ACCESS_DENIED;
                }
-       } else if (r->in.secure_channel_type == SEC_CHAN_DOMAIN) {
+       } else if (r->in.secure_channel_type == SEC_CHAN_DOMAIN || 
+                  r->in.secure_channel_type == SEC_CHAN_DNS_DOMAIN) {
                if (!(user_account_control & UF_INTERDOMAIN_TRUST_ACCOUNT)) {
                        DEBUG(1, ("Client asked for a trusted domain secure channel, but is not a trusted domain: acb flags: 0x%x\n", user_account_control));
                        
@@ -335,7 +386,6 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
        struct ldb_context *sam_ctx;
        NTSTATUS nt_status;
        char new_pass[512];
-       uint32_t new_pass_len;
        bool ret;
 
        struct samr_CryptPassword password_buf;
@@ -356,7 +406,7 @@ static NTSTATUS dcesrv_netr_ServerPasswordSet2(struct dcesrv_call_state *dce_cal
        creds_arcfour_crypt(creds, password_buf.data, 516);
 
        ret = decode_pw_buffer(password_buf.data, new_pass, sizeof(new_pass),
-                              &new_pass_len, STR_UNICODE);
+                              STR_UNICODE);
        if (!ret) {
                DEBUG(3,("netr_ServerPasswordSet2: failed to decode password buffer\n"));
                return NT_STATUS_ACCESS_DENIED;
@@ -488,7 +538,48 @@ static NTSTATUS dcesrv_netr_LogonSamLogon_base(struct dcesrv_call_state *dce_cal
                
        case NetlogonGenericInformation:
        {
-               /* Until we get enough information for an implemetnation */
+               if (creds->negotiate_flags & NETLOGON_NEG_ARCFOUR) {
+                       creds_arcfour_crypt(creds, 
+                                           r->in.logon.generic->data, r->in.logon.generic->length);
+               } else {
+                       /* Using DES to verify kerberos tickets makes no sense */
+                       return NT_STATUS_INVALID_PARAMETER;
+               }
+
+               if (strcmp(r->in.logon.generic->package_name.string, "Kerberos") == 0) {
+                       NTSTATUS status;
+                       struct server_id *kdc;
+                       struct kdc_check_generic_kerberos check;
+                       struct netr_GenericInfo2 *generic = talloc_zero(mem_ctx, struct netr_GenericInfo2);
+                       NT_STATUS_HAVE_NO_MEMORY(generic);
+                       r->out.authoritative = 1;
+                       
+                       /* TODO: Describe and deal with these flags */
+                       r->out.flags = 0;
+
+                       r->out.validation.generic = generic;
+       
+                       kdc = irpc_servers_byname(dce_call->msg_ctx, mem_ctx, "kdc_server");
+                       if ((kdc == NULL) || (kdc[0].id == 0)) {
+                               return NT_STATUS_NO_LOGON_SERVERS;
+                       }
+                       
+                       check.in.generic_request = 
+                               data_blob_const(r->in.logon.generic->data,
+                                               r->in.logon.generic->length);   
+                       
+                       status = irpc_call(dce_call->msg_ctx, kdc[0],
+                                          &ndr_table_irpc, NDR_KDC_CHECK_GENERIC_KERBEROS,
+                                          &check, mem_ctx);
+                       if (!NT_STATUS_IS_OK(status)) {
+                               return status;
+                       }
+                       generic->length = check.out.generic_reply.length;
+                       generic->data = check.out.generic_reply.data;
+                       return NT_STATUS_OK;
+               }
+
+               /* Until we get an implemetnation of these other packages */
                return NT_STATUS_INVALID_PARAMETER;
        }
        default:
@@ -856,20 +947,37 @@ static NTSTATUS fill_domain_trust_info(TALLOC_CTX *mem_ctx,
                                       struct ldb_message *res,
                                       struct ldb_message *ref_res,
                                       struct netr_DomainTrustInfo *info, 
-                                      bool is_local)
+                                      bool is_local, bool is_trust_list)
 {
        ZERO_STRUCTP(info);
 
+       info->trust_extension.info = talloc_zero(mem_ctx, struct netr_trust_extension);
+       info->trust_extension.length = 16;
+       info->trust_extension.info->flags = 
+               NETR_TRUST_FLAG_TREEROOT | 
+               NETR_TRUST_FLAG_IN_FOREST | 
+               NETR_TRUST_FLAG_PRIMARY;
+       info->trust_extension.info->parent_index = 0; /* should be index into array
+                                                        of parent */
+       info->trust_extension.info->trust_type = LSA_TRUST_TYPE_UPLEVEL; /* should be based on ldb search for trusts */
+       info->trust_extension.info->trust_attributes = LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE; /* needs to be based on ldb search */
+
+       if (is_trust_list) {
+               /* MS-NRPC 3.5.4.3.9 - must be set to NULL for trust list */
+               info->forest.string = NULL;
+       } else {
+               /* TODO: we need a common function for pulling the forest */
+               info->forest.string = samdb_result_string(ref_res, "dnsRoot", NULL);
+       }
+
        if (is_local) {
                info->domainname.string = samdb_result_string(ref_res, "nETBIOSName", NULL);
                info->fulldomainname.string = samdb_result_string(ref_res, "dnsRoot", NULL);
-               info->forest.string = NULL;
                info->guid = samdb_result_guid(res, "objectGUID");
                info->sid = samdb_result_dom_sid(mem_ctx, res, "objectSid");
        } else {
                info->domainname.string = samdb_result_string(res, "flatName", NULL);
                info->fulldomainname.string = samdb_result_string(res, "trustPartner", NULL);
-               info->forest.string = NULL;
                info->guid = samdb_result_guid(res, "objectGUID");
                info->sid = samdb_result_dom_sid(mem_ctx, res, "securityIdentifier");
        }
@@ -905,6 +1013,9 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                                              r->in.credential, 
                                              r->out.return_authenticator,
                                              NULL);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0,(__location__ " Bad credentials - error\n"));
+       }
        NT_STATUS_NOT_OK_RETURN(status);
 
        sam_ctx = samdb_connect(mem_ctx, dce_call->event_ctx, dce_call->conn->dce_ctx->lp_ctx, dce_call->conn->auth_state.session_info);
@@ -950,17 +1061,25 @@ static NTSTATUS dcesrv_netr_LogonGetDomainInfo(struct dcesrv_call_state *dce_cal
                                       info1->num_trusts);
        NT_STATUS_HAVE_NO_MEMORY(info1->trusts);
 
-       status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->domaininfo, true);
+       status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->domaininfo, 
+                                       true, false);
        NT_STATUS_NOT_OK_RETURN(status);
 
        for (i=0;i<ret2;i++) {
-               status = fill_domain_trust_info(mem_ctx, res2[i], NULL, &info1->trusts[i], false);
+               status = fill_domain_trust_info(mem_ctx, res2[i], NULL, &info1->trusts[i], 
+                                               false, true);
                NT_STATUS_NOT_OK_RETURN(status);
        }
 
-       status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->trusts[i], true);
+       status = fill_domain_trust_info(mem_ctx, res1[0], ref_res[0], &info1->trusts[i], 
+                                       true, true);
        NT_STATUS_NOT_OK_RETURN(status);
 
+       info1->dns_hostname.string = samdb_result_string(ref_res[0], "dnsRoot", NULL);
+       info1->workstation_flags = 
+               NETR_WS_FLAG_HANDLES_INBOUND_TRUSTS | NETR_WS_FLAG_HANDLES_SPN_UPDATE;
+       info1->supported_enc_types = 0; /* w2008 gives this 0 */
+
        r->out.info.info1 = info1;
 
        return NT_STATUS_OK;