r12865: Upgrade the librpc and libnet code.
[samba.git] / source4 / libnet / libnet_vampire.c
index fb6098ed17586f47378edad0c5023583651941f4..26e39392051f1b3f2e10fb272f83926f082db300 100644 (file)
@@ -1,6 +1,8 @@
 /* 
    Unix SMB/CIFS implementation.
    
+   Extract the user/system database from a remote SamSync server
+
    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
    
    This program is free software; you can redistribute it and/or modify
 
 #include "includes.h"
 #include "libnet/libnet.h"
-#include "librpc/gen_ndr/ndr_netlogon.h"
-#include "librpc/gen_ndr/ndr_samr.h"
-
-static NTSTATUS vampire_samdump_handle_user(TALLOC_CTX *mem_ctx,
-                                           struct creds_CredentialState *creds,
-                                           struct netr_DELTA_ENUM *delta) 
-{
-       uint32_t rid = delta->delta_id_union.rid;
-       struct netr_DELTA_USER *user = delta->delta_union.user;
-       const char *username = user->account_name.string;
-       char *hex_lm_password;
-       char *hex_nt_password;
-
-       hex_lm_password = smbpasswd_sethexpwd(mem_ctx, 
-                                             user->lm_password_present ? &user->lmpassword : NULL, 
-                                             user->acct_flags);
-       hex_nt_password = smbpasswd_sethexpwd(mem_ctx, 
-                                             user->nt_password_present ? &user->ntpassword : NULL, 
-                                             user->acct_flags);
-
-       printf("%s:%d:%s:%s:%s:LCT-%08X\n", username,
-              rid, hex_lm_password, hex_nt_password,
-              smbpasswd_encode_acb_info(mem_ctx, user->acct_flags),
-              (unsigned int)nt_time_to_unix(user->last_password_change));
 
-       return NT_STATUS_OK;
-}
-
-static NTSTATUS libnet_samdump_fn(TALLOC_CTX *mem_ctx,                 
-                                 void *private,                        
-                                 struct creds_CredentialState *creds,
-                                 enum netr_SamDatabaseID database,
-                                 struct netr_DELTA_ENUM *delta,
-                                 char **error_string)
-{
-       NTSTATUS nt_status = NT_STATUS_OK;
-       *error_string = NULL;
-       switch (database) {
-       case SAM_DATABASE_DOMAIN: 
-       {
-               switch (delta->delta_type) {
-               case NETR_DELTA_USER:
-               {
-                       nt_status = vampire_samdump_handle_user(mem_ctx, 
-                                                               creds,
-                                                               delta);
-                       break;
-               }
-               }
-               break;
-       }
-       }
-       return nt_status;
-}
 
 /**
  * Decrypt and extract the user's passwords.  
@@ -135,6 +84,27 @@ static NTSTATUS fix_user(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
+/**
+ * Decrypt and extract the secrets
+ * 
+ * The writes decrypted secrets back into the structure
+ */
+static NTSTATUS fix_secret(TALLOC_CTX *mem_ctx,
+                          struct creds_CredentialState *creds,
+                          enum netr_SamDatabaseID database,
+                          struct netr_DELTA_ENUM *delta,
+                          char **error_string) 
+{
+       struct netr_DELTA_SECRET *secret = delta->delta_union.secret;
+       creds_arcfour_crypt(creds, secret->current_cipher.cipher_data, 
+                           secret->current_cipher.maxlen); 
+
+       creds_arcfour_crypt(creds, secret->old_cipher.cipher_data, 
+                           secret->old_cipher.maxlen); 
+
+       return NT_STATUS_OK;
+}
+
 /**
  * Fix up the delta, dealing with encryption issues so that the final
  * callback need only do the printing or application logic
@@ -158,84 +128,141 @@ static NTSTATUS fix_delta(TALLOC_CTX *mem_ctx,
                                     error_string);
                break;
        }
+       case NETR_DELTA_SECRET:
+       {
+               nt_status = fix_secret(mem_ctx, 
+                                      creds,
+                                      database,
+                                      delta,
+                                      error_string);
+               break;
+       }
+       default:
+               break;
        }
        return nt_status;
 }
 
-static NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamSync *r)
+NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, struct libnet_SamSync *r)
 {
        NTSTATUS nt_status, dbsync_nt_status;
-       TALLOC_CTX *loop_ctx, *delta_ctx;
+       TALLOC_CTX *samsync_ctx, *loop_ctx, *delta_ctx;
        struct creds_CredentialState *creds;
        struct netr_DatabaseSync dbsync;
        struct cli_credentials *machine_account;
-       struct dcerpc_binding *b;
        struct dcerpc_pipe *p;
+       struct libnet_context *machine_net_ctx;
+       struct libnet_RpcConnect *c;
        const enum netr_SamDatabaseID database_ids[] = {SAM_DATABASE_DOMAIN, SAM_DATABASE_BUILTIN, SAM_DATABASE_PRIVS}; 
        int i;
 
-       /* TODO: This is bogus */
-       const char **bindings = lp_passwordserver();
-       const char *binding;
+       samsync_ctx = talloc_named(mem_ctx, 0, "SamSync top context");
 
