50d6f5933a8e29b4f0f0c1868c2b96ef8d73d6f0
[bbaumbach/samba-autobuild/.git] / source4 / rpc_server / dcesrv_auth.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    server side dcerpc authentication code
5
6    Copyright (C) Andrew Tridgell 2003
7    Copyright (C) Stefan (metze) Metzmacher 2004
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 "rpc_server/dcerpc_server.h"
25 #include "rpc_server/dcerpc_server_proto.h"
26 #include "librpc/rpc/dcerpc_proto.h"
27 #include "librpc/gen_ndr/ndr_dcerpc.h"
28 #include "auth/credentials/credentials.h"
29 #include "auth/gensec/gensec.h"
30 #include "auth/auth.h"
31 #include "param/param.h"
32
33 /*
34   parse any auth information from a dcerpc bind request
35   return false if we can't handle the auth request for some 
36   reason (in which case we send a bind_nak)
37 */
38 bool dcesrv_auth_bind(struct dcesrv_call_state *call)
39 {
40         struct cli_credentials *server_credentials;
41         struct ncacn_packet *pkt = &call->pkt;
42         struct dcesrv_connection *dce_conn = call->conn;
43         struct dcesrv_auth *auth = &dce_conn->auth_state;
44         NTSTATUS status;
45         uint32_t auth_length;
46
47         if (pkt->u.bind.auth_info.length == 0) {
48                 dce_conn->auth_state.auth_info = NULL;
49                 return true;
50         }
51
52         dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
53         if (!dce_conn->auth_state.auth_info) {
54                 return false;
55         }
56
57         status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.bind.auth_info,
58                                           dce_conn->auth_state.auth_info,
59                                           &auth_length, false);
60         server_credentials 
61                 = cli_credentials_init(call);
62         if (!server_credentials) {
63                 DEBUG(1, ("Failed to init server credentials\n"));
64                 return false;
65         }
66         
67         cli_credentials_set_conf(server_credentials, call->conn->dce_ctx->lp_ctx);
68         status = cli_credentials_set_machine_account(server_credentials, call->conn->dce_ctx->lp_ctx);
69         if (!NT_STATUS_IS_OK(status)) {
70                 DEBUG(10, ("Failed to obtain server credentials, perhaps a standalone server?: %s\n", nt_errstr(status)));
71                 talloc_free(server_credentials);
72                 server_credentials = NULL;
73         }
74
75         status = samba_server_gensec_start(dce_conn, call->event_ctx, 
76                                            call->msg_ctx,
77                                            call->conn->dce_ctx->lp_ctx, 
78                                            server_credentials,
79                                            NULL,
80                                            &auth->gensec_security);
81
82         status = gensec_start_mech_by_authtype(auth->gensec_security, auth->auth_info->auth_type, 
83                                                auth->auth_info->auth_level);
84
85         if (!NT_STATUS_IS_OK(status)) {
86                 DEBUG(1, ("Failed to start GENSEC mechanism for DCERPC server: auth_type=%d, auth_level=%d: %s\n", 
87                           (int)auth->auth_info->auth_type,
88                           (int)auth->auth_info->auth_level,
89                           nt_errstr(status)));
90                 return false;
91         }
92
93         if (call->conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
94                 gensec_want_feature(auth->gensec_security, GENSEC_FEATURE_SIGN_PKT_HEADER);
95         }
96
97         return true;
98 }
99
100 /*
101   add any auth information needed in a bind ack, and process the authentication
102   information found in the bind.
103 */
104 NTSTATUS dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
105 {
106         struct dcesrv_connection *dce_conn = call->conn;
107         NTSTATUS status;
108
109         if (!call->conn->auth_state.gensec_security) {
110                 return NT_STATUS_OK;
111         }
112
113         status = gensec_update(dce_conn->auth_state.gensec_security,
114                                call,
115                                dce_conn->auth_state.auth_info->credentials, 
116                                &dce_conn->auth_state.auth_info->credentials);
117         
118         if (NT_STATUS_IS_OK(status)) {
119                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
120                                              &dce_conn->auth_state.session_info);
121                 if (!NT_STATUS_IS_OK(status)) {
122                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
123                         return status;
124                 }
125
126                 if (dce_conn->state_flags & DCESRV_CALL_STATE_FLAG_HEADER_SIGNING) {
127                         gensec_want_feature(dce_conn->auth_state.gensec_security,
128                                             GENSEC_FEATURE_SIGN_PKT_HEADER);
129                 }
130
131                 /* Now that we are authenticated, go back to the generic session key... */
132                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
133                 return NT_STATUS_OK;
134         } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
135                 dce_conn->auth_state.auth_info->auth_pad_length = 0;
136                 dce_conn->auth_state.auth_info->auth_reserved = 0;
137                 return NT_STATUS_OK;
138         } else {
139                 DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status)));
140                 return status;
141         }
142 }
143
144
145 /*
146   process the final stage of a auth request
147 */
148 bool dcesrv_auth_auth3(struct dcesrv_call_state *call)
149 {
150         struct ncacn_packet *pkt = &call->pkt;
151         struct dcesrv_connection *dce_conn = call->conn;
152         NTSTATUS status;
153         uint32_t auth_length;
154
155         /* We can't work without an existing gensec state, and an new blob to feed it */
156         if (!dce_conn->auth_state.auth_info ||
157             !dce_conn->auth_state.gensec_security ||
158             pkt->u.auth3.auth_info.length == 0) {
159                 return false;
160         }
161
162         status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.auth3.auth_info,
163                                           dce_conn->auth_state.auth_info, &auth_length, true);
164         if (!NT_STATUS_IS_OK(status)) {
165                 return false;
166         }
167
168         /* Pass the extra data we got from the client down to gensec for processing */
169         status = gensec_update(dce_conn->auth_state.gensec_security,
170                                call,
171                                dce_conn->auth_state.auth_info->credentials, 
172                                &dce_conn->auth_state.auth_info->credentials);
173         if (NT_STATUS_IS_OK(status)) {
174                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
175                                              &dce_conn->auth_state.session_info);
176                 if (!NT_STATUS_IS_OK(status)) {
177                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
178                         return false;
179                 }
180                 /* Now that we are authenticated, go back to the generic session key... */
181                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
182                 return true;
183         } else {
184                 DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n", 
185                           nt_errstr(status)));
186                 return false;
187         }
188 }
189
190 /*
191   parse any auth information from a dcerpc alter request
192   return false if we can't handle the auth request for some 
193   reason (in which case we send a bind_nak (is this true for here?))
194 */
195 bool dcesrv_auth_alter(struct dcesrv_call_state *call)
196 {
197         struct ncacn_packet *pkt = &call->pkt;
198         struct dcesrv_connection *dce_conn = call->conn;
199         NTSTATUS status;
200         uint32_t auth_length;
201
202         /* on a pure interface change there is no auth blob */
203         if (pkt->u.alter.auth_info.length == 0) {
204                 return true;
205         }
206
207         /* We can't work without an existing gensec state */
208         if (!dce_conn->auth_state.gensec_security) {
209                 return false;
210         }
211
212         dce_conn->auth_state.auth_info = talloc(dce_conn, struct dcerpc_auth);
213         if (!dce_conn->auth_state.auth_info) {
214                 return false;
215         }
216
217         status = dcerpc_pull_auth_trailer(pkt, call, &pkt->u.alter.auth_info,
218                                           dce_conn->auth_state.auth_info,
219                                           &auth_length, true);
220         if (!NT_STATUS_IS_OK(status)) {
221                 return false;
222         }
223
224         return true;
225 }
226
227 /*
228   add any auth information needed in a alter ack, and process the authentication
229   information found in the alter.
230 */
231 NTSTATUS dcesrv_auth_alter_ack(struct dcesrv_call_state *call, struct ncacn_packet *pkt)
232 {
233         struct dcesrv_connection *dce_conn = call->conn;
234         NTSTATUS status;
235
236         /* on a pure interface change there is no auth_info structure
237            setup */
238         if (!call->conn->auth_state.auth_info ||
239             dce_conn->auth_state.auth_info->credentials.length == 0) {
240                 return NT_STATUS_OK;
241         }
242
243         if (!call->conn->auth_state.gensec_security) {
244                 return NT_STATUS_INVALID_PARAMETER;
245         }
246
247         status = gensec_update(dce_conn->auth_state.gensec_security,
248                                call,
249                                dce_conn->auth_state.auth_info->credentials, 
250                                &dce_conn->auth_state.auth_info->credentials);
251
252         if (NT_STATUS_IS_OK(status)) {
253                 status = gensec_session_info(dce_conn->auth_state.gensec_security,
254                                              &dce_conn->auth_state.session_info);
255                 if (!NT_STATUS_IS_OK(status)) {
256                         DEBUG(1, ("Failed to establish session_info: %s\n", nt_errstr(status)));
257                         return status;
258                 }
259
260                 /* Now that we are authenticated, got back to the generic session key... */
261                 dce_conn->auth_state.session_key = dcesrv_generic_session_key;
262                 return NT_STATUS_OK;
263         } else if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
264                 dce_conn->auth_state.auth_info->auth_pad_length = 0;
265                 dce_conn->auth_state.auth_info->auth_reserved = 0;
266                 return NT_STATUS_OK;
267         }
268
269         DEBUG(2, ("Failed to finish dcesrv auth alter_ack: %s\n", nt_errstr(status)));
270         return status;
271 }
272
273 /*
274   check credentials on a request
275 */
276 bool dcesrv_auth_request(struct dcesrv_call_state *call, DATA_BLOB *full_packet)
277 {
278         struct ncacn_packet *pkt = &call->pkt;
279         struct dcesrv_connection *dce_conn = call->conn;
280         struct dcerpc_auth auth;
281         NTSTATUS status;
282         uint32_t auth_length;
283         size_t hdr_size = DCERPC_REQUEST_LENGTH;
284
285         if (!dce_conn->auth_state.auth_info ||
286             !dce_conn->auth_state.gensec_security) {
287                 return true;
288         }
289
290         if (pkt->pfc_flags & DCERPC_PFC_FLAG_OBJECT_UUID) {
291                 hdr_size += 16;
292         }
293
294         switch (dce_conn->auth_state.auth_info->auth_level) {
295         case DCERPC_AUTH_LEVEL_PRIVACY:
296         case DCERPC_AUTH_LEVEL_INTEGRITY:
297                 break;
298
299         case DCERPC_AUTH_LEVEL_CONNECT:
300                 if (pkt->auth_length != 0) {
301                         break;
302                 }
303                 return true;
304         case DCERPC_AUTH_LEVEL_NONE:
305                 if (pkt->auth_length != 0) {
306                         return false;
307                 }
308                 return true;
309
310         default:
311                 return false;
312         }
313
314         status = dcerpc_pull_auth_trailer(pkt, call,
315                                           &pkt->u.request.stub_and_verifier,
316                                           &auth, &auth_length, false);
317         if (!NT_STATUS_IS_OK(status)) {
318                 return false;
319         }
320
321         pkt->u.request.stub_and_verifier.length -= auth_length;
322
323         /* check signature or unseal the packet */
324         switch (dce_conn->auth_state.auth_info->auth_level) {
325         case DCERPC_AUTH_LEVEL_PRIVACY:
326                 status = gensec_unseal_packet(dce_conn->auth_state.gensec_security,
327                                               call,
328                                               full_packet->data + hdr_size,
329                                               pkt->u.request.stub_and_verifier.length, 
330                                               full_packet->data,
331                                               full_packet->length-auth.credentials.length,
332                                               &auth.credentials);
333                 memcpy(pkt->u.request.stub_and_verifier.data, 
334                        full_packet->data + hdr_size,
335                        pkt->u.request.stub_and_verifier.length);
336                 break;
337
338         case DCERPC_AUTH_LEVEL_INTEGRITY:
339                 status = gensec_check_packet(dce_conn->auth_state.gensec_security,
340                                              call,
341                                              pkt->u.request.stub_and_verifier.data, 
342                                              pkt->u.request.stub_and_verifier.length,
343                                              full_packet->data,
344                                              full_packet->length-auth.credentials.length,
345                                              &auth.credentials);
346                 break;
347
348         case DCERPC_AUTH_LEVEL_CONNECT:
349                 /* for now we ignore possible signatures here */
350                 status = NT_STATUS_OK;
351                 break;
352
353         default:
354                 status = NT_STATUS_INVALID_LEVEL;
355                 break;
356         }
357
358         /* remove the indicated amount of padding */
359         if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
360                 return false;
361         }
362         pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
363
364         return NT_STATUS_IS_OK(status);
365 }
366
367
368 /* 
369    push a signed or sealed dcerpc request packet into a blob
370 */
371 bool dcesrv_auth_response(struct dcesrv_call_state *call,
372                           DATA_BLOB *blob, size_t sig_size,
373                           struct ncacn_packet *pkt)
374 {
375         struct dcesrv_connection *dce_conn = call->conn;
376         NTSTATUS status;
377         enum ndr_err_code ndr_err;
378         struct ndr_push *ndr;
379         uint32_t payload_length;
380         DATA_BLOB creds2;
381
382         /* non-signed packets are simple */
383         if (sig_size == 0) {
384                 status = ncacn_push_auth(blob, call, pkt, NULL);
385                 return NT_STATUS_IS_OK(status);
386         }
387
388         switch (dce_conn->auth_state.auth_info->auth_level) {
389         case DCERPC_AUTH_LEVEL_PRIVACY:
390         case DCERPC_AUTH_LEVEL_INTEGRITY:
391                 break;
392
393         case DCERPC_AUTH_LEVEL_CONNECT:
394                 /*
395                  * TODO: let the gensec mech decide if it wants to generate a signature
396                  *       that might be needed for schannel...
397                  */
398                 status = ncacn_push_auth(blob, call, pkt, NULL);
399                 return NT_STATUS_IS_OK(status);
400
401         case DCERPC_AUTH_LEVEL_NONE:
402                 status = ncacn_push_auth(blob, call, pkt, NULL);
403                 return NT_STATUS_IS_OK(status);
404
405         default:
406                 return false;
407         }
408
409         ndr = ndr_push_init_ctx(call);
410         if (!ndr) {
411                 return false;
412         }
413
414         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
415                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
416         }
417
418         ndr_err = ndr_push_ncacn_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
419         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
420                 return false;
421         }
422
423         /* pad to 16 byte multiple in the payload portion of the
424            packet. This matches what w2k3 does. Note that we can't use
425            ndr_push_align() as that is relative to the start of the
426            whole packet, whereas w2k8 wants it relative to the start
427            of the stub */
428         dce_conn->auth_state.auth_info->auth_pad_length =
429                 (16 - (pkt->u.response.stub_and_verifier.length & 15)) & 15;
430         ndr_err = ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
431         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
432                 return false;
433         }
434
435         payload_length = pkt->u.response.stub_and_verifier.length +
436                 dce_conn->auth_state.auth_info->auth_pad_length;
437
438         /* we start without signature, it will appended later */
439         dce_conn->auth_state.auth_info->credentials = data_blob(NULL, 0);
440
441         /* add the auth verifier */
442         ndr_err = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS,
443                                       dce_conn->auth_state.auth_info);
444         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
445                 return false;
446         }
447
448         /* extract the whole packet as a blob */
449         *blob = ndr_push_blob(ndr);
450
451         /*
452          * Setup the frag and auth length in the packet buffer.
453          * This is needed if the GENSEC mech does AEAD signing
454          * of the packet headers. The signature itself will be
455          * appended later.
456          */
457         dcerpc_set_frag_length(blob, blob->length + sig_size);
458         dcerpc_set_auth_length(blob, sig_size);
459
460         /* sign or seal the packet */
461         switch (dce_conn->auth_state.auth_info->auth_level) {
462         case DCERPC_AUTH_LEVEL_PRIVACY:
463                 status = gensec_seal_packet(dce_conn->auth_state.gensec_security, 
464                                             call,
465                                             ndr->data + DCERPC_REQUEST_LENGTH, 
466                                             payload_length,
467                                             blob->data,
468                                             blob->length,
469                                             &creds2);
470                 break;
471
472         case DCERPC_AUTH_LEVEL_INTEGRITY:
473                 status = gensec_sign_packet(dce_conn->auth_state.gensec_security, 
474                                             call,
475                                             ndr->data + DCERPC_REQUEST_LENGTH, 
476                                             payload_length,
477                                             blob->data,
478                                             blob->length,
479                                             &creds2);
480                 break;
481
482         default:
483                 status = NT_STATUS_INVALID_LEVEL;
484                 break;
485         }
486
487         if (!NT_STATUS_IS_OK(status)) {
488                 return false;
489         }       
490
491         if (creds2.length != sig_size) {
492                 DEBUG(3,("dcesrv_auth_response: creds2.length[%u] != sig_size[%u] pad[%u] stub[%u]\n",
493                          (unsigned)creds2.length, (uint32_t)sig_size,
494                          (unsigned)dce_conn->auth_state.auth_info->auth_pad_length,
495                          (unsigned)pkt->u.response.stub_and_verifier.length));
496                 dcerpc_set_frag_length(blob, blob->length + creds2.length);
497                 dcerpc_set_auth_length(blob, creds2.length);
498         }
499
500         if (!data_blob_append(call, blob, creds2.data, creds2.length)) {
501                 status = NT_STATUS_NO_MEMORY;
502                 return false;
503         }
504         data_blob_free(&creds2);
505
506         return true;
507 }