Only ask for specific permissions required when setting an ACL.
[Samba/gebeck_regimport.git] / source3 / rpc_server / dcesrv_spnego.c
blobed7d772d595ee13dea234f9f544c9b9990929848
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_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,
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 NTSTATUS status;
59 const char *oid;
61 switch (sp_ctx->mech) {
62 case SPNEGO_KRB5:
63 oid = GENSEC_OID_KERBEROS5;
64 break;
65 case SPNEGO_NTLMSSP:
66 oid = GENSEC_OID_NTLMSSP;
67 break;
68 default:
69 DEBUG(3, ("No known mechanisms available\n"));
70 return NT_STATUS_INVALID_PARAMETER;
73 status = auth_generic_server_start(sp_ctx,
74 oid,
75 sp_ctx->do_sign,
76 sp_ctx->do_seal,
77 sp_ctx->is_dcerpc,
78 token_in,
79 token_out,
80 sp_ctx->remote_address,
81 &gensec_security);
82 if (!NT_STATUS_IS_OK(status)) {
83 DEBUG(0, ("Failed to init ntlmssp server "
84 "(%s)\n", nt_errstr(status)));
85 return status;
88 sp_ctx->gensec_security = gensec_security;
90 return NT_STATUS_OK;
93 NTSTATUS spnego_server_step(struct spnego_context *sp_ctx,
94 TALLOC_CTX *mem_ctx,
95 DATA_BLOB *spnego_in,
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;
103 ssize_t len_in = 0;
104 NTSTATUS status;
105 bool ret;
107 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
108 if (len_in == -1) {
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;
113 goto done;
115 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
116 status = NT_STATUS_INVALID_PARAMETER;
117 goto done;
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,
126 &token_in,
127 &token_out);
128 if (!NT_STATUS_IS_OK(status)) {
129 goto done;
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;
134 break;
136 case SPNEGO_CONV_AUTH_MORE:
138 status = auth_generic_server_step(
139 sp_ctx->gensec_security,
140 mem_ctx, &token_in, &token_out);
141 break;
143 case SPNEGO_CONV_AUTH_DONE:
144 /* we are already done, can't step further */
145 /* fall thorugh and return error */
146 default:
147 /* wrong case */
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);
155 if (ret) {
156 status = spnego_sigcheck(talloc_tos(), sp_ctx,
157 &MICblob, &MICblob,
158 &signature);
159 } else {
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);
169 done:
170 *spnego_out = spnego_gen_auth_response_and_mic(mem_ctx, status,
171 sp_ctx->mech_oid,
172 &token_out,
173 &signature);
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);
187 return status;
190 NTSTATUS spnego_server_auth_start(TALLOC_CTX *mem_ctx,
191 bool do_sign,
192 bool do_seal,
193 bool is_dcerpc,
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;
202 unsigned int i;
203 NTSTATUS status;
204 bool ret;
206 if (!spnego_in->length) {
207 return NT_STATUS_INVALID_PARAMETER;
210 status = spnego_init_server(mem_ctx,
211 do_sign,
212 do_seal,
213 is_dcerpc,
214 remote_address,
215 &sp_ctx);
216 if (!NT_STATUS_IS_OK(status)) {
217 return 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;
225 goto done;
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;
251 goto done;
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
263 * choosen mech */
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;
268 goto done;
271 status = spnego_server_mech_init(sp_ctx, &token_in, &token_out);
272 if (!NT_STATUS_IS_OK(status)) {
273 goto done;
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;
282 done:
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;
287 TALLOC_FREE(sp_ctx);
288 } else {
289 status = NT_STATUS_OK;
290 *spnego_ctx = sp_ctx;
293 data_blob_free(&token_in);
294 data_blob_free(&token_out);
296 return status;