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