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