/src/samba/source4/auth/gensec/gensec_tstream.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | |
4 | | tstream based generic authentication interface |
5 | | |
6 | | Copyright (c) 2010 Stefan Metzmacher |
7 | | Copyright (c) 2010 Andreas Schneider <asn@redhat.com> |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | #include "includes.h" |
24 | | #include "system/network.h" |
25 | | #include "auth/gensec/gensec.h" |
26 | | #include "auth/gensec/gensec_proto.h" |
27 | | #include "auth/gensec/gensec_tstream.h" |
28 | | #include "lib/tsocket/tsocket.h" |
29 | | #include "lib/tsocket/tsocket_internal.h" |
30 | | #include "auth/gensec/gensec_toplevel_proto.h" |
31 | | |
32 | | static const struct tstream_context_ops tstream_gensec_ops; |
33 | | |
34 | | struct tstream_gensec { |
35 | | struct tstream_context *plain_stream; |
36 | | |
37 | | struct gensec_security *gensec_security; |
38 | | |
39 | | int error; |
40 | | |
41 | | struct { |
42 | | size_t max_unwrapped_size; |
43 | | size_t max_wrapped_size; |
44 | | } write; |
45 | | |
46 | | struct { |
47 | | off_t ofs; |
48 | | size_t left; |
49 | | DATA_BLOB unwrapped; |
50 | | } read; |
51 | | }; |
52 | | |
53 | | _PUBLIC_ NTSTATUS _gensec_create_tstream(TALLOC_CTX *mem_ctx, |
54 | | struct gensec_security *gensec_security, |
55 | | struct tstream_context *plain_stream, |
56 | | struct tstream_context **_gensec_stream, |
57 | | const char *location) |
58 | 0 | { |
59 | 0 | struct tstream_context *gensec_stream; |
60 | 0 | struct tstream_gensec *tgss; |
61 | |
|
62 | 0 | gensec_stream = tstream_context_create(mem_ctx, |
63 | 0 | &tstream_gensec_ops, |
64 | 0 | &tgss, |
65 | 0 | struct tstream_gensec, |
66 | 0 | location); |
67 | 0 | if (gensec_stream == NULL) { |
68 | 0 | return NT_STATUS_NO_MEMORY; |
69 | 0 | } |
70 | | |
71 | 0 | tgss->plain_stream = plain_stream; |
72 | 0 | tgss->gensec_security = gensec_security; |
73 | 0 | tgss->error = 0; |
74 | |
|
75 | 0 | if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN) && |
76 | 0 | !gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { |
77 | 0 | talloc_free(gensec_stream); |
78 | 0 | return NT_STATUS_INVALID_PARAMETER; |
79 | 0 | } |
80 | | |
81 | 0 | tgss->write.max_unwrapped_size = gensec_max_input_size(gensec_security); |
82 | 0 | tgss->write.max_wrapped_size = gensec_max_wrapped_size(gensec_security); |
83 | |
|
84 | 0 | ZERO_STRUCT(tgss->read); |
85 | |
|
86 | 0 | *_gensec_stream = gensec_stream; |
87 | 0 | return NT_STATUS_OK; |
88 | 0 | } |
89 | | |
90 | | static ssize_t tstream_gensec_pending_bytes(struct tstream_context *stream) |
91 | 0 | { |
92 | 0 | struct tstream_gensec *tgss = |
93 | 0 | tstream_context_data(stream, |
94 | 0 | struct tstream_gensec); |
95 | |
|
96 | 0 | if (tgss->error != 0) { |
97 | 0 | errno = tgss->error; |
98 | 0 | return -1; |
99 | 0 | } |
100 | | |
101 | 0 | return tgss->read.left; |
102 | 0 | } |
103 | | |
104 | | struct tstream_gensec_readv_state { |
105 | | struct tevent_context *ev; |
106 | | struct tstream_context *stream; |
107 | | |
108 | | struct iovec *vector; |
109 | | int count; |
110 | | |
111 | | struct { |
112 | | bool asked_for_hdr; |
113 | | uint8_t hdr[4]; |
114 | | bool asked_for_blob; |
115 | | DATA_BLOB blob; |
116 | | } wrapped; |
117 | | |
118 | | int ret; |
119 | | }; |
120 | | |
121 | | static void tstream_gensec_readv_wrapped_next(struct tevent_req *req); |
122 | | |
123 | | static struct tevent_req *tstream_gensec_readv_send(TALLOC_CTX *mem_ctx, |
124 | | struct tevent_context *ev, |
125 | | struct tstream_context *stream, |
126 | | struct iovec *vector, |
127 | | size_t count) |
128 | 0 | { |
129 | 0 | struct tstream_gensec *tgss = |
130 | 0 | tstream_context_data(stream, |
131 | 0 | struct tstream_gensec); |
132 | 0 | struct tevent_req *req; |
133 | 0 | struct tstream_gensec_readv_state *state; |
134 | |
|
135 | 0 | req = tevent_req_create(mem_ctx, &state, |
136 | 0 | struct tstream_gensec_readv_state); |
137 | 0 | if (!req) { |
138 | 0 | return NULL; |
139 | 0 | } |
140 | | |
141 | 0 | if (tgss->error != 0) { |
142 | 0 | tevent_req_error(req, tgss->error); |
143 | 0 | return tevent_req_post(req, ev); |
144 | 0 | } |
145 | | |
146 | 0 | state->ev = ev; |
147 | 0 | state->stream = stream; |
148 | 0 | state->ret = 0; |
149 | | |
150 | | /* |
151 | | * we make a copy of the vector so we can change the structure |
152 | | */ |
153 | 0 | state->vector = talloc_array(state, struct iovec, count); |
154 | 0 | if (tevent_req_nomem(state->vector, req)) { |
155 | 0 | return tevent_req_post(req, ev); |
156 | 0 | } |
157 | 0 | memcpy(state->vector, vector, sizeof(struct iovec) * count); |
158 | 0 | state->count = count; |
159 | |
|
160 | 0 | tstream_gensec_readv_wrapped_next(req); |
161 | 0 | if (!tevent_req_is_in_progress(req)) { |
162 | 0 | return tevent_req_post(req, ev); |
163 | 0 | } |
164 | | |
165 | 0 | return req; |
166 | 0 | } |
167 | | |
168 | | static int tstream_gensec_readv_next_vector(struct tstream_context *unix_stream, |
169 | | void *private_data, |
170 | | TALLOC_CTX *mem_ctx, |
171 | | struct iovec **_vector, |
172 | | size_t *_count); |
173 | | static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq); |
174 | | |
175 | | static void tstream_gensec_readv_wrapped_next(struct tevent_req *req) |
176 | 0 | { |
177 | 0 | struct tstream_gensec_readv_state *state = |
178 | 0 | tevent_req_data(req, |
179 | 0 | struct tstream_gensec_readv_state); |
180 | 0 | struct tstream_gensec *tgss = |
181 | 0 | tstream_context_data(state->stream, |
182 | 0 | struct tstream_gensec); |
183 | 0 | struct tevent_req *subreq; |
184 | | |
185 | | /* |
186 | | * copy the pending buffer first |
187 | | */ |
188 | 0 | while (tgss->read.left > 0 && state->count > 0) { |
189 | 0 | uint8_t *base = (uint8_t *)state->vector[0].iov_base; |
190 | 0 | size_t len = MIN(tgss->read.left, state->vector[0].iov_len); |
191 | |
|
192 | 0 | memcpy(base, tgss->read.unwrapped.data + tgss->read.ofs, len); |
193 | |
|
194 | 0 | base += len; |
195 | 0 | state->vector[0].iov_base = (char *) base; |
196 | 0 | state->vector[0].iov_len -= len; |
197 | |
|
198 | 0 | tgss->read.ofs += len; |
199 | 0 | tgss->read.left -= len; |
200 | |
|
201 | 0 | if (state->vector[0].iov_len == 0) { |
202 | 0 | state->vector += 1; |
203 | 0 | state->count -= 1; |
204 | 0 | } |
205 | |
|
206 | 0 | state->ret += len; |
207 | 0 | } |
208 | |
|
209 | 0 | if (state->count == 0) { |
210 | 0 | tevent_req_done(req); |
211 | 0 | return; |
212 | 0 | } |
213 | | |
214 | 0 | data_blob_free(&tgss->read.unwrapped); |
215 | 0 | ZERO_STRUCT(state->wrapped); |
216 | |
|
217 | 0 | subreq = tstream_readv_pdu_send(state, state->ev, |
218 | 0 | tgss->plain_stream, |
219 | 0 | tstream_gensec_readv_next_vector, |
220 | 0 | state); |
221 | 0 | if (tevent_req_nomem(subreq, req)) { |
222 | 0 | return; |
223 | 0 | } |
224 | 0 | tevent_req_set_callback(subreq, tstream_gensec_readv_wrapped_done, req); |
225 | 0 | } |
226 | | |
227 | | static int tstream_gensec_readv_next_vector(struct tstream_context *unix_stream, |
228 | | void *private_data, |
229 | | TALLOC_CTX *mem_ctx, |
230 | | struct iovec **_vector, |
231 | | size_t *_count) |
232 | 0 | { |
233 | 0 | struct tstream_gensec_readv_state *state = |
234 | 0 | talloc_get_type_abort(private_data, |
235 | 0 | struct tstream_gensec_readv_state); |
236 | 0 | struct iovec *vector; |
237 | 0 | size_t count = 1; |
238 | | |
239 | | /* we need to get a message header */ |
240 | 0 | vector = talloc_array(mem_ctx, struct iovec, count); |
241 | 0 | if (!vector) { |
242 | 0 | return -1; |
243 | 0 | } |
244 | | |
245 | 0 | if (!state->wrapped.asked_for_hdr) { |
246 | 0 | state->wrapped.asked_for_hdr = true; |
247 | 0 | vector[0].iov_base = (char *)state->wrapped.hdr; |
248 | 0 | vector[0].iov_len = sizeof(state->wrapped.hdr); |
249 | 0 | } else if (!state->wrapped.asked_for_blob) { |
250 | 0 | uint32_t msg_len; |
251 | |
|
252 | 0 | state->wrapped.asked_for_blob = true; |
253 | |
|
254 | 0 | msg_len = RIVAL(state->wrapped.hdr, 0); |
255 | | |
256 | | /* |
257 | | * I got a Windows 2012R2 server responding with |
258 | | * a message of 0x1b28a33. |
259 | | */ |
260 | 0 | if (msg_len > 0x0FFFFFFF) { |
261 | 0 | errno = EMSGSIZE; |
262 | 0 | return -1; |
263 | 0 | } |
264 | | |
265 | 0 | if (msg_len == 0) { |
266 | 0 | errno = EMSGSIZE; |
267 | 0 | return -1; |
268 | 0 | } |
269 | | |
270 | 0 | state->wrapped.blob = data_blob_talloc(state, NULL, msg_len); |
271 | 0 | if (state->wrapped.blob.data == NULL) { |
272 | 0 | return -1; |
273 | 0 | } |
274 | | |
275 | 0 | vector[0].iov_base = (char *)state->wrapped.blob.data; |
276 | 0 | vector[0].iov_len = state->wrapped.blob.length; |
277 | 0 | } else { |
278 | 0 | *_vector = NULL; |
279 | 0 | *_count = 0; |
280 | 0 | return 0; |
281 | 0 | } |
282 | | |
283 | 0 | *_vector = vector; |
284 | 0 | *_count = count; |
285 | 0 | return 0; |
286 | 0 | } |
287 | | |
288 | | static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq) |
289 | 0 | { |
290 | 0 | struct tevent_req *req = |
291 | 0 | tevent_req_callback_data(subreq, |
292 | 0 | struct tevent_req); |
293 | 0 | struct tstream_gensec_readv_state *state = |
294 | 0 | tevent_req_data(req, |
295 | 0 | struct tstream_gensec_readv_state); |
296 | 0 | struct tstream_gensec *tgss = |
297 | 0 | tstream_context_data(state->stream, |
298 | 0 | struct tstream_gensec); |
299 | 0 | int ret; |
300 | 0 | int sys_errno; |
301 | 0 | NTSTATUS status; |
302 | |
|
303 | 0 | ret = tstream_readv_pdu_recv(subreq, &sys_errno); |
304 | 0 | TALLOC_FREE(subreq); |
305 | 0 | if (ret == -1) { |
306 | 0 | tgss->error = sys_errno; |
307 | 0 | tevent_req_error(req, sys_errno); |
308 | 0 | return; |
309 | 0 | } |
310 | | |
311 | 0 | status = gensec_unwrap(tgss->gensec_security, |
312 | 0 | state, |
313 | 0 | &state->wrapped.blob, |
314 | 0 | &tgss->read.unwrapped); |
315 | 0 | if (!NT_STATUS_IS_OK(status)) { |
316 | 0 | tgss->error = EIO; |
317 | 0 | tevent_req_error(req, tgss->error); |
318 | 0 | return; |
319 | 0 | } |
320 | | |
321 | 0 | data_blob_free(&state->wrapped.blob); |
322 | |
|
323 | 0 | talloc_steal(tgss, tgss->read.unwrapped.data); |
324 | 0 | tgss->read.left = tgss->read.unwrapped.length; |
325 | 0 | tgss->read.ofs = 0; |
326 | |
|
327 | 0 | tstream_gensec_readv_wrapped_next(req); |
328 | 0 | } |
329 | | |
330 | | static int tstream_gensec_readv_recv(struct tevent_req *req, int *perrno) |
331 | 0 | { |
332 | 0 | struct tstream_gensec_readv_state *state = |
333 | 0 | tevent_req_data(req, |
334 | 0 | struct tstream_gensec_readv_state); |
335 | 0 | int ret; |
336 | |
|
337 | 0 | ret = tsocket_simple_int_recv(req, perrno); |
338 | 0 | if (ret == 0) { |
339 | 0 | ret = state->ret; |
340 | 0 | } |
341 | |
|
342 | 0 | tevent_req_received(req); |
343 | 0 | return ret; |
344 | 0 | } |
345 | | |
346 | | struct tstream_gensec_writev_state { |
347 | | struct tevent_context *ev; |
348 | | struct tstream_context *stream; |
349 | | |
350 | | struct iovec *vector; |
351 | | int count; |
352 | | |
353 | | struct { |
354 | | off_t ofs; |
355 | | size_t left; |
356 | | DATA_BLOB blob; |
357 | | } unwrapped; |
358 | | |
359 | | struct { |
360 | | uint8_t hdr[4]; |
361 | | DATA_BLOB blob; |
362 | | struct iovec iov[2]; |
363 | | } wrapped; |
364 | | |
365 | | int ret; |
366 | | }; |
367 | | |
368 | | static void tstream_gensec_writev_wrapped_next(struct tevent_req *req); |
369 | | |
370 | | static struct tevent_req *tstream_gensec_writev_send(TALLOC_CTX *mem_ctx, |
371 | | struct tevent_context *ev, |
372 | | struct tstream_context *stream, |
373 | | const struct iovec *vector, |
374 | | size_t count) |
375 | 0 | { |
376 | 0 | struct tstream_gensec *tgss = |
377 | 0 | tstream_context_data(stream, |
378 | 0 | struct tstream_gensec); |
379 | 0 | struct tevent_req *req; |
380 | 0 | struct tstream_gensec_writev_state *state; |
381 | 0 | size_t i; |
382 | 0 | int total; |
383 | 0 | int chunk; |
384 | |
|
385 | 0 | req = tevent_req_create(mem_ctx, &state, |
386 | 0 | struct tstream_gensec_writev_state); |
387 | 0 | if (req == NULL) { |
388 | 0 | return NULL; |
389 | 0 | } |
390 | | |
391 | 0 | if (tgss->error != 0) { |
392 | 0 | tevent_req_error(req, tgss->error); |
393 | 0 | return tevent_req_post(req, ev); |
394 | 0 | } |
395 | | |
396 | 0 | state->ev = ev; |
397 | 0 | state->stream = stream; |
398 | 0 | state->ret = 0; |
399 | | |
400 | | /* |
401 | | * we make a copy of the vector so we can change the structure |
402 | | */ |
403 | 0 | state->vector = talloc_array(state, struct iovec, count); |
404 | 0 | if (tevent_req_nomem(state->vector, req)) { |
405 | 0 | return tevent_req_post(req, ev); |
406 | 0 | } |
407 | 0 | memcpy(state->vector, vector, sizeof(struct iovec) * count); |
408 | 0 | state->count = count; |
409 | |
|
410 | 0 | total = 0; |
411 | 0 | for (i = 0; i < count; i++) { |
412 | | /* |
413 | | * the generic tstream code makes sure that |
414 | | * this never wraps. |
415 | | */ |
416 | 0 | total += vector[i].iov_len; |
417 | 0 | } |
418 | | |
419 | | /* |
420 | | * We may need to send data in chunks. |
421 | | */ |
422 | 0 | chunk = MIN(total, tgss->write.max_unwrapped_size); |
423 | |
|
424 | 0 | state->unwrapped.blob = data_blob_talloc(state, NULL, chunk); |
425 | 0 | if (tevent_req_nomem(state->unwrapped.blob.data, req)) { |
426 | 0 | return tevent_req_post(req, ev); |
427 | 0 | } |
428 | | |
429 | 0 | tstream_gensec_writev_wrapped_next(req); |
430 | 0 | if (!tevent_req_is_in_progress(req)) { |
431 | 0 | return tevent_req_post(req, ev); |
432 | 0 | } |
433 | | |
434 | 0 | return req; |
435 | 0 | } |
436 | | |
437 | | static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq); |
438 | | |
439 | | static void tstream_gensec_writev_wrapped_next(struct tevent_req *req) |
440 | 0 | { |
441 | 0 | struct tstream_gensec_writev_state *state = |
442 | 0 | tevent_req_data(req, |
443 | 0 | struct tstream_gensec_writev_state); |
444 | 0 | struct tstream_gensec *tgss = |
445 | 0 | tstream_context_data(state->stream, |
446 | 0 | struct tstream_gensec); |
447 | 0 | struct tevent_req *subreq; |
448 | 0 | NTSTATUS status; |
449 | |
|
450 | 0 | data_blob_free(&state->wrapped.blob); |
451 | |
|
452 | 0 | state->unwrapped.left = state->unwrapped.blob.length; |
453 | 0 | state->unwrapped.ofs = 0; |
454 | | |
455 | | /* |
456 | | * first fill our buffer |
457 | | */ |
458 | 0 | while (state->unwrapped.left > 0 && state->count > 0) { |
459 | 0 | uint8_t *base = (uint8_t *)state->vector[0].iov_base; |
460 | 0 | size_t len = MIN(state->unwrapped.left, state->vector[0].iov_len); |
461 | |
|
462 | 0 | memcpy(state->unwrapped.blob.data + state->unwrapped.ofs, base, len); |
463 | |
|
464 | 0 | base += len; |
465 | 0 | state->vector[0].iov_base = (char *) base; |
466 | 0 | state->vector[0].iov_len -= len; |
467 | |
|
468 | 0 | state->unwrapped.ofs += len; |
469 | 0 | state->unwrapped.left -= len; |
470 | |
|
471 | 0 | if (state->vector[0].iov_len == 0) { |
472 | 0 | state->vector += 1; |
473 | 0 | state->count -= 1; |
474 | 0 | } |
475 | |
|
476 | 0 | state->ret += len; |
477 | 0 | } |
478 | |
|
479 | 0 | if (state->unwrapped.ofs == 0) { |
480 | 0 | tevent_req_done(req); |
481 | 0 | return; |
482 | 0 | } |
483 | | |
484 | 0 | state->unwrapped.blob.length = state->unwrapped.ofs; |
485 | |
|
486 | 0 | status = gensec_wrap(tgss->gensec_security, |
487 | 0 | state, |
488 | 0 | &state->unwrapped.blob, |
489 | 0 | &state->wrapped.blob); |
490 | 0 | if (!NT_STATUS_IS_OK(status)) { |
491 | 0 | tgss->error = EIO; |
492 | 0 | tevent_req_error(req, tgss->error); |
493 | 0 | return; |
494 | 0 | } |
495 | | |
496 | 0 | RSIVAL(state->wrapped.hdr, 0, state->wrapped.blob.length); |
497 | |
|
498 | 0 | state->wrapped.iov[0].iov_base = (void *)state->wrapped.hdr; |
499 | 0 | state->wrapped.iov[0].iov_len = sizeof(state->wrapped.hdr); |
500 | 0 | state->wrapped.iov[1].iov_base = (void *)state->wrapped.blob.data; |
501 | 0 | state->wrapped.iov[1].iov_len = state->wrapped.blob.length; |
502 | |
|
503 | 0 | subreq = tstream_writev_send(state, state->ev, |
504 | 0 | tgss->plain_stream, |
505 | 0 | state->wrapped.iov, 2); |
506 | 0 | if (tevent_req_nomem(subreq, req)) { |
507 | 0 | return; |
508 | 0 | } |
509 | 0 | tevent_req_set_callback(subreq, |
510 | 0 | tstream_gensec_writev_wrapped_done, |
511 | 0 | req); |
512 | 0 | } |
513 | | |
514 | | static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq) |
515 | 0 | { |
516 | 0 | struct tevent_req *req = |
517 | 0 | tevent_req_callback_data(subreq, |
518 | 0 | struct tevent_req); |
519 | 0 | struct tstream_gensec_writev_state *state = |
520 | 0 | tevent_req_data(req, |
521 | 0 | struct tstream_gensec_writev_state); |
522 | 0 | struct tstream_gensec *tgss = |
523 | 0 | tstream_context_data(state->stream, |
524 | 0 | struct tstream_gensec); |
525 | 0 | int sys_errno; |
526 | 0 | int ret; |
527 | |
|
528 | 0 | ret = tstream_writev_recv(subreq, &sys_errno); |
529 | 0 | TALLOC_FREE(subreq); |
530 | 0 | if (ret == -1) { |
531 | 0 | tgss->error = sys_errno; |
532 | 0 | tevent_req_error(req, sys_errno); |
533 | 0 | return; |
534 | 0 | } |
535 | | |
536 | 0 | tstream_gensec_writev_wrapped_next(req); |
537 | 0 | } |
538 | | |
539 | | static int tstream_gensec_writev_recv(struct tevent_req *req, |
540 | | int *perrno) |
541 | 0 | { |
542 | 0 | struct tstream_gensec_writev_state *state = |
543 | 0 | tevent_req_data(req, |
544 | 0 | struct tstream_gensec_writev_state); |
545 | 0 | int ret; |
546 | |
|
547 | 0 | ret = tsocket_simple_int_recv(req, perrno); |
548 | 0 | if (ret == 0) { |
549 | 0 | ret = state->ret; |
550 | 0 | } |
551 | |
|
552 | 0 | tevent_req_received(req); |
553 | 0 | return ret; |
554 | 0 | } |
555 | | |
556 | | struct tstream_gensec_disconnect_state { |
557 | | uint8_t _dummy; |
558 | | }; |
559 | | |
560 | | static struct tevent_req *tstream_gensec_disconnect_send(TALLOC_CTX *mem_ctx, |
561 | | struct tevent_context *ev, |
562 | | struct tstream_context *stream) |
563 | 0 | { |
564 | 0 | struct tstream_gensec *tgss = |
565 | 0 | tstream_context_data(stream, |
566 | 0 | struct tstream_gensec); |
567 | 0 | struct tevent_req *req; |
568 | 0 | struct tstream_gensec_disconnect_state *state; |
569 | |
|
570 | 0 | req = tevent_req_create(mem_ctx, &state, |
571 | 0 | struct tstream_gensec_disconnect_state); |
572 | 0 | if (req == NULL) { |
573 | 0 | return NULL; |
574 | 0 | } |
575 | | |
576 | 0 | if (tgss->error != 0) { |
577 | 0 | tevent_req_error(req, tgss->error); |
578 | 0 | return tevent_req_post(req, ev); |
579 | 0 | } |
580 | | |
581 | | /* |
582 | | * The caller is responsible to do the real disconnect |
583 | | * on the plain stream! |
584 | | */ |
585 | 0 | tgss->plain_stream = NULL; |
586 | 0 | tgss->error = ENOTCONN; |
587 | |
|
588 | 0 | tevent_req_done(req); |
589 | 0 | return tevent_req_post(req, ev); |
590 | 0 | } |
591 | | |
592 | | static int tstream_gensec_disconnect_recv(struct tevent_req *req, |
593 | | int *perrno) |
594 | 0 | { |
595 | 0 | int ret; |
596 | |
|
597 | 0 | ret = tsocket_simple_int_recv(req, perrno); |
598 | |
|
599 | 0 | tevent_req_received(req); |
600 | 0 | return ret; |
601 | 0 | } |
602 | | |
603 | | static const struct tstream_context_ops tstream_gensec_ops = { |
604 | | .name = "gensec", |
605 | | |
606 | | .pending_bytes = tstream_gensec_pending_bytes, |
607 | | |
608 | | .readv_send = tstream_gensec_readv_send, |
609 | | .readv_recv = tstream_gensec_readv_recv, |
610 | | |
611 | | .writev_send = tstream_gensec_writev_send, |
612 | | .writev_recv = tstream_gensec_writev_recv, |
613 | | |
614 | | .disconnect_send = tstream_gensec_disconnect_send, |
615 | | .disconnect_recv = tstream_gensec_disconnect_recv, |
616 | | }; |