2
Unix SMB/CIFS implementation.
4
Generic Authentication Interface
6
Copyright (C) Andrew Tridgell 2003
7
Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004-2005
8
Copyright (C) Stefan Metzmacher 2004
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.
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.
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/>.
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"
32
return the rpc syntax and transfer syntax given the pipe uuid and version
34
static NTSTATUS dcerpc_init_syntaxes(const struct ndr_interface_table *table,
35
struct ndr_syntax_id *syntax,
36
struct ndr_syntax_id *transfer_syntax)
38
syntax->uuid = table->syntax_id.uuid;
39
syntax->if_version = table->syntax_id.if_version;
41
*transfer_syntax = ndr_transfer_syntax;
48
Send request to do a non-authenticated dcerpc bind
50
struct composite_context *dcerpc_bind_auth_none_send(TALLOC_CTX *mem_ctx,
51
struct dcerpc_pipe *p,
52
const struct ndr_interface_table *table)
54
struct ndr_syntax_id syntax;
55
struct ndr_syntax_id transfer_syntax;
57
struct composite_context *c;
59
c = composite_create(mem_ctx, p->conn->event_ctx);
60
if (c == NULL) return NULL;
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);
71
/* c was only allocated as a container for a possible error */
74
return dcerpc_bind_send(p, mem_ctx, &syntax, &transfer_syntax);
79
Receive result of a non-authenticated dcerpc bind
81
NTSTATUS dcerpc_bind_auth_none_recv(struct composite_context *ctx)
83
return dcerpc_bind_recv(ctx);
88
Perform sync non-authenticated dcerpc bind
90
_PUBLIC_ NTSTATUS dcerpc_bind_auth_none(struct dcerpc_pipe *p,
91
const struct ndr_interface_table *table)
93
struct composite_context *ctx;
95
ctx = dcerpc_bind_auth_none_send(p, p, table);
96
return dcerpc_bind_auth_none_recv(ctx);
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? */
107
static void bind_auth_recv_alter(struct composite_context *creq);
109
static void bind_auth_next_step(struct composite_context *c)
111
struct bind_auth_state *state;
112
struct dcerpc_security *sec;
113
struct composite_context *creq;
114
bool more_processing = false;
116
state = talloc_get_type(c->private_data, struct bind_auth_state);
117
sec = &state->pipe->conn->security_state;
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.
125
* Likewise, you must not feed GENSEC too much (after the OK),
126
* it doesn't like that either
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);
134
if (NT_STATUS_EQUAL(c->status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
135
more_processing = true;
136
c->status = NT_STATUS_OK;
139
if (!composite_is_ok(c)) return;
141
if (state->pipe->conn->flags & DCERPC_HEADER_SIGNING) {
142
gensec_want_feature(sec->generic_state, GENSEC_FEATURE_SIGN_PKT_HEADER);
145
if (state->credentials.length == 0) {
150
sec->auth_info->credentials = state->credentials;
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;
163
/* We are demanding a reply, so use a request that will get us one */
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;
172
composite_continue(c, creq, bind_auth_recv_alter, c);
176
static void bind_auth_recv_alter(struct composite_context *creq)
178
struct composite_context *c = talloc_get_type(creq->async.private_data,
179
struct composite_context);
181
c->status = dcerpc_alter_context_recv(creq);
182
if (!composite_is_ok(c)) return;
184
bind_auth_next_step(c);
188
static void bind_auth_recv_bindreply(struct composite_context *creq)
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);
195
c->status = dcerpc_bind_recv(creq);
196
if (!composite_is_ok(c)) return;
198
if (!state->more_processing) {
199
/* The first gensec_update has not requested a second run, so
200
* we're done here. */
205
bind_auth_next_step(c);
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
221
struct composite_context *dcerpc_bind_auth_send(TALLOC_CTX *mem_ctx,
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,
229
struct composite_context *c, *creq;
230
struct bind_auth_state *state;
231
struct dcerpc_security *sec;
233
struct ndr_syntax_id syntax, transfer_syntax;
235
/* composite context allocation and setup */
236
c = composite_create(mem_ctx, p->conn->event_ctx);
237
if (c == NULL) return NULL;
239
state = talloc(c, struct bind_auth_state);
240
if (composite_nomem(state, c)) return c;
241
c->private_data = state;
245
c->status = dcerpc_init_syntaxes(table,
248
if (!composite_is_ok(c)) return c;
250
sec = &p->conn->security_state;
252
c->status = gensec_client_start(p, &sec->generic_state,
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);
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);
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);
279
if (service != NULL) {
280
c->status = gensec_set_target_service(sec->generic_state,
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);
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);
300
sec->auth_info = talloc(p, struct dcerpc_auth);
301
if (composite_nomem(sec->auth_info, c)) return c;
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);
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.
316
* Likewise, you must not feed GENSEC too much (after the OK),
317
* it doesn't like that either
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);
329
state->more_processing = NT_STATUS_EQUAL(c->status,
330
NT_STATUS_MORE_PROCESSING_REQUIRED);
332
if (state->credentials.length == 0) {
337
sec->auth_info->credentials = state->credentials;
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;
346
composite_continue(c, creq, bind_auth_recv_bindreply, c);
352
Bind to a DCE/RPC pipe, receive result
353
@param creq A composite context describing state of async call
354
@retval NTSTATUS code
357
NTSTATUS dcerpc_bind_auth_recv(struct composite_context *creq)
359
NTSTATUS result = composite_wait(creq);
360
struct bind_auth_state *state = talloc_get_type(creq->private_data,
361
struct bind_auth_state);
363
if (NT_STATUS_IS_OK(result)) {
365
after a successful authenticated bind the session
366
key reverts to the generic session key
368
state->pipe->conn->security_state.session_key = dcerpc_generic_session_key;
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
387
_PUBLIC_ NTSTATUS dcerpc_bind_auth(struct dcerpc_pipe *p,
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,
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);