| 1 | /* 
 | 
|---|
| 2 |    Unix SMB/CIFS implementation.
 | 
|---|
| 3 | 
 | 
|---|
| 4 |    GENSEC socket interface
 | 
|---|
| 5 | 
 | 
|---|
| 6 |    Copyright (C) Andrew Bartlett 2006
 | 
|---|
| 7 |  
 | 
|---|
| 8 |    This program is free software; you can redistribute it and/or modify
 | 
|---|
| 9 |    it under the terms of the GNU General Public License as published by
 | 
|---|
| 10 |    the Free Software Foundation; either version 3 of the License, or
 | 
|---|
| 11 |    (at your option) any later version.
 | 
|---|
| 12 |    
 | 
|---|
| 13 |    This program is distributed in the hope that it will be useful,
 | 
|---|
| 14 |    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
|---|
| 15 |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
|---|
| 16 |    GNU General Public License for more details.
 | 
|---|
| 17 |    
 | 
|---|
| 18 |    You should have received a copy of the GNU General Public License
 | 
|---|
| 19 |    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
|---|
| 20 | */
 | 
|---|
| 21 | 
 | 
|---|
| 22 | #include "includes.h"
 | 
|---|
| 23 | #include "lib/events/events.h"
 | 
|---|
| 24 | #include "lib/socket/socket.h"
 | 
|---|
| 25 | #include "lib/stream/packet.h"
 | 
|---|
| 26 | #include "auth/gensec/gensec.h"
 | 
|---|
| 27 | #include "auth/gensec/gensec_proto.h"
 | 
|---|
| 28 | 
 | 
|---|
| 29 | static const struct socket_ops gensec_socket_ops;
 | 
|---|
| 30 | 
 | 
|---|
| 31 | struct gensec_socket {
 | 
|---|
| 32 |         struct gensec_security *gensec_security;
 | 
|---|
| 33 |         struct socket_context *socket;
 | 
|---|
| 34 |         struct tevent_context *ev;
 | 
|---|
| 35 |         struct packet_context *packet;
 | 
|---|
| 36 |         DATA_BLOB read_buffer;  /* SASL packets are turned into liniarlised data here, for reading */
 | 
|---|
| 37 |         size_t orig_send_len;
 | 
|---|
| 38 |         bool eof;
 | 
|---|
| 39 |         NTSTATUS error;
 | 
|---|
| 40 |         bool interrupted;
 | 
|---|
| 41 |         void (*recv_handler)(void *, uint16_t);
 | 
|---|
| 42 |         void *recv_private;
 | 
|---|
| 43 |         int in_extra_read;
 | 
|---|
| 44 |         bool wrap; /* Should we be wrapping on this socket at all? */
 | 
|---|
| 45 | };
 | 
|---|
| 46 | 
 | 
|---|
| 47 | static NTSTATUS gensec_socket_init_fn(struct socket_context *sock)
 | 
|---|
| 48 | {
 | 
|---|
| 49 |         switch (sock->type) {
 | 
|---|
| 50 |         case SOCKET_TYPE_STREAM:
 | 
|---|
| 51 |                 break;
 | 
|---|
| 52 |         default:
 | 
|---|
| 53 |                 return NT_STATUS_INVALID_PARAMETER;
 | 
|---|
| 54 |         }
 | 
|---|
| 55 | 
 | 
|---|
| 56 |         sock->backend_name = "gensec";
 | 
|---|
| 57 | 
 | 
|---|
| 58 |         return NT_STATUS_OK;
 | 
|---|
| 59 | }
 | 
|---|
| 60 | 
 | 
|---|
| 61 | /* These functions are for use here only (public because SPNEGO must
 | 
|---|
| 62 |  * use them for recursion) */
 | 
|---|
| 63 | _PUBLIC_ NTSTATUS gensec_wrap_packets(struct gensec_security *gensec_security, 
 | 
|---|
| 64 |                              TALLOC_CTX *mem_ctx, 
 | 
|---|
| 65 |                              const DATA_BLOB *in, 
 | 
|---|
| 66 |                              DATA_BLOB *out,
 | 
|---|
| 67 |                              size_t *len_processed) 
 | 