-       if (bindings && bindings[0]) {
-               binding = bindings[0];
-       }
-
-       machine_account = cli_credentials_init(mem_ctx);
-       if (!machine_account) {
-               return NT_STATUS_NO_MEMORY;
+       if (!r->in.machine_account) { 
+               machine_account = cli_credentials_init(samsync_ctx);
+               if (!machine_account) {
+                       talloc_free(samsync_ctx);
+                       return NT_STATUS_NO_MEMORY;
+               }
+               cli_credentials_set_conf(machine_account);
+               nt_status = cli_credentials_set_machine_account(machine_account);
+               if (!NT_STATUS_IS_OK(nt_status)) {
+                       r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
+                       talloc_free(samsync_ctx);
+                       return nt_status;
+               }
+       } else {
+               machine_account = r->in.machine_account;
        }
 
-       cli_credentials_set_conf(machine_account);
-       nt_status = cli_credentials_set_machine_account(machine_account);
-       
-       if (!NT_STATUS_IS_OK(nt_status)) {
-               r->netlogon.error_string = talloc_strdup(mem_ctx, "Could not obtain machine account password - are we joined to the domain?");
-               return nt_status;
-       }
-       
+       /* We cannot do this unless we are a BDC.  Check, before we get odd errors later */
        if (cli_credentials_get_secure_channel_type(machine_account) != SEC_CHAN_BDC) {
-               r->netlogon.error_string
+               r->out.error_string
                        = talloc_asprintf(mem_ctx, 
                                          "Our join to domain %s is not as a BDC (%d), please rejoin as a BDC",
                                          
                                          cli_credentials_get_domain(machine_account),
                                          cli_credentials_get_secure_channel_type(machine_account));
+               talloc_free(samsync_ctx);
                return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
        }
 
-       /* Connect to DC (take a binding string for now) */
+       c = talloc(samsync_ctx, struct libnet_RpcConnect);
+       if (!c) {
+               r->out.error_string = NULL;
+               talloc_free(samsync_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
 
-       nt_status = dcerpc_parse_binding(mem_ctx, binding, &b);
+       if (r->in.binding_string) {
+               c->level      = LIBNET_RPC_CONNECT_BINDING;
+               c->in.binding = r->in.binding_string;
+       } else {
+               /* prepare connect to the NETLOGON pipe of PDC */
+               c->level      = LIBNET_RPC_CONNECT_PDC;
+               c->in.name    = cli_credentials_get_domain(machine_account);
+       }
+       c->in.dcerpc_iface      = &dcerpc_table_netlogon;
+
+       /* We must do this as the machine, not as any command-line
+        * user.  So we override the credentials in the
+        * libnet_context */
+       machine_net_ctx = talloc(samsync_ctx, struct libnet_context);
+       if (!machine_net_ctx) {
+               r->out.error_string = NULL;
+               talloc_free(samsync_ctx);
+               return NT_STATUS_NO_MEMORY;
+       }
+       *machine_net_ctx = *ctx;
+       machine_net_ctx->cred = machine_account;
+
+       /* connect to the NETLOGON pipe of the PDC */
+       nt_status = libnet_RpcConnect(machine_net_ctx, c, c);
        if (!NT_STATUS_IS_OK(nt_status)) {
-               r->netlogon.error_string = talloc_asprintf(mem_ctx, "Bad binding string %s\n", binding);
-               return NT_STATUS_INVALID_PARAMETER;
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                     "Connection to NETLOGON pipe of DC failed: %s",
+                                                     c->out.error_string);
+               talloc_free(samsync_ctx);
+               return nt_status;
        }
 
-       /* We like schannel */
-       b->flags &= ~DCERPC_AUTH_OPTIONS;
-       b->flags |= DCERPC_SCHANNEL | DCERPC_SEAL | DCERPC_SCHANNEL_128;
+       /* This makes a new pipe, on which we can do schannel.  We
+        * should do this in the RpcConnect code, but the abstaction
+        * layers do not suit yet */
 
-       /* Setup schannel */
-       nt_status = dcerpc_pipe_connect_b(mem_ctx, &p, b, 
-                                         DCERPC_NETLOGON_UUID,
-                                         DCERPC_NETLOGON_VERSION,
-                                         machine_account);
+       nt_status = dcerpc_secondary_connection(c->out.dcerpc_pipe, &p,
+                                               c->out.dcerpc_pipe->binding);
 
        if (!NT_STATUS_IS_OK(nt_status)) {
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                     "Secondary connection to NETLOGON pipe of DC %s failed: %s",
+                                                     dcerpc_server_name(p), nt_errstr(nt_status));
+               talloc_free(samsync_ctx);
+               return nt_status;
+       }
+
+       nt_status = dcerpc_bind_auth_schannel(samsync_ctx, p, &dcerpc_table_netlogon,
+                                             machine_account, DCERPC_AUTH_LEVEL_PRIVACY);
+
+       if (!NT_STATUS_IS_OK(nt_status)) {
+               r->out.error_string = talloc_asprintf(mem_ctx,
+                                                     "SCHANNEL authentication to NETLOGON pipe of DC %s failed: %s",
+                                                     dcerpc_server_name(p), nt_errstr(nt_status));
+               talloc_free(samsync_ctx);
                return nt_status;
        }
 
        /* get NETLOGON credentails */
 
-       nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, mem_ctx, &creds);
+       nt_status = dcerpc_schannel_creds(p->conn->security_state.generic_state, samsync_ctx, &creds);
        if (!NT_STATUS_IS_OK(nt_status)) {
-               r->netlogon.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
+               r->out.error_string = talloc_strdup(mem_ctx, "Could not obtain NETLOGON credentials from DCERPC/GENSEC layer");
+               talloc_free(samsync_ctx);
                return nt_status;
        }
 
