s3-ntlmssp split auth_ntlmssp_client_start() into two parts
[mat/samba.git] / source3 / librpc / crypto / cli_spnego.c
1 /*
2  *  SPNEGO Encapsulation
3  *  Client 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 "include/ntlmssp_wrap.h"
23 #include "librpc/gen_ndr/ntlmssp.h"
24 #include "auth/ntlmssp/ntlmssp.h"
25 #include "librpc/crypto/gse.h"
26 #include "librpc/crypto/spnego.h"
27
28 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
29                                     bool do_sign, bool do_seal,
30                                     struct spnego_context **spnego_ctx)
31 {
32         struct spnego_context *sp_ctx;
33
34         sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
35         if (!sp_ctx) {
36                 return NT_STATUS_NO_MEMORY;
37         }
38
39         sp_ctx->do_sign = do_sign;
40         sp_ctx->do_seal = do_seal;
41         sp_ctx->state = SPNEGO_CONV_INIT;
42
43         *spnego_ctx = sp_ctx;
44         return NT_STATUS_OK;
45 }
46
47 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
48                                    bool do_sign, bool do_seal,
49                                    bool is_dcerpc,
50                                    const char *ccache_name,
51                                    const char *server,
52                                    const char *service,
53                                    const char *username,
54                                    const char *password,
55                                    struct spnego_context **spnego_ctx)
56 {
57         struct spnego_context *sp_ctx = NULL;
58         uint32_t add_gss_c_flags = 0;
59         NTSTATUS status;
60
61         status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
62         if (!NT_STATUS_IS_OK(status)) {
63                 return status;
64         }
65         sp_ctx->mech = SPNEGO_KRB5;
66
67         if (is_dcerpc) {
68                 add_gss_c_flags = GSS_C_DCE_STYLE;
69         }
70
71         status = gse_init_client(sp_ctx,
72                                  do_sign, do_seal,
73                                  ccache_name, server, service,
74                                  username, password, add_gss_c_flags,
75                                  &sp_ctx->mech_ctx.gssapi_state);
76         if (!NT_STATUS_IS_OK(status)) {
77                 TALLOC_FREE(sp_ctx);
78                 return status;
79         }
80
81         *spnego_ctx = sp_ctx;
82         return NT_STATUS_OK;
83 }
84
85 NTSTATUS spnego_ntlmssp_init_client(TALLOC_CTX *mem_ctx,
86                                     bool do_sign, bool do_seal,
87                                     bool is_dcerpc,
88                                     const char *domain,
89                                     const char *username,
90                                     const char *password,
91                                     struct spnego_context **spnego_ctx)
92 {
93         struct spnego_context *sp_ctx = NULL;
94         NTSTATUS status;
95
96         status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
97         if (!NT_STATUS_IS_OK(status)) {
98                 return status;
99         }
100         sp_ctx->mech = SPNEGO_NTLMSSP;
101
102         status = auth_ntlmssp_client_prepare(sp_ctx,
103                                         &sp_ctx->mech_ctx.ntlmssp_state);
104         if (!NT_STATUS_IS_OK(status)) {
105                 TALLOC_FREE(sp_ctx);
106                 return status;
107         }
108
109         status = auth_ntlmssp_set_username(sp_ctx->mech_ctx.ntlmssp_state,
110                                            username);
111         if (!NT_STATUS_IS_OK(status)) {
112                 TALLOC_FREE(sp_ctx);
113                 return status;
114         }
115
116         status = auth_ntlmssp_set_domain(sp_ctx->mech_ctx.ntlmssp_state,
117                                          domain);
118         if (!NT_STATUS_IS_OK(status)) {
119                 TALLOC_FREE(sp_ctx);
120                 return status;
121         }
122
123         status = auth_ntlmssp_set_password(sp_ctx->mech_ctx.ntlmssp_state,
124                                            password);
125         if (!NT_STATUS_IS_OK(status)) {
126                 TALLOC_FREE(sp_ctx);
127                 return status;
128         }
129
130         if (do_sign) {
131                 auth_ntlmssp_want_feature(sp_ctx->mech_ctx.ntlmssp_state,
132                                           NTLMSSP_FEATURE_SIGN);
133         } else if (do_seal) {
134                 auth_ntlmssp_want_feature(sp_ctx->mech_ctx.ntlmssp_state,
135                                           NTLMSSP_FEATURE_SEAL);
136         }
137
138         status = auth_ntlmssp_client_start(sp_ctx->mech_ctx.ntlmssp_state);
139         if (!NT_STATUS_IS_OK(status)) {
140                 TALLOC_FREE(sp_ctx);
141                 return status;
142         }
143
144         *spnego_ctx = sp_ctx;
145         return NT_STATUS_OK;
146 }
147
148 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
149                                       struct spnego_context *sp_ctx,
150                                       DATA_BLOB *spnego_in,
151                                       DATA_BLOB *spnego_out)
152 {
153         struct gse_context *gse_ctx;
154         struct auth_ntlmssp_state *ntlmssp_ctx;
155         struct spnego_data sp_in, sp_out;
156         DATA_BLOB token_in = data_blob_null;
157         DATA_BLOB token_out = data_blob_null;
158         const char *mech_oids[2] = { NULL, NULL };
159         char *principal = NULL;
160         ssize_t len_in = 0;
161         ssize_t len_out = 0;
162         bool mech_wants_more = false;
163         NTSTATUS status;
164
165         if (!spnego_in->length) {
166                 /* server didn't send anything, is init ? */
167                 if (sp_ctx->state != SPNEGO_CONV_INIT) {
168                         return NT_STATUS_INVALID_PARAMETER;
169                 }
170         } else {
171                 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
172                 if (len_in == -1) {
173                         status = NT_STATUS_INVALID_PARAMETER;
174                         goto done;
175                 }
176                 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
177                         status = NT_STATUS_INVALID_PARAMETER;
178                         goto done;
179                 }
180                 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
181                         status = NT_STATUS_ACCESS_DENIED;
182                         goto done;
183                 }
184                 token_in = sp_in.negTokenTarg.responseToken;
185         }
186
187         if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
188                 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
189                         sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
190                         *spnego_out = data_blob_null;
191                         status = NT_STATUS_OK;
192                 } else {
193                         status = NT_STATUS_ACCESS_DENIED;
194                 }
195                 goto done;
196         }
197
198         switch (sp_ctx->mech) {
199         case SPNEGO_KRB5:
200
201                 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
202                 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
203                                                    &token_in, &token_out);
204                 if (!NT_STATUS_IS_OK(status)) {
205                         goto done;
206                 }
207
208                 mech_oids[0] = OID_KERBEROS5;
209                 mech_wants_more = gse_require_more_processing(gse_ctx);
210
211                 break;
212
213         case SPNEGO_NTLMSSP:
214
215                 ntlmssp_ctx = sp_ctx->mech_ctx.ntlmssp_state;
216                 status = auth_ntlmssp_update(ntlmssp_ctx, mem_ctx,
217                                              token_in, &token_out);
218                 if (NT_STATUS_EQUAL(status,
219                                     NT_STATUS_MORE_PROCESSING_REQUIRED)) {
220                         mech_wants_more = true;
221                 } else if (!NT_STATUS_IS_OK(status)) {
222                         goto done;
223                 }
224
225                 mech_oids[0] = OID_NTLMSSP;
226
227                 break;
228
229         default:
230                 status = NT_STATUS_INTERNAL_ERROR;
231                 goto done;
232         }
233
234         switch (sp_ctx->state) {
235         case SPNEGO_CONV_INIT:
236                 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
237                                                       &token_out, principal);
238                 if (!spnego_out->data) {
239                         status = NT_STATUS_INTERNAL_ERROR;
240                         goto done;
241                 }
242                 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
243                 break;
244
245         case SPNEGO_CONV_AUTH_MORE:
246                 /* server says it's done and we do not seem to agree */
247                 if (sp_in.negTokenTarg.negResult ==
248                                                 SPNEGO_ACCEPT_COMPLETED) {
249                         status = NT_STATUS_INVALID_PARAMETER;
250                         goto done;
251                 }
252
253                 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
254                 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
255                 sp_out.negTokenTarg.supportedMech = NULL;
256                 sp_out.negTokenTarg.responseToken = token_out;
257                 sp_out.negTokenTarg.mechListMIC = data_blob_null;
258
259                 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
260                 if (len_out == -1) {
261                         status = NT_STATUS_INTERNAL_ERROR;
262                         goto done;
263                 }
264
265                 if (!mech_wants_more) {
266                         /* we still need to get an ack from the server */
267                         sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
268                 }
269
270                 break;
271
272         default:
273                 status = NT_STATUS_INTERNAL_ERROR;
274                 goto done;
275         }
276
277         status = NT_STATUS_OK;
278
279 done:
280         if (len_in > 0) {
281                 spnego_free_data(&sp_in);
282         }
283         data_blob_free(&token_out);
284         return status;
285 }
286
287 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
288 {
289         struct gse_context *gse_ctx;
290
291         /* see if spnego processing itself requires more */
292         if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
293             sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
294                 return true;
295         }
296
297         /* otherwise see if underlying mechnism does */
298         switch (sp_ctx->mech) {
299         case SPNEGO_KRB5:
300                 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
301                 return gse_require_more_processing(gse_ctx);
302         case SPNEGO_NTLMSSP:
303                 return false;
304         default:
305                 DEBUG(0, ("Unsupported type in request!\n"));
306                 return false;
307         }
308 }
309
310 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
311                                     enum spnego_mech *type,
312                                     void **auth_context)
313 {
314         switch (sp_ctx->mech) {
315         case SPNEGO_KRB5:
316                 *auth_context = sp_ctx->mech_ctx.gssapi_state;
317                 break;
318         case SPNEGO_NTLMSSP:
319                 *auth_context = sp_ctx->mech_ctx.ntlmssp_state;
320                 break;
321         default:
322                 return NT_STATUS_INTERNAL_ERROR;
323         }
324
325         *type = sp_ctx->mech;
326         return NT_STATUS_OK;
327 }
328
329 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
330                                  struct spnego_context *sp_ctx)
331 {
332         switch (sp_ctx->mech) {
333         case SPNEGO_KRB5:
334                 return gse_get_session_key(mem_ctx,
335                                            sp_ctx->mech_ctx.gssapi_state);
336         case SPNEGO_NTLMSSP:
337                 return auth_ntlmssp_get_session_key(
338                         sp_ctx->mech_ctx.ntlmssp_state, mem_ctx);
339         default:
340                 DEBUG(0, ("Unsupported type in request!\n"));
341                 return data_blob_null;
342         }
343 }
344
345 NTSTATUS spnego_sign(TALLOC_CTX *mem_ctx,
346                         struct spnego_context *sp_ctx,
347                         DATA_BLOB *data, DATA_BLOB *full_data,
348                         DATA_BLOB *signature)
349 {
350         switch(sp_ctx->mech) {
351         case SPNEGO_KRB5:
352                 return gse_sign(mem_ctx,
353                                 sp_ctx->mech_ctx.gssapi_state,
354                                 data, signature);
355         case SPNEGO_NTLMSSP:
356                 return auth_ntlmssp_sign_packet(
357                                         sp_ctx->mech_ctx.ntlmssp_state,
358                                         mem_ctx,
359                                         data->data, data->length,
360                                         full_data->data, full_data->length,
361                                         signature);
362         default:
363                 return NT_STATUS_INVALID_PARAMETER;
364         }
365 }
366
367 NTSTATUS spnego_sigcheck(TALLOC_CTX *mem_ctx,
368                          struct spnego_context *sp_ctx,
369                          DATA_BLOB *data, DATA_BLOB *full_data,
370                          DATA_BLOB *signature)
371 {
372         switch(sp_ctx->mech) {
373         case SPNEGO_KRB5:
374                 return gse_sigcheck(mem_ctx,
375                                     sp_ctx->mech_ctx.gssapi_state,
376                                     data, signature);
377         case SPNEGO_NTLMSSP:
378                 return auth_ntlmssp_check_packet(
379                                         sp_ctx->mech_ctx.ntlmssp_state,
380                                         data->data, data->length,
381                                         full_data->data, full_data->length,
382                                         signature);
383         default:
384                 return NT_STATUS_INVALID_PARAMETER;
385         }
386 }
387
388 NTSTATUS spnego_seal(TALLOC_CTX *mem_ctx,
389                         struct spnego_context *sp_ctx,
390                         DATA_BLOB *data, DATA_BLOB *full_data,
391                         DATA_BLOB *signature)
392 {
393         switch(sp_ctx->mech) {
394         case SPNEGO_KRB5:
395                 return gse_seal(mem_ctx,
396                                 sp_ctx->mech_ctx.gssapi_state,
397                                 data, signature);
398         case SPNEGO_NTLMSSP:
399                 return auth_ntlmssp_seal_packet(
400                                         sp_ctx->mech_ctx.ntlmssp_state,
401                                         mem_ctx,
402                                         data->data, data->length,
403                                         full_data->data, full_data->length,
404                                         signature);
405         default:
406                 return NT_STATUS_INVALID_PARAMETER;
407         }
408 }
409
410 NTSTATUS spnego_unseal(TALLOC_CTX *mem_ctx,
411                         struct spnego_context *sp_ctx,
412                         DATA_BLOB *data, DATA_BLOB *full_data,
413                         DATA_BLOB *signature)
414 {
415         switch(sp_ctx->mech) {
416         case SPNEGO_KRB5:
417                 return gse_unseal(mem_ctx,
418                                     sp_ctx->mech_ctx.gssapi_state,
419                                     data, signature);
420         case SPNEGO_NTLMSSP:
421                 return auth_ntlmssp_unseal_packet(
422                                         sp_ctx->mech_ctx.ntlmssp_state,
423                                         data->data, data->length,
424                                         full_data->data, full_data->length,
425                                         signature);
426         default:
427                 return NT_STATUS_INVALID_PARAMETER;
428         }
429 }