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/>.
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"
29 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
30 bool do_sign, bool do_seal,
31 struct spnego_context **spnego_ctx)
33 struct spnego_context *sp_ctx;
35 sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
37 return NT_STATUS_NO_MEMORY;
40 sp_ctx->do_sign = do_sign;
41 sp_ctx->do_seal = do_seal;
42 sp_ctx->state = SPNEGO_CONV_INIT;
48 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
49 bool do_sign, bool do_seal,
51 const char *ccache_name,
56 struct spnego_context **spnego_ctx)
58 struct spnego_context *sp_ctx = NULL;
59 uint32_t add_gss_c_flags = 0;
62 status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
63 if (!NT_STATUS_IS_OK(status)) {
66 sp_ctx->mech = SPNEGO_KRB5;
69 add_gss_c_flags = GSS_C_DCE_STYLE;
72 status = gse_init_client(sp_ctx,
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)) {
86 NTSTATUS spnego_ntlmssp_init_client(TALLOC_CTX *mem_ctx,
87 bool do_sign, bool do_seal,
92 struct spnego_context **spnego_ctx)
94 struct spnego_context *sp_ctx = NULL;
95 struct auth_ntlmssp_state *auth_ntlmssp_state;
98 status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
99 if (!NT_STATUS_IS_OK(status)) {
102 sp_ctx->mech = SPNEGO_NTLMSSP;
104 status = auth_ntlmssp_client_prepare(sp_ctx,
105 &auth_ntlmssp_state);
106 if (!NT_STATUS_IS_OK(status)) {
111 status = auth_ntlmssp_set_username(auth_ntlmssp_state,
113 if (!NT_STATUS_IS_OK(status)) {
118 status = auth_ntlmssp_set_domain(auth_ntlmssp_state,
120 if (!NT_STATUS_IS_OK(status)) {
125 status = auth_ntlmssp_set_password(auth_ntlmssp_state,
127 if (!NT_STATUS_IS_OK(status)) {
133 gensec_want_feature(auth_ntlmssp_state->gensec_security,
134 GENSEC_FEATURE_SIGN);
135 } else if (do_seal) {
136 gensec_want_feature(auth_ntlmssp_state->gensec_security,
137 GENSEC_FEATURE_SEAL);
140 status = auth_ntlmssp_client_start(auth_ntlmssp_state);
141 if (!NT_STATUS_IS_OK(status)) {
146 sp_ctx->mech_ctx.gensec_security = talloc_move(sp_ctx, &auth_ntlmssp_state->gensec_security);
147 TALLOC_FREE(auth_ntlmssp_state);
148 *spnego_ctx = sp_ctx;
152 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
153 struct spnego_context *sp_ctx,
154 DATA_BLOB *spnego_in,
155 DATA_BLOB *spnego_out)
157 struct gse_context *gse_ctx;
158 struct gensec_security *gensec_security;
159 struct spnego_data sp_in, sp_out;
160 DATA_BLOB token_in = data_blob_null;
161 DATA_BLOB token_out = data_blob_null;
162 const char *mech_oids[2] = { NULL, NULL };
163 char *principal = NULL;
166 bool mech_wants_more = false;
169 if (!spnego_in->length) {
170 /* server didn't send anything, is init ? */
171 if (sp_ctx->state != SPNEGO_CONV_INIT) {
172 return NT_STATUS_INVALID_PARAMETER;
175 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
177 status = NT_STATUS_INVALID_PARAMETER;
180 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
181 status = NT_STATUS_INVALID_PARAMETER;
184 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
185 status = NT_STATUS_ACCESS_DENIED;
188 token_in = sp_in.negTokenTarg.responseToken;
191 if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
192 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
193 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
194 *spnego_out = data_blob_null;
195 status = NT_STATUS_OK;
197 status = NT_STATUS_ACCESS_DENIED;
202 switch (sp_ctx->mech) {
205 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
206 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
207 &token_in, &token_out);
208 if (!NT_STATUS_IS_OK(status)) {
212 mech_oids[0] = OID_KERBEROS5;
213 mech_wants_more = gse_require_more_processing(gse_ctx);
219 gensec_security = sp_ctx->mech_ctx.gensec_security;
220 status = gensec_update(gensec_security, mem_ctx, NULL,
221 token_in, &token_out);
222 if (NT_STATUS_EQUAL(status,
223 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
224 mech_wants_more = true;
225 } else if (!NT_STATUS_IS_OK(status)) {
229 mech_oids[0] = OID_NTLMSSP;
234 status = NT_STATUS_INTERNAL_ERROR;
238 switch (sp_ctx->state) {
239 case SPNEGO_CONV_INIT:
240 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
241 &token_out, principal);
242 if (!spnego_out->data) {
243 status = NT_STATUS_INTERNAL_ERROR;
246 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
249 case SPNEGO_CONV_AUTH_MORE:
250 /* server says it's done and we do not seem to agree */
251 if (sp_in.negTokenTarg.negResult ==
252 SPNEGO_ACCEPT_COMPLETED) {
253 status = NT_STATUS_INVALID_PARAMETER;
257 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
258 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
259 sp_out.negTokenTarg.supportedMech = NULL;
260 sp_out.negTokenTarg.responseToken = token_out;
261 sp_out.negTokenTarg.mechListMIC = data_blob_null;
263 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
265 status = NT_STATUS_INTERNAL_ERROR;
269 if (!mech_wants_more) {
270 /* we still need to get an ack from the server */
271 sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
277 status = NT_STATUS_INTERNAL_ERROR;
281 status = NT_STATUS_OK;
285 spnego_free_data(&sp_in);
287 data_blob_free(&token_out);
291 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
293 struct gse_context *gse_ctx;
295 /* see if spnego processing itself requires more */
296 if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
297 sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
301 /* otherwise see if underlying mechnism does */
302 switch (sp_ctx->mech) {
304 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
305 return gse_require_more_processing(gse_ctx);
309 DEBUG(0, ("Unsupported type in request!\n"));
314 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
315 enum spnego_mech *type,
318 switch (sp_ctx->mech) {
320 *auth_context = sp_ctx->mech_ctx.gssapi_state;
323 *auth_context = sp_ctx->mech_ctx.gensec_security;
326 return NT_STATUS_INTERNAL_ERROR;
329 *type = sp_ctx->mech;
333 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
334 struct spnego_context *sp_ctx)
338 switch (sp_ctx->mech) {
340 return gse_get_session_key(mem_ctx,
341 sp_ctx->mech_ctx.gssapi_state);
343 status = gensec_session_key(sp_ctx->mech_ctx.gensec_security, mem_ctx, &sk);
344 if (!NT_STATUS_IS_OK(status)) {
345 return data_blob_null;
349 DEBUG(0, ("Unsupported type in request!\n"));
350 return data_blob_null;
354 NTSTATUS spnego_sign(TALLOC_CTX *mem_ctx,
355 struct spnego_context *sp_ctx,
356 DATA_BLOB *data, DATA_BLOB *full_data,
357 DATA_BLOB *signature)
359 switch(sp_ctx->mech) {
361 return gse_sign(mem_ctx,
362 sp_ctx->mech_ctx.gssapi_state,
365 return gensec_sign_packet(
366 sp_ctx->mech_ctx.gensec_security,
368 data->data, data->length,
369 full_data->data, full_data->length,
372 return NT_STATUS_INVALID_PARAMETER;
376 NTSTATUS spnego_sigcheck(TALLOC_CTX *mem_ctx,
377 struct spnego_context *sp_ctx,
378 DATA_BLOB *data, DATA_BLOB *full_data,
379 DATA_BLOB *signature)
381 switch(sp_ctx->mech) {
383 return gse_sigcheck(mem_ctx,
384 sp_ctx->mech_ctx.gssapi_state,
387 return gensec_check_packet(
388 sp_ctx->mech_ctx.gensec_security,
389 data->data, data->length,
390 full_data->data, full_data->length,
393 return NT_STATUS_INVALID_PARAMETER;
397 NTSTATUS spnego_seal(TALLOC_CTX *mem_ctx,
398 struct spnego_context *sp_ctx,
399 DATA_BLOB *data, DATA_BLOB *full_data,
400 DATA_BLOB *signature)
402 switch(sp_ctx->mech) {
404 return gse_seal(mem_ctx,
405 sp_ctx->mech_ctx.gssapi_state,
408 return gensec_seal_packet(
409 sp_ctx->mech_ctx.gensec_security,
411 data->data, data->length,
412 full_data->data, full_data->length,
415 return NT_STATUS_INVALID_PARAMETER;
419 NTSTATUS spnego_unseal(TALLOC_CTX *mem_ctx,
420 struct spnego_context *sp_ctx,
421 DATA_BLOB *data, DATA_BLOB *full_data,
422 DATA_BLOB *signature)
424 switch(sp_ctx->mech) {
426 return gse_unseal(mem_ctx,
427 sp_ctx->mech_ctx.gssapi_state,
430 return gensec_unseal_packet(
431 sp_ctx->mech_ctx.gensec_security,
432 data->data, data->length,
433 full_data->data, full_data->length,
436 return NT_STATUS_INVALID_PARAMETER;