-       dbsync.in.logon_server = talloc_asprintf(mem_ctx, "\\\\%s", dcerpc_server_name(p));
+       /* Setup details for the syncronisation */
+       dbsync.in.logon_server = talloc_asprintf(samsync_ctx, "\\\\%s", dcerpc_server_name(p));
        dbsync.in.computername = cli_credentials_get_workstation(machine_account);
        dbsync.in.preferredmaximumlength = (uint32_t)-1;
        ZERO_STRUCT(dbsync.in.return_authenticator);
@@ -246,45 +273,54 @@ static NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *
                
                do {
                        int d;
-                       loop_ctx = talloc_named(mem_ctx, 0, "DatabaseSync loop context");
+                       loop_ctx = talloc_named(samsync_ctx, 0, "DatabaseSync loop context");
                        creds_client_authenticator(creds, &dbsync.in.credential);
                        
                        dbsync_nt_status = dcerpc_netr_DatabaseSync(p, loop_ctx, &dbsync);
                        if (!NT_STATUS_IS_OK(dbsync_nt_status) &&
                            !NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES)) {
-                               r->netlogon.error_string = talloc_asprintf(mem_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
+                               r->out.error_string = talloc_asprintf(samsync_ctx, "DatabaseSync failed - %s", nt_errstr(nt_status));
+                               talloc_free(samsync_ctx);
                                return nt_status;
                        }
                        
                        if (!creds_client_check(creds, &dbsync.out.return_authenticator.cred)) {
-                               r->netlogon.error_string = talloc_strdup(mem_ctx, "Credential chaining failed");
+                               r->out.error_string = talloc_strdup(samsync_ctx, "Credential chaining failed");
+                               talloc_free(samsync_ctx);
                                return NT_STATUS_ACCESS_DENIED;
                        }
                        
                        dbsync.in.sync_context = dbsync.out.sync_context;
                        
+                       /* For every single remote 'delta' entry: */
                        for (d=0; d < dbsync.out.delta_enum_array->num_deltas; d++) {
                                char *error_string = NULL;
                                delta_ctx = talloc_named(loop_ctx, 0, "DatabaseSync delta context");
+                               /* 'Fix' elements, by decrypting and
+                                * de-obfustiating the data */
                                nt_status = fix_delta(delta_ctx, 
                                                      creds, 
                                                      dbsync.in.database_id,
                                                      &dbsync.out.delta_enum_array->delta_enum[d], 
                                                      &error_string);
                                if (!NT_STATUS_IS_OK(nt_status)) {
-                                       r->netlogon.error_string = talloc_steal(mem_ctx, error_string);
-                                       talloc_free(delta_ctx);
+                                       r->out.error_string = talloc_steal(samsync_ctx, error_string);
+                                       talloc_free(samsync_ctx);
                                        return nt_status;
                                }
-                               nt_status = r->netlogon.delta_fn(delta_ctx, 
-                                                                r->netlogon.fn_ctx,
-                                                                creds,
-                                                                dbsync.in.database_id,
-                                                                &dbsync.out.delta_enum_array->delta_enum[d], 
-                                                                &error_string);
+
+                               /* Now call the callback.  This will
+                                * do something like print the data or
+                                * write to an ldb */
+                               nt_status = r->in.delta_fn(delta_ctx, 
+                                                          r->in.fn_ctx,
+                                                          creds,
+                                                          dbsync.in.database_id,
+                                                          &dbsync.out.delta_enum_array->delta_enum[d], 
+                                                          &error_string);
                                if (!NT_STATUS_IS_OK(nt_status)) {
-                                       r->netlogon.error_string = talloc_steal(mem_ctx, error_string);
-                                       talloc_free(delta_ctx);
+                                       r->out.error_string = talloc_steal(samsync_ctx, error_string);
+                                       talloc_free(samsync_ctx);
                                        return nt_status;
                                }
                                talloc_free(delta_ctx);
@@ -293,49 +329,7 @@ static NTSTATUS libnet_SamSync_netlogon(struct libnet_context *ctx, TALLOC_CTX *
                } while (NT_STATUS_EQUAL(dbsync_nt_status, STATUS_MORE_ENTRIES));
                nt_status = dbsync_nt_status;
        }
+       talloc_free(samsync_ctx);
        return nt_status;
 }
 