|---|
| 68 | {
 | 
|---|
| 69 |         if (!gensec_security->ops->wrap_packets) {
 | 
|---|
| 70 |                 NTSTATUS nt_status;
 | 
|---|
| 71 |                 size_t max_input_size;
 | 
|---|
| 72 |                 DATA_BLOB unwrapped, wrapped;
 | 
|---|
| 73 |                 max_input_size = gensec_max_input_size(gensec_security);
 | 
|---|
| 74 |                 unwrapped = data_blob_const(in->data, MIN(max_input_size, (size_t)in->length));
 | 
|---|
| 75 |                 
 | 
|---|
| 76 |                 nt_status = gensec_wrap(gensec_security, 
 | 
|---|
| 77 |                                         mem_ctx,
 | 
|---|
| 78 |                                         &unwrapped, &wrapped);
 | 
|---|
| 79 |                 if (!NT_STATUS_IS_OK(nt_status)) {
 | 
|---|
| 80 |                         return nt_status;
 | 
|---|
| 81 |                 }
 | 
|---|
| 82 |                 
 | 
|---|
| 83 |                 *out = data_blob_talloc(mem_ctx, NULL, 4);
 | 
|---|
| 84 |                 if (!out->data) {
 | 
|---|
| 85 |                         return NT_STATUS_NO_MEMORY;
 | 
|---|
| 86 |                 }
 | 
|---|
| 87 |                 RSIVAL(out->data, 0, wrapped.length);
 | 
|---|
| 88 |                 
 | 
|---|
| 89 |                 if (!data_blob_append(mem_ctx, out, wrapped.data, wrapped.length)) {
 | 
|---|
| 90 |                         return NT_STATUS_NO_MEMORY;
 | 
|---|
| 91 |                 }
 | 
|---|
| 92 |                 *len_processed = unwrapped.length;
 | 
|---|
| 93 |                 return NT_STATUS_OK;
 | 
|---|
| 94 |         }
 | 
|---|
| 95 |         return gensec_security->ops->wrap_packets(gensec_security, mem_ctx, in, out,
 | 
|---|
| 96 |                                                   len_processed);
 | 
|---|
| 97 | }
 | 
|---|
| 98 | 
 | 
|---|
| 99 | /* These functions are for use here only (public because SPNEGO must
 | 
|---|
| 100 |  * use them for recursion) */
 | 
|---|
| 101 | NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security, 
 | 
|---|
| 102 |                                         TALLOC_CTX *mem_ctx, 
 | 
|---|
| 103 |                                         const DATA_BLOB *in, 
 | 
|---|
| 104 |                                         DATA_BLOB *out,
 | 
|---|
| 105 |                                         size_t *len_processed) 
 | 
|---|
| 106 | {
 | 
|---|
| 107 |         if (!gensec_security->ops->unwrap_packets) {
 | 
|---|
| 108 |                 DATA_BLOB wrapped;
 | 
|---|
| 109 |                 NTSTATUS nt_status;
 | 
|---|
| 110 |                 size_t packet_size;
 | 
|---|
| 111 |                 if (in->length < 4) {
 | 
|---|
| 112 |                         /* Missing the header we already had! */
 | 
|---|
| 113 |                         DEBUG(0, ("Asked to unwrap packet of bogus length!  How did we get the short packet?!\n"));
 | 
|---|
| 114 |                         return NT_STATUS_INVALID_PARAMETER;
 | 
|---|
| 115 |                 }
 | 
|---|
| 116 |                 
 | 
|---|
| 117 |                 packet_size = RIVAL(in->data, 0);
 | 
|---|
| 118 |                 
 | 
|---|
| 119 |                 wrapped = data_blob_const(in->data + 4, packet_size);
 | 
|---|
| 120 |                 
 | 
|---|
| 121 |                 if (wrapped.length > (in->length - 4)) {
 | 
|---|
| 122 |                         DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d!  How did we get this?!\n",
 | 
|---|
| 123 |                                   (int)wrapped.length, (int)(in->length - 4)));
 | 
|---|
| 124 |                         return NT_STATUS_INTERNAL_ERROR;
 | 
|---|
| 125 |                 }
 | 
|---|
| 126 |                 
 | 
|---|
| 127 |                 nt_status = gensec_unwrap(gensec_security, 
 | 
|---|
| 128 |                                           mem_ctx,
 | 
|---|
| 129 |                                           &wrapped, out);
 | 
|---|
| 130 |                 if (!NT_STATUS_IS_OK(nt_status)) {
 | 
|---|
| 131 |                         return nt_status;
 | 
|---|
| 132 |                 }
 | 
|---|
| 133 |                 
 | 
|---|
| 134 |                 *len_processed = packet_size + 4;
 | 
