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 "dcerpc_gssapi.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
, DCERPC_AUTH_TYPE_KRB5
, auth_level
,
81 ccache_name
, server
, service
,
82 username
, password
, add_gss_c_flags
,
83 &sp_ctx
->mech_ctx
.gssapi_state
);
84 if (!NT_STATUS_IS_OK(status
)) {
93 NTSTATUS
spnego_ntlmssp_init_client(TALLOC_CTX
*mem_ctx
,
94 enum dcerpc_AuthLevel auth_level
,
98 struct spnego_context
**spnego_ctx
)
100 struct spnego_context
*sp_ctx
= NULL
;
103 status
= spnego_context_init(mem_ctx
,
104 DCERPC_AUTH_TYPE_NTLMSSP
, &sp_ctx
);
105 if (!NT_STATUS_IS_OK(status
)) {
109 status
= auth_ntlmssp_client_start(sp_ctx
,
112 lp_client_ntlmv2_auth(),
113 &sp_ctx
->mech_ctx
.ntlmssp_state
);
114 if (!NT_STATUS_IS_OK(status
)) {
119 status
= auth_ntlmssp_set_username(sp_ctx
->mech_ctx
.ntlmssp_state
,
121 if (!NT_STATUS_IS_OK(status
)) {
126 status
= auth_ntlmssp_set_domain(sp_ctx
->mech_ctx
.ntlmssp_state
,
128 if (!NT_STATUS_IS_OK(status
)) {
133 status
= auth_ntlmssp_set_password(sp_ctx
->mech_ctx
.ntlmssp_state
,
135 if (!NT_STATUS_IS_OK(status
)) {
141 * Turn off sign+seal to allow selected auth level to turn it back on.
143 auth_ntlmssp_and_flags(sp_ctx
->mech_ctx
.ntlmssp_state
,
144 ~(NTLMSSP_NEGOTIATE_SIGN
|
145 NTLMSSP_NEGOTIATE_SEAL
));
147 if (auth_level
== DCERPC_AUTH_LEVEL_INTEGRITY
) {
148 auth_ntlmssp_or_flags(sp_ctx
->mech_ctx
.ntlmssp_state
,
149 NTLMSSP_NEGOTIATE_SIGN
);
150 } else if (auth_level
== DCERPC_AUTH_LEVEL_PRIVACY
) {
151 auth_ntlmssp_or_flags(sp_ctx
->mech_ctx
.ntlmssp_state
,
152 NTLMSSP_NEGOTIATE_SEAL
|
153 NTLMSSP_NEGOTIATE_SIGN
);
156 *spnego_ctx
= sp_ctx
;
160 NTSTATUS
spnego_get_client_auth_token(TALLOC_CTX
*mem_ctx
,
161 struct spnego_context
*sp_ctx
,
162 DATA_BLOB
*spnego_in
,
163 DATA_BLOB
*spnego_out
)
165 struct gse_context
*gse_ctx
;
166 struct auth_ntlmssp_state
*ntlmssp_ctx
;
167 struct spnego_data sp_in
, sp_out
;
168 DATA_BLOB token_in
= data_blob_null
;
169 DATA_BLOB token_out
= data_blob_null
;
170 const char *mech_oids
[2] = { NULL
, NULL
};
171 char *principal
= NULL
;
174 bool mech_wants_more
= false;
177 if (!spnego_in
->length
) {
178 /* server didn't send anything, is init ? */
179 if (sp_ctx
->state
!= SPNEGO_CONV_INIT
) {
180 return NT_STATUS_INVALID_PARAMETER
;
183 len_in
= spnego_read_data(mem_ctx
, *spnego_in
, &sp_in
);
185 status
= NT_STATUS_INVALID_PARAMETER
;
188 if (sp_in
.type
!= SPNEGO_NEG_TOKEN_TARG
) {
189 status
= NT_STATUS_INVALID_PARAMETER
;
192 if (sp_in
.negTokenTarg
.negResult
== SPNEGO_REJECT
) {
193 status
= NT_STATUS_ACCESS_DENIED
;
196 token_in
= sp_in
.negTokenTarg
.responseToken
;
199 if (sp_ctx
->state
== SPNEGO_CONV_AUTH_CONFIRM
) {
200 if (sp_in
.negTokenTarg
.negResult
== SPNEGO_ACCEPT_COMPLETED
) {
201 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
202 *spnego_out
= data_blob_null
;
203 status
= NT_STATUS_OK
;
205 status
= NT_STATUS_ACCESS_DENIED
;
210 switch (sp_ctx
->auth_type
) {
211 case DCERPC_AUTH_TYPE_KRB5
:
213 gse_ctx
= sp_ctx
->mech_ctx
.gssapi_state
;
214 status
= gse_get_client_auth_token(mem_ctx
, gse_ctx
,
215 &token_in
, &token_out
);
216 if (!NT_STATUS_IS_OK(status
)) {
220 mech_oids
[0] = OID_KERBEROS5
;
221 mech_wants_more
= gse_require_more_processing(gse_ctx
);
225 case DCERPC_AUTH_TYPE_NTLMSSP
:
227 ntlmssp_ctx
= sp_ctx
->mech_ctx
.ntlmssp_state
;
228 status
= auth_ntlmssp_update(ntlmssp_ctx
,
229 token_in
, &token_out
);
230 if (NT_STATUS_EQUAL(status
,
231 NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
232 mech_wants_more
= true;
233 } else if (!NT_STATUS_IS_OK(status
)) {
237 mech_oids
[0] = OID_NTLMSSP
;
242 status
= NT_STATUS_INTERNAL_ERROR
;
246 switch (sp_ctx
->state
) {
247 case SPNEGO_CONV_INIT
:
248 *spnego_out
= spnego_gen_negTokenInit(mem_ctx
, mech_oids
,
249 &token_out
, principal
);
250 if (!spnego_out
->data
) {
251 status
= NT_STATUS_INTERNAL_ERROR
;
254 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
257 case SPNEGO_CONV_AUTH_MORE
:
258 /* server says it's done and we do not seem to agree */
259 if (sp_in
.negTokenTarg
.negResult
==
260 SPNEGO_ACCEPT_COMPLETED
) {
261 status
= NT_STATUS_INVALID_PARAMETER
;
265 sp_out
.type
= SPNEGO_NEG_TOKEN_TARG
;
266 sp_out
.negTokenTarg
.negResult
= SPNEGO_NONE_RESULT
;
267 sp_out
.negTokenTarg
.supportedMech
= NULL
;
268 sp_out
.negTokenTarg
.responseToken
= token_out
;
269 sp_out
.negTokenTarg
.mechListMIC
= data_blob_null
;
271 len_out
= spnego_write_data(mem_ctx
, spnego_out
, &sp_out
);
273 status
= NT_STATUS_INTERNAL_ERROR
;
277 if (!mech_wants_more
) {
278 /* we still need to get an ack from the server */
279 sp_ctx
->state
= SPNEGO_CONV_AUTH_CONFIRM
;
285 status
= NT_STATUS_INTERNAL_ERROR
;
289 status
= NT_STATUS_OK
;
293 spnego_free_data(&sp_in
);
295 data_blob_free(&token_out
);
299 bool spnego_require_more_processing(struct spnego_context
*sp_ctx
)
301 struct gse_context
*gse_ctx
;
303 /* see if spnego processing itself requires more */
304 if (sp_ctx
->state
== SPNEGO_CONV_AUTH_MORE
||
305 sp_ctx
->state
== SPNEGO_CONV_AUTH_CONFIRM
) {
309 /* otherwise see if underlying mechnism does */
310 switch (sp_ctx
->auth_type
) {
311 case DCERPC_AUTH_TYPE_KRB5
:
312 gse_ctx
= sp_ctx
->mech_ctx
.gssapi_state
;
313 return gse_require_more_processing(gse_ctx
);
314 case DCERPC_AUTH_TYPE_NTLMSSP
:
317 DEBUG(0, ("Unsupported type in request!\n"));
322 NTSTATUS
spnego_get_negotiated_mech(struct spnego_context
*sp_ctx
,
323 enum dcerpc_AuthType
*auth_type
,
326 switch (sp_ctx
->auth_type
) {
327 case DCERPC_AUTH_TYPE_KRB5
:
328 *auth_context
= sp_ctx
->mech_ctx
.gssapi_state
;
330 case DCERPC_AUTH_TYPE_NTLMSSP
:
331 *auth_context
= sp_ctx
->mech_ctx
.ntlmssp_state
;
334 return NT_STATUS_INTERNAL_ERROR
;
337 *auth_type
= sp_ctx
->auth_type
;
341 DATA_BLOB
spnego_get_session_key(TALLOC_CTX
*mem_ctx
,
342 struct spnego_context
*sp_ctx
)
346 switch (sp_ctx
->auth_type
) {
347 case DCERPC_AUTH_TYPE_KRB5
:
348 return gse_get_session_key(mem_ctx
,
349 sp_ctx
->mech_ctx
.gssapi_state
);
350 case DCERPC_AUTH_TYPE_NTLMSSP
:
351 sk
= auth_ntlmssp_get_session_key(
352 sp_ctx
->mech_ctx
.ntlmssp_state
);
353 return data_blob_dup_talloc(mem_ctx
, &sk
);
355 DEBUG(0, ("Unsupported type in request!\n"));
356 return data_blob_null
;