94c282cdfc282c8305a3f6f94efbff327ee33a85
[mat/samba.git] / source3 / rpc_server / dcesrv_spnego.c
1 /*
2  *  SPNEGO Encapsulation
3  *  DCERPC Server functions
4  *  Copyright (C) Simo Sorce 2010.
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "includes.h"
21 #include "../libcli/auth/spnego.h"
22 #include "../lib/tsocket/tsocket.h"
23 #include "dcesrv_ntlmssp.h"
24 #include "dcesrv_gssapi.h"
25 #include "dcesrv_spnego.h"
26
27 static NTSTATUS spnego_init_server(TALLOC_CTX *mem_ctx,
28                                    bool do_sign, bool do_seal,
29                                    bool is_dcerpc,
30                                    const struct tsocket_address *remote_address,
31                                    struct spnego_context **spnego_ctx)
32 {
33         struct spnego_context *sp_ctx = NULL;
34
35         sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
36         if (!sp_ctx) {
37                 return NT_STATUS_NO_MEMORY;
38         }
39
40         sp_ctx->remote_address = tsocket_address_copy(remote_address, sp_ctx);
41         if (sp_ctx->remote_address == NULL) {
42                 return NT_STATUS_NO_MEMORY;
43         }
44
45         sp_ctx->do_sign = do_sign;
46         sp_ctx->do_seal = do_seal;
47         sp_ctx->is_dcerpc = is_dcerpc;
48
49         *spnego_ctx = sp_ctx;
50         return NT_STATUS_OK;
51 }
52
53 static NTSTATUS spnego_server_mech_init(struct spnego_context *sp_ctx,
54                                         DATA_BLOB *token_in,
55                                         DATA_BLOB *token_out)
56 {
57         struct gensec_security *gensec_security;
58         struct gse_context *gse_ctx;
59         NTSTATUS status;
60
61         switch (sp_ctx->mech) {
62         case SPNEGO_KRB5:
63                 status = gssapi_server_auth_start(sp_ctx,
64                                                   sp_ctx->do_sign,
65                                                   sp_ctx->do_seal,
66                                                   sp_ctx->is_dcerpc,
67                                                   token_in,
68                                                   token_out,
69                                                   &gse_ctx);
70                 if (!NT_STATUS_IS_OK(status)) {
71                         DEBUG(0, ("Failed to init gssapi server "
72                                   "(%s)\n", nt_errstr(status)));
73                         return status;
74                 }
75
76                 sp_ctx->mech_ctx.gssapi_state = gse_ctx;
77                 break;
78
79         case SPNEGO_NTLMSSP:
80                 status = auth_generic_server_start(sp_ctx,
81                                                    OID_NTLMSSP,
82                                                    sp_ctx->do_sign,
83                                                    sp_ctx->do_seal,
84                                                    sp_ctx->is_dcerpc,
85                                                    token_in,
86                                                    token_out,
87                                                    sp_ctx->remote_address,
88                                                    &gensec_security);
89                 if (!NT_STATUS_IS_OK(status)) {
90                         DEBUG(0, ("Failed to init ntlmssp server "
91                                   "(%s)\n", nt_errstr(status)));
92                         return status;
93                 }
94
95                 sp_ctx->mech_ctx.gensec_security = gensec_security;
96                 break;
97
98         default:
99                 DEBUG(3, ("No known mechanisms available\n"));
100                 return NT_STATUS_INVALID_PARAMETER;
101         }
102
103         return NT_STATUS_OK;
104 }
105
106 NTSTATUS spnego_server_step(struct spnego_context *sp_ctx,
107                             TALLOC_CTX *mem_ctx,
108                             DATA_BLOB *spnego_in,
109                             DATA_BLOB *spnego_out)
110 {
111         DATA_BLOB token_in = data_blob_null;
112         DATA_BLOB token_out = data_blob_null;
113         DATA_BLOB signature = data_blob_null;
114         DATA_BLOB MICblob = data_blob_null;
115         struct spnego_data sp_in;
116         ssize_t len_in = 0;
117         NTSTATUS status;
118         bool ret;
119
120         len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
121         if (len_in == -1) {
122                 DEBUG(1, (__location__ ": invalid SPNEGO blob.\n"));
123                 dump_data(10, spnego_in->data, spnego_in->length);
124                 status = NT_STATUS_INVALID_PARAMETER;
125                 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
126                 goto done;
127         }
128         if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
129                 status = NT_STATUS_INVALID_PARAMETER;
130                 goto done;
131         }
132         token_in = sp_in.negTokenTarg.responseToken;
133         signature = sp_in.negTokenTarg.mechListMIC;
134
135         switch (sp_ctx->state) {
136         case SPNEGO_CONV_NEGO:
137                 /* still to initialize */
138                 status = spnego_server_mech_init(sp_ctx,
139                                                  &token_in,
140                                                  &token_out);
141                 if (!NT_STATUS_IS_OK(status)) {
142                         goto done;
143                 }
144                 /* server always need at least one reply from client */
145                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
146                 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
147                 break;
148
149         case SPNEGO_CONV_AUTH_MORE:
150
151                 switch(sp_ctx->mech) {
152                 case SPNEGO_KRB5:
153                         status = gssapi_server_step(
154                                         sp_ctx->mech_ctx.gssapi_state,
155                                         mem_ctx, &token_in, &token_out);
156                         break;
157                 case SPNEGO_NTLMSSP:
158                         status = auth_generic_server_step(
159                                         sp_ctx->mech_ctx.gensec_security,
160                                         mem_ctx, &token_in, &token_out);
161                         break;
162                 default:
163                         status = NT_STATUS_INVALID_PARAMETER;
164                         goto done;
165                 }
166
167                 break;
168
169         case SPNEGO_CONV_AUTH_DONE:
170                 /* we are already done, can't step further */
171                 /* fall thorugh and return error */
172         default:
173                 /* wrong case */
174                 return NT_STATUS_INVALID_PARAMETER;
175         }
176
177         if (NT_STATUS_IS_OK(status) && signature.length != 0) {
178                 /* last packet and requires signature check */
179                 ret = spnego_mech_list_blob(talloc_tos(),
180                                             sp_ctx->oid_list, &MICblob);
181                 if (ret) {
182                         status = spnego_sigcheck(talloc_tos(), sp_ctx,
183                                                  &MICblob, &MICblob,
184                                                  &signature);
185                 } else {
186                         status = NT_STATUS_UNSUCCESSFUL;
187                 }
188         }
189         if (NT_STATUS_IS_OK(status) && signature.length != 0) {
190                 /* if signature was good, sign our own packet too */
191                 status = spnego_sign(talloc_tos(), sp_ctx,
192                                      &MICblob, &MICblob, &signature);
193         }
194
195 done:
196         *spnego_out = spnego_gen_auth_response_and_mic(mem_ctx, status,
197                                                         sp_ctx->mech_oid,
198                                                         &token_out,
199                                                         &signature);
200         if (!spnego_out->data) {
201                 DEBUG(1, ("SPNEGO wrapping failed!\n"));
202                 status = NT_STATUS_UNSUCCESSFUL;
203         }
204
205         if (NT_STATUS_IS_OK(status) ||
206             !NT_STATUS_EQUAL(status,
207                         NT_STATUS_MORE_PROCESSING_REQUIRED)) {
208                 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
209         }
210
211         data_blob_free(&token_in);
212         data_blob_free(&token_out);
213         return status;
214 }
215
216 NTSTATUS spnego_server_auth_start(TALLOC_CTX *mem_ctx,
217                                   bool do_sign,
218                                   bool do_seal,
219                                   bool is_dcerpc,
220                                   DATA_BLOB *spnego_in,
221                                   DATA_BLOB *spnego_out,
222                                   const struct tsocket_address *remote_address,
223                                   struct spnego_context **spnego_ctx)
224 {
225         struct spnego_context *sp_ctx;
226         DATA_BLOB token_in = data_blob_null;
227         DATA_BLOB token_out = data_blob_null;
228         unsigned int i;
229         NTSTATUS status;
230         bool ret;
231
232         if (!spnego_in->length) {
233                 return NT_STATUS_INVALID_PARAMETER;
234         }
235
236         status = spnego_init_server(mem_ctx,
237                                     do_sign,
238                                     do_seal,
239                                     is_dcerpc,
240                                     remote_address,
241                                     &sp_ctx);
242         if (!NT_STATUS_IS_OK(status)) {
243                 return status;
244         }
245
246         ret = spnego_parse_negTokenInit(sp_ctx, *spnego_in,
247                                         sp_ctx->oid_list, NULL, &token_in);
248         if (!ret || sp_ctx->oid_list[0] == NULL) {
249                 DEBUG(3, ("Invalid SPNEGO message\n"));
250                 status = NT_STATUS_INVALID_PARAMETER;
251                 goto done;
252         }
253
254         /* try for krb auth first */
255         for (i = 0; sp_ctx->oid_list[i] && sp_ctx->mech == SPNEGO_NONE; i++) {
256                 if (strcmp(OID_KERBEROS5, sp_ctx->oid_list[i]) == 0 ||
257                     strcmp(OID_KERBEROS5_OLD, sp_ctx->oid_list[i]) == 0) {
258
259                         if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
260                                 sp_ctx->mech = SPNEGO_KRB5;
261                                 sp_ctx->mech_oid = sp_ctx->oid_list[i];
262                         }
263                 }
264         }
265
266         /* if auth type still undetermined, try for NTLMSSP */
267         for (i = 0; sp_ctx->oid_list[i] && sp_ctx->mech == SPNEGO_NONE; i++) {
268                 if (strcmp(OID_NTLMSSP, sp_ctx->oid_list[i]) == 0) {
269                         sp_ctx->mech = SPNEGO_NTLMSSP;
270                         sp_ctx->mech_oid = sp_ctx->oid_list[i];
271                 }
272         }
273
274         if (!sp_ctx->mech_oid) {
275                 DEBUG(3, ("No known mechanisms available\n"));
276                 status = NT_STATUS_INVALID_PARAMETER;
277                 goto done;
278         }
279
280         if (DEBUGLEVEL >= 10) {
281                 DEBUG(10, ("Client Provided OIDs:\n"));
282                 for (i = 0; sp_ctx->oid_list[i]; i++) {
283                         DEBUG(10, ("  %d: %s\n", i, sp_ctx->oid_list[i]));
284                 }
285                 DEBUG(10, ("Chosen OID: %s\n", sp_ctx->mech_oid));
286         }
287
288         /* If it is not the first OID, then token_in is not valid for the
289          * choosen mech */
290         if (sp_ctx->mech_oid != sp_ctx->oid_list[0]) {
291                 /* request more and send back empty token */
292                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
293                 sp_ctx->state = SPNEGO_CONV_NEGO;
294                 goto done;
295         }
296
297         status = spnego_server_mech_init(sp_ctx, &token_in, &token_out);
298         if (!NT_STATUS_IS_OK(status)) {
299                 goto done;
300         }
301
302         DEBUG(10, ("SPNEGO(%d) auth started\n", sp_ctx->mech));
303
304         /* server always need at least one reply from client */
305         status = NT_STATUS_MORE_PROCESSING_REQUIRED;
306         sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
307
308 done:
309         *spnego_out = spnego_gen_auth_response(mem_ctx, &token_out,
310                                                 status, sp_ctx->mech_oid);
311         if (!spnego_out->data) {
312                 status = NT_STATUS_INVALID_PARAMETER;
313                 TALLOC_FREE(sp_ctx);
314         } else {
315                 status = NT_STATUS_OK;
316                 *spnego_ctx = sp_ctx;
317         }
318
319         data_blob_free(&token_in);
320         data_blob_free(&token_out);
321
322         return status;
323 }