2 Unix SMB/CIFS implementation.
4 RFC2478 Compliant SPNEGO implementation
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
7 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #define DBGC_CLASS DBGC_AUTH
30 enum spnego_state_position {
41 enum spnego_message_type expected_packet;
42 enum spnego_message_type state_position;
43 enum spnego_negResult result;
44 struct gensec_security *sub_sec_security;
47 static NTSTATUS gensec_spnego_client_start(struct gensec_security *gensec_security)
49 struct spnego_state *spnego_state;
50 TALLOC_CTX *mem_ctx = talloc_init("gensec_spengo_client_start");
52 return NT_STATUS_NO_MEMORY;
54 spnego_state = talloc_p(mem_ctx, struct spnego_state);
57 return NT_STATUS_NO_MEMORY;
60 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_INIT;
61 spnego_state->state_position = SPNEGO_CLIENT_START;
62 spnego_state->result = SPNEGO_ACCEPT_INCOMPLETE;
63 spnego_state->mem_ctx = mem_ctx;
64 spnego_state->sub_sec_security = NULL;
66 gensec_security->private_data = spnego_state;
71 wrappers for the spnego_*() functions
73 static NTSTATUS gensec_spnego_unseal_packet(struct gensec_security *gensec_security,
75 uint8_t *data, size_t length, DATA_BLOB *sig)
77 struct spnego_state *spnego_state = gensec_security->private_data;
79 if (spnego_state->state_position != SPNEGO_DONE
80 && spnego_state->state_position != SPNEGO_FALLBACK) {
81 return NT_STATUS_INVALID_PARAMETER;
84 return gensec_unseal_packet(spnego_state->sub_sec_security,
85 mem_ctx, data, length, sig);
88 static NTSTATUS gensec_spnego_check_packet(struct gensec_security *gensec_security,
90 const uint8_t *data, size_t length,
93 struct spnego_state *spnego_state = gensec_security->private_data;
95 return NT_STATUS_NOT_IMPLEMENTED;
96 if (spnego_state->state_position != SPNEGO_DONE
97 && spnego_state->state_position != SPNEGO_FALLBACK) {
98 return NT_STATUS_INVALID_PARAMETER;
101 return gensec_check_packet(spnego_state->sub_sec_security,
102 mem_ctx, data, length, sig);
105 static NTSTATUS gensec_spnego_seal_packet(struct gensec_security *gensec_security,
107 uint8_t *data, size_t length,
110 struct spnego_state *spnego_state = gensec_security->private_data;
112 return NT_STATUS_NOT_IMPLEMENTED;
113 if (spnego_state->state_position != SPNEGO_DONE
114 && spnego_state->state_position != SPNEGO_FALLBACK) {
115 return NT_STATUS_INVALID_PARAMETER;
118 return gensec_seal_packet(spnego_state->sub_sec_security,
119 mem_ctx, data, length, sig);
122 static NTSTATUS gensec_spnego_sign_packet(struct gensec_security *gensec_security,
124 const uint8_t *data, size_t length,
127 struct spnego_state *spnego_state = gensec_security->private_data;
129 if (spnego_state->state_position != SPNEGO_DONE
130 && spnego_state->state_position != SPNEGO_FALLBACK) {
131 return NT_STATUS_INVALID_PARAMETER;
134 return gensec_sign_packet(spnego_state->sub_sec_security,
135 mem_ctx, data, length, sig);
138 static NTSTATUS gensec_spnego_session_key(struct gensec_security *gensec_security,
139 DATA_BLOB *session_key)
141 struct spnego_state *spnego_state = gensec_security->private_data;
142 if (spnego_state->state_position != SPNEGO_DONE
143 && spnego_state->state_position != SPNEGO_FALLBACK) {
144 return NT_STATUS_INVALID_PARAMETER;
147 return gensec_session_key(spnego_state->sub_sec_security,
151 /** Fallback to another GENSEC mechanism, based on magic strings
153 * This is the 'fallback' case, where we don't get SPENGO, and have to
154 * try all the other options (and hope they all have a magic string
158 static NTSTATUS gensec_spengo_server_try_fallback(struct gensec_security *gensec_security,
159 struct spnego_state *spnego_state,
160 TALLOC_CTX *out_mem_ctx,
161 const DATA_BLOB in, DATA_BLOB *out)
165 const struct gensec_security_ops **all_ops = gensec_security_all(&num_ops);
166 for (i=0; i < num_ops; i++) {
168 if (!all_ops[i]->oid) {
171 nt_status = gensec_subcontext_start(gensec_security,
172 &spnego_state->sub_sec_security);
173 if (!NT_STATUS_IS_OK(nt_status)) {
176 /* select the sub context */
177 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
179 if (!NT_STATUS_IS_OK(nt_status)) {
180 gensec_end(&spnego_state->sub_sec_security);
183 nt_status = gensec_update(spnego_state->sub_sec_security,
184 out_mem_ctx, in, out);
185 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
186 spnego_state->state_position = SPNEGO_FALLBACK;
189 gensec_end(&spnego_state->sub_sec_security);
191 DEBUG(1, ("Failed to parse SPENGO request\n"));
192 return NT_STATUS_INVALID_PARAMETER;
196 static NTSTATUS gensec_spnego_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx,
197 const DATA_BLOB in, DATA_BLOB *out)
199 struct spnego_state *spnego_state = gensec_security->private_data;
200 DATA_BLOB null_data_blob = data_blob(NULL, 0);
201 DATA_BLOB unwrapped_out;
202 struct spnego_data spnego_out;
203 struct spnego_data spnego;
208 out_mem_ctx = spnego_state->mem_ctx;
211 /* and switch into the state machine */
213 switch (spnego_state->state_position) {
214 case SPNEGO_FALLBACK:
215 return gensec_update(spnego_state->sub_sec_security,
216 out_mem_ctx, in, out);
217 case SPNEGO_SERVER_START:
220 len = spnego_read_data(in, &spnego);
222 return gensec_spengo_server_try_fallback(gensec_security, spnego_state, out_mem_ctx, in, out);
224 /* client sent NegTargetInit */
227 /* server needs to send NegTargetInit */
231 case SPNEGO_CLIENT_START:
233 /* The server offers a list of mechanisms */
236 char *my_mechs[] = {NULL, NULL};
241 /* client to produce negTokenInit */
242 return NT_STATUS_INVALID_PARAMETER;
245 len = spnego_read_data(in, &spnego);
248 return NT_STATUS_INVALID_PARAMETER;
251 /* OK, so it's real SPNEGO, check the packet's the one we expect */
252 if (spnego.type != spnego_state->expected_packet) {
253 spnego_free_data(&spnego);
254 DEBUG(1, ("Invalid SPENGO request: %d, expected %d\n", spnego.type,
255 spnego_state->expected_packet));
256 return NT_STATUS_INVALID_PARAMETER;
259 mechType = spnego.negTokenInit.mechTypes;
260 for (i=0; mechType && mechType[i]; i++) {
261 nt_status = gensec_client_start(&spnego_state->sub_sec_security);
262 if (!NT_STATUS_IS_OK(nt_status)) {
265 /* select the sub context */
266 nt_status = gensec_start_mech_by_oid(spnego_state->sub_sec_security,
268 if (!NT_STATUS_IS_OK(nt_status)) {
269 gensec_end(&spnego_state->sub_sec_security);
274 nt_status = gensec_update(spnego_state->sub_sec_security,
276 spnego.negTokenInit.mechToken,
279 /* only get the helping start blob for the first OID */
280 nt_status = gensec_update(spnego_state->sub_sec_security,
285 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
286 DEBUG(1, ("SPENGO(%s) NEG_TOKEN_INIT failed: %s\n",
287 spnego_state->sub_sec_security->ops->name, nt_errstr(nt_status)));
288 gensec_end(&spnego_state->sub_sec_security);
293 if (!mechType || !mechType[i]) {
294 DEBUG(1, ("SPENGO: Could not find a suitable mechtype in NEG_TOKEN_INIT\n"));
297 spnego_free_data(&spnego);
298 if (!NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
303 my_mechs[0] = spnego_state->sub_sec_security->ops->oid;
305 spnego_out.type = SPNEGO_NEG_TOKEN_INIT;
306 spnego_out.negTokenInit.mechTypes = my_mechs;
307 spnego_out.negTokenInit.reqFlags = 0;
308 spnego_out.negTokenInit.mechListMIC = null_data_blob;
309 spnego_out.negTokenInit.mechToken = unwrapped_out;
311 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
312 DEBUG(1, ("Failed to write SPENGO reply to NEG_TOKEN_INIT\n"));
313 return NT_STATUS_INVALID_PARAMETER;
317 spnego_state->expected_packet = SPNEGO_NEG_TOKEN_TARG;
318 spnego_state->state_position = SPNEGO_TARG;
326 return NT_STATUS_INVALID_PARAMETER;
329 len = spnego_read_data(in, &spnego);
332 return NT_STATUS_INVALID_PARAMETER;
335 /* OK, so it's real SPNEGO, check the packet's the one we expect */
336 if (spnego.type != spnego_state->expected_packet) {
337 spnego_free_data(&spnego);
338 DEBUG(1, ("Invalid SPENGO request: %d, expected %d\n", spnego.type,
339 spnego_state->expected_packet));
340 return NT_STATUS_INVALID_PARAMETER;
343 if (spnego.negTokenTarg.negResult == SPNEGO_REJECT) {
344 return NT_STATUS_ACCESS_DENIED;
347 if (spnego.negTokenTarg.responseToken.length) {
348 nt_status = gensec_update(spnego_state->sub_sec_security,
350 spnego.negTokenTarg.responseToken,
353 unwrapped_out = data_blob(NULL, 0);
354 nt_status = NT_STATUS_OK;
357 if (NT_STATUS_IS_OK(nt_status)
358 && (spnego.negTokenTarg.negResult != SPNEGO_ACCEPT_COMPLETED)) {
359 nt_status = NT_STATUS_INVALID_PARAMETER;
362 spnego_state->result = spnego.negTokenTarg.negResult;
363 spnego_free_data(&spnego);
365 if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) {
367 spnego_out.type = SPNEGO_NEG_TOKEN_TARG;
368 spnego_out.negTokenTarg.negResult = SPNEGO_ACCEPT_INCOMPLETE;
369 spnego_out.negTokenTarg.supportedMech
370 = spnego_state->sub_sec_security->ops->oid;
371 spnego_out.negTokenTarg.responseToken = unwrapped_out;
372 spnego_out.negTokenTarg.mechListMIC = null_data_blob;
374 if (spnego_write_data(out_mem_ctx, out, &spnego_out) == -1) {
375 DEBUG(1, ("Failed to write SPENGO reply to NEG_TOKEN_TARG\n"));
376 return NT_STATUS_INVALID_PARAMETER;
378 spnego_state->state_position = SPNEGO_TARG;
379 } else if (NT_STATUS_IS_OK(nt_status)) {
380 spnego_state->state_position = SPNEGO_DONE;
382 DEBUG(1, ("SPENGO(%s) login failed: %s\n",
383 spnego_state->sub_sec_security->ops->name,
384 nt_errstr(nt_status)));
391 spnego_free_data(&spnego);
392 DEBUG(1, ("Invalid SPENGO request: %d\n", spnego.type));
393 return NT_STATUS_INVALID_PARAMETER;
397 static void gensec_spnego_end(struct gensec_security *gensec_security)
399 struct spnego_state *spnego_state = gensec_security->private_data;
401 if (spnego_state->sub_sec_security) {
402 gensec_end(&spnego_state->sub_sec_security);
405 talloc_destroy(spnego_state->mem_ctx);
407 gensec_security->private_data = NULL;
410 static const struct gensec_security_ops gensec_spnego_security_ops = {
412 .sasl_name = "GSS-SPNEGO",
413 .auth_type = DCERPC_AUTH_TYPE_SPNEGO,
415 .client_start = gensec_spnego_client_start,
416 .update = gensec_spnego_update,
417 .seal_packet = gensec_spnego_seal_packet,
418 .sign_packet = gensec_spnego_sign_packet,
419 .check_packet = gensec_spnego_check_packet,
420 .unseal_packet = gensec_spnego_unseal_packet,
421 .session_key = gensec_spnego_session_key,
422 .end = gensec_spnego_end
425 NTSTATUS gensec_spengo_init(void)
428 ret = register_backend("gensec", &gensec_spnego_security_ops);
429 if (!NT_STATUS_IS_OK(ret)) {
430 DEBUG(0,("Failed to register '%s' gensec backend!\n",
431 gensec_spnego_security_ops.name));