source: trunk/server/source4/auth/gensec/gensec_tstream.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 14.9 KB
Line 
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
31
32static const struct tstream_context_ops tstream_gensec_ops;
33
34struct 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{
59 struct tstream_context *gensec_stream;
60 struct tstream_gensec *tgss;
61
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;
69 }
70
71 tgss->plain_stream = plain_stream;
72 tgss->gensec_security = gensec_security;
73 tgss->error = 0;
74
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;
79 }
80
81 tgss->write.max_unwrapped_size = gensec_max_input_size(gensec_security);
82 tgss->write.max_wrapped_size = gensec_max_wrapped_size(gensec_security);
83
84 ZERO_STRUCT(tgss->read);
85
86 *_gensec_stream = gensec_stream;
87 return NT_STATUS_OK;
88}
89
90static ssize_t tstream_gensec_pending_bytes(struct tstream_context *stream)
91{
92 struct tstream_gensec *tgss =
93 tstream_context_data(stream,
94 struct tstream_gensec);
95
96 if (tgss->error != 0) {
97 errno = tgss->error;
98 return -1;
99 }
100
101 return tgss->read.left;
102}
103
104struct 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
121static void tstream_gensec_readv_wrapped_next(struct tevent_req *req);
122
123static 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{
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;
134
135 req = tevent_req_create(mem_ctx, &state,
136 struct tstream_gensec_readv_state);
137 if (!req) {
138 return NULL;
139 }
140
141 if (tgss->error != 0) {
142 tevent_req_error(req, tgss->error);
143 return tevent_req_post(req, ev);
144 }
145
146 state->ev = ev;
147 state->stream = stream;
148 state->ret = 0;
149
150 /*
151 * we make a copy of the vector so we can change the structure
152 */
153 state->vector = talloc_array(state, struct iovec, count);
154 if (tevent_req_nomem(state->vector, req)) {
155 return tevent_req_post(req, ev);
156 }
157 memcpy(state->vector, vector, sizeof(struct iovec) * count);
158 state->count = count;
159
160 tstream_gensec_readv_wrapped_next(req);
161 if (!tevent_req_is_in_progress(req)) {
162 return tevent_req_post(req, ev);
163 }
164
165 return req;
166}
167
168static 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);
173static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq);
174
175static void tstream_gensec_readv_wrapped_next(struct tevent_req *req)
176{
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;
184
185 /*
186 * copy the pending buffer first
187 */
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);
191
192 memcpy(base, tgss->read.unwrapped.data + tgss->read.ofs, len);
193
194 base += len;
195 state->vector[0].iov_base = (char *) base;
196 state->vector[0].iov_len -= len;
197
198 tgss->read.ofs += len;
199 tgss->read.left -= len;
200
201 if (state->vector[0].iov_len == 0) {
202 state->vector += 1;
203 state->count -= 1;
204 }
205
206 state->ret += len;
207 }
208
209 if (state->count == 0) {
210 tevent_req_done(req);
211 return;
212 }
213
214 data_blob_free(&tgss->read.unwrapped);
215 ZERO_STRUCT(state->wrapped);
216
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;
223 }
224 tevent_req_set_callback(subreq, tstream_gensec_readv_wrapped_done, req);
225}
226
227static 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{
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;
238
239 /* we need to get a message header */
240 vector = talloc_array(mem_ctx, struct iovec, count);
241 if (!vector) {
242 return -1;
243 }
244
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 state->wrapped.asked_for_blob = true;
251 uint32_t msg_len;
252
253 msg_len = RIVAL(state->wrapped.hdr, 0);
254
255 if (msg_len > 0x00FFFFFF) {
256 errno = EMSGSIZE;
257 return -1;
258 }
259
260 if (msg_len == 0) {
261 errno = EMSGSIZE;
262 return -1;
263 }
264
265 state->wrapped.blob = data_blob_talloc(state, NULL, msg_len);
266 if (state->wrapped.blob.data == NULL) {
267 return -1;
268 }
269
270 vector[0].iov_base = (char *)state->wrapped.blob.data;
271 vector[0].iov_len = state->wrapped.blob.length;
272 } else {
273 *_vector = NULL;
274 *_count = 0;
275 return 0;
276 }
277
278 *_vector = vector;
279 *_count = count;
280 return 0;
281}
282
283static void tstream_gensec_readv_wrapped_done(struct tevent_req *subreq)
284{
285 struct tevent_req *req =
286 tevent_req_callback_data(subreq,
287 struct tevent_req);
288 struct tstream_gensec_readv_state *state =
289 tevent_req_data(req,
290 struct tstream_gensec_readv_state);
291 struct tstream_gensec *tgss =
292 tstream_context_data(state->stream,
293 struct tstream_gensec);
294 int ret;
295 int sys_errno;
296 NTSTATUS status;
297
298 ret = tstream_readv_pdu_recv(subreq, &sys_errno);
299 TALLOC_FREE(subreq);
300 if (ret == -1) {
301 tgss->error = sys_errno;
302 tevent_req_error(req, sys_errno);
303 return;
304 }
305
306 status = gensec_unwrap(tgss->gensec_security,
307 state,
308 &state->wrapped.blob,
309 &tgss->read.unwrapped);
310 if (!NT_STATUS_IS_OK(status)) {
311 tgss->error = EIO;
312 tevent_req_error(req, tgss->error);
313 return;
314 }
315
316 data_blob_free(&state->wrapped.blob);
317
318 talloc_steal(tgss, tgss->read.unwrapped.data);
319 tgss->read.left = tgss->read.unwrapped.length;
320 tgss->read.ofs = 0;
321
322 tstream_gensec_readv_wrapped_next(req);
323}
324
325static int tstream_gensec_readv_recv(struct tevent_req *req, int *perrno)
326{
327 struct tstream_gensec_readv_state *state =
328 tevent_req_data(req,
329 struct tstream_gensec_readv_state);
330 int ret;
331
332 ret = tsocket_simple_int_recv(req, perrno);
333 if (ret == 0) {
334 ret = state->ret;
335 }
336
337 tevent_req_received(req);
338 return ret;
339}
340
341struct tstream_gensec_writev_state {
342 struct tevent_context *ev;
343 struct tstream_context *stream;
344
345 struct iovec *vector;
346 int count;
347
348 struct {
349 off_t ofs;
350 size_t left;
351 DATA_BLOB blob;
352 } unwrapped;
353
354 struct {
355 uint8_t hdr[4];
356 DATA_BLOB blob;
357 struct iovec iov[2];
358 } wrapped;
359
360 int ret;
361};
362
363static void tstream_gensec_writev_wrapped_next(struct tevent_req *req);
364
365static struct tevent_req *tstream_gensec_writev_send(TALLOC_CTX *mem_ctx,
366 struct tevent_context *ev,
367 struct tstream_context *stream,
368 const struct iovec *vector,
369 size_t count)
370{
371 struct tstream_gensec *tgss =
372 tstream_context_data(stream,
373 struct tstream_gensec);
374 struct tevent_req *req;
375 struct tstream_gensec_writev_state *state;
376 int i;
377 int total;
378 int chunk;
379
380 req = tevent_req_create(mem_ctx, &state,
381 struct tstream_gensec_writev_state);
382 if (req == NULL) {
383 return NULL;
384 }
385
386 if (tgss->error != 0) {
387 tevent_req_error(req, tgss->error);
388 return tevent_req_post(req, ev);
389 }
390
391 state->ev = ev;
392 state->stream = stream;
393 state->ret = 0;
394
395 /*
396 * we make a copy of the vector so we can change the structure
397 */
398 state->vector = talloc_array(state, struct iovec, count);
399 if (tevent_req_nomem(state->vector, req)) {
400 return tevent_req_post(req, ev);
401 }
402 memcpy(state->vector, vector, sizeof(struct iovec) * count);
403 state->count = count;
404
405 total = 0;
406 for (i = 0; i < count; i++) {
407 /*
408 * the generic tstream code makes sure that
409 * this never wraps.
410 */
411 total += vector[i].iov_len;
412 }
413
414 /*
415 * We may need to send data in chunks.
416 */
417 chunk = MIN(total, tgss->write.max_unwrapped_size);
418
419 state->unwrapped.blob = data_blob_talloc(state, NULL, chunk);
420 if (tevent_req_nomem(state->unwrapped.blob.data, req)) {
421 return tevent_req_post(req, ev);
422 }
423
424 tstream_gensec_writev_wrapped_next(req);
425 if (!tevent_req_is_in_progress(req)) {
426 return tevent_req_post(req, ev);
427 }
428
429 return req;
430}
431
432static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq);
433
434static void tstream_gensec_writev_wrapped_next(struct tevent_req *req)
435{
436 struct tstream_gensec_writev_state *state =
437 tevent_req_data(req,
438 struct tstream_gensec_writev_state);
439 struct tstream_gensec *tgss =
440 tstream_context_data(state->stream,
441 struct tstream_gensec);
442 struct tevent_req *subreq;
443 NTSTATUS status;
444
445 data_blob_free(&state->wrapped.blob);
446
447 state->unwrapped.left = state->unwrapped.blob.length;
448 state->unwrapped.ofs = 0;
449
450 /*
451 * first fill our buffer
452 */
453 while (state->unwrapped.left > 0 && state->count > 0) {
454 uint8_t *base = (uint8_t *)state->vector[0].iov_base;
455 size_t len = MIN(state->unwrapped.left, state->vector[0].iov_len);
456
457 memcpy(state->unwrapped.blob.data + state->unwrapped.ofs, base, len);
458
459 base += len;
460 state->vector[0].iov_base = (char *) base;
461 state->vector[0].iov_len -= len;
462
463 state->unwrapped.ofs += len;
464 state->unwrapped.left -= len;
465
466 if (state->vector[0].iov_len == 0) {
467 state->vector += 1;
468 state->count -= 1;
469 }
470
471 state->ret += len;
472 }
473
474 if (state->unwrapped.ofs == 0) {
475 tevent_req_done(req);
476 return;
477 }
478
479 state->unwrapped.blob.length = state->unwrapped.ofs;
480
481 status = gensec_wrap(tgss->gensec_security,
482 state,
483 &state->unwrapped.blob,
484 &state->wrapped.blob);
485 if (!NT_STATUS_IS_OK(status)) {
486 tgss->error = EIO;
487 tevent_req_error(req, tgss->error);
488 return;
489 }
490
491 RSIVAL(state->wrapped.hdr, 0, state->wrapped.blob.length);
492
493 state->wrapped.iov[0].iov_base = (void *)state->wrapped.hdr;
494 state->wrapped.iov[0].iov_len = sizeof(state->wrapped.hdr);
495 state->wrapped.iov[1].iov_base = (void *)state->wrapped.blob.data;
496 state->wrapped.iov[1].iov_len = state->wrapped.blob.length;
497
498 subreq = tstream_writev_send(state, state->ev,
499 tgss->plain_stream,
500 state->wrapped.iov, 2);
501 if (tevent_req_nomem(subreq, req)) {
502 return;
503 }
504 tevent_req_set_callback(subreq,
505 tstream_gensec_writev_wrapped_done,
506 req);
507}
508
509static void tstream_gensec_writev_wrapped_done(struct tevent_req *subreq)
510{
511 struct tevent_req *req =
512 tevent_req_callback_data(subreq,
513 struct tevent_req);
514 struct tstream_gensec_writev_state *state =
515 tevent_req_data(req,
516 struct tstream_gensec_writev_state);
517 struct tstream_gensec *tgss =
518 tstream_context_data(state->stream,
519 struct tstream_gensec);
520 int sys_errno;
521 int ret;
522
523 ret = tstream_writev_recv(subreq, &sys_errno);
524 TALLOC_FREE(subreq);
525 if (ret == -1) {
526 tgss->error = sys_errno;
527 tevent_req_error(req, sys_errno);
528 return;
529 }
530
531 tstream_gensec_writev_wrapped_next(req);
532}
533
534static int tstream_gensec_writev_recv(struct tevent_req *req,
535 int *perrno)
536{
537 struct tstream_gensec_writev_state *state =
538 tevent_req_data(req,
539 struct tstream_gensec_writev_state);
540 int ret;
541
542 ret = tsocket_simple_int_recv(req, perrno);
543 if (ret == 0) {
544 ret = state->ret;
545 }
546
547 tevent_req_received(req);
548 return ret;
549}
550
551struct tstream_gensec_disconnect_state {
552 uint8_t _dummy;
553};
554
555static struct tevent_req *tstream_gensec_disconnect_send(TALLOC_CTX *mem_ctx,
556 struct tevent_context *ev,
557 struct tstream_context *stream)
558{
559 struct tstream_gensec *tgss =
560 tstream_context_data(stream,
561 struct tstream_gensec);
562 struct tevent_req *req;
563 struct tstream_gensec_disconnect_state *state;
564
565 req = tevent_req_create(mem_ctx, &state,
566 struct tstream_gensec_disconnect_state);
567 if (req == NULL) {
568 return NULL;
569 }
570
571 if (tgss->error != 0) {
572 tevent_req_error(req, tgss->error);
573 return tevent_req_post(req, ev);
574 }
575
576 /*
577 * The caller is responsible to do the real disconnect
578 * on the plain stream!
579 */
580 tgss->plain_stream = NULL;
581 tgss->error = ENOTCONN;
582
583 tevent_req_done(req);
584 return tevent_req_post(req, ev);
585}
586
587static int tstream_gensec_disconnect_recv(struct tevent_req *req,
588 int *perrno)
589{
590 int ret;
591
592 ret = tsocket_simple_int_recv(req, perrno);
593
594 tevent_req_received(req);
595 return ret;
596}
597
598static const struct tstream_context_ops tstream_gensec_ops = {
599 .name = "gensec",
600
601 .pending_bytes = tstream_gensec_pending_bytes,
602
603 .readv_send = tstream_gensec_readv_send,
604 .readv_recv = tstream_gensec_readv_recv,
605
606 .writev_send = tstream_gensec_writev_send,
607 .writev_recv = tstream_gensec_writev_recv,
608
609 .disconnect_send = tstream_gensec_disconnect_send,
610 .disconnect_recv = tstream_gensec_disconnect_recv,
611};
Note: See TracBrowser for help on using the repository browser.