|---|
| 135 |                 return nt_status;
 | 
|---|
| 136 |         }
 | 
|---|
| 137 |         return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out,
 | 
|---|
| 138 |                                                     len_processed);
 | 
|---|
| 139 | }
 | 
|---|
| 140 | 
 | 
|---|
| 141 | /* These functions are for use here only (public because SPNEGO must
 | 
|---|
| 142 |  * use them for recursion) */
 | 
|---|
| 143 | NTSTATUS gensec_packet_full_request(struct gensec_security *gensec_security,
 | 
|---|
| 144 |                                     DATA_BLOB blob, size_t *size) 
 | 
|---|
| 145 | {
 | 
|---|
| 146 |         if (gensec_security->ops->packet_full_request) {
 | 
|---|
| 147 |                 return gensec_security->ops->packet_full_request(gensec_security,
 | 
|---|
| 148 |                                                                  blob, size);
 | 
|---|
| 149 |         }
 | 
|---|
| 150 |         if (gensec_security->ops->unwrap_packets) {
 | 
|---|
| 151 |                 if (blob.length) {
 | 
|---|
| 152 |                         *size = blob.length;
 | 
|---|
| 153 |                         return NT_STATUS_OK;
 | 
|---|
| 154 |                 }
 | 
|---|
| 155 |                 return STATUS_MORE_ENTRIES;
 | 
|---|
| 156 |         }
 | 
|---|
| 157 |         return packet_full_request_u32(NULL, blob, size);
 | 
|---|
| 158 | }
 | 
|---|
| 159 | 
 | 
|---|
| 160 | static NTSTATUS gensec_socket_full_request(void *private_data, DATA_BLOB blob, size_t *size)
 | 
|---|
| 161 | {
 | 
|---|
| 162 |         struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
 | 
|---|
| 163 |         struct gensec_security *gensec_security = gensec_socket->gensec_security;
 | 
|---|
| 164 |         return gensec_packet_full_request(gensec_security, blob, size);
 | 
|---|
| 165 | }
 | 
|---|
| 166 | 
 | 
|---|
| 167 | /* Try to figure out how much data is waiting to be read */
 | 
|---|
| 168 | static NTSTATUS gensec_socket_pending(struct socket_context *sock, size_t *npending) 
 | 
|---|
| 169 | {
 | 
|---|
| 170 |         struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 171 |         if (!gensec_socket->wrap) {
 | 
|---|
| 172 |                 return socket_pending(gensec_socket->socket, npending);
 | 
|---|
| 173 |         }
 | 
|---|
| 174 | 
 | 
|---|
| 175 |         if (gensec_socket->read_buffer.length > 0) {
 | 
|---|
| 176 |                 *npending = gensec_socket->read_buffer.length;
 | 
|---|
| 177 |                 return NT_STATUS_OK;
 | 
|---|
| 178 |         }
 | 
|---|
| 179 | 
 | 
|---|
| 180 |         /* This is a lie.  We hope the decrypted data will always be
 | 
|---|
| 181 |          * less than this value, so the application just gets a short
 | 
|---|
| 182 |          * read.  Without reading and decrypting it, we can't tell.
 | 
|---|
| 183 |          * If the SASL mech does compression, then we just need to
 | 
|---|
| 184 |          * manually trigger read events */
 | 
|---|
| 185 |         return socket_pending(gensec_socket->socket, npending);
 | 
|---|
| 186 | }      
 | 
|---|
| 187 | 
 | 
|---|
| 188 | /* Note if an error occours, so we can return it up the stack */
 | 
|---|
| 189 | static void gensec_socket_error_handler(void *private_data, NTSTATUS status)
 | 
|---|
| 190 | {
 | 
|---|
| 191 |         struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
 | 
|---|
| 192 |         if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
 | 
|---|
| 193 |                 gensec_socket->eof = true;
 | 
|---|
| 194 |         } else {
 | 
|---|
| 195 |                 gensec_socket->error = status;
 | 
|---|
| 196 |         }
 | 
|---|
| 197 | }
 | 
|---|
| 198 | 
 | 
|---|
| 199 | static void gensec_socket_trigger_read(struct tevent_context *ev, 
 | 
|---|
| 200 |                                        struct tevent_timer *te, 
 | 
|---|
| 201 |                                        struct timeval t, void *private_data)
 | 