-NTSTATUS libnet_SamDump_netlogon(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamDump *r)
-{
-       NTSTATUS nt_status;
-       union libnet_SamSync r2;
-
-       r2.netlogon.level = LIBNET_SAMDUMP_NETLOGON;
-       r2.netlogon.error_string = NULL;
-       r2.netlogon.delta_fn = libnet_samdump_fn;
-       r2.netlogon.fn_ctx = NULL;
-       nt_status = libnet_SamSync_netlogon(ctx, mem_ctx, &r2);
-       r->generic.error_string = r2.netlogon.error_string;
-
-       
-       return nt_status;
-}
-
-
-
-NTSTATUS libnet_SamDump_generic(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamDump *r)
-{
-       NTSTATUS nt_status;
-       union libnet_SamDump r2;
-
-       r2.generic.level = LIBNET_SAMDUMP_NETLOGON;
-       r2.generic.error_string = NULL;
-       nt_status = libnet_SamDump(ctx, mem_ctx, &r2);
-       r->generic.error_string = r2.netlogon.error_string;
-
-       
-       return nt_status;
-}
-
-NTSTATUS libnet_SamDump(struct libnet_context *ctx, TALLOC_CTX *mem_ctx, union libnet_SamDump *r)
-{
-       switch (r->generic.level) {
-       case LIBNET_SAMDUMP_GENERIC:
-               return libnet_SamDump_generic(ctx, mem_ctx, r);
-       case LIBNET_SAMDUMP_NETLOGON:
-               return libnet_SamDump_netlogon(ctx, mem_ctx, r);
-       }
-
-       return NT_STATUS_INVALID_LEVEL;
-}