wafsamba: add -Werror=return-type for developer builds
[Samba.git] / source4 / auth / gensec / gensec_tstream.c
blob92f4fa6687cef7921a7e07577a526047842ef774
1 /*
2 Unix SMB/CIFS implementation.
4 tstream based generic authentication interface
6 Copyright (c) 2010 Stefan Metzmacher
7 Copyright (c) 2010 Andreas Schneider <asn@redhat.com>
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.
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.
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/>.
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"
32 static const struct tstream_context_ops tstream_gensec_ops;
34 struct tstream_gensec {
35 struct tstream_context *plain_stream;
37 struct gensec_security *gensec_security;
39 int error;
41 struct {
42 size_t max_unwrapped_size;
43 size_t max_wrapped_size;
44 } write;
46 struct {
47 off_t ofs;
48 size_t left;
49 DATA_BLOB unwrapped;
50 } read;
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)
59 struct tstream_context *gensec_stream;
60 struct tstream_gensec *tgss;
62 gensec_stream = tstream_context_create(mem_ctx,
63 &tstream_gensec_ops,
64 &tgss,
65 struct tstream_gensec,
66 location);
67 if (gensec_stream == NULL) {
68 return NT_STATUS_NO_MEMORY;
71 tgss->plain_stream = plain_stream;
72 tgss->gensec_security = gensec_security;
73 tgss->error = 0;
75 if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN) &&
76 !gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) {
77 talloc_free(gensec_stream);
78 return NT_STATUS_INVALID_PARAMETER;
81 tgss->write.max_unwrapped_size = gensec_max_input_size(gensec_security);
82 tgss->write.max_wrapped_size = gensec_max_wrapped_size(gensec_security);
84 ZERO_STRUCT(tgss->read);
86 *_gensec_stream = gensec_stream;
87 return NT_STATUS_OK;
90 static ssize_t tstream_gensec_pending_bytes(struct tstream_context *stream)
92 struct tstream_gensec *tgss =
93 tstream_context_data(stream,
94 struct tstream_gensec);
96 if (tgss->error != 0) {
97 errno = tgss->error;
98 return -1;
101 return tgss->read.left;
104 struct tstream_gensec_readv_state {
105 struct tevent_context *ev;
106 struct tstream_context *stream;
108 struct iovec *vector;
109 int count;
111 struct {
112 bool asked_for_hdr;
113 uint8_t hdr[4];
114 bool asked_for_blob;
115 DATA_BLOB blob;
116 } wrapped;
118 int ret;
121 static void tstream_gensec_readv_wrapped_next(struct tevent_req *req);
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)
129 struct tstream_gensec *tgss =
130 tstream_context_data(stream,
131 struct tstream_gensec);
132 struct tevent_req *req;
133 struct tstream_gensec_readv_state *state;
135 req = tevent_req_create(mem_ctx, &state,
136 struct tstream_gensec_readv_state);
137 if (!req) {
138 return NULL;
141 if (tgss->error != 0) {
142 tevent_req_error(req, tgss->error);
143 return tevent_req_post(req, ev);
146 state->ev = ev;
147 state->stream = stream;
148 state->ret = 0;
151 * we make a copy of the vector so we can change the structure
153 state->vector = talloc_array(state, struct iovec, count);
154 if (tevent_req_nomem(state->vector, req)) {
155 return tevent_req_post(req, ev);
157 memcpy(state->vector, vector, sizeof(struct iovec) * count);
158 state->count = count;
160 tstream_gensec_readv_wrapped_next(req);
161 if (!tevent_req_is_in_progress(req)) {
162 return tevent_req_post(req, ev);
165 return req;
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);
175 static void tstream_gensec_readv_wrapped_next(struct tevent_req *req)
177 struct tstream_gensec_readv_state *state =
178 tevent_req_data(req,
179 struct tstream_gensec_readv_state);
180 struct tstream_gensec *tgss =
181 tstream_context_data(state->stream,
182 struct tstream_gensec);
183 struct tevent_req *subreq;
186 * copy the pending buffer first
188 while (tgss->read.left > 0 && state->count > 0) {
189 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
190 size_t len = MIN(tgss->read.left, state->vector[0].iov_len);
192 memcpy(base, tgss->read.unwrapped.data + tgss->read.ofs, len);
194 base += len;
195 state->vector[0].iov_base = (char *) base;
196 state->vector[0].iov_len -= len;
198 tgss->read.ofs += len;
199 tgss->read.left -= len;
201 if (state->vector[0].iov_len == 0) {
202 state->vector += 1;
203 state->count -= 1;
206 state->ret += len;
209 if (state->count == 0) {
210 tevent_req_done(req);
211 return;
214 data_blob_free(&tgss->read.unwrapped);
215 ZERO_STRUCT(state->wrapped);
217 subreq = tstream_readv_pdu_send(state, state->ev,
218 tgss->plain_stream,
219 tstream_gensec_readv_next_vector,
220 state);
221 if (tevent_req_nomem(subreq, req)) {
222 return;
224 tevent_req_set_callback(subreq, tstream_gensec_readv_wrapped_done, req);
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)
233 struct tstream_gensec_readv_state *state =
234 talloc_get_type_abort(private_data,
235 struct tstream_gensec_readv_state);
236 struct iovec *vector;
237 size_t count = 1;
239 /* we need to get a message header */
240 vector = talloc_array(mem_ctx, struct iovec, count);
241 if (!vector) {
242 return -1;
245 if (!state->wrapped.asked_for_hdr) {
246 state->wrapped.asked_for_hdr = true;
247 vector[0].iov_base = (char *)state->wrapped.hdr;
248 vector[0].iov_len = sizeof(state->wrapped.hdr);
249 } else if (!state->wrapped.asked_for_blob) {
250 uint32_t msg_len;
252 state->wrapped.asked_for_blob = true;
254 msg_len = RIVAL(state->wrapped.hdr, 0);
256 if (msg_len > 0x00FFFFFF) {
257 errno = EMSGSIZE;
258 return -1;
261 if (msg_len == 0) {
262 errno = EMSGSIZE;
263 return -1;
266 state->wrapped.blob = data_blob_talloc(state, NULL, msg_len);
267 if (state->wrapped.blob.data == NULL) {
268 return -1;
271 vector[0].iov_base = (char *)state->wrapped.blob.data;
272 vector[0].iov_len = state->wrapped.blob.length;
273 } else {
274 *_vector = NULL;
275 *_count = 0;
276 return 0;
279 *_vector = vector;
280 *_count = count;
281 return 0;
284 static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq)
286 struct tevent_req *req =
287 tevent_req_callback_data(subreq,
288 struct tevent_req);
289 struct tstream_gensec_readv_state *state =
290 tevent_req_data(req,
291 struct tstream_gensec_readv_state);
292 struct tstream_gensec *tgss =
293 tstream_context_data(state->stream,
294 struct tstream_gensec);
295 int ret;
296 int sys_errno;
297 NTSTATUS status;
299 ret = tstream_readv_pdu_recv(subreq, &sys_errno);
300 TALLOC_FREE(subreq);
301 if (ret == -1) {
302 tgss->error = sys_errno;
303 tevent_req_error(req, sys_errno);
304 return;
307 status = gensec_unwrap(tgss->gensec_security,
308 state,
309 &state->wrapped.blob,
310 &tgss->read.unwrapped);
311 if (!NT_STATUS_IS_OK(status)) {
312 tgss->error = EIO;
313 tevent_req_error(req, tgss->error);
314 return;
317 data_blob_free(&state->wrapped.blob);
319 talloc_steal(tgss, tgss->read.unwrapped.data);
320 tgss->read.left = tgss->read.unwrapped.length;
321 tgss->read.ofs = 0;
323 tstream_gensec_readv_wrapped_next(req);
326 static int tstream_gensec_readv_recv(struct tevent_req *req, int *perrno)
328 struct tstream_gensec_readv_state *state =
329 tevent_req_data(req,
330 struct tstream_gensec_readv_state);
331 int ret;
333 ret = tsocket_simple_int_recv(req, perrno);
334 if (ret == 0) {
335 ret = state->ret;
338 tevent_req_received(req);
339 return ret;
342 struct tstream_gensec_writev_state {
343 struct tevent_context *ev;
344 struct tstream_context *stream;
346 struct iovec *vector;
347 int count;
349 struct {
350 off_t ofs;
351 size_t left;
352 DATA_BLOB blob;
353 } unwrapped;
355 struct {
356 uint8_t hdr[4];
357 DATA_BLOB blob;
358 struct iovec iov[2];
359 } wrapped;
361 int ret;
364 static void tstream_gensec_writev_wrapped_next(struct tevent_req *req);
366 static struct tevent_req *tstream_gensec_writev_send(TALLOC_CTX *mem_ctx,
367 struct tevent_context *ev,
368 struct tstream_context *stream,
369 const struct iovec *vector,
370 size_t count)
372 struct tstream_gensec *tgss =
373 tstream_context_data(stream,
374 struct tstream_gensec);
375 struct tevent_req *req;
376 struct tstream_gensec_writev_state *state;
377 int i;
378 int total;
379 int chunk;
381 req = tevent_req_create(mem_ctx, &state,
382 struct tstream_gensec_writev_state);
383 if (req == NULL) {
384 return NULL;
387 if (tgss->error != 0) {
388 tevent_req_error(req, tgss->error);
389 return tevent_req_post(req, ev);
392 state->ev = ev;
393 state->stream = stream;
394 state->ret = 0;
397 * we make a copy of the vector so we can change the structure
399 state->vector = talloc_array(state, struct iovec, count);
400 if (tevent_req_nomem(state->vector, req)) {
401 return tevent_req_post(req, ev);
403 memcpy(state->vector, vector, sizeof(struct iovec) * count);
404 state->count = count;
406 total = 0;
407 for (i = 0; i < count; i++) {
409 * the generic tstream code makes sure that
410 * this never wraps.
412 total += vector[i].iov_len;
416 * We may need to send data in chunks.
418 chunk = MIN(total, tgss->write.max_unwrapped_size);
420 state->unwrapped.blob = data_blob_talloc(state, NULL, chunk);
421 if (tevent_req_nomem(state->unwrapped.blob.data, req)) {
422 return tevent_req_post(req, ev);
425 tstream_gensec_writev_wrapped_next(req);
426 if (!tevent_req_is_in_progress(req)) {
427 return tevent_req_post(req, ev);
430 return req;
433 static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq);
435 static void tstream_gensec_writev_wrapped_next(struct tevent_req *req)
437 struct tstream_gensec_writev_state *state =
438 tevent_req_data(req,
439 struct tstream_gensec_writev_state);
440 struct tstream_gensec *tgss =
441 tstream_context_data(state->stream,
442 struct tstream_gensec);
443 struct tevent_req *subreq;
444 NTSTATUS status;
446 data_blob_free(&state->wrapped.blob);
448 state->unwrapped.left = state->unwrapped.blob.length;
449 state->unwrapped.ofs = 0;
452 * first fill our buffer
454 while (state->unwrapped.left > 0 && state->count > 0) {
455 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
456 size_t len = MIN(state->unwrapped.left, state->vector[0].iov_len);
458 memcpy(state->unwrapped.blob.data + state->unwrapped.ofs, base, len);
460 base += len;
461 state->vector[0].iov_base = (char *) base;
462 state->vector[0].iov_len -= len;
464 state->unwrapped.ofs += len;
465 state->unwrapped.left -= len;
467 if (state->vector[0].iov_len == 0) {
468 state->vector += 1;
469 state->count -= 1;
472 state->ret += len;
475 if (state->unwrapped.ofs == 0) {
476 tevent_req_done(req);
477 return;
480 state->unwrapped.blob.length = state->unwrapped.ofs;
482 status = gensec_wrap(tgss->gensec_security,
483 state,
484 &state->unwrapped.blob,
485 &state->wrapped.blob);
486 if (!NT_STATUS_IS_OK(status)) {
487 tgss->error = EIO;
488 tevent_req_error(req, tgss->error);
489 return;
492 RSIVAL(state->wrapped.hdr, 0, state->wrapped.blob.length);
494 state->wrapped.iov[0].iov_base = (void *)state->wrapped.hdr;
495 state->wrapped.iov[0].iov_len = sizeof(state->wrapped.hdr);
496 state->wrapped.iov[1].iov_base = (void *)state->wrapped.blob.data;
497 state->wrapped.iov[1].iov_len = state->wrapped.blob.length;
499 subreq = tstream_writev_send(state, state->ev,
500 tgss->plain_stream,
501 state->wrapped.iov, 2);
502 if (tevent_req_nomem(subreq, req)) {
503 return;
505 tevent_req_set_callback(subreq,
506 tstream_gensec_writev_wrapped_done,
507 req);
510 static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq)
512 struct tevent_req *req =
513 tevent_req_callback_data(subreq,
514 struct tevent_req);
515 struct tstream_gensec_writev_state *state =
516 tevent_req_data(req,
517 struct tstream_gensec_writev_state);
518 struct tstream_gensec *tgss =
519 tstream_context_data(state->stream,
520 struct tstream_gensec);
521 int sys_errno;
522 int ret;
524 ret = tstream_writev_recv(subreq, &sys_errno);
525 TALLOC_FREE(subreq);
526 if (ret == -1) {
527 tgss->error = sys_errno;
528 tevent_req_error(req, sys_errno);
529 return;
532 tstream_gensec_writev_wrapped_next(req);
535 static int tstream_gensec_writev_recv(struct tevent_req *req,
536 int *perrno)
538 struct tstream_gensec_writev_state *state =
539 tevent_req_data(req,
540 struct tstream_gensec_writev_state);
541 int ret;
543 ret = tsocket_simple_int_recv(req, perrno);
544 if (ret == 0) {
545 ret = state->ret;
548 tevent_req_received(req);
549 return ret;
552 struct tstream_gensec_disconnect_state {
553 uint8_t _dummy;
556 static struct tevent_req *tstream_gensec_disconnect_send(TALLOC_CTX *mem_ctx,
557 struct tevent_context *ev,
558 struct tstream_context *stream)
560 struct tstream_gensec *tgss =
561 tstream_context_data(stream,
562 struct tstream_gensec);
563 struct tevent_req *req;
564 struct tstream_gensec_disconnect_state *state;
566 req = tevent_req_create(mem_ctx, &state,
567 struct tstream_gensec_disconnect_state);
568 if (req == NULL) {
569 return NULL;
572 if (tgss->error != 0) {
573 tevent_req_error(req, tgss->error);
574 return tevent_req_post(req, ev);
578 * The caller is responsible to do the real disconnect
579 * on the plain stream!
581 tgss->plain_stream = NULL;
582 tgss->error = ENOTCONN;
584 tevent_req_done(req);
585 return tevent_req_post(req, ev);
588 static int tstream_gensec_disconnect_recv(struct tevent_req *req,
589 int *perrno)
591 int ret;
593 ret = tsocket_simple_int_recv(req, perrno);
595 tevent_req_received(req);
596 return ret;
599 static const struct tstream_context_ops tstream_gensec_ops = {
600 .name = "gensec",
602 .pending_bytes = tstream_gensec_pending_bytes,
604 .readv_send = tstream_gensec_readv_send,
605 .readv_recv = tstream_gensec_readv_recv,
607 .writev_send = tstream_gensec_writev_send,
608 .writev_recv = tstream_gensec_writev_recv,
610 .disconnect_send = tstream_gensec_disconnect_send,
611 .disconnect_recv = tstream_gensec_disconnect_recv,