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
;