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