r1004: continue tridge's work on dcerpc server auth/crypto code
[nivanova/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   parse any auth information from a dcerpc bind request
27   return False if we can't handle the auth request for some 
28   reason (in which case we send a bind_nak)
29 */
30 BOOL dcesrv_auth_bind(struct dcesrv_call_state *call)
31 {
32         struct dcerpc_packet *pkt = &call->pkt;
33         struct dcesrv_connection *dce_conn = call->conn;
34         NTSTATUS status;
35
36         if (pkt->u.bind.auth_info.length == 0) {
37                 dce_conn->auth_state.auth_info = NULL;
38                 return True;
39         }
40
41         dce_conn->auth_state.auth_info = talloc_p(dce_conn->mem_ctx, struct dcerpc_auth);
42         if (!dce_conn->auth_state.auth_info) {
43                 return False;
44         }
45
46         status = ndr_pull_struct_blob(&pkt->u.bind.auth_info,
47                                       call->mem_ctx,
48                                       dce_conn->auth_state.auth_info,
49                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
50         if (!NT_STATUS_IS_OK(status)) {
51                 return False;
52         }
53
54         status = dcesrv_crypto_select_type(dce_conn, &dce_conn->auth_state);
55         if (!NT_STATUS_IS_OK(status)) {
56                 return False;
57         }
58
59         status = dcesrv_crypto_start(&dce_conn->auth_state);
60         if (!NT_STATUS_IS_OK(status)) {
61                 return False;
62         }
63
64         return True;
65 }
66
67 /*
68   add any auth information needed in a bind ack
69 */
70 BOOL dcesrv_auth_bind_ack(struct dcesrv_call_state *call, struct dcerpc_packet *pkt)
71 {
72         struct dcesrv_connection *dce_conn = call->conn;
73         NTSTATUS status;
74
75         if (!call->conn->auth_state.crypto_ctx.ops) {
76                 return True;
77         }
78
79         status = dcesrv_crypto_update(&dce_conn->auth_state,
80                                       call->mem_ctx,
81                                       dce_conn->auth_state.auth_info->credentials, 
82                                       &dce_conn->auth_state.auth_info->credentials);
83         if (!NT_STATUS_IS_OK(status) && 
84             !NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
85                 DEBUG(2, ("Failed to start dcesrv auth negotiate: %s\n", nt_errstr(status)));
86                 return False;
87         }
88
89         dce_conn->auth_state.auth_info->auth_pad_length = 0;
90         dce_conn->auth_state.auth_info->auth_reserved = 0;
91                                      
92         return True;
93 }
94
95
96 /*
97   process the final stage of a auth request
98 */
99 BOOL dcesrv_auth_auth3(struct dcesrv_call_state *call)
100 {
101         struct dcerpc_packet *pkt = &call->pkt;
102         struct dcesrv_connection *dce_conn = call->conn;
103         NTSTATUS status;
104
105         if (!dce_conn->auth_state.auth_info ||
106             !dce_conn->auth_state.crypto_ctx.ops ||
107             pkt->u.auth.auth_info.length == 0) {
108                 return False;
109         }
110
111         status = ndr_pull_struct_blob(&pkt->u.auth.auth_info,
112                                       call->mem_ctx,
113                                       dce_conn->auth_state.auth_info,
114                                       (ndr_pull_flags_fn_t)ndr_pull_dcerpc_auth);
115         if (!NT_STATUS_IS_OK(status)) {
116                 return False;
117         }
118
119         status = dcesrv_crypto_update(&dce_conn->auth_state,
120                                       call->mem_ctx,
121                                       dce_conn->auth_state.auth_info->credentials, 
122                                       &dce_conn->auth_state.auth_info->credentials);
123         if (!NT_STATUS_IS_OK(status)) {
124                 DEBUG(4, ("dcesrv_auth_auth3: failed to authenticate: %s\n", 
125                           nt_errstr(status)));
126                 return False;
127         }
128
129         return True;
130 }
131
132
133 /*
134   check credentials on a request
135 */
136 BOOL dcesrv_auth_request(struct dcesrv_call_state *call)
137 {
138         struct dcerpc_packet *pkt = &call->pkt;
139         struct dcesrv_connection *dce_conn = call->conn;
140         DATA_BLOB auth_blob;
141         struct dcerpc_auth auth;
142         struct ndr_pull *ndr;
143         NTSTATUS status;
144
145         if (!dce_conn->auth_state.auth_info ||
146             !dce_conn->auth_state.crypto_ctx.ops) {
147                 return True;
148         }
149
150         auth_blob.length = 8 + pkt->auth_length;
151
152         /* check for a valid length */
153         if (pkt->u.request.stub_and_verifier.length < auth_blob.length) {
154                 return False;
155         }
156
157         auth_blob.data = 
158                 pkt->u.request.stub_and_verifier.data + 
159                 pkt->u.request.stub_and_verifier.length - auth_blob.length;
160         pkt->u.request.stub_and_verifier.length -= auth_blob.length;
161
162         /* pull the auth structure */
163         ndr = ndr_pull_init_blob(&auth_blob, call->mem_ctx);
164         if (!ndr) {
165                 return False;
166         }
167
168         if (!(pkt->drep[0] & DCERPC_DREP_LE)) {
169                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
170         }
171
172         status = ndr_pull_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, &auth);
173         if (!NT_STATUS_IS_OK(status)) {
174                 return False;
175         }
176
177         /* check signature or unseal the packet */
178         switch (dce_conn->auth_state.auth_info->auth_level) {
179         case DCERPC_AUTH_LEVEL_PRIVACY:
180                 status = dcesrv_crypto_unseal(&dce_conn->auth_state,
181                                               call->mem_ctx,
182                                               pkt->u.request.stub_and_verifier.data, 
183                                               pkt->u.request.stub_and_verifier.length, 
184                                               &auth.credentials);
185                 break;
186
187         case DCERPC_AUTH_LEVEL_INTEGRITY:
188                 status = dcesrv_crypto_check_sig(&dce_conn->auth_state,
189                                                  call->mem_ctx,
190                                                  pkt->u.request.stub_and_verifier.data, 
191                                                  pkt->u.request.stub_and_verifier.length,
192                                                  &auth.credentials);
193                 break;
194
195         default:
196                 status = NT_STATUS_INVALID_LEVEL;
197                 break;
198         }
199
200         /* remove the indicated amount of paddiing */
201         if (pkt->u.request.stub_and_verifier.length < auth.auth_pad_length) {
202                 return False;
203         }
204         pkt->u.request.stub_and_verifier.length -= auth.auth_pad_length;
205
206         return NT_STATUS_IS_OK(status);
207 }
208
209
210 /* 
211    push a signed or sealed dcerpc request packet into a blob
212 */
213 BOOL dcesrv_auth_response(struct dcesrv_call_state *call,
214                           DATA_BLOB *blob, struct dcerpc_packet *pkt)
215 {
216         struct dcesrv_connection *dce_conn = call->conn;
217         NTSTATUS status;
218         struct ndr_push *ndr;
219
220         /* non-signed packets are simple */
221         if (!dce_conn->auth_state.auth_info || !dce_conn->auth_state.crypto_ctx.ops) {
222                 status = dcerpc_push_auth(blob, call->mem_ctx, pkt, NULL);
223                 return NT_STATUS_IS_OK(status);
224         }
225
226         ndr = ndr_push_init_ctx(call->mem_ctx);
227         if (!ndr) {
228                 return False;
229         }
230
231         if (pkt->drep[0] & DCERPC_DREP_LE) {
232                 ndr->flags |= LIBNDR_FLAG_BIGENDIAN;
233         }
234
235         status = ndr_push_dcerpc_packet(ndr, NDR_SCALARS|NDR_BUFFERS, pkt);
236         if (!NT_STATUS_IS_OK(status)) {
237                 return False;
238         }
239
240         /* pad to 8 byte multiple */
241         dce_conn->auth_state.auth_info->auth_pad_length = NDR_ALIGN(ndr, 8);
242         ndr_push_zero(ndr, dce_conn->auth_state.auth_info->auth_pad_length);
243
244         /* sign or seal the packet */
245         switch (dce_conn->auth_state.auth_info->auth_level) {
246         case DCERPC_AUTH_LEVEL_PRIVACY:
247                 status = dcesrv_crypto_seal(&dce_conn->auth_state, 
248                                             call->mem_ctx,
249                                             ndr->data + DCERPC_REQUEST_LENGTH, 
250                                             ndr->offset - DCERPC_REQUEST_LENGTH,
251                                             &dce_conn->auth_state.auth_info->credentials);
252                 break;
253
254         case DCERPC_AUTH_LEVEL_INTEGRITY:
255                 status = dcesrv_crypto_sign(&dce_conn->auth_state, 
256                                             call->mem_ctx,
257                                             ndr->data + DCERPC_REQUEST_LENGTH, 
258                                             ndr->offset - DCERPC_REQUEST_LENGTH,
259                                             &dce_conn->auth_state.auth_info->credentials);
260                 break;
261         default:
262                 status = NT_STATUS_INVALID_LEVEL;
263                 break;
264         }
265
266         if (!NT_STATUS_IS_OK(status)) {
267                 return False;
268         }       
269
270         /* add the auth verifier */
271         status = ndr_push_dcerpc_auth(ndr, NDR_SCALARS|NDR_BUFFERS, dce_conn->auth_state.auth_info);
272         if (!NT_STATUS_IS_OK(status)) {
273                 return False;
274         }
275
276         /* extract the whole packet as a blob */
277         *blob = ndr_push_blob(ndr);
278
279         /* fill in the fragment length and auth_length, we can't fill
280            in these earlier as we don't know the signature length (it
281            could be variable length) */
282         dcerpc_set_frag_length(blob, blob->length);
283         dcerpc_set_auth_length(blob, dce_conn->auth_state.auth_info->credentials.length);
284
285         data_blob_free(&dce_conn->auth_state.auth_info->credentials);
286
287         return True;
288 }