|---|
| 202 | {
 | 
|---|
| 203 |         struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
 | 
|---|
| 204 | 
 | 
|---|
| 205 |         gensec_socket->in_extra_read++;
 | 
|---|
| 206 |         gensec_socket->recv_handler(gensec_socket->recv_private, EVENT_FD_READ);
 | 
|---|
| 207 |         gensec_socket->in_extra_read--;
 | 
|---|
| 208 | 
 | 
|---|
| 209 |         /* It may well be that, having run the recv handler, we still
 | 
|---|
| 210 |          * have even more data waiting for us! 
 | 
|---|
| 211 |          */
 | 
|---|
| 212 |         if (gensec_socket->read_buffer.length && gensec_socket->recv_handler) {
 | 
|---|
| 213 |                 /* Schedule this funcion to run again */
 | 
|---|
| 214 |                 event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(), 
 | 
|---|
| 215 |                                 gensec_socket_trigger_read, gensec_socket);
 | 
|---|
| 216 |         }
 | 
|---|
| 217 | }
 | 
|---|
| 218 | 
 | 
|---|
| 219 | /* These two routines could be changed to use a circular buffer of
 | 
|---|
| 220 |  * some kind, or linked lists, or ... */
 | 
|---|
| 221 | static NTSTATUS gensec_socket_recv(struct socket_context *sock, void *buf,
 | 
|---|
| 222 |                                    size_t wantlen, size_t *nread) 
 | 
|---|
| 223 | {
 | 
|---|
| 224 |         struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 225 | 
 | 
|---|
| 226 |         if (!gensec_socket->wrap) {
 | 
|---|
| 227 |                 return socket_recv(gensec_socket->socket, buf, wantlen, nread);
 | 
|---|
| 228 |         }
 | 
|---|
| 229 | 
 | 
|---|
| 230 |         gensec_socket->error = NT_STATUS_OK;
 | 
|---|
| 231 | 
 | 
|---|
| 232 |         if (gensec_socket->read_buffer.length == 0) {
 | 
|---|
| 233 |                 /* Process any data on the socket, into the read buffer. At
 | 
|---|
| 234 |                  * this point, the socket is not available for read any
 | 
|---|
| 235 |                  * longer */
 | 
|---|
| 236 |                 packet_recv(gensec_socket->packet);
 | 
|---|
| 237 | 
 | 
|---|
| 238 |                 if (gensec_socket->eof) {
 | 
|---|
| 239 |                         *nread = 0;
 | 
|---|
| 240 |                         return NT_STATUS_OK;
 | 
|---|
| 241 |                 }
 | 
|---|
| 242 |                 
 | 
|---|
| 243 |                 if (!NT_STATUS_IS_OK(gensec_socket->error)) {
 | 
|---|
| 244 |                         return gensec_socket->error;
 | 
|---|
| 245 |                 }
 | 
|---|
| 246 | 
 | 
|---|
| 247 |                 if (gensec_socket->read_buffer.length == 0) {
 | 
|---|
| 248 |                         /* Clearly we don't have the entire SASL packet yet,
 | 
|---|
| 249 |                          * so it has not been written into the buffer */
 | 
|---|
| 250 |                         *nread = 0;
 | 
|---|
| 251 |                         return STATUS_MORE_ENTRIES;
 | 
|---|
| 252 |                 }
 | 
|---|
| 253 |         }
 | 
|---|
| 254 | 
 | 
|---|
| 255 | 
 | 
|---|
| 256 |         *nread = MIN(wantlen, gensec_socket->read_buffer.length);
 | 
|---|
| 257 |         memcpy(buf, gensec_socket->read_buffer.data, *nread);
 | 
|---|
| 258 | 
 | 
|---|
| 259 |         if (gensec_socket->read_buffer.length > *nread) {
 | 
|---|
| 260 |                 memmove(gensec_socket->read_buffer.data, 
 | 
|---|
| 261 |                         gensec_socket->read_buffer.data + *nread, 
 | 
|---|
| 262 |                         gensec_socket->read_buffer.length - *nread);
 | 
|---|
| 263 |         }
 | 
|---|
| 264 | 
 | 
|---|
| 265 |         gensec_socket->read_buffer.length -= *nread;
 | 
|---|
| 266 |         gensec_socket->read_buffer.data = talloc_realloc(gensec_socket, 
 | 
|---|
| 267 |                                                          gensec_socket->read_buffer.data, 
 | 
|---|
| 268 |                                                          uint8_t, 
 | 
|---|
| 269 |                                                          gensec_socket->read_buffer.length);
 | 
|---|
| 270 | 
 | 
|---|
| 271 |         if (gensec_socket->read_buffer.length && 
 | 
|---|
| 272 |             gensec_socket->in_extra_read == 0 && 
 | 
|---|
| 273 |             gensec_socket->recv_handler) {
 | 
|---|
| 274 |                 /* Manually call a read event, to get this moving
 | 
|---|
| 275 |                  * again (as the socket should be dry, so the normal
 | 
|---|
| 276 |                  * event handler won't trigger) */
 | 
|---|
| 277 |                 event_add_timed(gensec_socket->ev, gensec_socket, timeval_zero(), 
 | 
|---|
| 278 |                                 gensec_socket_trigger_read, gensec_socket);
 | 
|---|
| 279 |         }
 | 
|---|
| 280 | 
 | 
|---|
| 281 |         return NT_STATUS_OK;
 | 
|---|
| 282 | }
 | 
