3 * DCERPC Server functions
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 "../lib/tsocket/tsocket.h"
23 #include "dcesrv_ntlmssp.h"
24 #include "dcesrv_gssapi.h"
25 #include "dcesrv_spnego.h"
27 static NTSTATUS
spnego_init_server(TALLOC_CTX
*mem_ctx
,
28 bool do_sign
, bool do_seal
,
30 const struct tsocket_address
*remote_address
,
31 struct spnego_context
**spnego_ctx
)
33 struct spnego_context
*sp_ctx
= NULL
;
35 sp_ctx
= talloc_zero(mem_ctx
, struct spnego_context
);
37 return NT_STATUS_NO_MEMORY
;
40 sp_ctx
->remote_address
= tsocket_address_copy(remote_address
, sp_ctx
);
41 if (sp_ctx
->remote_address
== NULL
) {
42 return NT_STATUS_NO_MEMORY
;
45 sp_ctx
->do_sign
= do_sign
;
46 sp_ctx
->do_seal
= do_seal
;
47 sp_ctx
->is_dcerpc
= is_dcerpc
;
53 static NTSTATUS
spnego_server_mech_init(struct spnego_context
*sp_ctx
,
57 struct gensec_security
*gensec_security
;
58 struct gse_context
*gse_ctx
;
61 switch (sp_ctx
->mech
) {
63 status
= gssapi_server_auth_start(sp_ctx
,
70 if (!NT_STATUS_IS_OK(status
)) {
71 DEBUG(0, ("Failed to init gssapi server "
72 "(%s)\n", nt_errstr(status
)));
76 sp_ctx
->mech_ctx
.gssapi_state
= gse_ctx
;
80 status
= ntlmssp_server_auth_start(sp_ctx
,
86 sp_ctx
->remote_address
,
88 if (!NT_STATUS_IS_OK(status
)) {
89 DEBUG(0, ("Failed to init ntlmssp server "
90 "(%s)\n", nt_errstr(status
)));
94 sp_ctx
->mech_ctx
.gensec_security
= gensec_security
;
98 DEBUG(3, ("No known mechanisms available\n"));
99 return NT_STATUS_INVALID_PARAMETER
;
105 NTSTATUS
spnego_server_step(struct spnego_context
*sp_ctx
,
107 DATA_BLOB
*spnego_in
,
108 DATA_BLOB
*spnego_out
)
110 DATA_BLOB token_in
= data_blob_null
;
111 DATA_BLOB token_out
= data_blob_null
;
112 DATA_BLOB signature
= data_blob_null
;
113 DATA_BLOB MICblob
= data_blob_null
;
114 struct spnego_data sp_in
;
119 len_in
= spnego_read_data(mem_ctx
, *spnego_in
, &sp_in
);
121 DEBUG(1, (__location__
": invalid SPNEGO blob.\n"));
122 dump_data(10, spnego_in
->data
, spnego_in
->length
);
123 status
= NT_STATUS_INVALID_PARAMETER
;
124 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
127 if (sp_in
.type
!= SPNEGO_NEG_TOKEN_TARG
) {
128 status
= NT_STATUS_INVALID_PARAMETER
;
131 token_in
= sp_in
.negTokenTarg
.responseToken
;
132 signature
= sp_in
.negTokenTarg
.mechListMIC
;
134 switch (sp_ctx
->state
) {
135 case SPNEGO_CONV_NEGO
:
136 /* still to initialize */
137 status
= spnego_server_mech_init(sp_ctx
,
140 if (!NT_STATUS_IS_OK(status
)) {
143 /* server always need at least one reply from client */
144 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
145 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
148 case SPNEGO_CONV_AUTH_MORE
:
150 switch(sp_ctx
->mech
) {
152 status
= gssapi_server_step(
153 sp_ctx
->mech_ctx
.gssapi_state
,
154 mem_ctx
, &token_in
, &token_out
);
157 status
= ntlmssp_server_step(
158 sp_ctx
->mech_ctx
.gensec_security
,
159 mem_ctx
, &token_in
, &token_out
);
162 status
= NT_STATUS_INVALID_PARAMETER
;
168 case SPNEGO_CONV_AUTH_DONE
:
169 /* we are already done, can't step further */
170 /* fall thorugh and return error */
173 return NT_STATUS_INVALID_PARAMETER
;
176 if (NT_STATUS_IS_OK(status
) && signature
.length
!= 0) {
177 /* last packet and requires signature check */
178 ret
= spnego_mech_list_blob(talloc_tos(),
179 sp_ctx
->oid_list
, &MICblob
);
181 status
= spnego_sigcheck(talloc_tos(), sp_ctx
,
185 status
= NT_STATUS_UNSUCCESSFUL
;
188 if (NT_STATUS_IS_OK(status
) && signature
.length
!= 0) {
189 /* if signature was good, sign our own packet too */
190 status
= spnego_sign(talloc_tos(), sp_ctx
,
191 &MICblob
, &MICblob
, &signature
);
195 *spnego_out
= spnego_gen_auth_response_and_mic(mem_ctx
, status
,
199 if (!spnego_out
->data
) {
200 DEBUG(1, ("SPNEGO wrapping failed!\n"));
201 status
= NT_STATUS_UNSUCCESSFUL
;
204 if (NT_STATUS_IS_OK(status
) ||
205 !NT_STATUS_EQUAL(status
,
206 NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
207 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
210 data_blob_free(&token_in
);
211 data_blob_free(&token_out
);
215 NTSTATUS
spnego_server_auth_start(TALLOC_CTX
*mem_ctx
,
219 DATA_BLOB
*spnego_in
,
220 DATA_BLOB
*spnego_out
,
221 const struct tsocket_address
*remote_address
,
222 struct spnego_context
**spnego_ctx
)
224 struct spnego_context
*sp_ctx
;
225 DATA_BLOB token_in
= data_blob_null
;
226 DATA_BLOB token_out
= data_blob_null
;
231 if (!spnego_in
->length
) {
232 return NT_STATUS_INVALID_PARAMETER
;
235 status
= spnego_init_server(mem_ctx
,
241 if (!NT_STATUS_IS_OK(status
)) {
245 ret
= spnego_parse_negTokenInit(sp_ctx
, *spnego_in
,
246 sp_ctx
->oid_list
, NULL
, &token_in
);
247 if (!ret
|| sp_ctx
->oid_list
[0] == NULL
) {
248 DEBUG(3, ("Invalid SPNEGO message\n"));
249 status
= NT_STATUS_INVALID_PARAMETER
;
253 /* try for krb auth first */
254 for (i
= 0; sp_ctx
->oid_list
[i
] && sp_ctx
->mech
== SPNEGO_NONE
; i
++) {
255 if (strcmp(OID_KERBEROS5
, sp_ctx
->oid_list
[i
]) == 0 ||
256 strcmp(OID_KERBEROS5_OLD
, sp_ctx
->oid_list
[i
]) == 0) {
258 if (lp_security() == SEC_ADS
|| USE_KERBEROS_KEYTAB
) {
259 sp_ctx
->mech
= SPNEGO_KRB5
;
260 sp_ctx
->mech_oid
= sp_ctx
->oid_list
[i
];
265 /* if auth type still undetermined, try for NTLMSSP */
266 for (i
= 0; sp_ctx
->oid_list
[i
] && sp_ctx
->mech
== SPNEGO_NONE
; i
++) {
267 if (strcmp(OID_NTLMSSP
, sp_ctx
->oid_list
[i
]) == 0) {
268 sp_ctx
->mech
= SPNEGO_NTLMSSP
;
269 sp_ctx
->mech_oid
= sp_ctx
->oid_list
[i
];
273 if (!sp_ctx
->mech_oid
) {
274 DEBUG(3, ("No known mechanisms available\n"));
275 status
= NT_STATUS_INVALID_PARAMETER
;
279 if (DEBUGLEVEL
>= 10) {
280 DEBUG(10, ("Client Provided OIDs:\n"));
281 for (i
= 0; sp_ctx
->oid_list
[i
]; i
++) {
282 DEBUG(10, (" %d: %s\n", i
, sp_ctx
->oid_list
[i
]));
284 DEBUG(10, ("Chosen OID: %s\n", sp_ctx
->mech_oid
));
287 /* If it is not the first OID, then token_in is not valid for the
289 if (sp_ctx
->mech_oid
!= sp_ctx
->oid_list
[0]) {
290 /* request more and send back empty token */
291 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
292 sp_ctx
->state
= SPNEGO_CONV_NEGO
;
296 status
= spnego_server_mech_init(sp_ctx
, &token_in
, &token_out
);
297 if (!NT_STATUS_IS_OK(status
)) {
301 DEBUG(10, ("SPNEGO(%d) auth started\n", sp_ctx
->mech
));
303 /* server always need at least one reply from client */
304 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
305 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
308 *spnego_out
= spnego_gen_auth_response(mem_ctx
, &token_out
,
309 status
, sp_ctx
->mech_oid
);
310 if (!spnego_out
->data
) {
311 status
= NT_STATUS_INVALID_PARAMETER
;
314 status
= NT_STATUS_OK
;
315 *spnego_ctx
= sp_ctx
;
318 data_blob_free(&token_in
);
319 data_blob_free(&token_out
);