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_auth_generic.h"
24 #include "dcesrv_spnego.h"
25 #include "auth/gensec/gensec.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
;
61 switch (sp_ctx
->mech
) {
63 oid
= GENSEC_OID_KERBEROS5
;
66 oid
= GENSEC_OID_NTLMSSP
;
69 DEBUG(3, ("No known mechanisms available\n"));
70 return NT_STATUS_INVALID_PARAMETER
;
73 status
= auth_generic_server_start(sp_ctx
,
80 sp_ctx
->remote_address
,
82 if (!NT_STATUS_IS_OK(status
)) {
83 DEBUG(0, ("Failed to init ntlmssp server "
84 "(%s)\n", nt_errstr(status
)));
88 sp_ctx
->gensec_security
= gensec_security
;
93 NTSTATUS
spnego_server_step(struct spnego_context
*sp_ctx
,
96 DATA_BLOB
*spnego_out
)
98 DATA_BLOB token_in
= data_blob_null
;
99 DATA_BLOB token_out
= data_blob_null
;
100 DATA_BLOB signature
= data_blob_null
;
101 DATA_BLOB MICblob
= data_blob_null
;
102 struct spnego_data sp_in
;
107 len_in
= spnego_read_data(mem_ctx
, *spnego_in
, &sp_in
);
109 DEBUG(1, (__location__
": invalid SPNEGO blob.\n"));
110 dump_data(10, spnego_in
->data
, spnego_in
->length
);
111 status
= NT_STATUS_INVALID_PARAMETER
;
112 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
115 if (sp_in
.type
!= SPNEGO_NEG_TOKEN_TARG
) {
116 status
= NT_STATUS_INVALID_PARAMETER
;
119 token_in
= sp_in
.negTokenTarg
.responseToken
;
120 signature
= sp_in
.negTokenTarg
.mechListMIC
;
122 switch (sp_ctx
->state
) {
123 case SPNEGO_CONV_NEGO
:
124 /* still to initialize */
125 status
= spnego_server_mech_init(sp_ctx
,
128 if (!NT_STATUS_IS_OK(status
)) {
131 /* server always need at least one reply from client */
132 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
133 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
136 case SPNEGO_CONV_AUTH_MORE
:
138 status
= auth_generic_server_step(
139 sp_ctx
->gensec_security
,
140 mem_ctx
, &token_in
, &token_out
);
143 case SPNEGO_CONV_AUTH_DONE
:
144 /* we are already done, can't step further */
145 /* fall thorugh and return error */
148 return NT_STATUS_INVALID_PARAMETER
;
151 if (NT_STATUS_IS_OK(status
) && signature
.length
!= 0) {
152 /* last packet and requires signature check */
153 ret
= spnego_mech_list_blob(talloc_tos(),
154 sp_ctx
->oid_list
, &MICblob
);
156 status
= spnego_sigcheck(talloc_tos(), sp_ctx
,
160 status
= NT_STATUS_UNSUCCESSFUL
;
163 if (NT_STATUS_IS_OK(status
) && signature
.length
!= 0) {
164 /* if signature was good, sign our own packet too */
165 status
= spnego_sign(talloc_tos(), sp_ctx
,
166 &MICblob
, &MICblob
, &signature
);
170 *spnego_out
= spnego_gen_auth_response_and_mic(mem_ctx
, status
,
174 if (!spnego_out
->data
) {
175 DEBUG(1, ("SPNEGO wrapping failed!\n"));
176 status
= NT_STATUS_UNSUCCESSFUL
;
179 if (NT_STATUS_IS_OK(status
) ||
180 !NT_STATUS_EQUAL(status
,
181 NT_STATUS_MORE_PROCESSING_REQUIRED
)) {
182 sp_ctx
->state
= SPNEGO_CONV_AUTH_DONE
;
185 data_blob_free(&token_in
);
186 data_blob_free(&token_out
);
190 NTSTATUS
spnego_server_auth_start(TALLOC_CTX
*mem_ctx
,
194 DATA_BLOB
*spnego_in
,
195 DATA_BLOB
*spnego_out
,
196 const struct tsocket_address
*remote_address
,
197 struct spnego_context
**spnego_ctx
)
199 struct spnego_context
*sp_ctx
;
200 DATA_BLOB token_in
= data_blob_null
;
201 DATA_BLOB token_out
= data_blob_null
;
206 if (!spnego_in
->length
) {
207 return NT_STATUS_INVALID_PARAMETER
;
210 status
= spnego_init_server(mem_ctx
,
216 if (!NT_STATUS_IS_OK(status
)) {
220 ret
= spnego_parse_negTokenInit(sp_ctx
, *spnego_in
,
221 sp_ctx
->oid_list
, NULL
, &token_in
);
222 if (!ret
|| sp_ctx
->oid_list
[0] == NULL
) {
223 DEBUG(3, ("Invalid SPNEGO message\n"));
224 status
= NT_STATUS_INVALID_PARAMETER
;
228 /* try for krb auth first */
229 for (i
= 0; sp_ctx
->oid_list
[i
] && sp_ctx
->mech
== SPNEGO_NONE
; i
++) {
230 if (strcmp(OID_KERBEROS5
, sp_ctx
->oid_list
[i
]) == 0 ||
231 strcmp(OID_KERBEROS5_OLD
, sp_ctx
->oid_list
[i
]) == 0) {
233 if (lp_security() == SEC_ADS
|| USE_KERBEROS_KEYTAB
) {
234 sp_ctx
->mech
= SPNEGO_KRB5
;
235 sp_ctx
->mech_oid
= sp_ctx
->oid_list
[i
];
240 /* if auth type still undetermined, try for NTLMSSP */
241 for (i
= 0; sp_ctx
->oid_list
[i
] && sp_ctx
->mech
== SPNEGO_NONE
; i
++) {
242 if (strcmp(OID_NTLMSSP
, sp_ctx
->oid_list
[i
]) == 0) {
243 sp_ctx
->mech
= SPNEGO_NTLMSSP
;
244 sp_ctx
->mech_oid
= sp_ctx
->oid_list
[i
];
248 if (!sp_ctx
->mech_oid
) {
249 DEBUG(3, ("No known mechanisms available\n"));
250 status
= NT_STATUS_INVALID_PARAMETER
;
254 if (DEBUGLEVEL
>= 10) {
255 DEBUG(10, ("Client Provided OIDs:\n"));
256 for (i
= 0; sp_ctx
->oid_list
[i
]; i
++) {
257 DEBUG(10, (" %d: %s\n", i
, sp_ctx
->oid_list
[i
]));
259 DEBUG(10, ("Chosen OID: %s\n", sp_ctx
->mech_oid
));
262 /* If it is not the first OID, then token_in is not valid for the
264 if (sp_ctx
->mech_oid
!= sp_ctx
->oid_list
[0]) {
265 /* request more and send back empty token */
266 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
267 sp_ctx
->state
= SPNEGO_CONV_NEGO
;
271 status
= spnego_server_mech_init(sp_ctx
, &token_in
, &token_out
);
272 if (!NT_STATUS_IS_OK(status
)) {
276 DEBUG(10, ("SPNEGO(%d) auth started\n", sp_ctx
->mech
));
278 /* server always need at least one reply from client */
279 status
= NT_STATUS_MORE_PROCESSING_REQUIRED
;
280 sp_ctx
->state
= SPNEGO_CONV_AUTH_MORE
;
283 *spnego_out
= spnego_gen_auth_response(mem_ctx
, &token_out
,
284 status
, sp_ctx
->mech_oid
);
285 if (!spnego_out
->data
) {
286 status
= NT_STATUS_INVALID_PARAMETER
;
289 status
= NT_STATUS_OK
;
290 *spnego_ctx
= sp_ctx
;
293 data_blob_free(&token_in
);
294 data_blob_free(&token_out
);