|---|
| 283 | 
 | 
|---|
| 284 | /* Completed SASL packet callback.  When we have a 'whole' SASL
 | 
|---|
| 285 |  * packet, decrypt it, and add it to the read buffer
 | 
|---|
| 286 |  *
 | 
|---|
| 287 |  * This function (and anything under it) MUST NOT call the event system
 | 
|---|
| 288 |  */
 | 
|---|
| 289 | static NTSTATUS gensec_socket_unwrap(void *private_data, DATA_BLOB blob)
 | 
|---|
| 290 | {
 | 
|---|
| 291 |         struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
 | 
|---|
| 292 |         DATA_BLOB unwrapped;
 | 
|---|
| 293 |         NTSTATUS nt_status;
 | 
|---|
| 294 |         TALLOC_CTX *mem_ctx;
 | 
|---|
| 295 |         size_t packet_size;
 | 
|---|
| 296 | 
 | 
|---|
| 297 |         mem_ctx = talloc_new(gensec_socket);
 | 
|---|
| 298 |         if (!mem_ctx) {
 | 
|---|
| 299 |                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 300 |         }
 | 
|---|
| 301 |         nt_status = gensec_unwrap_packets(gensec_socket->gensec_security, 
 | 
|---|
| 302 |                                           mem_ctx,
 | 
|---|
| 303 |                                           &blob, &unwrapped, 
 | 
|---|
| 304 |                                           &packet_size);
 | 
|---|
| 305 |         if (!NT_STATUS_IS_OK(nt_status)) {
 | 
|---|
| 306 |                 talloc_free(mem_ctx);
 | 
|---|
| 307 |                 return nt_status;
 | 
|---|
| 308 |         }
 | 
|---|
| 309 | 
 | 
|---|
| 310 |         if (packet_size != blob.length) {
 | 
|---|
| 311 |                 DEBUG(0, ("gensec_socket_unwrap: Did not consume entire packet!\n"));
 | 
|---|
| 312 |                 talloc_free(mem_ctx);
 | 
|---|
| 313 |                 return NT_STATUS_INTERNAL_ERROR;
 | 
|---|
| 314 |         }
 | 
|---|
| 315 | 
 | 
|---|
| 316 |         /* We could change this into a linked list, and have
 | 
|---|
| 317 |          * gensec_socket_recv() and gensec_socket_pending() walk the
 | 
|---|
| 318 |          * linked list */
 | 
|---|
| 319 | 
 | 
|---|
| 320 |         if (!data_blob_append(gensec_socket, &gensec_socket->read_buffer, 
 | 
|---|
| 321 |                                      unwrapped.data, unwrapped.length)) {
 | 
|---|
| 322 |                 talloc_free(mem_ctx);
 | 
|---|
| 323 |                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 324 |         }
 | 
|---|
| 325 | 
 | 
|---|
| 326 |         talloc_free(mem_ctx);
 | 
|---|
| 327 |         return NT_STATUS_OK;
 | 
|---|
| 328 | }
 | 
|---|
| 329 | 
 | 
|---|
| 330 | /* when the data is sent, we know we have not been interrupted */
 | 
|---|
| 331 | static void send_callback(void *private_data)
 | 
|---|
| 332 | {
 | 
|---|
| 333 |         struct gensec_socket *gensec_socket = talloc_get_type(private_data, struct gensec_socket);
 | 
|---|
| 334 |         gensec_socket->interrupted = false;
 | 
|---|
| 335 | }
 | 
|---|
| 336 | 
 | 
