r874: This patch is a pile of work on NTLMSSP:
[kai/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
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25
26 /*
27   parse any auth information from a dcerpc bind request
28   return False if we can't handle the auth request for some 
29   reason (in which case we send a bind_nak)
30 */
31 BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
32 {
33         struct dcerpc_packet *pkt = &call->pkt;
34         struct dcesrv_connection *dce_conn = call->conn;
35         NTSTATUS status;
36
37         if (pkt->u.bind.auth_info.length == 0) {
38                 dce_conn->auth_state.auth_info = NULL;
39                 return True;
40         }
41
42         dce_conn->auth_state.auth_info = talloc_p(dce_conn->mem_ctx, struct dcerpc_auth);
43         if (!dce_conn->auth_state.auth_info) {
44                 return False;
45         }
46
47         status = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
48                                       call->mem_ctx,
49                                       dce_conn->auth_state.auth_info,
50                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
51         if (!NT_STATUS_IS_OK(status)) {
52                 return False;
53         }
54
55         if (dce_conn->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_NTLMSSP) {
56                 /* only do NTLMSSP for now */
57                 DEBUG(2,("auth_type %d not supported\n", dce_conn->auth_state.auth_info->auth_type));
58                 return False;
59         }
60
61         if (dce_conn->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY &&
62             dce_conn->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
63                 DEBUG(2,("auth_level %d not supported\n", dce_conn->auth_state.auth_info->auth_level));
64                 return False;
65         }
66
67         status = auth_ntlmssp_start(&dce_conn->auth_state.ntlmssp_state);
68         if (!NT_STATUS_IS_OK(status)) {
69                 DEBUG(2, ("Failed to start NTLMSSP subsystem!\n"));
70                 return False;
71         }
72
73         return True;
74 }
75
76 /*
77   add any auth information needed in a bind ack
78 */
79 BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *pkt)
80 {
81         struct dcesrv_connection *dce_conn = call->conn;
82         NTSTATUS status;
83
84         if (!call->conn->auth_state.ntlmssp_state) {
85                 return True;
86         }
87
88         status = auth_ntlmssp_update(dce_conn->auth_state.ntlmssp_state,
89                                      call->mem_ctx,
90                                      dce_conn->auth_state.auth_info->credentials, 
91                                      &dce_conn->auth_state.auth_info->credentials);
92         if (!NT_STATUS_IS_OK(status) && 
93             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
94                 DEBUG(2, ("Failed to start NTLMSSP process NTLMSSP negotiate: %s\n", nt_errstr(status)));
95                 return False;
96         }
97
98         dce_conn->auth_state.auth_info->auth_pad_length = 0;
99         dce_conn->auth_state.auth_info->auth_reserved = 0;
100                                      
101         return True;
102 }
103
104
105 /*
106   process the final stage of a NTLMSSP auth request
107 */
108 BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
109 {
110         struct dcerpc_packet *pkt = &call->pkt;
111         struct dcesrv_connection *dce_conn = call->conn;
112         NTSTATUS status;
113
114         if (!dce_conn->auth_state.auth_info ||
115             !dce_conn->auth_state.ntlmssp_state ||
116             pkt->u.auth.auth_info.length == 0) {
117                 return False;
118         }
119
120         status = ndr_pull_struct_blob(&pkt->u.auth.auth_info,
121                                       call->mem_ctx,
122                                       dce_conn->auth_state.auth_info,
123                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
124         if (!NT_STATUS_IS_OK(status)) {
125                 return False;
126         }
127
128         if (dce_conn->auth_state.auth_info->auth_type != DCERPC_AUTH_TYPE_NTLMSSP) {
129                 return False;
130         }
131         if (dce_conn->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_INTEGRITY &&
132             dce_conn->auth_state.auth_info->auth_level != DCERPC_AUTH_LEVEL_PRIVACY) {
133                 return False;
134         }
135
136         status = auth_ntlmssp_update(dce_conn->auth_state.ntlmssp_state,
137                                      call->mem_ctx,
138                                      dce_conn->auth_state.auth_info->credentials, 
139                                      &dce_conn->auth_state.auth_info->credentials);
140         if (!NT_STATUS_IS_OK(status)) {
141                 DEBUG(4, ("User failed to authenticated with NTLMSSP: %s\n", nt_errstr(status)));
142                 return False;
143         }
144
145         return True;
146 }
147
148
149 /*
150   check credentials on a request
151 */
152 BOOL dcesrv_auth_request(struct dcesrv_call_state *call)
153 {
154         struct dcerpc_packet *pkt = &call->pkt;
155         struct dcesrv_connection *dce_conn = call->conn;
156         DATA_BLOB auth_blob;
157         struct dcerpc_auth auth;
158         struct ndr_pull *ndr;
159         NTSTATUS status;
160
161         if (!dce_conn->auth_state.auth_info ||
162             !dce_conn->auth_state.ntlmssp_state) {
163                 return True;
164         }
165
166         auth_blob.length = 8 + pkt->auth_length;
167
168         /* check for a valid length */
169         if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
170                 return False;
171         }
172
173         auth_blob.data = 
174                 pkt->u.request.stub_and_verifier.data + 
175                 pkt->u.request.stub_and_verifier.length - auth_blob.length;
176         pkt->u.request.stub_and_verifier.length -= auth_blob.length;
177
178         /* pull the auth structure */
179         ndr = ndr_pull_init_blob(&auth_blob, call->mem_ctx);
180         if (!ndr) {
181                 return False;
182         }
183
184         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
185                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
186         }
187
188         status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
189         if (!NT_STATUS_IS_OK(status)) {
190                 return False;
191         }
192
193         /* check signature or unseal the packet */
194         switch (dce_conn->auth_state.auth_info->auth_level) {
195         case DCERPC_AUTH_LEVEL_PRIVACY:
196                 status = ntlmssp_unseal_packet(dce_conn->auth_state.ntlmssp_state->ntlmssp_state, 
197                                                call->mem_ctx,
198                                                pkt->u.request.stub_and_verifier.data, 
199                                                pkt->u.request.stub_and_verifier.length, 
200                                                &auth.credentials);
201                 break;
202
203         case DCERPC_AUTH_LEVEL_INTEGRITY:
204                 status = ntlmssp_check_packet(dce_conn->auth_state.ntlmssp_state->ntlmssp_state, 
205                                               call->mem_ctx,
206                                               pkt->u.request.stub_and_verifier.data, 
207                                               pkt->u.request.stub_and_verifier.length, 
208                                               &auth.credentials);
209                 break;
210
211         default:
212                 status = NT_STATUS_INVALID_LEVEL;
213                 break;
214         }
215
216         /* remove the indicated amount of paddiing */
217         if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
218                 return False;
219         }
220         pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
221
222         return NT_STATUS_IS_OK(status);
223 }
224
225
226 /* 
227    push a signed or sealed dcerpc request packet into a blob
228 */
229 BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
230                           DATA_BLOB *blob, struct dcerpc_packet *pkt)
231 {
232         struct dcesrv_connection *dce_conn = call->conn;
233         NTSTATUS status;
234         struct ndr_push *ndr;
235
236         /* non-signed packets are simple */
237         if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.ntlmssp_state) {
238                 status = dcerpc_push_auth(blob, call->mem_ctx, pkt, NULL);
239                 return NT_STATUS_IS_OK(status);
240         }
241
242         ndr = ndr_push_init_ctx(call->mem_ctx);
243         if (!ndr) {
244                 return False;
245         }
246
247         if (pkt->drep[0] & DCERPC_DREP_LE) {
248                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
249         }
250
251         status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
252         if (!NT_STATUS_IS_OK(status)) {
253                 return False;
254         }
255
256         /* pad to 8 byte multiple */
257         dce_conn->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
258         ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
259
260         /* sign or seal the packet */
261         switch (dce_conn->auth_state.auth_info->auth_level) {
262         case DCERPC_AUTH_LEVEL_PRIVACY:
263                 status = ntlmssp_seal_packet(dce_conn->auth_state.ntlmssp_state->ntlmssp_state, 
264                                              call->mem_ctx,
265                                              ndr->data + DCERPC_REQUEST_LENGTH, 
266                                              ndr->offset - DCERPC_REQUEST_LENGTH,
267                                              &dce_conn->auth_state.auth_info->credentials);
268                 break;
269
270         case DCERPC_AUTH_LEVEL_INTEGRITY:
271                 status = ntlmssp_sign_packet(dce_conn->auth_state.ntlmssp_state->ntlmssp_state, 
272                                              call->mem_ctx,
273                                              ndr->data + DCERPC_REQUEST_LENGTH, 
274                                              ndr->offset - DCERPC_REQUEST_LENGTH,
275                                              &dce_conn->auth_state.auth_info->credentials);
276                 break;
277         default:
278                 status = NT_STATUS_INVALID_LEVEL;
279                 break;
280         }
281
282         if (!NT_STATUS_IS_OK(status)) {
283                 return False;
284         }       
285
286         /* add the auth verifier */
287         status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce_conn->auth_state.auth_info);
288         if (!NT_STATUS_IS_OK(status)) {
289                 return False;
290         }
291
292         /* extract the whole packet as a blob */
293         *blob = ndr_push_blob(ndr);
294
295         /* fill in the fragment length and auth_length, we can't fill
296            in these earlier as we don't know the signature length (it
297            could be variable length) */
298         dcerpc_set_frag_length(blob, blob->length);
299         dcerpc_set_auth_length(blob, dce_conn->auth_state.auth_info->credentials.length);
300
301         data_blob_free(&dce_conn->auth_state.auth_info->credentials);
302
303         return True;
304 }