4 * Copyright (C) Simo Sorce 2010.
5 * Copyright (C) Andrew Bartlett 2011.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "../libcli/auth/spnego.h"
23 #include "include/auth_generic.h"
24 #include "librpc/gen_ndr/ntlmssp.h"
25 #include "auth/ntlmssp/ntlmssp.h"
26 #include "librpc/crypto/gse.h"
27 #include "librpc/crypto/spnego.h"
28 #include "auth/gensec/gensec.h"
30 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
31 bool do_sign, bool do_seal,
32 struct spnego_context **spnego_ctx)
34 struct spnego_context *sp_ctx;
36 sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
38 return NT_STATUS_NO_MEMORY;
41 sp_ctx->do_sign = do_sign;
42 sp_ctx->do_seal = do_seal;
43 sp_ctx->state = SPNEGO_CONV_INIT;
49 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
50 bool do_sign, bool do_seal,
52 const char *ccache_name,
57 struct spnego_context **spnego_ctx)
59 struct spnego_context *sp_ctx = NULL;
60 uint32_t add_gss_c_flags = 0;
63 status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
64 if (!NT_STATUS_IS_OK(status)) {
67 sp_ctx->mech = SPNEGO_KRB5;
70 add_gss_c_flags = GSS_C_DCE_STYLE;
73 status = gse_init_client(sp_ctx,
75 ccache_name, server, service,
76 username, password, add_gss_c_flags,
77 &sp_ctx->mech_ctx.gssapi_state);
78 if (!NT_STATUS_IS_OK(status)) {
87 NTSTATUS spnego_generic_init_client(TALLOC_CTX *mem_ctx,
89 bool do_sign, bool do_seal,
94 struct spnego_context **spnego_ctx)
96 struct spnego_context *sp_ctx = NULL;
97 struct auth_generic_state *auth_generic_state;
100 status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
101 if (!NT_STATUS_IS_OK(status)) {
104 if (strcmp(oid, GENSEC_OID_NTLMSSP) == 0) {
105 sp_ctx->mech = SPNEGO_NTLMSSP;
107 return NT_STATUS_INVALID_PARAMETER;
110 status = auth_generic_client_prepare(sp_ctx,
111 &auth_generic_state);
112 if (!NT_STATUS_IS_OK(status)) {
117 status = auth_generic_set_username(auth_generic_state,
119 if (!NT_STATUS_IS_OK(status)) {
124 status = auth_generic_set_domain(auth_generic_state,
126 if (!NT_STATUS_IS_OK(status)) {
131 status = auth_generic_set_password(auth_generic_state,
133 if (!NT_STATUS_IS_OK(status)) {
139 gensec_want_feature(auth_generic_state->gensec_security,
140 GENSEC_FEATURE_SIGN);
141 } else if (do_seal) {
142 gensec_want_feature(auth_generic_state->gensec_security,
143 GENSEC_FEATURE_SEAL);
147 gensec_want_feature(auth_generic_state->gensec_security,
148 GENSEC_FEATURE_DCE_STYLE);
151 status = auth_generic_client_start(auth_generic_state, oid);
152 if (!NT_STATUS_IS_OK(status)) {
157 sp_ctx->mech_ctx.gensec_security = talloc_move(sp_ctx, &auth_generic_state->gensec_security);
158 TALLOC_FREE(auth_generic_state);
159 *spnego_ctx = sp_ctx;
163 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
164 struct spnego_context *sp_ctx,
165 DATA_BLOB *spnego_in,
166 DATA_BLOB *spnego_out)
168 struct gse_context *gse_ctx;
169 struct gensec_security *gensec_security;
170 struct spnego_data sp_in, sp_out;
171 DATA_BLOB token_in = data_blob_null;
172 DATA_BLOB token_out = data_blob_null;
173 const char *mech_oids[2] = { NULL, NULL };
174 char *principal = NULL;
177 bool mech_wants_more = false;
180 if (!spnego_in->length) {
181 /* server didn't send anything, is init ? */
182 if (sp_ctx->state != SPNEGO_CONV_INIT) {
183 return NT_STATUS_INVALID_PARAMETER;
186 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
188 status = NT_STATUS_INVALID_PARAMETER;
191 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
192 status = NT_STATUS_INVALID_PARAMETER;
195 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
196 status = NT_STATUS_ACCESS_DENIED;
199 token_in = sp_in.negTokenTarg.responseToken;
202 if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
203 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
204 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
205 *spnego_out = data_blob_null;
206 status = NT_STATUS_OK;
208 status = NT_STATUS_ACCESS_DENIED;
213 switch (sp_ctx->mech) {
216 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
217 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
218 &token_in, &token_out);
219 if (!NT_STATUS_IS_OK(status)) {
223 mech_oids[0] = OID_KERBEROS5;
224 mech_wants_more = gse_require_more_processing(gse_ctx);
230 gensec_security = sp_ctx->mech_ctx.gensec_security;
231 status = gensec_update(gensec_security, mem_ctx, NULL,
232 token_in, &token_out);
233 if (NT_STATUS_EQUAL(status,
234 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
235 mech_wants_more = true;
236 } else if (!NT_STATUS_IS_OK(status)) {
240 mech_oids[0] = OID_NTLMSSP;
245 status = NT_STATUS_INTERNAL_ERROR;
249 switch (sp_ctx->state) {
250 case SPNEGO_CONV_INIT:
251 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
252 &token_out, principal);
253 if (!spnego_out->data) {
254 status = NT_STATUS_INTERNAL_ERROR;
257 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
260 case SPNEGO_CONV_AUTH_MORE:
261 /* server says it's done and we do not seem to agree */
262 if (sp_in.negTokenTarg.negResult ==
263 SPNEGO_ACCEPT_COMPLETED) {
264 status = NT_STATUS_INVALID_PARAMETER;
268 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
269 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
270 sp_out.negTokenTarg.supportedMech = NULL;
271 sp_out.negTokenTarg.responseToken = token_out;
272 sp_out.negTokenTarg.mechListMIC = data_blob_null;
274 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
276 status = NT_STATUS_INTERNAL_ERROR;
280 if (!mech_wants_more) {
281 /* we still need to get an ack from the server */
282 sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
288 status = NT_STATUS_INTERNAL_ERROR;
292 status = NT_STATUS_OK;
296 spnego_free_data(&sp_in);
298 data_blob_free(&token_out);
302 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
304 struct gse_context *gse_ctx;
306 /* see if spnego processing itself requires more */
307 if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
308 sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
312 /* otherwise see if underlying mechnism does */
313 switch (sp_ctx->mech) {
315 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
316 return gse_require_more_processing(gse_ctx);
320 DEBUG(0, ("Unsupported type in request!\n"));
325 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
326 enum spnego_mech *type,
329 switch (sp_ctx->mech) {
331 *auth_context = sp_ctx->mech_ctx.gssapi_state;
334 *auth_context = sp_ctx->mech_ctx.gensec_security;
337 return NT_STATUS_INTERNAL_ERROR;
340 *type = sp_ctx->mech;
344 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
345 struct spnego_context *sp_ctx)
349 switch (sp_ctx->mech) {
351 return gse_get_session_key(mem_ctx,
352 sp_ctx->mech_ctx.gssapi_state);
354 status = gensec_session_key(sp_ctx->mech_ctx.gensec_security, mem_ctx, &sk);
355 if (!NT_STATUS_IS_OK(status)) {
356 return data_blob_null;
360 DEBUG(0, ("Unsupported type in request!\n"));
361 return data_blob_null;
365 NTSTATUS spnego_sign(TALLOC_CTX *mem_ctx,
366 struct spnego_context *sp_ctx,
367 DATA_BLOB *data, DATA_BLOB *full_data,
368 DATA_BLOB *signature)
370 switch(sp_ctx->mech) {
372 return gse_sign(mem_ctx,
373 sp_ctx->mech_ctx.gssapi_state,
376 return gensec_sign_packet(
377 sp_ctx->mech_ctx.gensec_security,
379 data->data, data->length,
380 full_data->data, full_data->length,
383 return NT_STATUS_INVALID_PARAMETER;
387 NTSTATUS spnego_sigcheck(TALLOC_CTX *mem_ctx,
388 struct spnego_context *sp_ctx,
389 DATA_BLOB *data, DATA_BLOB *full_data,
390 DATA_BLOB *signature)
392 switch(sp_ctx->mech) {
394 return gse_sigcheck(mem_ctx,
395 sp_ctx->mech_ctx.gssapi_state,
398 return gensec_check_packet(
399 sp_ctx->mech_ctx.gensec_security,
400 data->data, data->length,
401 full_data->data, full_data->length,
404 return NT_STATUS_INVALID_PARAMETER;
408 NTSTATUS spnego_seal(TALLOC_CTX *mem_ctx,
409 struct spnego_context *sp_ctx,
410 DATA_BLOB *data, DATA_BLOB *full_data,
411 DATA_BLOB *signature)
413 switch(sp_ctx->mech) {
415 return gse_seal(mem_ctx,
416 sp_ctx->mech_ctx.gssapi_state,
419 return gensec_seal_packet(
420 sp_ctx->mech_ctx.gensec_security,
422 data->data, data->length,
423 full_data->data, full_data->length,
426 return NT_STATUS_INVALID_PARAMETER;
430 NTSTATUS spnego_unseal(TALLOC_CTX *mem_ctx,
431 struct spnego_context *sp_ctx,
432 DATA_BLOB *data, DATA_BLOB *full_data,
433 DATA_BLOB *signature)
435 switch(sp_ctx->mech) {
437 return gse_unseal(mem_ctx,
438 sp_ctx->mech_ctx.gssapi_state,
441 return gensec_unseal_packet(
442 sp_ctx->mech_ctx.gensec_security,
443 data->data, data->length,
444 full_data->data, full_data->length,
447 return NT_STATUS_INVALID_PARAMETER;