|---|
| 337 | /*
 | 
|---|
| 338 |   send data, but only as much as we allow in one packet.  
 | 
|---|
| 339 | 
 | 
|---|
| 340 |   If this returns STATUS_MORE_ENTRIES, the caller must retry with
 | 
|---|
| 341 |   exactly the same data, or a NULL blob.
 | 
|---|
| 342 | */
 | 
|---|
| 343 | static NTSTATUS gensec_socket_send(struct socket_context *sock, 
 | 
|---|
| 344 |                                    const DATA_BLOB *blob, size_t *sendlen)
 | 
|---|
| 345 | {
 | 
|---|
| 346 |         NTSTATUS nt_status;
 | 
|---|
| 347 |         struct gensec_socket *gensec_socket = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 348 |         DATA_BLOB wrapped;
 | 
|---|
| 349 |         TALLOC_CTX *mem_ctx;
 | 
|---|
| 350 | 
 | 
|---|
| 351 |         if (!gensec_socket->wrap) {
 | 
|---|
| 352 |                 return socket_send(gensec_socket->socket, blob, sendlen);
 | 
|---|
| 353 |         }
 | 
|---|
| 354 | 
 | 
|---|
| 355 |         *sendlen = 0;
 | 
|---|
| 356 | 
 | 
|---|
| 357 |         /* We have have been interupted, so the caller should be
 | 
|---|
| 358 |          * giving us the same data again.  */
 | 
|---|
| 359 |         if (gensec_socket->interrupted) {
 | 
|---|
| 360 |                 packet_queue_run(gensec_socket->packet);
 | 
|---|
| 361 | 
 | 
|---|
| 362 |                 if (!NT_STATUS_IS_OK(gensec_socket->error)) {
 | 
|---|
| 363 |                         return gensec_socket->error;
 | 
|---|
| 364 |                 } else if (gensec_socket->interrupted) {
 | 
|---|
| 365 |                         return STATUS_MORE_ENTRIES;
 | 
|---|
| 366 |                 } else {
 | 
|---|
| 367 |                         *sendlen = gensec_socket->orig_send_len;
 | 
|---|
| 368 |                         gensec_socket->orig_send_len = 0;
 | 
|---|
| 369 |                         return NT_STATUS_OK;
 | 
|---|
| 370 |                 }
 | 
|---|
| 371 |         }
 | 
|---|
| 372 | 
 | 
|---|
| 373 |         mem_ctx = talloc_new(gensec_socket);
 | 
|---|
| 374 |         if (!mem_ctx) {
 | 
|---|
| 375 |                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 376 |         }
 | 
|---|
| 377 | 
 | 
|---|
| 378 |         nt_status = gensec_wrap_packets(gensec_socket->gensec_security, 
 | 
|---|
| 379 |                                         mem_ctx,
 | 
|---|
| 380 |                                         blob, &wrapped, 
 | 
|---|
| 381 |                                         &gensec_socket->orig_send_len);
 | 
|---|
| 382 |         if (!NT_STATUS_IS_OK(nt_status)) {
 | 
|---|
| 383 |                 talloc_free(mem_ctx);
 | 
|---|
| 384 |                 return nt_status;
 | 
|---|
| 385 |         }
 | 
|---|
| 386 |         
 | 
|---|
| 387 |         gensec_socket->interrupted = true;
 | 
|---|
| 388 |         gensec_socket->error = NT_STATUS_OK;
 | 
|---|
| 389 | 
 | 
|---|
| 390 |         nt_status = packet_send_callback(gensec_socket->packet, 
 | 
|---|
| 391 |                                          wrapped,
 | 
|---|
| 392 |                                          send_callback, gensec_socket);
 | 
|---|
| 393 | 
 | 
|---|
| 394 |         talloc_free(mem_ctx);
 | 
|---|
| 395 | 
 | 
|---|
| 396 |         packet_queue_run(gensec_socket->packet);
 | 
|---|
| 397 | 
 | 
|---|
| 398 |         if (!NT_STATUS_IS_OK(gensec_socket->error)) {
 | 
|---|
| 399 |                 return gensec_socket->error;
 | 
|---|
| 400 |         } else if (gensec_socket->interrupted) {
 | 
|---|
| 401 |                 return STATUS_MORE_ENTRIES;
 | 
|---|
| 402 |         } else {
 | 
|---|
| 403 |                 *sendlen = gensec_socket->orig_send_len;
 | 
|---|
| 404 |                 gensec_socket->orig_send_len = 0;
 | 
|---|
| 405 |                 return NT_STATUS_OK;
 | 
|---|
| 406 |         }
 | 
|---|
| 407 | }
 | 
