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 "dcerpc_spnego.h"
25 #include "librpc/crypto/gse.h"
27 struct spnego_context {
28 enum dcerpc_AuthType auth_type;
31 struct auth_ntlmssp_state *ntlmssp_state;
32 struct gse_context *gssapi_state;
37 SPNEGO_CONV_AUTH_MORE,
38 SPNEGO_CONV_AUTH_CONFIRM,
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);
51 return NT_STATUS_NO_MEMORY;
54 sp_ctx->auth_type = auth_type;
55 sp_ctx->state = SPNEGO_CONV_INIT;
61 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
62 enum dcerpc_AuthLevel auth_level,
63 const char *ccache_name,
68 uint32_t add_gss_c_flags,
69 struct spnego_context **spnego_ctx)
71 struct spnego_context *sp_ctx = NULL;
74 status = spnego_context_init(mem_ctx,
75 DCERPC_AUTH_TYPE_KRB5, &sp_ctx);
76 if (!NT_STATUS_IS_OK(status)) {
80 status = gse_init_client(sp_ctx,
81 (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY),
82 (auth_level == DCERPC_AUTH_LEVEL_PRIVACY),
83 ccache_name, server, service,
84 username, password, add_gss_c_flags,
85 &sp_ctx->mech_ctx.gssapi_state);
86 if (!NT_STATUS_IS_OK(status)) {
95 NTSTATUS spnego_ntlmssp_init_client(TALLOC_CTX *mem_ctx,
96 enum dcerpc_AuthLevel auth_level,
100 struct spnego_context **spnego_ctx)
102 struct spnego_context *sp_ctx = NULL;
105 status = spnego_context_init(mem_ctx,
106 DCERPC_AUTH_TYPE_NTLMSSP, &sp_ctx);
107 if (!NT_STATUS_IS_OK(status)) {
111 status = auth_ntlmssp_client_start(sp_ctx,
114 lp_client_ntlmv2_auth(),
115 &sp_ctx->mech_ctx.ntlmssp_state);
116 if (!NT_STATUS_IS_OK(status)) {
121 status = auth_ntlmssp_set_username(sp_ctx->mech_ctx.ntlmssp_state,
123 if (!NT_STATUS_IS_OK(status)) {
128 status = auth_ntlmssp_set_domain(sp_ctx->mech_ctx.ntlmssp_state,
130 if (!NT_STATUS_IS_OK(status)) {
135 status = auth_ntlmssp_set_password(sp_ctx->mech_ctx.ntlmssp_state,
137 if (!NT_STATUS_IS_OK(status)) {
143 * Turn off sign+seal to allow selected auth level to turn it back on.
145 auth_ntlmssp_and_flags(sp_ctx->mech_ctx.ntlmssp_state,
146 ~(NTLMSSP_NEGOTIATE_SIGN |
147 NTLMSSP_NEGOTIATE_SEAL));
149 if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
150 auth_ntlmssp_or_flags(sp_ctx->mech_ctx.ntlmssp_state,
151 NTLMSSP_NEGOTIATE_SIGN);
152 } else if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
153 auth_ntlmssp_or_flags(sp_ctx->mech_ctx.ntlmssp_state,
154 NTLMSSP_NEGOTIATE_SEAL |
155 NTLMSSP_NEGOTIATE_SIGN);
158 *spnego_ctx = sp_ctx;
162 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
163 struct spnego_context *sp_ctx,
164 DATA_BLOB *spnego_in,
165 DATA_BLOB *spnego_out)
167 struct gse_context *gse_ctx;
168 struct auth_ntlmssp_state *ntlmssp_ctx;
169 struct spnego_data sp_in, sp_out;
170 DATA_BLOB token_in = data_blob_null;
171 DATA_BLOB token_out = data_blob_null;
172 const char *mech_oids[2] = { NULL, NULL };
173 char *principal = NULL;
176 bool mech_wants_more = false;
179 if (!spnego_in->length) {
180 /* server didn't send anything, is init ? */
181 if (sp_ctx->state != SPNEGO_CONV_INIT) {
182 return NT_STATUS_INVALID_PARAMETER;
185 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
187 status = NT_STATUS_INVALID_PARAMETER;
190 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
191 status = NT_STATUS_INVALID_PARAMETER;
194 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
195 status = NT_STATUS_ACCESS_DENIED;
198 token_in = sp_in.negTokenTarg.responseToken;
201 if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
202 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
203 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
204 *spnego_out = data_blob_null;
205 status = NT_STATUS_OK;
207 status = NT_STATUS_ACCESS_DENIED;
212 switch (sp_ctx->auth_type) {
213 case DCERPC_AUTH_TYPE_KRB5:
215 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
216 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
217 &token_in, &token_out);
218 if (!NT_STATUS_IS_OK(status)) {
222 mech_oids[0] = OID_KERBEROS5;
223 mech_wants_more = gse_require_more_processing(gse_ctx);
227 case DCERPC_AUTH_TYPE_NTLMSSP:
229 ntlmssp_ctx = sp_ctx->mech_ctx.ntlmssp_state;
230 status = auth_ntlmssp_update(ntlmssp_ctx,
231 token_in, &token_out);
232 if (NT_STATUS_EQUAL(status,
233 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
234 mech_wants_more = true;
235 } else if (!NT_STATUS_IS_OK(status)) {
239 mech_oids[0] = OID_NTLMSSP;
244 status = NT_STATUS_INTERNAL_ERROR;
248 switch (sp_ctx->state) {
249 case SPNEGO_CONV_INIT:
250 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
251 &token_out, principal);
252 if (!spnego_out->data) {
253 status = NT_STATUS_INTERNAL_ERROR;
256 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
259 case SPNEGO_CONV_AUTH_MORE:
260 /* server says it's done and we do not seem to agree */
261 if (sp_in.negTokenTarg.negResult ==
262 SPNEGO_ACCEPT_COMPLETED) {
263 status = NT_STATUS_INVALID_PARAMETER;
267 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
268 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
269 sp_out.negTokenTarg.supportedMech = NULL;
270 sp_out.negTokenTarg.responseToken = token_out;
271 sp_out.negTokenTarg.mechListMIC = data_blob_null;
273 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
275 status = NT_STATUS_INTERNAL_ERROR;
279 if (!mech_wants_more) {
280 /* we still need to get an ack from the server */
281 sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
287 status = NT_STATUS_INTERNAL_ERROR;
291 status = NT_STATUS_OK;
295 spnego_free_data(&sp_in);
297 data_blob_free(&token_out);
301 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
303 struct gse_context *gse_ctx;
305 /* see if spnego processing itself requires more */
306 if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
307 sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
311 /* otherwise see if underlying mechnism does */
312 switch (sp_ctx->auth_type) {
313 case DCERPC_AUTH_TYPE_KRB5:
314 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
315 return gse_require_more_processing(gse_ctx);
316 case DCERPC_AUTH_TYPE_NTLMSSP:
319 DEBUG(0, ("Unsupported type in request!\n"));
324 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
325 enum dcerpc_AuthType *auth_type,
328 switch (sp_ctx->auth_type) {
329 case DCERPC_AUTH_TYPE_KRB5:
330 *auth_context = sp_ctx->mech_ctx.gssapi_state;
332 case DCERPC_AUTH_TYPE_NTLMSSP:
333 *auth_context = sp_ctx->mech_ctx.ntlmssp_state;
336 return NT_STATUS_INTERNAL_ERROR;
339 *auth_type = sp_ctx->auth_type;
343 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
344 struct spnego_context *sp_ctx)
348 switch (sp_ctx->auth_type) {
349 case DCERPC_AUTH_TYPE_KRB5:
350 return gse_get_session_key(mem_ctx,
351 sp_ctx->mech_ctx.gssapi_state);
352 case DCERPC_AUTH_TYPE_NTLMSSP:
353 sk = auth_ntlmssp_get_session_key(
354 sp_ctx->mech_ctx.ntlmssp_state);
355 return data_blob_dup_talloc(mem_ctx, &sk);
357 DEBUG(0, ("Unsupported type in request!\n"));
358 return data_blob_null;