gssapi: avoid explicit dependency on dcerpc specific structures
[Samba.git] / source3 / librpc / rpc / dcerpc_spnego.c
blob83c2137a1fe7d42de36289d9f983a33e8a2a45ef
1 /*
2 * SPNEGO Encapsulation
3 * RPC Pipe client routines
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 "include/ntlmssp_wrap.h"
23 #include "librpc/gen_ndr/ntlmssp.h"
24 #include "dcerpc_spnego.h"
25 #include "librpc/crypto/gse.h"
27 struct spnego_context {
28 enum dcerpc_AuthType auth_type;
30 union {
31 struct auth_ntlmssp_state *ntlmssp_state;
32 struct gse_context *gssapi_state;
33 } mech_ctx;
35 enum {
36 SPNEGO_CONV_INIT = 0,
37 SPNEGO_CONV_AUTH_MORE,
38 SPNEGO_CONV_AUTH_CONFIRM,
39 SPNEGO_CONV_AUTH_DONE
40 } state;
43 static NTSTATUS spnego_context_init(TALLOC_CTX *mem_ctx,
44 enum dcerpc_AuthType auth_type,
45 struct spnego_context **spnego_ctx)
47 struct spnego_context *sp_ctx;
49 sp_ctx = talloc_zero(mem_ctx, struct spnego_context);
50 if (!sp_ctx) {
51 return NT_STATUS_NO_MEMORY;
54 sp_ctx->auth_type = auth_type;
55 sp_ctx->state = SPNEGO_CONV_INIT;
57 *spnego_ctx = sp_ctx;
58 return NT_STATUS_OK;
61 NTSTATUS spnego_gssapi_init_client(TALLOC_CTX *mem_ctx,
62 enum dcerpc_AuthLevel auth_level,
63 const char *ccache_name,
64 const char *server,
65 const char *service,
66 const char *username,
67 const char *password,
68 uint32_t add_gss_c_flags,
69 struct spnego_context **spnego_ctx)
71 struct spnego_context *sp_ctx = NULL;
72 NTSTATUS status;
74 status = spnego_context_init(mem_ctx,
75 DCERPC_AUTH_TYPE_KRB5, &sp_ctx);
76 if (!NT_STATUS_IS_OK(status)) {
77 return status;
80 status = gse_init_client(sp_ctx,
81 (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY),
82 (auth_level == DCERPC_AUTH_LEVEL_PRIVACY),
83 ccache_name, server, service,
84 username, password, add_gss_c_flags,
85 &sp_ctx->mech_ctx.gssapi_state);
86 if (!NT_STATUS_IS_OK(status)) {
87 TALLOC_FREE(sp_ctx);
88 return status;
91 *spnego_ctx = sp_ctx;
92 return NT_STATUS_OK;
95 NTSTATUS spnego_ntlmssp_init_client(TALLOC_CTX *mem_ctx,
96 enum dcerpc_AuthLevel auth_level,
97 const char *domain,
98 const char *username,
99 const char *password,
100 struct spnego_context **spnego_ctx)
102 struct spnego_context *sp_ctx = NULL;
103 NTSTATUS status;
105 status = spnego_context_init(mem_ctx,
106 DCERPC_AUTH_TYPE_NTLMSSP, &sp_ctx);
107 if (!NT_STATUS_IS_OK(status)) {
108 return status;
111 status = auth_ntlmssp_client_start(sp_ctx,
112 global_myname(),
113 lp_workgroup(),
114 lp_client_ntlmv2_auth(),
115 &sp_ctx->mech_ctx.ntlmssp_state);
116 if (!NT_STATUS_IS_OK(status)) {
117 TALLOC_FREE(sp_ctx);
118 return status;
121 status = auth_ntlmssp_set_username(sp_ctx->mech_ctx.ntlmssp_state,
122 username);
123 if (!NT_STATUS_IS_OK(status)) {
124 TALLOC_FREE(sp_ctx);
125 return status;
128 status = auth_ntlmssp_set_domain(sp_ctx->mech_ctx.ntlmssp_state,
129 domain);
130 if (!NT_STATUS_IS_OK(status)) {
131 TALLOC_FREE(sp_ctx);
132 return status;
135 status = auth_ntlmssp_set_password(sp_ctx->mech_ctx.ntlmssp_state,
136 password);
137 if (!NT_STATUS_IS_OK(status)) {
138 TALLOC_FREE(sp_ctx);
139 return status;
143 * Turn off sign+seal to allow selected auth level to turn it back on.
145 auth_ntlmssp_and_flags(sp_ctx->mech_ctx.ntlmssp_state,
146 ~(NTLMSSP_NEGOTIATE_SIGN |
147 NTLMSSP_NEGOTIATE_SEAL));
149 if (auth_level == DCERPC_AUTH_LEVEL_INTEGRITY) {
150 auth_ntlmssp_or_flags(sp_ctx->mech_ctx.ntlmssp_state,
151 NTLMSSP_NEGOTIATE_SIGN);
152 } else if (auth_level == DCERPC_AUTH_LEVEL_PRIVACY) {
153 auth_ntlmssp_or_flags(sp_ctx->mech_ctx.ntlmssp_state,
154 NTLMSSP_NEGOTIATE_SEAL |
155 NTLMSSP_NEGOTIATE_SIGN);
158 *spnego_ctx = sp_ctx;
159 return NT_STATUS_OK;
162 NTSTATUS spnego_get_client_auth_token(TALLOC_CTX *mem_ctx,
163 struct spnego_context *sp_ctx,
164 DATA_BLOB *spnego_in,
165 DATA_BLOB *spnego_out)
167 struct gse_context *gse_ctx;
168 struct auth_ntlmssp_state *ntlmssp_ctx;
169 struct spnego_data sp_in, sp_out;
170 DATA_BLOB token_in = data_blob_null;
171 DATA_BLOB token_out = data_blob_null;
172 const char *mech_oids[2] = { NULL, NULL };
173 char *principal = NULL;
174 ssize_t len_in = 0;
175 ssize_t len_out = 0;
176 bool mech_wants_more = false;
177 NTSTATUS status;
179 if (!spnego_in->length) {
180 /* server didn't send anything, is init ? */
181 if (sp_ctx->state != SPNEGO_CONV_INIT) {
182 return NT_STATUS_INVALID_PARAMETER;
184 } else {
185 len_in = spnego_read_data(mem_ctx, *spnego_in, &sp_in);
186 if (len_in == -1) {
187 status = NT_STATUS_INVALID_PARAMETER;
188 goto done;
190 if (sp_in.type != SPNEGO_NEG_TOKEN_TARG) {
191 status = NT_STATUS_INVALID_PARAMETER;
192 goto done;
194 if (sp_in.negTokenTarg.negResult == SPNEGO_REJECT) {
195 status = NT_STATUS_ACCESS_DENIED;
196 goto done;
198 token_in = sp_in.negTokenTarg.responseToken;
201 if (sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
202 if (sp_in.negTokenTarg.negResult == SPNEGO_ACCEPT_COMPLETED) {
203 sp_ctx->state = SPNEGO_CONV_AUTH_DONE;
204 *spnego_out = data_blob_null;
205 status = NT_STATUS_OK;
206 } else {
207 status = NT_STATUS_ACCESS_DENIED;
209 goto done;
212 switch (sp_ctx->auth_type) {
213 case DCERPC_AUTH_TYPE_KRB5:
215 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
216 status = gse_get_client_auth_token(mem_ctx, gse_ctx,
217 &token_in, &token_out);
218 if (!NT_STATUS_IS_OK(status)) {
219 goto done;
222 mech_oids[0] = OID_KERBEROS5;
223 mech_wants_more = gse_require_more_processing(gse_ctx);
225 break;
227 case DCERPC_AUTH_TYPE_NTLMSSP:
229 ntlmssp_ctx = sp_ctx->mech_ctx.ntlmssp_state;
230 status = auth_ntlmssp_update(ntlmssp_ctx,
231 token_in, &token_out);
232 if (NT_STATUS_EQUAL(status,
233 NT_STATUS_MORE_PROCESSING_REQUIRED)) {
234 mech_wants_more = true;
235 } else if (!NT_STATUS_IS_OK(status)) {
236 goto done;
239 mech_oids[0] = OID_NTLMSSP;
241 break;
243 default:
244 status = NT_STATUS_INTERNAL_ERROR;
245 goto done;
248 switch (sp_ctx->state) {
249 case SPNEGO_CONV_INIT:
250 *spnego_out = spnego_gen_negTokenInit(mem_ctx, mech_oids,
251 &token_out, principal);
252 if (!spnego_out->data) {
253 status = NT_STATUS_INTERNAL_ERROR;
254 goto done;
256 sp_ctx->state = SPNEGO_CONV_AUTH_MORE;
257 break;
259 case SPNEGO_CONV_AUTH_MORE:
260 /* server says it's done and we do not seem to agree */
261 if (sp_in.negTokenTarg.negResult ==
262 SPNEGO_ACCEPT_COMPLETED) {
263 status = NT_STATUS_INVALID_PARAMETER;
264 goto done;
267 sp_out.type = SPNEGO_NEG_TOKEN_TARG;
268 sp_out.negTokenTarg.negResult = SPNEGO_NONE_RESULT;
269 sp_out.negTokenTarg.supportedMech = NULL;
270 sp_out.negTokenTarg.responseToken = token_out;
271 sp_out.negTokenTarg.mechListMIC = data_blob_null;
273 len_out = spnego_write_data(mem_ctx, spnego_out, &sp_out);
274 if (len_out == -1) {
275 status = NT_STATUS_INTERNAL_ERROR;
276 goto done;
279 if (!mech_wants_more) {
280 /* we still need to get an ack from the server */
281 sp_ctx->state = SPNEGO_CONV_AUTH_CONFIRM;
284 break;
286 default:
287 status = NT_STATUS_INTERNAL_ERROR;
288 goto done;
291 status = NT_STATUS_OK;
293 done:
294 if (len_in > 0) {
295 spnego_free_data(&sp_in);
297 data_blob_free(&token_out);
298 return status;
301 bool spnego_require_more_processing(struct spnego_context *sp_ctx)
303 struct gse_context *gse_ctx;
305 /* see if spnego processing itself requires more */
306 if (sp_ctx->state == SPNEGO_CONV_AUTH_MORE ||
307 sp_ctx->state == SPNEGO_CONV_AUTH_CONFIRM) {
308 return true;
311 /* otherwise see if underlying mechnism does */
312 switch (sp_ctx->auth_type) {
313 case DCERPC_AUTH_TYPE_KRB5:
314 gse_ctx = sp_ctx->mech_ctx.gssapi_state;
315 return gse_require_more_processing(gse_ctx);
316 case DCERPC_AUTH_TYPE_NTLMSSP:
317 return false;
318 default:
319 DEBUG(0, ("Unsupported type in request!\n"));
320 return false;
324 NTSTATUS spnego_get_negotiated_mech(struct spnego_context *sp_ctx,
325 enum dcerpc_AuthType *auth_type,
326 void **auth_context)
328 switch (sp_ctx->auth_type) {
329 case DCERPC_AUTH_TYPE_KRB5:
330 *auth_context = sp_ctx->mech_ctx.gssapi_state;
331 break;
332 case DCERPC_AUTH_TYPE_NTLMSSP:
333 *auth_context = sp_ctx->mech_ctx.ntlmssp_state;
334 break;
335 default:
336 return NT_STATUS_INTERNAL_ERROR;
339 *auth_type = sp_ctx->auth_type;
340 return NT_STATUS_OK;
343 DATA_BLOB spnego_get_session_key(TALLOC_CTX *mem_ctx,
344 struct spnego_context *sp_ctx)
346 DATA_BLOB sk;
348 switch (sp_ctx->auth_type) {
349 case DCERPC_AUTH_TYPE_KRB5:
350 return gse_get_session_key(mem_ctx,
351 sp_ctx->mech_ctx.gssapi_state);
352 case DCERPC_AUTH_TYPE_NTLMSSP:
353 sk = auth_ntlmssp_get_session_key(
354 sp_ctx->mech_ctx.ntlmssp_state);
355 return data_blob_dup_talloc(mem_ctx, &sk);
356 default:
357 DEBUG(0, ("Unsupported type in request!\n"));
358 return data_blob_null;