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