root/source4/winbind/wb_init_domain.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_domain_binding
  2. wb_init_domain_send
  3. init_domain_recv_netlogonpipe
  4. retry_with_schannel
  5. init_domain_recv_lsa_pipe
  6. init_domain_recv_lsa_policy
  7. init_domain_recv_queryinfo
  8. init_domain_recv_samr
  9. wb_init_domain_recv
  10. wb_init_domain

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    A composite API for initializing a domain
   5 
   6    Copyright (C) Volker Lendecke 2005
   7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2007
   8 
   9    This program is free software; you can redistribute it and/or modify
  10    it under the terms of the GNU General Public License as published by
  11    the Free Software Foundation; either version 3 of the License, or
  12    (at your option) any later version.
  13    
  14    This program is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18    
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21 */
  22 
  23 #include "includes.h"
  24 #include "libcli/composite/composite.h"
  25 #include "libcli/smb_composite/smb_composite.h"
  26 #include "winbind/wb_server.h"
  27 #include "winbind/wb_async_helpers.h"
  28 #include "winbind/wb_helper.h"
  29 #include "smbd/service_task.h"
  30 #include "librpc/gen_ndr/ndr_netlogon.h"
  31 #include "librpc/gen_ndr/ndr_lsa_c.h"
  32 #include "librpc/gen_ndr/ndr_samr_c.h"
  33 #include "libcli/libcli.h"
  34 
  35 #include "libcli/auth/credentials.h"
  36 #include "libcli/security/security.h"
  37 
  38 #include "libcli/ldap/ldap_client.h"
  39 
  40 #include "auth/credentials/credentials.h"
  41 #include "param/param.h"
  42 
  43 /*
  44  * Initialize a domain:
  45  *
  46  * - With schannel credentials, try to open the SMB connection and
  47  *   NETLOGON pipe with the machine creds. This works against W2k3SP1
  48  *   with an NTLMSSP session setup. Fall back to anonymous (for the CIFS level).
  49  *
  50  * - If we have schannel creds, do the auth2 and open the schannel'ed netlogon
  51  *   pipe.
  52  *
  53  * - Open LSA. If we have machine creds, try to open with SPNEGO or NTLMSSP. Fall back
  54  *   to schannel.
  55  *
  56  * - With queryinfopolicy, verify that we're talking to the right domain
  57  *
  58  * A bit complex, but with all the combinations I think it's the best we can
  59  * get. NT4, W2k3 and W2k all have different combinations, but in the end we
  60  * have a signed&sealed lsa connection on all of them.
  61  *
  62  * Not sure if it is overkill, but it seems to work.
  63  */
  64 
  65 struct init_domain_state {
  66         struct composite_context *ctx;
  67         struct wbsrv_domain *domain;
  68         struct wbsrv_service *service;
  69 
  70         struct lsa_ObjectAttribute objectattr;
  71         struct lsa_OpenPolicy2 lsa_openpolicy;
  72         struct lsa_QueryInfoPolicy queryinfo;
  73         union lsa_PolicyInformation *info;
  74 };
  75 
  76 static void init_domain_recv_netlogonpipe(struct composite_context *ctx);
  77 static void init_domain_recv_lsa_pipe(struct composite_context *ctx);
  78 static void init_domain_recv_lsa_policy(struct rpc_request *req);
  79 static void init_domain_recv_queryinfo(struct rpc_request *req);
  80 static void init_domain_recv_samr(struct composite_context *ctx);
  81 
  82 static struct dcerpc_binding *init_domain_binding(struct init_domain_state *state, 
     /* [<][>][^][v][top][bottom][index][help] */
  83                                                   const struct ndr_interface_table *table) 
  84 {
  85         struct dcerpc_binding *binding;
  86         NTSTATUS status;
  87 
  88         /* Make a binding string */
  89         {
  90                 char *s = talloc_asprintf(state, "ncacn_np:%s", state->domain->dc_name);
  91                 if (s == NULL) return NULL;
  92                 status = dcerpc_parse_binding(state, s, &binding);
  93                 talloc_free(s);
  94                 if (!NT_STATUS_IS_OK(status)) {
  95                         return NULL;
  96                 }
  97         }
  98 
  99         /* Alter binding to contain hostname, but also address (so we don't look it up twice) */
 100         binding->target_hostname = state->domain->dc_name;
 101         binding->host = state->domain->dc_address;
 102 
 103         /* This shouldn't make a network call, as the mappings for named pipes are well known */
 104         status = dcerpc_epm_map_binding(binding, binding, table, state->service->task->event_ctx,
 105                                         state->service->task->lp_ctx);
 106         if (!NT_STATUS_IS_OK(status)) {
 107                 return NULL;
 108         }
 109 
 110         return binding;
 111 }
 112 
 113 struct composite_context *wb_init_domain_send(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 114                                               struct wbsrv_service *service,
 115                                               struct wb_dom_info *dom_info)
 116 {
 117         struct composite_context *result, *ctx;
 118         struct init_domain_state *state;
 119 
 120         result = composite_create(mem_ctx, service->task->event_ctx);
 121         if (result == NULL) goto failed;
 122 
 123         state = talloc_zero(result, struct init_domain_state);
 124         if (state == NULL) goto failed;
 125         state->ctx = result;
 126         result->private_data = state;
 127 
 128         state->service = service;
 129 
 130         state->domain = talloc(state, struct wbsrv_domain);
 131         if (state->domain == NULL) goto failed;
 132 
 133         state->domain->info = talloc_reference(state->domain, dom_info);
 134         if (state->domain->info == NULL) goto failed;
 135 
 136         /* Caller should check, but to be safe: */
 137         if (dom_info->num_dcs < 1) {
 138                 goto failed;
 139         }
 140         
 141         /* For now, we just pick the first.  The next step will be to
 142          * walk the entire list.  Also need to fix finddcs() to return
 143          * the entire list */
 144         state->domain->dc_name = dom_info->dcs[0].name;
 145         state->domain->dc_address = dom_info->dcs[0].address;
 146 
 147         state->domain->libnet_ctx = libnet_context_init(service->task->event_ctx, 
 148                                                         service->task->lp_ctx);
 149 
 150         /* Create a credentials structure */
 151         state->domain->libnet_ctx->cred = cli_credentials_init(state->domain);
 152         if (state->domain->libnet_ctx->cred == NULL) goto failed;
 153 
 154         cli_credentials_set_conf(state->domain->libnet_ctx->cred, service->task->lp_ctx);
 155 
 156         /* Connect the machine account to the credentials */
 157         state->ctx->status =
 158                 cli_credentials_set_machine_account(state->domain->libnet_ctx->cred, state->domain->libnet_ctx->lp_ctx);
 159         if (!NT_STATUS_IS_OK(state->ctx->status)) goto failed;
 160 
 161         state->domain->netlogon_binding = init_domain_binding(state, &ndr_table_netlogon);
 162 
 163         state->domain->netlogon_pipe = NULL;
 164 
 165         if ((!cli_credentials_is_anonymous(state->domain->libnet_ctx->cred)) &&
 166             ((lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_MEMBER) ||
 167              (lp_server_role(service->task->lp_ctx) == ROLE_DOMAIN_CONTROLLER)) &&
 168             (dom_sid_equal(state->domain->info->sid,
 169                            state->service->primary_sid))) {
 170                 state->domain->netlogon_binding->flags |= DCERPC_SCHANNEL;
 171 
 172                 /* For debugging, it can be a real pain if all the traffic is encrypted */
 173                 if (lp_winbind_sealed_pipes(service->task->lp_ctx)) {
 174                         state->domain->netlogon_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL );
 175                 } else {
 176                         state->domain->netlogon_binding->flags |= (DCERPC_SIGN);
 177                 }
 178         }
 179 
 180         /* No encryption on anonymous pipes */
 181 
 182         ctx = dcerpc_pipe_connect_b_send(state, state->domain->netlogon_binding, 
 183                                          &ndr_table_netlogon,
 184                                          state->domain->libnet_ctx->cred,
 185                                          service->task->event_ctx,
 186                                          service->task->lp_ctx);
 187         
 188         if (composite_nomem(ctx, state->ctx)) {
 189                 goto failed;
 190         }
 191         
 192         composite_continue(state->ctx, ctx, init_domain_recv_netlogonpipe,
 193                            state);
 194         return result;
 195  failed:
 196         talloc_free(result);
 197         return NULL;
 198 }
 199 
 200 /* Having make a netlogon connection (possibly secured with schannel),
 201  * make an LSA connection to the same DC, on the same IPC$ share */
 202 static void init_domain_recv_netlogonpipe(struct composite_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
 203 {
 204         struct init_domain_state *state =
 205                 talloc_get_type(ctx->async.private_data,
 206                                 struct init_domain_state);
 207 
 208         state->ctx->status = dcerpc_pipe_connect_b_recv(ctx, state->domain, 
 209                                                    &state->domain->netlogon_pipe);
 210         
 211         if (!composite_is_ok(state->ctx)) {
 212                 return;
 213         }
 214         talloc_steal(state->domain->netlogon_pipe, state->domain->netlogon_binding);
 215 
 216         state->domain->lsa_binding = init_domain_binding(state, &ndr_table_lsarpc);
 217 
 218         /* For debugging, it can be a real pain if all the traffic is encrypted */
 219         if (lp_winbind_sealed_pipes(state->service->task->lp_ctx)) {
 220                 state->domain->lsa_binding->flags |= (DCERPC_SIGN | DCERPC_SEAL );
 221         } else {
 222                 state->domain->lsa_binding->flags |= (DCERPC_SIGN);
 223         }
 224 
 225         state->domain->libnet_ctx->lsa.pipe = NULL;
 226 
 227         /* this will make the secondary connection on the same IPC$ share, 
 228            secured with SPNEGO or NTLMSSP */
 229         ctx = dcerpc_secondary_auth_connection_send(state->domain->netlogon_pipe,
 230                                                     state->domain->lsa_binding,
 231                                                     &ndr_table_lsarpc,
 232                                                     state->domain->libnet_ctx->cred,
 233                                                     state->domain->libnet_ctx->lp_ctx
 234                 );
 235         composite_continue(state->ctx, ctx, init_domain_recv_lsa_pipe, state);
 236 }
 237 
 238 static bool retry_with_schannel(struct init_domain_state *state, 
     /* [<][>][^][v][top][bottom][index][help] */
 239                                 struct dcerpc_binding *binding,
 240                                 const struct ndr_interface_table *table,
 241                                 void (*continuation)(struct composite_context *))
 242 {
 243         struct composite_context *ctx;
 244         state->ctx->status = NT_STATUS_OK;
 245         if (state->domain->netlogon_binding->flags & DCERPC_SCHANNEL 
 246             && !(binding->flags & DCERPC_SCHANNEL)) {
 247                 /* Opening a policy handle failed, perhaps it was
 248                  * because we don't get a 'wrong password' error on
 249                  * NTLMSSP binds */
 250 
 251                 /* Try again with schannel */
 252                 binding->flags |= DCERPC_SCHANNEL;
 253 
 254                 /* Try again, likewise on the same IPC$ share, 
 255                    secured with SCHANNEL */
 256                 ctx = dcerpc_secondary_auth_connection_send(state->domain->netlogon_pipe,
 257                                                             binding,
 258                                                             table, 
 259                                                             state->domain->libnet_ctx->cred,
 260                                                             state->domain->libnet_ctx->lp_ctx);
 261                 composite_continue(state->ctx, ctx, continuation, state);               
 262                 return true;
 263         } else {
 264                 return false;
 265         }
 266 }
 267 /* We should now have either an authenticated LSA pipe, or an error.  
 268  * On success, open a policy handle
 269  */     
 270 static void init_domain_recv_lsa_pipe(struct composite_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
 271 {
 272         struct rpc_request *req;
 273         struct init_domain_state *state =
 274                 talloc_get_type(ctx->async.private_data,
 275                                 struct init_domain_state);
 276 
 277         state->ctx->status = dcerpc_secondary_auth_connection_recv(ctx, state->domain,
 278                                                                    &state->domain->libnet_ctx->lsa.pipe);
 279         if (NT_STATUS_EQUAL(state->ctx->status, NT_STATUS_LOGON_FAILURE)) {
 280                 if (retry_with_schannel(state, state->domain->lsa_binding, 
 281                                         &ndr_table_lsarpc,
 282                                         init_domain_recv_lsa_pipe)) {
 283                         return;
 284                 }
 285         }
 286         if (!composite_is_ok(state->ctx)) return;
 287 
 288         talloc_steal(state->domain->libnet_ctx, state->domain->libnet_ctx->lsa.pipe);
 289         talloc_steal(state->domain->libnet_ctx->lsa.pipe, state->domain->lsa_binding);
 290         state->domain->libnet_ctx->lsa.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
 291         state->domain->libnet_ctx->lsa.name = state->domain->info->name;
 292 
 293         ZERO_STRUCT(state->domain->libnet_ctx->lsa.handle);
 294         state->lsa_openpolicy.in.system_name =
 295                 talloc_asprintf(state, "\\\\%s",
 296                                 dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe));
 297         ZERO_STRUCT(state->objectattr);
 298         state->lsa_openpolicy.in.attr = &state->objectattr;
 299         state->lsa_openpolicy.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
 300         state->lsa_openpolicy.out.handle = &state->domain->libnet_ctx->lsa.handle;
 301 
 302         req = dcerpc_lsa_OpenPolicy2_send(state->domain->libnet_ctx->lsa.pipe, state,
 303                                           &state->lsa_openpolicy);
 304 
 305         composite_continue_rpc(state->ctx, req, init_domain_recv_lsa_policy, state);
 306 }
 307 
 308 /* Receive a policy handle (or not, and retry the authentication) and
 309  * obtain some basic information about the domain */
 310 
 311 static void init_domain_recv_lsa_policy(struct rpc_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 312 {
 313         struct init_domain_state *state =
 314                 talloc_get_type(req->async.private_data,
 315                                 struct init_domain_state);
 316 
 317         state->ctx->status = dcerpc_ndr_request_recv(req);
 318         if ((!NT_STATUS_IS_OK(state->ctx->status)
 319               || !NT_STATUS_IS_OK(state->lsa_openpolicy.out.result))) {
 320                 if (retry_with_schannel(state, state->domain->lsa_binding, 
 321                                         &ndr_table_lsarpc,
 322                                         init_domain_recv_lsa_pipe)) {
 323                         return;
 324                 }
 325         }
 326         if (!composite_is_ok(state->ctx)) return;
 327         state->ctx->status = state->lsa_openpolicy.out.result;
 328         if (!composite_is_ok(state->ctx)) return;
 329 
 330         state->info = talloc_zero(state->ctx, union lsa_PolicyInformation);
 331         if (composite_nomem(state->info, state->ctx)) return;
 332 
 333         state->queryinfo.in.handle = &state->domain->libnet_ctx->lsa.handle;
 334         state->queryinfo.in.level = LSA_POLICY_INFO_ACCOUNT_DOMAIN;
 335         state->queryinfo.out.info = &state->info;
 336 
 337         req = dcerpc_lsa_QueryInfoPolicy_send(state->domain->libnet_ctx->lsa.pipe, state,
 338                                               &state->queryinfo);
 339         composite_continue_rpc(state->ctx, req,
 340                                init_domain_recv_queryinfo, state);
 341 }
 342 
 343 static void init_domain_recv_queryinfo(struct rpc_request *req)
     /* [<][>][^][v][top][bottom][index][help] */
 344 {
 345         struct init_domain_state *state =
 346                 talloc_get_type(req->async.private_data, struct init_domain_state);
 347         struct lsa_DomainInfo *dominfo;
 348         struct composite_context *ctx;
 349 
 350         state->ctx->status = dcerpc_ndr_request_recv(req);
 351         if (!composite_is_ok(state->ctx)) return;
 352         state->ctx->status = state->queryinfo.out.result;
 353         if (!composite_is_ok(state->ctx)) return;
 354 
 355         dominfo = &(*state->queryinfo.out.info)->account_domain;
 356 
 357         if (strcasecmp(state->domain->info->name, dominfo->name.string) != 0) {
 358                 DEBUG(2, ("Expected domain name %s, DC %s said %s\n",
 359                           state->domain->info->name,
 360                           dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe),
 361                           dominfo->name.string));
 362                 composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
 363                 return;
 364         }
 365 
 366         if (!dom_sid_equal(state->domain->info->sid, dominfo->sid)) {
 367                 DEBUG(2, ("Expected domain sid %s, DC %s said %s\n",
 368                           dom_sid_string(state, state->domain->info->sid),
 369                           dcerpc_server_name(state->domain->libnet_ctx->lsa.pipe),
 370                           dom_sid_string(state, dominfo->sid)));
 371                 composite_error(state->ctx, NT_STATUS_INVALID_DOMAIN_STATE);
 372                 return;
 373         }
 374 
 375         state->domain->samr_binding = init_domain_binding(state, &ndr_table_samr);
 376 
 377         /* We want to use the same flags as the LSA pipe did (so, if
 378          * it needed schannel, then we need that here too) */
 379         state->domain->samr_binding->flags = state->domain->lsa_binding->flags;
 380 
 381         state->domain->libnet_ctx->samr.pipe = NULL;
 382 
 383         ctx = wb_connect_samr_send(state, state->domain);
 384         composite_continue(state->ctx, ctx, init_domain_recv_samr, state);
 385 }
 386 
 387 /* Recv the SAMR details (SamrConnect and SamrOpenDomain handle) and
 388  * open an LDAP connection */
 389 static void init_domain_recv_samr(struct composite_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
 390 {
 391         struct init_domain_state *state =
 392                 talloc_get_type(ctx->async.private_data,
 393                                 struct init_domain_state);
 394 
 395         state->ctx->status = wb_connect_samr_recv(
 396                 ctx, state->domain,
 397                 &state->domain->libnet_ctx->samr.pipe,
 398                 &state->domain->libnet_ctx->samr.connect_handle,
 399                 &state->domain->libnet_ctx->samr.handle);
 400         if (!composite_is_ok(state->ctx)) return;
 401 
 402         talloc_steal(state->domain->libnet_ctx->samr.pipe, state->domain->samr_binding);
 403         state->domain->libnet_ctx->samr.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
 404         state->domain->libnet_ctx->samr.name = state->domain->info->name;
 405         state->domain->libnet_ctx->samr.sid = dom_sid_dup(
 406                                                 state->domain->libnet_ctx,
 407                                                 state->domain->info->sid);
 408 
 409         composite_done(state->ctx);
 410 }
 411 
 412 NTSTATUS wb_init_domain_recv(struct composite_context *c,
     /* [<][>][^][v][top][bottom][index][help] */
 413                              TALLOC_CTX *mem_ctx,
 414                              struct wbsrv_domain **result)
 415 {
 416         NTSTATUS status = composite_wait(c);
 417         if (NT_STATUS_IS_OK(status)) {
 418                 struct init_domain_state *state =
 419                         talloc_get_type(c->private_data,
 420                                         struct init_domain_state);
 421                 *result = talloc_steal(mem_ctx, state->domain);
 422         }
 423         talloc_free(c);
 424         return status;
 425 }
 426 
 427 NTSTATUS wb_init_domain(TALLOC_CTX *mem_ctx, struct wbsrv_service *service,
     /* [<][>][^][v][top][bottom][index][help] */
 428                         struct wb_dom_info *dom_info,
 429                         struct wbsrv_domain **result)
 430 {
 431         struct composite_context *c =
 432                 wb_init_domain_send(mem_ctx, service, dom_info);
 433         return wb_init_domain_recv(c, mem_ctx, result);
 434 }

/* [<][>][^][v][top][bottom][index][help] */