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 "dcesrv_ntlmssp.h"
23 #include "dcesrv_gssapi.h"
24 #include "dcesrv_spnego.h"
26 static NTSTATUS
spnego_init_server(TALLOC_CTX
*mem_ctx
,
27 bool do_sign
, bool do_seal
,
29 struct spnego_context
**spnego_ctx
)
31 struct spnego_context
*sp_ctx
= NULL
;
33 sp_ctx
= talloc_zero(mem_ctx
, struct spnego_context
);
35 return NT_STATUS_NO_MEMORY
;
38 sp_ctx
->do_sign
= do_sign
;
39 sp_ctx
->do_seal
= do_seal
;
40 sp_ctx
->is_dcerpc
= is_dcerpc
;
46 static NTSTATUS
spnego_server_mech_init(struct spnego_context
*sp_ctx
,
50 struct auth_ntlmssp_state
*ntlmssp_ctx
;
51 struct gse_context
*gse_ctx
;
54 switch (sp_ctx
->mech
) {
56 status
= gssapi_server_auth_start(sp_ctx
,
63 if (!NT_STATUS_IS_OK(status
)) {
64 DEBUG(0, ("Failed to init gssapi server "
65 "(%s)\n", nt_errstr(status
)));
69 sp_ctx
->mech_ctx
.gssapi_state
= gse_ctx
;
73 status
= ntlmssp_server_auth_start(sp_ctx
,
80 if (!NT_STATUS_IS_OK(status
)) {
81 DEBUG(0, ("Failed to init ntlmssp server "
82 "(%s)\n", nt_errstr(status
)));
86 sp_ctx
->mech_ctx
.ntlmssp_state
= ntlmssp_ctx
;
90 DEBUG(3, ("No known mechanisms available\n"));
91 return NT_STATUS_INVALID_PARAMETER
;
97 NTSTATUS
spnego_server_step(struct spnego_context
*sp_ctx
,
100 DATA_BLOB
*spnego_out
)
102 DATA_BLOB token_in
= data_blob_null
;
103 DATA_BLOB token_out
= data_blob_null
;
104 DATA_BLOB signature
= data_blob_null
;
105 DATA_BLOB MICblob
= data_blob_null
;
106 struct spnego_data sp_in
;
111 len_in
= spnego_read_data(mem_ctx
, *spnego_in
, &sp_in
);
113 DEBUG(1, (__location__
": invalid SPNEGO blob.\n"));
114 dump_data(10, spnego_in
->data
, spnego_in
->length
);
115 status
= NT_STATUS_INVALID_PARAMETER
;
116 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
119 if (sp_in
.type
!= SPNEGO_NEG_TOKEN_TARG
) {
120 status
= NT_STATUS_INVALID_PARAMETER
;
123 token_in
= sp_in
.negTokenTarg
.responseToken
;
124 signature
= sp_in
.negTokenTarg
.mechListMIC
;
126 switch (sp_ctx
->state
) {
127 case SPNEGO_CONV_NEGO
:
128 /* still to initialize */
129 status
= spnego_server_mech_init(sp_ctx
,
132 if (!NT_STATUS_IS_OK(status
)) {
135 /* server always need at least one reply from client */
136 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
137 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
140 case SPNEGO_CONV_AUTH_MORE
:
142 switch(sp_ctx
->mech
) {
144 status
= gssapi_server_step(
145 sp_ctx
->mech_ctx
.gssapi_state
,
146 mem_ctx
, &token_in
, &token_out
);
149 status
= ntlmssp_server_step(
150 sp_ctx
->mech_ctx
.ntlmssp_state
,
151 mem_ctx
, &token_in
, &token_out
);
154 status
= NT_STATUS_INVALID_PARAMETER
;
160 case SPNEGO_CONV_AUTH_DONE
:
161 /* we are already done, can't step further */
162 /* fall thorugh and return error */
165 return NT_STATUS_INVALID_PARAMETER
;
168 if (NT_STATUS_IS_OK(status
) && signature
.length
!= 0) {
169 /* last packet and requires signature check */
170 ret
= spnego_mech_list_blob(talloc_tos(),
171 sp_ctx
->oid_list
, &MICblob
);
173 status
= spnego_sigcheck(talloc_tos(), sp_ctx
,
177 status
= NT_STATUS_UNSUCCESSFUL
;
180 if (NT_STATUS_IS_OK(status
) && signature
.length
!= 0) {
181 /* if signature was good, sign our own packet too */
182 status
= spnego_sign(talloc_tos(), sp_ctx
,
183 &MICblob
, &MICblob
, &signature
);
187 *spnego_out
= spnego_gen_auth_response_and_mic(mem_ctx
, status
,
191 if (!spnego_out
->data
) {
192 DEBUG(1, ("SPNEGO wrapping failed!\n"));
193 status
= NT_STATUS_UNSUCCESSFUL
;
196 if (NT_STATUS_IS_OK(status
) ||
197 !NT_STATUS_EQUAL(status
,
198 NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
199 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
202 data_blob_free(&token_in
);
203 data_blob_free(&token_out
);
207 NTSTATUS
spnego_server_auth_start(TALLOC_CTX
*mem_ctx
,
211 DATA_BLOB
*spnego_in
,
212 DATA_BLOB
*spnego_out
,
213 struct spnego_context
**spnego_ctx
)
215 struct spnego_context
*sp_ctx
;
216 DATA_BLOB token_in
= data_blob_null
;
217 DATA_BLOB token_out
= data_blob_null
;
222 if (!spnego_in
->length
) {
223 return NT_STATUS_INVALID_PARAMETER
;
226 status
= spnego_init_server(mem_ctx
, do_sign
, do_seal
, is_dcerpc
, &sp_ctx
);
227 if (!NT_STATUS_IS_OK(status
)) {
231 ret
= spnego_parse_negTokenInit(sp_ctx
, *spnego_in
,
232 sp_ctx
->oid_list
, NULL
, &token_in
);
233 if (!ret
|| sp_ctx
->oid_list
[0] == NULL
) {
234 DEBUG(3, ("Invalid SPNEGO message\n"));
235 status
= NT_STATUS_INVALID_PARAMETER
;
239 /* try for krb auth first */
240 for (i
= 0; sp_ctx
->oid_list
[i
] && sp_ctx
->mech
== SPNEGO_NONE
; i
++) {
241 if (strcmp(OID_KERBEROS5
, sp_ctx
->oid_list
[i
]) == 0 ||
242 strcmp(OID_KERBEROS5_OLD
, sp_ctx
->oid_list
[i
]) == 0) {
244 if (lp_security() == SEC_ADS
|| USE_KERBEROS_KEYTAB
) {
245 sp_ctx
->mech
= SPNEGO_KRB5
;
246 sp_ctx
->mech_oid
= sp_ctx
->oid_list
[i
];
251 /* if auth type still undetermined, try for NTLMSSP */
252 for (i
= 0; sp_ctx
->oid_list
[i
] && sp_ctx
->mech
== SPNEGO_NONE
; i
++) {
253 if (strcmp(OID_NTLMSSP
, sp_ctx
->oid_list
[i
]) == 0) {
254 sp_ctx
->mech
= SPNEGO_NTLMSSP
;
255 sp_ctx
->mech_oid
= sp_ctx
->oid_list
[i
];
259 if (!sp_ctx
->mech_oid
) {
260 DEBUG(3, ("No known mechanisms available\n"));
261 status
= NT_STATUS_INVALID_PARAMETER
;
265 if (DEBUGLEVEL
>= 10) {
266 DEBUG(10, ("Client Provided OIDs:\n"));
267 for (i
= 0; sp_ctx
->oid_list
[i
]; i
++) {
268 DEBUG(10, (" %d: %s\n", i
, sp_ctx
->oid_list
[i
]));
270 DEBUG(10, ("Chosen OID: %s\n", sp_ctx
->mech_oid
));
273 /* If it is not the first OID, then token_in is not valid for the
275 if (sp_ctx
->mech_oid
!= sp_ctx
->oid_list
[0]) {
276 /* request more and send back empty token */
277 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
278 sp_ctx
->state
= SPNEGO_CONV_NEGO
;
282 status
= spnego_server_mech_init(sp_ctx
, &token_in
, &token_out
);
283 if (!NT_STATUS_IS_OK(status
)) {
287 DEBUG(10, ("SPNEGO(%d) auth started\n", sp_ctx
->mech
));
289 /* server always need at least one reply from client */
290 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
291 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
294 *spnego_out
= spnego_gen_auth_response(mem_ctx
, &token_out
,
295 status
, sp_ctx
->mech_oid
);
296 if (!spnego_out
->data
) {
297 status
= NT_STATUS_INVALID_PARAMETER
;
300 status
= NT_STATUS_OK
;
301 *spnego_ctx
= sp_ctx
;
304 data_blob_free(&token_in
);
305 data_blob_free(&token_out
);