s3-utils: net - Fix one error/usage message
[Samba/gebeck_regimport.git] / source3 / rpc_server / dcesrv_spnego.c
blob88ffdf7b5ff3c7f79633a0da5422ed6d44c8157a
1 /*
2 * SPNEGO Encapsulation
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/>.
20 #include "includes.h"
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,
29 bool is_dcerpc,
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);
36 if (!sp_ctx) {
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;
49 *spnego_ctx = sp_ctx;
50 return NT_STATUS_OK;
53 static NTSTATUS spnego_server_mech_init(struct spnego_context *sp_ctx,
54 DATA_BLOB *token_in,
55 DATA_BLOB *token_out)
57 struct gensec_security *gensec_security;
58 struct gse_context *gse_ctx;
59 NTSTATUS status;
61 switch (sp_ctx->mech) {
62 case SPNEGO_KRB5:
63 status = gssapi_server_auth_start(sp_ctx,
64 sp_ctx->do_sign,
65 sp_ctx->do_seal,
66 sp_ctx->is_dcerpc,
67 token_in,
68 token_out,
69 &gse_ctx);
70 if (!NT_STATUS_IS_OK(status)) {
71 DEBUG(0, ("Failed to init gssapi server "
72 "(%s)\n", nt_errstr(status)));
73 return status;
76 sp_ctx->mech_ctx.gssapi_state = gse_ctx;
77 break;
79 case SPNEGO_NTLMSSP:
80 status = ntlmssp_server_auth_start(sp_ctx,
81 sp_ctx->do_sign,
82 sp_ctx->do_seal,
83 sp_ctx->is_dcerpc,
84 token_in,
85 token_out,
86 sp_ctx->remote_address,
87 &gensec_security);
88 if (!NT_STATUS_IS_OK(status)) {
89 DEBUG(0, ("Failed to init ntlmssp server "
90 "(%s)\n", nt_errstr(status)));
91 return status;
94 sp_ctx->mech_ctx.gensec_security = gensec_security;
95 break;
97 default:
98 DEBUG(3, ("No known mechanisms available\n"));
99 return NT_STATUS_INVALID_PARAMETER;
102 return NT_STATUS_OK;
105 NTSTATUS spnego_server_step(struct spnego_context *sp_ctx,
106 TALLOC_CTX *mem_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;
115 ssize_t len_in = 0;
116 NTSTATUS status;
117 bool ret;
119 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
120 if (len_in == -1) {
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;
125 goto done;
127 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
128 status = NT_STATUS_INVALID_PARAMETER;
129 goto done;
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,
138 &token_in,
139 &token_out);
140 if (!NT_STATUS_IS_OK(status)) {
141 goto done;
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;
146 break;
148 case SPNEGO_CONV_AUTH_MORE:
150 switch(sp_ctx->mech) {
151 case SPNEGO_KRB5:
152 status = gssapi_server_step(
153 sp_ctx->mech_ctx.gssapi_state,
154 mem_ctx, &token_in, &token_out);
155 break;
156 case SPNEGO_NTLMSSP:
157 status = ntlmssp_server_step(
158 sp_ctx->mech_ctx.gensec_security,
159 mem_ctx, &token_in, &token_out);
160 break;
161 default:
162 status = NT_STATUS_INVALID_PARAMETER;
163 goto done;
166 break;
168 case SPNEGO_CONV_AUTH_DONE:
169 /* we are already done, can't step further */
170 /* fall thorugh and return error */
171 default:
172 /* wrong case */
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);
180 if (ret) {
181 status = spnego_sigcheck(talloc_tos(), sp_ctx,
182 &MICblob, &MICblob,
183 &signature);
184 } else {
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);
194 done:
195 *spnego_out = spnego_gen_auth_response_and_mic(mem_ctx, status,
196 sp_ctx->mech_oid,
197 &token_out,
198 &signature);
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);
212 return status;
215 NTSTATUS spnego_server_auth_start(TALLOC_CTX *mem_ctx,
216 bool do_sign,
217 bool do_seal,
218 bool is_dcerpc,
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;
227 unsigned int i;
228 NTSTATUS status;
229 bool ret;
231 if (!spnego_in->length) {
232 return NT_STATUS_INVALID_PARAMETER;
235 status = spnego_init_server(mem_ctx,
236 do_sign,
237 do_seal,
238 is_dcerpc,
239 remote_address,
240 &sp_ctx);
241 if (!NT_STATUS_IS_OK(status)) {
242 return 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;
250 goto done;
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;
276 goto done;
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
288 * choosen mech */
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;
293 goto done;
296 status = spnego_server_mech_init(sp_ctx, &token_in, &token_out);
297 if (!NT_STATUS_IS_OK(status)) {
298 goto done;
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;
307 done:
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;
312 TALLOC_FREE(sp_ctx);
313 } else {
314 status = NT_STATUS_OK;
315 *spnego_ctx = sp_ctx;
318 data_blob_free(&token_in);
319 data_blob_free(&token_out);
321 return status;