s3-librpc: Simplify SPNEGO code now that all mechs use a struct gensec_security
[Samba/gebeck_regimport.git] / source3 / rpc_server / dcesrv_spnego.c
blob0a6b3b8512a88db5ca5e245398ab63487696daff
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_auth_generic.h"
24 #include "dcesrv_gssapi.h"
25 #include "dcesrv_spnego.h"
26 #include "auth/gensec/gensec.h"
28 static NTSTATUS spnego_init_server(TALLOC_CTX *mem_ctx,
29 bool do_sign, bool do_seal,
30 bool is_dcerpc,
31 const struct tsocket_address *remote_address,
32 struct spnego_context **spnego_ctx)
34 struct spnego_context *sp_ctx = NULL;
36 sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
37 if (!sp_ctx) {
38 return NT_STATUS_NO_MEMORY;
41 sp_ctx->remote_address = tsocket_address_copy(remote_address, sp_ctx);
42 if (sp_ctx->remote_address == NULL) {
43 return NT_STATUS_NO_MEMORY;
46 sp_ctx->do_sign = do_sign;
47 sp_ctx->do_seal = do_seal;
48 sp_ctx->is_dcerpc = is_dcerpc;
50 *spnego_ctx = sp_ctx;
51 return NT_STATUS_OK;
54 static NTSTATUS spnego_server_mech_init(struct spnego_context *sp_ctx,
55 DATA_BLOB *token_in,
56 DATA_BLOB *token_out)
58 struct gensec_security *gensec_security;
59 NTSTATUS status;
60 const char *oid;
62 switch (sp_ctx->mech) {
63 case SPNEGO_KRB5:
64 oid = GENSEC_OID_KERBEROS5;
65 break;
66 case SPNEGO_NTLMSSP:
67 oid = GENSEC_OID_NTLMSSP;
68 break;
69 default:
70 DEBUG(3, ("No known mechanisms available\n"));
71 return NT_STATUS_INVALID_PARAMETER;
74 status = auth_generic_server_start(sp_ctx,
75 oid,
76 sp_ctx->do_sign,
77 sp_ctx->do_seal,
78 sp_ctx->is_dcerpc,
79 token_in,
80 token_out,
81 sp_ctx->remote_address,
82 &gensec_security);
83 if (!NT_STATUS_IS_OK(status)) {
84 DEBUG(0, ("Failed to init ntlmssp server "
85 "(%s)\n", nt_errstr(status)));
86 return status;
89 sp_ctx->mech_ctx.gensec_security = gensec_security;
91 return NT_STATUS_OK;
94 NTSTATUS spnego_server_step(struct spnego_context *sp_ctx,
95 TALLOC_CTX *mem_ctx,
96 DATA_BLOB *spnego_in,
97 DATA_BLOB *spnego_out)
99 DATA_BLOB token_in = data_blob_null;
100 DATA_BLOB token_out = data_blob_null;
101 DATA_BLOB signature = data_blob_null;
102 DATA_BLOB MICblob = data_blob_null;
103 struct spnego_data sp_in;
104 ssize_t len_in = 0;
105 NTSTATUS status;
106 bool ret;
108 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
109 if (len_in == -1) {
110 DEBUG(1, (__location__ ": invalid SPNEGO blob.\n"));
111 dump_data(10, spnego_in->data, spnego_in->length);
112 status = NT_STATUS_INVALID_PARAMETER;
113 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
114 goto done;
116 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
117 status = NT_STATUS_INVALID_PARAMETER;
118 goto done;
120 token_in = sp_in.negTokenTarg.responseToken;
121 signature = sp_in.negTokenTarg.mechListMIC;
123 switch (sp_ctx->state) {
124 case SPNEGO_CONV_NEGO:
125 /* still to initialize */
126 status = spnego_server_mech_init(sp_ctx,
127 &token_in,
128 &token_out);
129 if (!NT_STATUS_IS_OK(status)) {
130 goto done;
132 /* server always need at least one reply from client */
133 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
134 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
135 break;
137 case SPNEGO_CONV_AUTH_MORE:
139 status = auth_generic_server_step(
140 sp_ctx->mech_ctx.gensec_security,
141 mem_ctx, &token_in, &token_out);
142 break;
144 case SPNEGO_CONV_AUTH_DONE:
145 /* we are already done, can't step further */
146 /* fall thorugh and return error */
147 default:
148 /* wrong case */
149 return NT_STATUS_INVALID_PARAMETER;
152 if (NT_STATUS_IS_OK(status) && signature.length != 0) {
153 /* last packet and requires signature check */
154 ret = spnego_mech_list_blob(talloc_tos(),
155 sp_ctx->oid_list, &MICblob);
156 if (ret) {
157 status = spnego_sigcheck(talloc_tos(), sp_ctx,
158 &MICblob, &MICblob,
159 &signature);
160 } else {
161 status = NT_STATUS_UNSUCCESSFUL;
164 if (NT_STATUS_IS_OK(status) && signature.length != 0) {
165 /* if signature was good, sign our own packet too */
166 status = spnego_sign(talloc_tos(), sp_ctx,
167 &MICblob, &MICblob, &signature);
170 done:
171 *spnego_out = spnego_gen_auth_response_and_mic(mem_ctx, status,
172 sp_ctx->mech_oid,
173 &token_out,
174 &signature);
175 if (!spnego_out->data) {
176 DEBUG(1, ("SPNEGO wrapping failed!\n"));
177 status = NT_STATUS_UNSUCCESSFUL;
180 if (NT_STATUS_IS_OK(status) ||
181 !NT_STATUS_EQUAL(status,
182 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
183 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
186 data_blob_free(&token_in);
187 data_blob_free(&token_out);
188 return status;
191 NTSTATUS spnego_server_auth_start(TALLOC_CTX *mem_ctx,
192 bool do_sign,
193 bool do_seal,
194 bool is_dcerpc,
195 DATA_BLOB *spnego_in,
196 DATA_BLOB *spnego_out,
197 const struct tsocket_address *remote_address,
198 struct spnego_context **spnego_ctx)
200 struct spnego_context *sp_ctx;
201 DATA_BLOB token_in = data_blob_null;
202 DATA_BLOB token_out = data_blob_null;
203 unsigned int i;
204 NTSTATUS status;
205 bool ret;
207 if (!spnego_in->length) {
208 return NT_STATUS_INVALID_PARAMETER;
211 status = spnego_init_server(mem_ctx,
212 do_sign,
213 do_seal,
214 is_dcerpc,
215 remote_address,
216 &sp_ctx);
217 if (!NT_STATUS_IS_OK(status)) {
218 return status;
221 ret = spnego_parse_negTokenInit(sp_ctx, *spnego_in,
222 sp_ctx->oid_list, NULL, &token_in);
223 if (!ret || sp_ctx->oid_list[0] == NULL) {
224 DEBUG(3, ("Invalid SPNEGO message\n"));
225 status = NT_STATUS_INVALID_PARAMETER;
226 goto done;
229 /* try for krb auth first */
230 for (i = 0; sp_ctx->oid_list[i] && sp_ctx->mech == SPNEGO_NONE; i++) {
231 if (strcmp(OID_KERBEROS5, sp_ctx->oid_list[i]) == 0 ||
232 strcmp(OID_KERBEROS5_OLD, sp_ctx->oid_list[i]) == 0) {
234 if (lp_security() == SEC_ADS || USE_KERBEROS_KEYTAB) {
235 sp_ctx->mech = SPNEGO_KRB5;
236 sp_ctx->mech_oid = sp_ctx->oid_list[i];
241 /* if auth type still undetermined, try for NTLMSSP */
242 for (i = 0; sp_ctx->oid_list[i] && sp_ctx->mech == SPNEGO_NONE; i++) {
243 if (strcmp(OID_NTLMSSP, sp_ctx->oid_list[i]) == 0) {
244 sp_ctx->mech = SPNEGO_NTLMSSP;
245 sp_ctx->mech_oid = sp_ctx->oid_list[i];
249 if (!sp_ctx->mech_oid) {
250 DEBUG(3, ("No known mechanisms available\n"));
251 status = NT_STATUS_INVALID_PARAMETER;
252 goto done;
255 if (DEBUGLEVEL >= 10) {
256 DEBUG(10, ("Client Provided OIDs:\n"));
257 for (i = 0; sp_ctx->oid_list[i]; i++) {
258 DEBUG(10, (" %d: %s\n", i, sp_ctx->oid_list[i]));
260 DEBUG(10, ("Chosen OID: %s\n", sp_ctx->mech_oid));
263 /* If it is not the first OID, then token_in is not valid for the
264 * choosen mech */
265 if (sp_ctx->mech_oid != sp_ctx->oid_list[0]) {
266 /* request more and send back empty token */
267 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
268 sp_ctx->state = SPNEGO_CONV_NEGO;
269 goto done;
272 status = spnego_server_mech_init(sp_ctx, &token_in, &token_out);
273 if (!NT_STATUS_IS_OK(status)) {
274 goto done;
277 DEBUG(10, ("SPNEGO(%d) auth started\n", sp_ctx->mech));
279 /* server always need at least one reply from client */
280 status = NT_STATUS_MORE_PROCESSING_REQUIRED;
281 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
283 done:
284 *spnego_out = spnego_gen_auth_response(mem_ctx, &token_out,
285 status, sp_ctx->mech_oid);
286 if (!spnego_out->data) {
287 status = NT_STATUS_INVALID_PARAMETER;
288 TALLOC_FREE(sp_ctx);
289 } else {
290 status = NT_STATUS_OK;
291 *spnego_ctx = sp_ctx;
294 data_blob_free(&token_in);
295 data_blob_free(&token_out);
297 return status;