root/source4/librpc/rpc/dcerpc_auth.c

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

DEFINITIONS

This source file includes following definitions.
  1. dcerpc_init_syntaxes
  2. dcerpc_bind_auth_none_send
  3. dcerpc_bind_auth_none_recv
  4. dcerpc_bind_auth_none
  5. bind_auth_next_step
  6. bind_auth_recv_alter
  7. bind_auth_recv_bindreply
  8. dcerpc_bind_auth_send
  9. dcerpc_bind_auth_recv
  10. dcerpc_bind_auth

   1 /* 
   2    Unix SMB/CIFS implementation.
   3 
   4    Generic Authentication Interface
   5 
   6    Copyright (C) Andrew Tridgell 2003
   7    Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
   8    Copyright (C) Stefan Metzmacher 2004
   9    
  10    This program is free software; you can redistribute it and/or modify
  11    it under the terms of the GNU General Public License as published by
  12    the Free Software Foundation; either version 3 of the License, or
  13    (at your option) any later version.
  14    
  15    This program is distributed in the hope that it will be useful,
  16    but WITHOUT ANY WARRANTY; without even the implied warranty of
  17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18    GNU General Public License for more details.
  19    
  20    You should have received a copy of the GNU General Public License
  21    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  22 */
  23 
  24 #include "includes.h"
  25 #include "libcli/composite/composite.h"
  26 #include "auth/gensec/gensec.h"
  27 #include "librpc/rpc/dcerpc.h"
  28 #include "librpc/rpc/dcerpc_proto.h"
  29 #include "param/param.h"
  30 
  31 /*
  32   return the rpc syntax and transfer syntax given the pipe uuid and version
  33 */
  34 static NTSTATUS dcerpc_init_syntaxes(const struct ndr_interface_table *table,
     /* [<][>][^][v][top][bottom][index][help] */
  35                               struct ndr_syntax_id *syntax,
  36                               struct ndr_syntax_id *transfer_syntax)
  37 {
  38         syntax->uuid = table->syntax_id.uuid;
  39         syntax->if_version = table->syntax_id.if_version;
  40 
  41         *transfer_syntax = ndr_transfer_syntax;
  42 
  43         return NT_STATUS_OK;
  44 }
  45 
  46 
  47 /*
  48   Send request to do a non-authenticated dcerpc bind
  49 */
  50 struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
  51                                                      struct dcerpc_pipe *p,
  52                                                      const struct ndr_interface_table *table)
  53 {
  54         struct ndr_syntax_id syntax;
  55         struct ndr_syntax_id transfer_syntax;
  56 
  57         struct composite_context *c;
  58 
  59         c = composite_create(mem_ctx, p->conn->event_ctx);
  60         if (c == NULL) return NULL;
  61 
  62         c->status = dcerpc_init_syntaxes(table,
  63                                          &syntax, &transfer_syntax);
  64         if (!NT_STATUS_IS_OK(c->status)) {
  65                 DEBUG(2,("Invalid uuid string in "
  66                          "dcerpc_bind_auth_none_send\n"));
  67                 composite_error(c, c->status);
  68                 return c;
  69         }
  70 
  71         /* c was only allocated as a container for a possible error */
  72         talloc_free(c);
  73 
  74         return dcerpc_bind_send(p, mem_ctx, &syntax, &transfer_syntax);
  75 }
  76 
  77 
  78 /*
  79   Receive result of a non-authenticated dcerpc bind
  80 */
  81 NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
     /* [<][>][^][v][top][bottom][index][help] */
  82 {
  83         return dcerpc_bind_recv(ctx);
  84 }
  85 
  86 
  87 /*
  88   Perform sync non-authenticated dcerpc bind
  89 */
  90 _PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
     /* [<][>][^][v][top][bottom][index][help] */
  91                                const struct ndr_interface_table *table)
  92 {
  93         struct composite_context *ctx;
  94 
  95         ctx = dcerpc_bind_auth_none_send(p, p, table);
  96         return dcerpc_bind_auth_none_recv(ctx);
  97 }
  98 
  99 
 100 struct bind_auth_state {
 101         struct dcerpc_pipe *pipe;
 102         DATA_BLOB credentials;
 103         bool more_processing;   /* Is there anything more to do after the
 104                                  * first bind itself received? */
 105 };
 106 
 107 static void bind_auth_recv_alter(struct composite_context *creq);
 108 
 109 static void bind_auth_next_step(struct composite_context *c)
     /* [<][>][^][v][top][bottom][index][help] */
 110 {
 111         struct bind_auth_state *state;
 112         struct dcerpc_security *sec;
 113         struct composite_context *creq;
 114         bool more_processing = false;
 115 
 116         state = talloc_get_type(c->private_data, struct bind_auth_state);
 117         sec = &state->pipe->conn->security_state;
 118 
 119         /* The status value here, from GENSEC is vital to the security
 120          * of the system.  Even if the other end accepts, if GENSEC
 121          * claims 'MORE_PROCESSING_REQUIRED' then you must keep
 122          * feeding it blobs, or else the remote host/attacker might
 123          * avoid mutal authentication requirements.
 124          *
 125          * Likewise, you must not feed GENSEC too much (after the OK),
 126          * it doesn't like that either
 127          */
 128 
 129         c->status = gensec_update(sec->generic_state, state,
 130                                   sec->auth_info->credentials,
 131                                   &state->credentials);
 132         data_blob_free(&sec->auth_info->credentials);
 133 
 134         if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 135                 more_processing = true;
 136                 c->status = NT_STATUS_OK;
 137         }
 138 
 139         if (!composite_is_ok(c)) return;
 140 
 141         if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
 142                 gensec_want_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER);
 143         }
 144 
 145         if (state->credentials.length == 0) {
 146                 composite_done(c);
 147                 return;
 148         }
 149 
 150         sec->auth_info->credentials = state->credentials;
 151 
 152         if (!more_processing) {
 153                 /* NO reply expected, so just send it */
 154                 c->status = dcerpc_auth3(state->pipe, state);
 155                 data_blob_free(&state->credentials);
 156                 sec->auth_info->credentials = data_blob(NULL, 0);
 157                 if (!composite_is_ok(c)) return;
 158 
 159                 composite_done(c);
 160                 return;
 161         }
 162 
 163         /* We are demanding a reply, so use a request that will get us one */
 164 
 165         creq = dcerpc_alter_context_send(state->pipe, state,
 166                                          &state->pipe->syntax,
 167                                          &state->pipe->transfer_syntax);
 168         data_blob_free(&state->credentials);
 169         sec->auth_info->credentials = data_blob(NULL, 0);
 170         if (composite_nomem(creq, c)) return;
 171 
 172         composite_continue(c, creq, bind_auth_recv_alter, c);
 173 }
 174 
 175 
 176 static void bind_auth_recv_alter(struct composite_context *creq)
     /* [<][>][^][v][top][bottom][index][help] */
 177 {
 178         struct composite_context *c = talloc_get_type(creq->async.private_data,
 179                                                       struct composite_context);
 180 
 181         c->status = dcerpc_alter_context_recv(creq);
 182         if (!composite_is_ok(c)) return;
 183 
 184         bind_auth_next_step(c);
 185 }
 186 
 187 
 188 static void bind_auth_recv_bindreply(struct composite_context *creq)
     /* [<][>][^][v][top][bottom][index][help] */
 189 {
 190         struct composite_context *c = talloc_get_type(creq->async.private_data,
 191                                                       struct composite_context);
 192         struct bind_auth_state *state = talloc_get_type(c->private_data,
 193                                                         struct bind_auth_state);
 194 
 195         c->status = dcerpc_bind_recv(creq);
 196         if (!composite_is_ok(c)) return;
 197 
 198         if (!state->more_processing) {
 199                 /* The first gensec_update has not requested a second run, so
 200                  * we're done here. */
 201                 composite_done(c);
 202                 return;
 203         }
 204 
 205         bind_auth_next_step(c);
 206 }
 207 
 208 
 209 /**
 210    Bind to a DCE/RPC pipe, send async request
 211    @param mem_ctx TALLOC_CTX for the allocation of the composite_context
 212    @param p The dcerpc_pipe to bind (must already be connected)
 213    @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
 214    @param credentials The credentials of the account to connect with 
 215    @param auth_type Select the authentication scheme to use
 216    @param auth_level Chooses between unprotected (connect), signed or sealed
 217    @param service The service (used by Kerberos to select the service principal to contact)
 218    @retval A composite context describing the partial state of the bind
 219 */
 220 
 221 struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
     /* [<][>][^][v][top][bottom][index][help] */
 222                                                 struct dcerpc_pipe *p,
 223                                                 const struct ndr_interface_table *table,
 224                                                 struct cli_credentials *credentials,
 225                                                 struct gensec_settings *gensec_settings,
 226                                                 uint8_t auth_type, uint8_t auth_level,
 227                                                 const char *service)
 228 {
 229         struct composite_context *c, *creq;
 230         struct bind_auth_state *state;
 231         struct dcerpc_security *sec;
 232 
 233         struct ndr_syntax_id syntax, transfer_syntax;
 234 
 235         /* composite context allocation and setup */
 236         c = composite_create(mem_ctx, p->conn->event_ctx);
 237         if (c == NULL) return NULL;
 238 
 239         state = talloc(c, struct bind_auth_state);
 240         if (composite_nomem(state, c)) return c;
 241         c->private_data = state;
 242 
 243         state->pipe = p;
 244 
 245         c->status = dcerpc_init_syntaxes(table,
 246                                          &syntax,
 247                                          &transfer_syntax);
 248         if (!composite_is_ok(c)) return c;
 249 
 250         sec = &p->conn->security_state;
 251 
 252         c->status = gensec_client_start(p, &sec->generic_state,
 253                                         p->conn->event_ctx,
 254                                         gensec_settings);
 255         if (!NT_STATUS_IS_OK(c->status)) {
 256                 DEBUG(1, ("Failed to start GENSEC client mode: %s\n",
 257                           nt_errstr(c->status)));
 258                 composite_error(c, c->status);
 259                 return c;
 260         }
 261 
 262         c->status = gensec_set_credentials(sec->generic_state, credentials);
 263         if (!NT_STATUS_IS_OK(c->status)) {
 264                 DEBUG(1, ("Failed to set GENSEC client credentials: %s\n",
 265                           nt_errstr(c->status)));
 266                 composite_error(c, c->status);
 267                 return c;
 268         }
 269 
 270         c->status = gensec_set_target_hostname(sec->generic_state,
 271                                                p->conn->transport.target_hostname(p->conn));
 272         if (!NT_STATUS_IS_OK(c->status)) {
 273                 DEBUG(1, ("Failed to set GENSEC target hostname: %s\n", 
 274                           nt_errstr(c->status)));
 275                 composite_error(c, c->status);
 276                 return c;
 277         }
 278 
 279         if (service != NULL) {
 280                 c->status = gensec_set_target_service(sec->generic_state,
 281                                                       service);
 282                 if (!NT_STATUS_IS_OK(c->status)) {
 283                         DEBUG(1, ("Failed to set GENSEC target service: %s\n",
 284                                   nt_errstr(c->status)));
 285                         composite_error(c, c->status);
 286                         return c;
 287                 }
 288         }
 289 
 290         c->status = gensec_start_mech_by_authtype(sec->generic_state,
 291                                                   auth_type, auth_level);
 292         if (!NT_STATUS_IS_OK(c->status)) {
 293                 DEBUG(1, ("Failed to start GENSEC client mechanism %s: %s\n",
 294                           gensec_get_name_by_authtype(sec->generic_state, auth_type),
 295                           nt_errstr(c->status)));
 296                 composite_error(c, c->status);
 297                 return c;
 298         }
 299 
 300         sec->auth_info = talloc(p, struct dcerpc_auth);
 301         if (composite_nomem(sec->auth_info, c)) return c;
 302 
 303         sec->auth_info->auth_type = auth_type;
 304         sec->auth_info->auth_level = auth_level,
 305         sec->auth_info->auth_pad_length = 0;
 306         sec->auth_info->auth_reserved = 0;
 307         sec->auth_info->auth_context_id = random();
 308         sec->auth_info->credentials = data_blob(NULL, 0);
 309 
 310         /* The status value here, from GENSEC is vital to the security
 311          * of the system.  Even if the other end accepts, if GENSEC
 312          * claims 'MORE_PROCESSING_REQUIRED' then you must keep
 313          * feeding it blobs, or else the remote host/attacker might
 314          * avoid mutal authentication requirements.
 315          *
 316          * Likewise, you must not feed GENSEC too much (after the OK),
 317          * it doesn't like that either
 318          */
 319 
 320         c->status = gensec_update(sec->generic_state, state,
 321                                   sec->auth_info->credentials,
 322                                   &state->credentials);
 323         if (!NT_STATUS_IS_OK(c->status) &&
 324             !NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
 325                 composite_error(c, c->status);
 326                 return c;
 327         }
 328 
 329         state->more_processing = NT_STATUS_EQUAL(c->status,
 330                                                  NT_STATUS_MORE_PROCESSING_REQUIRED);
 331 
 332         if (state->credentials.length == 0) {
 333                 composite_done(c);
 334                 return c;
 335         }
 336 
 337         sec->auth_info->credentials = state->credentials;
 338 
 339         /* The first request always is a dcerpc_bind. The subsequent ones
 340          * depend on gensec results */
 341         creq = dcerpc_bind_send(p, state, &syntax, &transfer_syntax);
 342         data_blob_free(&state->credentials);
 343         sec->auth_info->credentials = data_blob(NULL, 0);
 344         if (composite_nomem(creq, c)) return c;
 345 
 346         composite_continue(c, creq, bind_auth_recv_bindreply, c);
 347         return c;
 348 }
 349 
 350 
 351 /**
 352    Bind to a DCE/RPC pipe, receive result
 353    @param creq A composite context describing state of async call
 354    @retval NTSTATUS code
 355 */
 356 
 357 NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
     /* [<][>][^][v][top][bottom][index][help] */
 358 {
 359         NTSTATUS result = composite_wait(creq);
 360         struct bind_auth_state *state = talloc_get_type(creq->private_data,
 361                                                         struct bind_auth_state);
 362 
 363         if (NT_STATUS_IS_OK(result)) {
 364                 /*
 365                   after a successful authenticated bind the session
 366                   key reverts to the generic session key
 367                 */
 368                 state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
 369         }
 370         
 371         talloc_free(creq);
 372         return result;
 373 }
 374 
 375 
 376 /**
 377    Perform a GENSEC authenticated bind to a DCE/RPC pipe, sync
 378    @param p The dcerpc_pipe to bind (must already be connected)
 379    @param table The interface table to use (the DCE/RPC bind both selects and interface and authenticates)
 380    @param credentials The credentials of the account to connect with 
 381    @param auth_type Select the authentication scheme to use
 382    @param auth_level Chooses between unprotected (connect), signed or sealed
 383    @param service The service (used by Kerberos to select the service principal to contact)
 384    @retval NTSTATUS status code
 385 */
 386 
 387 _PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
     /* [<][>][^][v][top][bottom][index][help] */
 388                           const struct ndr_interface_table *table,
 389                           struct cli_credentials *credentials,
 390                           struct gensec_settings *gensec_settings,
 391                           uint8_t auth_type, uint8_t auth_level,
 392                           const char *service)
 393 {
 394         struct composite_context *creq;
 395         creq = dcerpc_bind_auth_send(p, p, table, credentials, gensec_settings,
 396                                      auth_type, auth_level, service);
 397         return dcerpc_bind_auth_recv(creq);
 398 }

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