s3-dcerpc: properly implement gse/spnego_get_session_key
[Samba.git] / source3 / librpc / rpc / dcerpc_spnego.c
blobec81a2c70180611d9d01b5a9bc6c5a766d21f05a
1 /*
2 * SPNEGO Encapsulation
3 * RPC Pipe client routines
4 * Copyright (C) Simo Sorce 2010.
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.
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.
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/>.
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"
27 struct spnego_context {
28 enum dcerpc_AuthType auth_type;
30 union {
31 struct auth_ntlmssp_state *ntlmssp_state;
32 struct gse_context *gssapi_state;
33 } mech_ctx;
35 enum {
36 SPNEGO_CONV_INIT = 0,
37 SPNEGO_CONV_AUTH_MORE,
38 SPNEGO_CONV_AUTH_CONFIRM,
39 SPNEGO_CONV_AUTH_DONE
40 } state;
43 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
44 enum dcerpc_AuthType auth_type,
45 struct spnego_context **spnego_ctx)
47 struct spnego_context *sp_ctx;
49 sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
50 if (!sp_ctx) {
51 return NT_STATUS_NO_MEMORY;
54 sp_ctx->auth_type = auth_type;
55 sp_ctx->state = SPNEGO_CONV_INIT;
57 *spnego_ctx = sp_ctx;
58 return NT_STATUS_OK;
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)
71 struct spnego_context *sp_ctx = NULL;
72 NTSTATUS status;
74 status = spnego_context_init(mem_ctx,
75 DCERPC_AUTH_TYPE_KRB5, &sp_ctx);
76 if (!NT_STATUS_IS_OK(status)) {
77 return status;
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;
89 *spnego_ctx = sp_ctx;
90 return NT_STATUS_OK;
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)
100 struct spnego_context *sp_ctx = NULL;
101 NTSTATUS status;
103 status = spnego_context_init(mem_ctx,
104 DCERPC_AUTH_TYPE_NTLMSSP, &sp_ctx);
105 if (!NT_STATUS_IS_OK(status)) {
106 return status;
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;
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;
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;
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;
141 * Turn off sign+seal to allow selected auth level to turn it back on.
143 auth_ntlmssp_and_flags(sp_ctx->mech_ctx.ntlmssp_state,
144 ~(NTLMSSP_NEGOTIATE_SIGN |
145 NTLMSSP_NEGOTIATE_SEAL));
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);
156 *spnego_ctx = sp_ctx;
157 return NT_STATUS_OK;
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)
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;
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;
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;
188 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
189 status = NT_STATUS_INVALID_PARAMETER;
190 goto done;
192 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
193 status = NT_STATUS_ACCESS_DENIED;
194 goto done;
196 token_in = sp_in.negTokenTarg.responseToken;
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;
207 goto done;
210 switch (sp_ctx->auth_type) {
211 case DCERPC_AUTH_TYPE_KRB5:
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;
220 mech_oids[0] = OID_KERBEROS5;
221 mech_wants_more = gse_require_more_processing(gse_ctx);
223 break;
225 case DCERPC_AUTH_TYPE_NTLMSSP:
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;
237 mech_oids[0] = OID_NTLMSSP;
239 break;
241 default:
242 status = NT_STATUS_INTERNAL_ERROR;
243 goto done;
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;
254 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
255 break;
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;
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;
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;
277 if (!mech_wants_more) {
278 /* we still need to get an ack from the server */
279 sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
282 break;
284 default:
285 status = NT_STATUS_INTERNAL_ERROR;
286 goto done;
289 status = NT_STATUS_OK;
291 done:
292 if (len_in > 0) {
293 spnego_free_data(&sp_in);
295 data_blob_free(&token_out);
296 return status;
299 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
301 struct gse_context *gse_ctx;
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;
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;
322 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
323 enum dcerpc_AuthType *auth_type,
324 void **auth_context)
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;
337 *auth_type = sp_ctx->auth_type;
338 return NT_STATUS_OK;
341 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
342 struct spnego_context *sp_ctx)
344 DATA_BLOB sk;
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;