|---|
| 408 | 
 | 
|---|
| 409 | /* Turn a normal socket into a potentially GENSEC wrapped socket */
 | 
|---|
| 410 | /* CAREFUL: this function will steal 'current_socket' */
 | 
|---|
| 411 | 
 | 
|---|
| 412 | NTSTATUS gensec_socket_init(struct gensec_security *gensec_security,
 | 
|---|
| 413 |                             TALLOC_CTX *mem_ctx,
 | 
|---|
| 414 |                             struct socket_context *current_socket,
 | 
|---|
| 415 |                             struct tevent_context *ev,
 | 
|---|
| 416 |                             void (*recv_handler)(void *, uint16_t),
 | 
|---|
| 417 |                             void *recv_private,
 | 
|---|
| 418 |                             struct socket_context **new_socket)
 | 
|---|
| 419 | {
 | 
|---|
| 420 |         struct gensec_socket *gensec_socket;
 | 
|---|
| 421 |         struct socket_context *new_sock;
 | 
|---|
| 422 |         NTSTATUS nt_status;
 | 
|---|
| 423 | 
 | 
|---|
| 424 |         nt_status = socket_create_with_ops(mem_ctx, &gensec_socket_ops, &new_sock, 
 | 
|---|
| 425 |                                            SOCKET_TYPE_STREAM, current_socket->flags | SOCKET_FLAG_ENCRYPT);
 | 
|---|
| 426 |         if (!NT_STATUS_IS_OK(nt_status)) {
 | 
|---|
| 427 |                 *new_socket = NULL;
 | 
|---|
| 428 |                 return nt_status;
 | 
|---|
| 429 |         }
 | 
|---|
| 430 | 
 | 
|---|
| 431 |         new_sock->state = current_socket->state;
 | 
|---|
| 432 | 
 | 
|---|
| 433 |         gensec_socket = talloc(new_sock, struct gensec_socket);
 | 
|---|
| 434 |         if (gensec_socket == NULL) {
 | 
|---|
| 435 |                 *new_socket = NULL;
 | 
|---|
| 436 |                 talloc_free(new_sock);
 | 
|---|
| 437 |                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 438 |         }
 | 
|---|
| 439 | 
 | 
|---|
| 440 |         new_sock->private_data       = gensec_socket;
 | 
|---|
| 441 |         gensec_socket->socket        = current_socket;
 | 
|---|
| 442 | 
 | 
|---|
| 443 |         /* Nothing to do here, if we are not actually wrapping on this socket */
 | 
|---|
| 444 |         if (!gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL) &&
 | 
|---|
| 445 |             !gensec_have_feature(gensec_security, GENSEC_FEATURE_SIGN)) {
 | 
|---|
| 446 |                 
 | 
|---|
| 447 |                 gensec_socket->wrap = false;
 | 
|---|
| 448 |                 talloc_steal(gensec_socket, current_socket);
 | 
|---|
| 449 |                 *new_socket = new_sock;
 | 
|---|
| 450 |                 return NT_STATUS_OK;
 | 
|---|
| 451 |         }
 | 
|---|
| 452 | 
 | 
|---|
| 453 |         gensec_socket->gensec_security = gensec_security;
 | 
|---|
| 454 | 
 | 
|---|
| 455 |         gensec_socket->wrap          = true;
 | 
|---|
| 456 |         gensec_socket->eof           = false;
 | 
|---|
| 457 |         gensec_socket->error         = NT_STATUS_OK;
 | 
|---|
| 458 |         gensec_socket->interrupted   = false;
 | 
|---|
| 459 |         gensec_socket->in_extra_read = 0;
 | 
|---|
| 460 | 
 | 
|---|
| 461 |         gensec_socket->read_buffer   = data_blob(NULL, 0);
 | 
|---|
| 462 | 
 | 
|---|
| 463 |         gensec_socket->recv_handler  = recv_handler;
 | 
|---|
| 464 |         gensec_socket->recv_private  = recv_private;
 | 
|---|
| 465 |         gensec_socket->ev            = ev;
 | 
|---|
| 466 | 
 | 
|---|
| 467 |         gensec_socket->packet = packet_init(gensec_socket);
 | 
