f64a537e202ee06d76a565882303f9c190be9e13
[metze/samba/wip.git] / source3 / librpc / crypto / cli_spnego.c
1 /*
2  *  SPNEGO Encapsulation
3  *  Client functions
4  *  Copyright (C) Simo Sorce 2010.
5  *  Copyright (C) Andrew Bartlett 2011.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "includes.h"
22 #include "../libcli/auth/spnego.h"
23 #include "include/auth_generic.h"
24 #include "librpc/gen_ndr/ntlmssp.h"
25 #include "auth/ntlmssp/ntlmssp.h"
26 #include "librpc/crypto/gse.h"
27 #include "librpc/crypto/spnego.h"
28 #include "auth/gensec/gensec.h"
29
30 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
31                                     bool do_sign, bool do_seal,
32                                     struct spnego_context **spnego_ctx)
33 {
34         struct spnego_context *sp_ctx;
35
36         sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
37         if (!sp_ctx) {
38                 return NT_STATUS_NO_MEMORY;
39         }
40
41         sp_ctx->do_sign = do_sign;
42         sp_ctx->do_seal = do_seal;
43         sp_ctx->state = SPNEGO_CONV_INIT;
44
45         *spnego_ctx = sp_ctx;
46         return NT_STATUS_OK;
47 }
48
49 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
50                                    bool do_sign, bool do_seal,
51                                    bool is_dcerpc,
52                                    const char *ccache_name,
53                                    const char *server,
54                                    const char *service,
55                                    const char *username,
56                                    const char *password,
57                                    struct spnego_context **spnego_ctx)
58 {
59         struct spnego_context *sp_ctx = NULL;
60         uint32_t add_gss_c_flags = 0;
61         NTSTATUS status;
62
63         status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
64         if (!NT_STATUS_IS_OK(status)) {
65                 return status;
66         }
67         sp_ctx->mech = SPNEGO_KRB5;
68
69         if (is_dcerpc) {
70                 add_gss_c_flags = GSS_C_DCE_STYLE;
71         }
72
73         status = gse_init_client(sp_ctx,
74                                  do_sign, do_seal,
75                                  ccache_name, server, service,
76                                  username, password, add_gss_c_flags,
77                                  &sp_ctx->mech_ctx.gssapi_state);
78         if (!NT_STATUS_IS_OK(status)) {
79                 TALLOC_FREE(sp_ctx);
80                 return status;
81         }
82
83         *spnego_ctx = sp_ctx;
84         return NT_STATUS_OK;
85 }
86
87 NTSTATUS spnego_generic_init_client(TALLOC_CTX *mem_ctx,
88                                     const char *oid,
89                                     bool do_sign, bool do_seal,
90                                     bool is_dcerpc,
91                                     const char *domain,
92                                     const char *username,
93                                     const char *password,
94                                     struct spnego_context **spnego_ctx)
95 {
96         struct spnego_context *sp_ctx = NULL;
97         struct auth_generic_state *auth_generic_state;
98         NTSTATUS status;
99
100         status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
101         if (!NT_STATUS_IS_OK(status)) {
102                 return status;
103         }
104         if (strcmp(oid, GENSEC_OID_NTLMSSP) == 0) {
105                 sp_ctx->mech = SPNEGO_NTLMSSP;
106         } else {
107                 return NT_STATUS_INVALID_PARAMETER;
108         }
109
110         status = auth_generic_client_prepare(sp_ctx,
111                                         &auth_generic_state);
112         if (!NT_STATUS_IS_OK(status)) {
113                 TALLOC_FREE(sp_ctx);
114                 return status;
115         }
116
117         status = auth_generic_set_username(auth_generic_state,
118                                            username);
119         if (!NT_STATUS_IS_OK(status)) {
120                 TALLOC_FREE(sp_ctx);
121                 return status;
122         }
123
124         status = auth_generic_set_domain(auth_generic_state,
125                                          domain);
126         if (!NT_STATUS_IS_OK(status)) {
127                 TALLOC_FREE(sp_ctx);
128                 return status;
129         }
130
131         status = auth_generic_set_password(auth_generic_state,
132                                            password);
133         if (!NT_STATUS_IS_OK(status)) {
134                 TALLOC_FREE(sp_ctx);
135                 return status;
136         }
137
138         if (do_sign) {
139                 gensec_want_feature(auth_generic_state->gensec_security,
140                                           GENSEC_FEATURE_SIGN);
141         } else if (do_seal) {
142                 gensec_want_feature(auth_generic_state->gensec_security,
143                                           GENSEC_FEATURE_SEAL);
144         }
145
146         if (is_dcerpc) {
147                 gensec_want_feature(auth_generic_state->gensec_security,
148                                     GENSEC_FEATURE_DCE_STYLE);
149         }
150
151         status = auth_generic_client_start(auth_generic_state, oid);
152         if (!NT_STATUS_IS_OK(status)) {
153                 TALLOC_FREE(sp_ctx);
154                 return status;
155         }
156
157         sp_ctx->mech_ctx.gensec_security = talloc_move(sp_ctx, &auth_generic_state->gensec_security);
158         TALLOC_FREE(auth_generic_state);
159         *spnego_ctx = sp_ctx;
160         return NT_STATUS_OK;
161 }
162
163 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
164                                       struct spnego_context *sp_ctx,
165                                       DATA_BLOB *spnego_in,
166                                       DATA_BLOB *spnego_out)
167 {
168         struct gse_context *gse_ctx;
169         struct gensec_security *gensec_security;
170         struct spnego_data sp_in, sp_out;
171         DATA_BLOB token_in = data_blob_null;
172         DATA_BLOB token_out = data_blob_null;
173         const char *mech_oids[2] = { NULL, NULL };
174         char *principal = NULL;
175         ssize_t len_in = 0;
176         ssize_t len_out = 0;
177         bool mech_wants_more = false;
178         NTSTATUS status;
179
180         if (!spnego_in->length) {
181                 /* server didn't send anything, is init ? */
182                 if (sp_ctx->state != SPNEGO_CONV_INIT) {
183                         return NT_STATUS_INVALID_PARAMETER;
184                 }
185         } else {
186                 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
187                 if (len_in == -1) {
188                         status = NT_STATUS_INVALID_PARAMETER;
189                         goto done;
190                 }
191                 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
192                         status = NT_STATUS_INVALID_PARAMETER;
193                         goto done;
194                 }
195                 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
196                         status = NT_STATUS_ACCESS_DENIED;
197                         goto done;
198                 }
199                 token_in = sp_in.negTokenTarg.responseToken;
200         }
201
202         if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
203                 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
204                         sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
205                         *spnego_out = data_blob_null;
206                         status = NT_STATUS_OK;
207                 } else {
208                         status = NT_STATUS_ACCESS_DENIED;
209                 }
210                 goto done;
211         }
212
213         switch (sp_ctx->mech) {
214         case SPNEGO_KRB5:
215
216                 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
217                 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
218                                                    &token_in, &token_out);
219                 if (!NT_STATUS_IS_OK(status)) {
220                         goto done;
221                 }
222
223                 mech_oids[0] = OID_KERBEROS5;
224                 mech_wants_more = gse_require_more_processing(gse_ctx);
225
226                 break;
227
228         case SPNEGO_NTLMSSP:
229
230                 gensec_security = sp_ctx->mech_ctx.gensec_security;
231                 status = gensec_update(gensec_security, mem_ctx, NULL,
232                                        token_in, &token_out);
233                 if (NT_STATUS_EQUAL(status,
234                                     NT_STATUS_MORE_PROCESSING_REQUIRED)) {
235                         mech_wants_more = true;
236                 } else if (!NT_STATUS_IS_OK(status)) {
237                         goto done;
238                 }
239
240                 mech_oids[0] = OID_NTLMSSP;
241
242                 break;
243
244         default:
245                 status = NT_STATUS_INTERNAL_ERROR;
246                 goto done;
247         }
248
249         switch (sp_ctx->state) {
250         case SPNEGO_CONV_INIT:
251                 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
252                                                       &token_out, principal);
253                 if (!spnego_out->data) {
254                         status = NT_STATUS_INTERNAL_ERROR;
255                         goto done;
256                 }
257                 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
258                 break;
259
260         case SPNEGO_CONV_AUTH_MORE:
261                 /* server says it's done and we do not seem to agree */
262                 if (sp_in.negTokenTarg.negResult ==
263                                                 SPNEGO_ACCEPT_COMPLETED) {
264                         status = NT_STATUS_INVALID_PARAMETER;
265                         goto done;
266                 }
267
268                 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
269                 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
270                 sp_out.negTokenTarg.supportedMech = NULL;
271                 sp_out.negTokenTarg.responseToken = token_out;
272                 sp_out.negTokenTarg.mechListMIC = data_blob_null;
273
274                 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
275                 if (len_out == -1) {
276                         status = NT_STATUS_INTERNAL_ERROR;
277                         goto done;
278                 }
279
280                 if (!mech_wants_more) {
281                         /* we still need to get an ack from the server */
282                         sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
283                 }
284
285                 break;
286
287         default:
288                 status = NT_STATUS_INTERNAL_ERROR;
289                 goto done;
290         }
291
292         status = NT_STATUS_OK;
293
294 done:
295         if (len_in > 0) {
296                 spnego_free_data(&sp_in);
297         }
298         data_blob_free(&token_out);
299         return status;
300 }
301
302 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
303 {
304         struct gse_context *gse_ctx;
305
306         /* see if spnego processing itself requires more */
307         if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
308             sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
309                 return true;
310         }
311
312         /* otherwise see if underlying mechnism does */
313         switch (sp_ctx->mech) {
314         case SPNEGO_KRB5:
315                 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
316                 return gse_require_more_processing(gse_ctx);
317         case SPNEGO_NTLMSSP:
318                 return false;
319         default:
320                 DEBUG(0, ("Unsupported type in request!\n"));
321                 return false;
322         }
323 }
324
325 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
326                                     enum spnego_mech *type,
327                                     void **auth_context)
328 {
329         switch (sp_ctx->mech) {
330         case SPNEGO_KRB5:
331                 *auth_context = sp_ctx->mech_ctx.gssapi_state;
332                 break;
333         case SPNEGO_NTLMSSP:
334                 *auth_context = sp_ctx->mech_ctx.gensec_security;
335                 break;
336         default:
337                 return NT_STATUS_INTERNAL_ERROR;
338         }
339
340         *type = sp_ctx->mech;
341         return NT_STATUS_OK;
342 }
343
344 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
345                                  struct spnego_context *sp_ctx)
346 {
347         DATA_BLOB sk;
348         NTSTATUS status;
349         switch (sp_ctx->mech) {
350         case SPNEGO_KRB5:
351                 return gse_get_session_key(mem_ctx,
352                                            sp_ctx->mech_ctx.gssapi_state);
353         case SPNEGO_NTLMSSP:
354                 status = gensec_session_key(sp_ctx->mech_ctx.gensec_security, mem_ctx, &sk);
355                 if (!NT_STATUS_IS_OK(status)) {
356                         return data_blob_null;
357                 }
358                 return sk;
359         default:
360                 DEBUG(0, ("Unsupported type in request!\n"));
361                 return data_blob_null;
362         }
363 }
364
365 NTSTATUS spnego_sign(TALLOC_CTX *mem_ctx,
366                         struct spnego_context *sp_ctx,
367                         DATA_BLOB *data, DATA_BLOB *full_data,
368                         DATA_BLOB *signature)
369 {
370         switch(sp_ctx->mech) {
371         case SPNEGO_KRB5:
372                 return gse_sign(mem_ctx,
373                                 sp_ctx->mech_ctx.gssapi_state,
374                                 data, signature);
375         case SPNEGO_NTLMSSP:
376                 return gensec_sign_packet(
377                         sp_ctx->mech_ctx.gensec_security,
378                         mem_ctx,
379                         data->data, data->length,
380                         full_data->data, full_data->length,
381                         signature);
382         default:
383                 return NT_STATUS_INVALID_PARAMETER;
384         }
385 }
386
387 NTSTATUS spnego_sigcheck(TALLOC_CTX *mem_ctx,
388                          struct spnego_context *sp_ctx,
389                          DATA_BLOB *data, DATA_BLOB *full_data,
390                          DATA_BLOB *signature)
391 {
392         switch(sp_ctx->mech) {
393         case SPNEGO_KRB5:
394                 return gse_sigcheck(mem_ctx,
395                                     sp_ctx->mech_ctx.gssapi_state,
396                                     data, signature);
397         case SPNEGO_NTLMSSP:
398                 return gensec_check_packet(
399                         sp_ctx->mech_ctx.gensec_security,
400                         data->data, data->length,
401                         full_data->data, full_data->length,
402                         signature);
403         default:
404                 return NT_STATUS_INVALID_PARAMETER;
405         }
406 }
407
408 NTSTATUS spnego_seal(TALLOC_CTX *mem_ctx,
409                         struct spnego_context *sp_ctx,
410                         DATA_BLOB *data, DATA_BLOB *full_data,
411                         DATA_BLOB *signature)
412 {
413         switch(sp_ctx->mech) {
414         case SPNEGO_KRB5:
415                 return gse_seal(mem_ctx,
416                                 sp_ctx->mech_ctx.gssapi_state,
417                                 data, signature);
418         case SPNEGO_NTLMSSP:
419                 return gensec_seal_packet(
420                         sp_ctx->mech_ctx.gensec_security,
421                         mem_ctx,
422                         data->data, data->length,
423                         full_data->data, full_data->length,
424                         signature);
425         default:
426                 return NT_STATUS_INVALID_PARAMETER;
427         }
428 }
429
430 NTSTATUS spnego_unseal(TALLOC_CTX *mem_ctx,
431                         struct spnego_context *sp_ctx,
432                         DATA_BLOB *data, DATA_BLOB *full_data,
433                         DATA_BLOB *signature)
434 {
435         switch(sp_ctx->mech) {
436         case SPNEGO_KRB5:
437                 return gse_unseal(mem_ctx,
438                                     sp_ctx->mech_ctx.gssapi_state,
439                                     data, signature);
440         case SPNEGO_NTLMSSP:
441                 return gensec_unseal_packet(
442                         sp_ctx->mech_ctx.gensec_security,
443                         data->data, data->length,
444                         full_data->data, full_data->length,
445                         signature);
446         default:
447                 return NT_STATUS_INVALID_PARAMETER;
448         }
449 }