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/>.
21 #include "../libcli/auth/spnego.h"
22 #include "include/ntlmssp_wrap.h"
23 #include "librpc/gen_ndr/ntlmssp.h"
24 #include "librpc/crypto/gse.h"
25 #include "dcerpc_spnego.h"
27 struct spnego_context {
28 enum spnego_mech mech;
34 struct auth_ntlmssp_state *ntlmssp_state;
35 struct gse_context *gssapi_state;
40 SPNEGO_CONV_AUTH_MORE,
41 SPNEGO_CONV_AUTH_CONFIRM,
46 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
47 bool do_sign, bool do_seal,
48 struct spnego_context **spnego_ctx)
50 struct spnego_context *sp_ctx;
52 sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
54 return NT_STATUS_NO_MEMORY;
57 sp_ctx->do_sign = do_sign;
58 sp_ctx->do_seal = do_seal;
59 sp_ctx->state = SPNEGO_CONV_INIT;
65 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
66 bool do_sign, bool do_seal,
68 const char *ccache_name,
73 struct spnego_context **spnego_ctx)
75 struct spnego_context *sp_ctx = NULL;
76 uint32_t add_gss_c_flags = 0;
79 status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
80 if (!NT_STATUS_IS_OK(status)) {
83 sp_ctx->mech = SPNEGO_KRB5;
86 add_gss_c_flags = GSS_C_DCE_STYLE;
89 status = gse_init_client(sp_ctx,
91 ccache_name, server, service,
92 username, password, add_gss_c_flags,
93 &sp_ctx->mech_ctx.gssapi_state);
94 if (!NT_STATUS_IS_OK(status)) {
103 NTSTATUS spnego_ntlmssp_init_client(TALLOC_CTX *mem_ctx,
104 bool do_sign, bool do_seal,
107 const char *username,
108 const char *password,
109 struct spnego_context **spnego_ctx)
111 struct spnego_context *sp_ctx = NULL;
114 status = spnego_context_init(mem_ctx, do_sign, do_seal, &sp_ctx);
115 if (!NT_STATUS_IS_OK(status)) {
118 sp_ctx->mech = SPNEGO_NTLMSSP;
120 status = auth_ntlmssp_client_start(sp_ctx,
123 lp_client_ntlmv2_auth(),
124 &sp_ctx->mech_ctx.ntlmssp_state);
125 if (!NT_STATUS_IS_OK(status)) {
130 status = auth_ntlmssp_set_username(sp_ctx->mech_ctx.ntlmssp_state,
132 if (!NT_STATUS_IS_OK(status)) {
137 status = auth_ntlmssp_set_domain(sp_ctx->mech_ctx.ntlmssp_state,
139 if (!NT_STATUS_IS_OK(status)) {
144 status = auth_ntlmssp_set_password(sp_ctx->mech_ctx.ntlmssp_state,
146 if (!NT_STATUS_IS_OK(status)) {
152 * Turn off sign+seal to allow selected auth level to turn it back on.
154 auth_ntlmssp_and_flags(sp_ctx->mech_ctx.ntlmssp_state,
155 ~(NTLMSSP_NEGOTIATE_SIGN |
156 NTLMSSP_NEGOTIATE_SEAL));
159 auth_ntlmssp_or_flags(sp_ctx->mech_ctx.ntlmssp_state,
160 NTLMSSP_NEGOTIATE_SIGN);
161 } else if (do_seal) {
162 auth_ntlmssp_or_flags(sp_ctx->mech_ctx.ntlmssp_state,
163 NTLMSSP_NEGOTIATE_SEAL |
164 NTLMSSP_NEGOTIATE_SIGN);
167 *spnego_ctx = sp_ctx;
171 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
172 struct spnego_context *sp_ctx,
173 DATA_BLOB *spnego_in,
174 DATA_BLOB *spnego_out)
176 struct gse_context *gse_ctx;
177 struct auth_ntlmssp_state *ntlmssp_ctx;
178 struct spnego_data sp_in, sp_out;
179 DATA_BLOB token_in = data_blob_null;
180 DATA_BLOB token_out = data_blob_null;
181 const char *mech_oids[2] = { NULL, NULL };
182 char *principal = NULL;
185 bool mech_wants_more = false;
188 if (!spnego_in->length) {
189 /* server didn't send anything, is init ? */
190 if (sp_ctx->state != SPNEGO_CONV_INIT) {
191 return NT_STATUS_INVALID_PARAMETER;
194 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
196 status = NT_STATUS_INVALID_PARAMETER;
199 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
200 status = NT_STATUS_INVALID_PARAMETER;
203 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
204 status = NT_STATUS_ACCESS_DENIED;
207 token_in = sp_in.negTokenTarg.responseToken;
210 if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
211 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
212 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
213 *spnego_out = data_blob_null;
214 status = NT_STATUS_OK;
216 status = NT_STATUS_ACCESS_DENIED;
221 switch (sp_ctx->mech) {
224 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
225 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
226 &token_in, &token_out);
227 if (!NT_STATUS_IS_OK(status)) {
231 mech_oids[0] = OID_KERBEROS5;
232 mech_wants_more = gse_require_more_processing(gse_ctx);
238 ntlmssp_ctx = sp_ctx->mech_ctx.ntlmssp_state;
239 status = auth_ntlmssp_update(ntlmssp_ctx,
240 token_in, &token_out);
241 if (NT_STATUS_EQUAL(status,
242 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
243 mech_wants_more = true;
244 } else if (!NT_STATUS_IS_OK(status)) {
248 mech_oids[0] = OID_NTLMSSP;
253 status = NT_STATUS_INTERNAL_ERROR;
257 switch (sp_ctx->state) {
258 case SPNEGO_CONV_INIT:
259 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
260 &token_out, principal);
261 if (!spnego_out->data) {
262 status = NT_STATUS_INTERNAL_ERROR;
265 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
268 case SPNEGO_CONV_AUTH_MORE:
269 /* server says it's done and we do not seem to agree */
270 if (sp_in.negTokenTarg.negResult ==
271 SPNEGO_ACCEPT_COMPLETED) {
272 status = NT_STATUS_INVALID_PARAMETER;
276 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
277 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
278 sp_out.negTokenTarg.supportedMech = NULL;
279 sp_out.negTokenTarg.responseToken = token_out;
280 sp_out.negTokenTarg.mechListMIC = data_blob_null;
282 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
284 status = NT_STATUS_INTERNAL_ERROR;
288 if (!mech_wants_more) {
289 /* we still need to get an ack from the server */
290 sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
296 status = NT_STATUS_INTERNAL_ERROR;
300 status = NT_STATUS_OK;
304 spnego_free_data(&sp_in);
306 data_blob_free(&token_out);
310 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
312 struct gse_context *gse_ctx;
314 /* see if spnego processing itself requires more */
315 if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
316 sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
320 /* otherwise see if underlying mechnism does */
321 switch (sp_ctx->mech) {
323 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
324 return gse_require_more_processing(gse_ctx);
328 DEBUG(0, ("Unsupported type in request!\n"));
333 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
334 enum spnego_mech *type,
337 switch (sp_ctx->mech) {
339 *auth_context = sp_ctx->mech_ctx.gssapi_state;
342 *auth_context = sp_ctx->mech_ctx.ntlmssp_state;
345 return NT_STATUS_INTERNAL_ERROR;
348 *type = sp_ctx->mech;
352 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
353 struct spnego_context *sp_ctx)
357 switch (sp_ctx->mech) {
359 return gse_get_session_key(mem_ctx,
360 sp_ctx->mech_ctx.gssapi_state);
362 sk = auth_ntlmssp_get_session_key(
363 sp_ctx->mech_ctx.ntlmssp_state);
364 return data_blob_dup_talloc(mem_ctx, &sk);
366 DEBUG(0, ("Unsupported type in request!\n"));
367 return data_blob_null;