|---|
| 468 |         if (gensec_socket->packet == NULL) {
 | 
|---|
| 469 |                 *new_socket = NULL;
 | 
|---|
| 470 |                 talloc_free(new_sock);
 | 
|---|
| 471 |                 return NT_STATUS_NO_MEMORY;
 | 
|---|
| 472 |         }
 | 
|---|
| 473 | 
 | 
|---|
| 474 |         packet_set_private(gensec_socket->packet, gensec_socket);
 | 
|---|
| 475 |         packet_set_socket(gensec_socket->packet, gensec_socket->socket);
 | 
|---|
| 476 |         packet_set_callback(gensec_socket->packet, gensec_socket_unwrap);
 | 
|---|
| 477 |         packet_set_full_request(gensec_socket->packet, gensec_socket_full_request);
 | 
|---|
| 478 |         packet_set_error_handler(gensec_socket->packet, gensec_socket_error_handler);
 | 
|---|
| 479 |         packet_set_serialise(gensec_socket->packet);
 | 
|---|
| 480 | 
 | 
|---|
| 481 |         /* TODO: full-request that knows about maximum packet size */
 | 
|---|
| 482 | 
 | 
|---|
| 483 |         talloc_steal(gensec_socket, current_socket);
 | 
|---|
| 484 |         *new_socket = new_sock;
 | 
|---|
| 485 |         return NT_STATUS_OK;
 | 
|---|
| 486 | }
 | 
|---|
| 487 | 
 | 
|---|
| 488 | 
 | 
|---|
| 489 | static NTSTATUS gensec_socket_set_option(struct socket_context *sock, const char *option, const char *val)
 | 
|---|
| 490 | {
 | 
|---|
| 491 |         set_socket_options(socket_get_fd(sock), option);
 | 
|---|
| 492 |         return NT_STATUS_OK;
 | 
|---|
| 493 | }
 | 
|---|
| 494 | 
 | 
|---|
| 495 | static char *gensec_socket_get_peer_name(struct socket_context *sock, TALLOC_CTX *mem_ctx)
 | 
|---|
| 496 | {
 | 
|---|
| 497 |         struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 498 |         return socket_get_peer_name(gensec->socket, mem_ctx);
 | 
|---|
| 499 | }
 | 
|---|
| 500 | 
 | 
|---|
| 501 | static struct socket_address *gensec_socket_get_peer_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
 | 
|---|
| 502 | {
 | 
|---|
| 503 |         struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 504 |         return socket_get_peer_addr(gensec->socket, mem_ctx);
 | 
|---|
| 505 | }
 | 
|---|
| 506 | 
 | 
|---|
| 507 | static struct socket_address *gensec_socket_get_my_addr(struct socket_context *sock, TALLOC_CTX *mem_ctx)
 | 
|---|
| 508 | {
 | 
|---|
| 509 |         struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 510 |         return socket_get_my_addr(gensec->socket, mem_ctx);
 | 
|---|
| 511 | }
 | 
|---|
| 512 | 
 | 
|---|
| 513 | static int gensec_socket_get_fd(struct socket_context *sock)
 | 
|---|
| 514 | {
 | 
|---|
| 515 |         struct gensec_socket *gensec = talloc_get_type(sock->private_data, struct gensec_socket);
 | 
|---|
| 516 |         return socket_get_fd(gensec->socket);
 | 
|---|
| 517 | }
 | 
|---|
| 518 | 
 | 
|---|
| 519 | static const struct socket_ops gensec_socket_ops = {
 | 
|---|
| 520 |         .name                   = "gensec",
 | 
|---|
| 521 |         .fn_init                = gensec_socket_init_fn,
 | 
|---|
| 522 |         .fn_recv                = gensec_socket_recv,
 | 
|---|
| 523 |         .fn_send                = gensec_socket_send,
 | 
|---|
| 524 |         .fn_pending             = gensec_socket_pending,
 | 
|---|
| 525 | 
 | 
|---|
| 526 |         .fn_set_option          = gensec_socket_set_option,
 | 
|---|
| 527 | 
 | 
|---|
| 528 |         .fn_get_peer_name       = gensec_socket_get_peer_name,
 | 
|---|
| 529 |         .fn_get_peer_addr       = gensec_socket_get_peer_addr,
 | 
|---|
| 530 |         .fn_get_my_addr         = gensec_socket_get_my_addr,
 | 
|---|
| 531 |         .fn_get_fd              = gensec_socket_get_fd
 | 
|---|
| 532 | };
 | 
|---|
| 533 | 
 | 
|---|