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