s3-ntlmssp Remove references to auth_ntlmssp_context from the rpc code
[kai/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 = ntlmssp_server_auth_start(sp_ctx,
81                                                    sp_ctx->do_sign,
82                                                    sp_ctx->do_seal,
83                                                    sp_ctx->is_dcerpc,
84                                                    token_in,
85                                                    token_out,
86                                                    sp_ctx->remote_address,
87                                                    &gensec_security);
88                 if (!NT_STATUS_IS_OK(status)) {
89                         DEBUG(0, ("Failed to init ntlmssp server "
90                                   "(%s)\n", nt_errstr(status)));
91                         return status;
92                 }
93
94                 sp_ctx->mech_ctx.gensec_security = gensec_security;
95                 break;
96
97         default:
98                 DEBUG(3, ("No known mechanisms available\n"));
99                 return NT_STATUS_INVALID_PARAMETER;
100         }
101
102         return NT_STATUS_OK;
103 }
104
105 NTSTATUS spnego_server_step(struct spnego_context *sp_ctx,
106                             TALLOC_CTX *mem_ctx,
107                             DATA_BLOB *spnego_in,
108                             DATA_BLOB *spnego_out)
109 {
110         DATA_BLOB token_in = data_blob_null;
111         DATA_BLOB token_out = data_blob_null;
112         DATA_BLOB signature = data_blob_null;
113         DATA_BLOB MICblob = data_blob_null;
114         struct spnego_data sp_in;
115         ssize_t len_in = 0;
116         NTSTATUS status;
117         bool ret;
118
119         len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
120         if (len_in == -1) {
121                 DEBUG(1, (__location__ ": invalid SPNEGO blob.\n"));
122                 dump_data(10, spnego_in->data, spnego_in->length);
123                 status = NT_STATUS_INVALID_PARAMETER;
124                 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
125                 goto done;
126         }
127         if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
128                 status = NT_STATUS_INVALID_PARAMETER;
129                 goto done;
130         }
131         token_in = sp_in.negTokenTarg.responseToken;
132         signature = sp_in.negTokenTarg.mechListMIC;
133
134         switch (sp_ctx->state) {
135         case SPNEGO_CONV_NEGO:
136                 /* still to initialize */
137                 status = spnego_server_mech_init(sp_ctx,
138                                                  &token_in,
139                                                  &token_out);
140                 if (!NT_STATUS_IS_OK(status)) {
141                         goto done;
142                 }
143                 /* server always need at least one reply from client */
144                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
145                 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
146                 break;
147
148         case SPNEGO_CONV_AUTH_MORE:
149
150                 switch(sp_ctx->mech) {
151                 case SPNEGO_KRB5:
152                         status = gssapi_server_step(
153                                         sp_ctx->mech_ctx.gssapi_state,
154                                         mem_ctx, &token_in, &token_out);
155                         break;
156                 case SPNEGO_NTLMSSP:
157                         status = ntlmssp_server_step(
158                                         sp_ctx->mech_ctx.gensec_security,
159                                         mem_ctx, &token_in, &token_out);
160                         break;
161                 default:
162                         status = NT_STATUS_INVALID_PARAMETER;
163                         goto done;
164                 }
165
166                 break;
167
168         case SPNEGO_CONV_AUTH_DONE:
169                 /* we are already done, can't step further */
170                 /* fall thorugh and return error */
171         default:
172                 /* wrong case */
173                 return NT_STATUS_INVALID_PARAMETER;
174         }
175
176         if (NT_STATUS_IS_OK(status) && signature.length != 0) {
177                 /* last packet and requires signature check */
178                 ret = spnego_mech_list_blob(talloc_tos(),
179                                             sp_ctx->oid_list, &MICblob);
180                 if (ret) {
181                         status = spnego_sigcheck(talloc_tos(), sp_ctx,
182                                                  &MICblob, &MICblob,
183                                                  &signature);
184                 } else {
185                         status = NT_STATUS_UNSUCCESSFUL;
186                 }
187         }
188         if (NT_STATUS_IS_OK(status) && signature.length != 0) {
189                 /* if signature was good, sign our own packet too */
190                 status = spnego_sign(talloc_tos(), sp_ctx,
191                                      &MICblob, &MICblob, &signature);
192         }
193
194 done:
195         *spnego_out = spnego_gen_auth_response_and_mic(mem_ctx, status,
196                                                         sp_ctx->mech_oid,
197                                                         &token_out,
198                                                         &signature);
199         if (!spnego_out->data) {
200                 DEBUG(1, ("SPNEGO wrapping failed!\n"));
201                 status = NT_STATUS_UNSUCCESSFUL;
202         }
203
204         if (NT_STATUS_IS_OK(status) ||
205             !NT_STATUS_EQUAL(status,
206                         NT_STATUS_MORE_PROCESSING_REQUIRED)) {
207                 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
208         }
209
210         data_blob_free(&token_in);
211         data_blob_free(&token_out);
212         return status;
213 }
214
215 NTSTATUS spnego_server_auth_start(TALLOC_CTX *mem_ctx,
216                                   bool do_sign,
217                                   bool do_seal,
218                                   bool is_dcerpc,
219                                   DATA_BLOB *spnego_in,
220                                   DATA_BLOB *spnego_out,
221                                   const struct tsocket_address *remote_address,
222                                   struct spnego_context **spnego_ctx)
223 {
224         struct spnego_context *sp_ctx;
225         DATA_BLOB token_in = data_blob_null;
226         DATA_BLOB token_out = data_blob_null;
227         unsigned int i;
228         NTSTATUS status;
229         bool ret;
230
231         if (!spnego_in->length) {
232                 return NT_STATUS_INVALID_PARAMETER;
233         }
234
235         status = spnego_init_server(mem_ctx,
236                                     do_sign,
237                                     do_seal,
238                                     is_dcerpc,
239                                     remote_address,
240                                     &sp_ctx);
241         if (!NT_STATUS_IS_OK(status)) {
242                 return status;
243         }
244
245         ret = spnego_parse_negTokenInit(sp_ctx, *spnego_in,
246                                         sp_ctx->oid_list, NULL, &token_in);
247         if (!ret || sp_ctx->oid_list[0] == NULL) {
248                 DEBUG(3, ("Invalid SPNEGO message\n"));
249                 status = NT_STATUS_INVALID_PARAMETER;
250                 goto done;
251         }
252
253         /* try for krb auth first */
254         for (i = 0; sp_ctx->oid_list[i] && sp_ctx->mech == SPNEGO_NONE; i++) {
255                 if (strcmp(OID_KERBEROS5, sp_ctx->oid_list[i]) == 0 ||
256                     strcmp(OID_KERBEROS5_OLD, sp_ctx->oid_list[i]) == 0) {
257
258                         if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
259                                 sp_ctx->mech = SPNEGO_KRB5;
260                                 sp_ctx->mech_oid = sp_ctx->oid_list[i];
261                         }
262                 }
263         }
264
265         /* if auth type still undetermined, try for NTLMSSP */
266         for (i = 0; sp_ctx->oid_list[i] && sp_ctx->mech == SPNEGO_NONE; i++) {
267                 if (strcmp(OID_NTLMSSP, sp_ctx->oid_list[i]) == 0) {
268                         sp_ctx->mech = SPNEGO_NTLMSSP;
269                         sp_ctx->mech_oid = sp_ctx->oid_list[i];
270                 }
271         }
272
273         if (!sp_ctx->mech_oid) {
274                 DEBUG(3, ("No known mechanisms available\n"));
275                 status = NT_STATUS_INVALID_PARAMETER;
276                 goto done;
277         }
278
279         if (DEBUGLEVEL >= 10) {
280                 DEBUG(10, ("Client Provided OIDs:\n"));
281                 for (i = 0; sp_ctx->oid_list[i]; i++) {
282                         DEBUG(10, ("  %d: %s\n", i, sp_ctx->oid_list[i]));
283                 }
284                 DEBUG(10, ("Chosen OID: %s\n", sp_ctx->mech_oid));
285         }
286
287         /* If it is not the first OID, then token_in is not valid for the
288          * choosen mech */
289         if (sp_ctx->mech_oid != sp_ctx->oid_list[0]) {
290                 /* request more and send back empty token */
291                 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
292                 sp_ctx->state = SPNEGO_CONV_NEGO;
293                 goto done;
294         }
295
296         status = spnego_server_mech_init(sp_ctx, &token_in, &token_out);
297         if (!NT_STATUS_IS_OK(status)) {
298                 goto done;
299         }
300
301         DEBUG(10, ("SPNEGO(%d) auth started\n", sp_ctx->mech));
302
303         /* server always need at least one reply from client */
304         status = NT_STATUS_MORE_PROCESSING_REQUIRED;
305         sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
306
307 done:
308         *spnego_out = spnego_gen_auth_response(mem_ctx, &token_out,
309                                                 status, sp_ctx->mech_oid);
310         if (!spnego_out->data) {
311                 status = NT_STATUS_INVALID_PARAMETER;
312                 TALLOC_FREE(sp_ctx);
313         } else {
314                 status = NT_STATUS_OK;
315                 *spnego_ctx = sp_ctx;
316         }
317
318         data_blob_free(&token_in);
319         data_blob_free(&token_out);
320
321         return status;
322 }