summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimo Sorce <ssorce@redhat.com>2010-05-19 17:02:52 -0400
committerStefan Metzmacher <metze@samba.org>2010-05-26 09:23:47 +0200
commitb7159e6ffd9f2c907301ce4d3e6ba3ff01c8a1f6 (patch)
treeafc12dc4e7584b0649fe4a06c0385cd4da9cd92e
parent9e194cd673a0d0a56cd064bab38cbf30b468999d (diff)
downloadsamba-b7159e6ffd9f2c907301ce4d3e6ba3ff01c8a1f6.tar.gz
samba-b7159e6ffd9f2c907301ce4d3e6ba3ff01c8a1f6.tar.bz2
samba-b7159e6ffd9f2c907301ce4d3e6ba3ff01c8a1f6.zip
named_pipe_auth: implement tstream_npa_accept_existing_send/recv
Pair-programmed-with: Stefan Metzmacher <metze@samba.org>
-rw-r--r--libcli/named_pipe_auth/npa_tstream.c455
-rw-r--r--libcli/named_pipe_auth/npa_tstream.h66
2 files changed, 521 insertions, 0 deletions
diff --git a/libcli/named_pipe_auth/npa_tstream.c b/libcli/named_pipe_auth/npa_tstream.c
index 20228a2df2..0834c7dda6 100644
--- a/libcli/named_pipe_auth/npa_tstream.c
+++ b/libcli/named_pipe_auth/npa_tstream.c
@@ -1077,3 +1077,458 @@ int _tstream_npa_existing_socket(TALLOC_CTX *mem_ctx,
return 0;
}
+
+struct tstream_npa_accept_state {
+ struct tevent_context *ev;
+ struct tstream_context *plain;
+ uint16_t file_type;
+ uint16_t device_state;
+ uint64_t alloc_size;
+
+ DATA_BLOB npa_blob;
+ struct iovec out_iov;
+
+ /* results */
+ NTSTATUS accept_status;
+ struct tsocket_address *client;
+ char *client_name;
+ struct tsocket_address *server;
+ char *server_name;
+ struct netr_SamInfo3 *info3;
+ DATA_BLOB session_key;
+ DATA_BLOB delegated_creds;
+};
+
+static int tstream_npa_accept_next_vector(struct tstream_context *unix_stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *_count);
+static void tstream_npa_accept_existing_reply(struct tevent_req *subreq);
+static void tstream_npa_accept_existing_done(struct tevent_req *subreq);
+
+struct tevent_req *tstream_npa_accept_existing_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain,
+ uint16_t file_type,
+ uint16_t device_state,
+ uint64_t allocation_size)
+{
+ struct tstream_npa_accept_state *state;
+ struct tevent_req *req, *subreq;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct tstream_npa_accept_state);
+ if (req == NULL) {
+ return NULL;
+ }
+
+ switch (file_type) {
+ case FILE_TYPE_BYTE_MODE_PIPE:
+ break;
+ case FILE_TYPE_MESSAGE_MODE_PIPE:
+ break;
+ default:
+ tevent_req_error(req, EINVAL);
+ goto post;
+ }
+
+ ZERO_STRUCTP(state);
+
+ state->ev = ev;
+ state->plain = plain;
+ state->file_type = file_type;
+ state->device_state = device_state;
+ state->alloc_size = allocation_size;
+
+ /*
+ * The named pipe pdu's have the length as 8 byte (initial_read_size),
+ * named_pipe_full_request provides the pdu length then.
+ */
+ subreq = tstream_readv_pdu_send(state, ev, plain,
+ tstream_npa_accept_next_vector,
+ state);
+ if (tevent_req_nomem(subreq, req)) {
+ goto post;
+ }
+
+ tevent_req_set_callback(subreq,
+ tstream_npa_accept_existing_reply, req);
+
+ return req;
+
+post:
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static int tstream_npa_accept_next_vector(struct tstream_context *unix_stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *_count)
+{
+ struct tstream_npa_accept_state *state =
+ talloc_get_type_abort(private_data,
+ struct tstream_npa_accept_state);
+ struct iovec *vector;
+ size_t count;
+ off_t ofs = 0;
+
+ if (state->npa_blob.length == 0) {
+ state->npa_blob = data_blob_talloc(state, NULL, 4);
+ if (!state->npa_blob.data) {
+ return -1;
+ }
+ } else if (state->npa_blob.length == 4) {
+ uint32_t msg_len;
+
+ ofs = 4;
+
+ msg_len = RIVAL(state->npa_blob.data, 0);
+
+ if (msg_len > 0x00FFFFFF) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ if (msg_len == 0) {
+ errno = EMSGSIZE;
+ return -1;
+ }
+
+ msg_len += ofs;
+
+ state->npa_blob.data = talloc_realloc(state,
+ state->npa_blob.data,
+ uint8_t, msg_len);
+ if (!state->npa_blob.data) {
+ return -1;
+ }
+ state->npa_blob.length = msg_len;
+ } else {
+ if (memcmp(&state->npa_blob.data[4],
+ NAMED_PIPE_AUTH_MAGIC, 4) != 0) {
+ DEBUG(0, ("Wrong protocol\n"));
+#if defined(EPROTONOSUPPORT)
+ errno = EPROTONOSUPPORT;
+#elif defined(EPROTO)
+ errno = EPROTO;
+#else
+ errno = EINVAL;
+#endif
+ return -1;
+ }
+ *_vector = NULL;
+ *_count = 0;
+ return 0;
+ }
+
+ /* we need to get a message header */
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+ vector[0].iov_base = state->npa_blob.data + ofs;
+ vector[0].iov_len = state->npa_blob.length - ofs;
+ count = 1;
+
+ *_vector = vector;
+ *_count = count;
+ return 0;
+}
+
+static void tstream_npa_accept_existing_reply(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct tstream_npa_accept_state *state =
+ tevent_req_data(req, struct tstream_npa_accept_state);
+ struct named_pipe_auth_req *pipe_request;
+ struct named_pipe_auth_rep pipe_reply;
+ struct named_pipe_auth_req_info3 i3;
+ enum ndr_err_code ndr_err;
+ DATA_BLOB out;
+ int sys_errno;
+ int ret;
+
+ ret = tstream_readv_pdu_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+
+ DEBUG(10, ("Received packet of length %lu\n",
+ (long)state->npa_blob.length));
+ dump_data(11, state->npa_blob.data, state->npa_blob.length);
+
+ ZERO_STRUCT(pipe_reply);
+ pipe_reply.level = 0;
+ pipe_reply.status = NT_STATUS_INTERNAL_ERROR;
+ /*
+ * TODO: check it's a root (uid == 0) pipe
+ */
+
+ pipe_request = talloc(state, struct named_pipe_auth_req);
+ if (!pipe_request) {
+ DEBUG(0, ("Out of memory!\n"));
+ goto reply;
+ }
+
+ /* parse the passed credentials */
+ ndr_err = ndr_pull_struct_blob_all(
+ &state->npa_blob, pipe_request, pipe_request,
+ (ndr_pull_flags_fn_t)ndr_pull_named_pipe_auth_req);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ pipe_reply.status = ndr_map_error2ntstatus(ndr_err);
+ DEBUG(2, ("Could not unmarshall named_pipe_auth_req: %s\n",
+ nt_errstr(pipe_reply.status)));
+ goto reply;
+ }
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(named_pipe_auth_req, pipe_request);
+ }
+
+ ZERO_STRUCT(i3);
+
+ switch (pipe_request->level) {
+ case 0:
+ pipe_reply.level = 0;
+ pipe_reply.status = NT_STATUS_OK;
+
+ /* we need to force byte mode in this level */
+ state->file_type = FILE_TYPE_BYTE_MODE_PIPE;
+ break;
+
+ case 1:
+ pipe_reply.level = 1;
+ pipe_reply.status = NT_STATUS_OK;
+
+ /* We must copy net3_SamInfo3, so that
+ * info3 is an actual talloc pointer, then we steal
+ * pipe_request on info3 so that all the allocated memory
+ * pointed by the structrue members is preserved */
+ state->info3 = (struct netr_SamInfo3 *)talloc_memdup(state,
+ &pipe_request->info.info1,
+ sizeof(struct netr_SamInfo3));
+ if (!state->info3) {
+ pipe_reply.status = NT_STATUS_NO_MEMORY;
+ DEBUG(0, ("Out of memory!\n"));
+ goto reply;
+ }
+ talloc_move(state->info3, &pipe_request);
+
+ /* we need to force byte mode in this level */
+ state->file_type = FILE_TYPE_BYTE_MODE_PIPE;
+ break;
+
+ case 2:
+ pipe_reply.level = 2;
+ pipe_reply.status = NT_STATUS_OK;
+ pipe_reply.info.info2.file_type = state->file_type;
+ pipe_reply.info.info2.device_state = state->device_state;
+ pipe_reply.info.info2.allocation_size = state->alloc_size;
+
+ i3.client_name = pipe_request->info.info2.client_name;
+ i3.client_addr = pipe_request->info.info2.client_addr;
+ i3.client_port = pipe_request->info.info2.client_port;
+ i3.server_name = pipe_request->info.info2.server_name;
+ i3.server_addr = pipe_request->info.info2.server_addr;
+ i3.server_port = pipe_request->info.info2.server_port;
+ i3.sam_info3 = pipe_request->info.info2.sam_info3;
+ i3.session_key_length =
+ pipe_request->info.info2.session_key_length;
+ i3.session_key = pipe_request->info.info2.session_key;
+ break;
+
+ case 3:
+ pipe_reply.level = 3;
+ pipe_reply.status = NT_STATUS_OK;
+ pipe_reply.info.info3.file_type = state->file_type;
+ pipe_reply.info.info3.device_state = state->device_state;
+ pipe_reply.info.info3.allocation_size = state->alloc_size;
+
+ i3 = pipe_request->info.info3;
+ break;
+
+ default:
+ DEBUG(0, ("Unknown level %u\n", pipe_request->level));
+ pipe_reply.level = 0;
+ pipe_reply.status = NT_STATUS_INVALID_LEVEL;
+ goto reply;
+ }
+
+ if (pipe_reply.level >=2) {
+
+ if (i3.server_addr == NULL) {
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ DEBUG(2, ("Missing server address\n"));
+ goto reply;
+ }
+ if (i3.client_addr == NULL) {
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ DEBUG(2, ("Missing client address\n"));
+ goto reply;
+ }
+
+ state->server_name = discard_const_p(char,
+ talloc_move(state, &i3.server_name));
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ i3.server_addr,
+ i3.server_port,
+ &state->server);
+ if (ret != 0) {
+ DEBUG(2, ("Invalid server address[%s:%u] - %s\n",
+ i3.server_addr, i3.server_port,
+ strerror(errno)));
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ goto reply;
+ }
+
+ state->client_name = discard_const_p(char,
+ talloc_move(state, &i3.client_name));
+ ret = tsocket_address_inet_from_strings(state, "ip",
+ i3.client_addr,
+ i3.client_port,
+ &state->client);
+ if (ret != 0) {
+ DEBUG(2, ("Invalid server address[%s:%u] - %s\n",
+ i3.client_addr, i3.client_port,
+ strerror(errno)));
+ pipe_reply.status = NT_STATUS_INVALID_ADDRESS;
+ goto reply;
+ }
+
+ state->info3 = talloc_move(state, &i3.sam_info3);
+ state->session_key.data = talloc_move(state, &i3.session_key);
+ state->session_key.length = i3.session_key_length;
+ }
+
+ if (pipe_reply.level >= 3) {
+ state->delegated_creds.data =
+ talloc_move(state, &i3.gssapi_delegated_creds);
+ state->delegated_creds.length =
+ i3.gssapi_delegated_creds_length;
+ }
+
+reply:
+ /* create the output */
+ ndr_err = ndr_push_struct_blob(&out, state, &pipe_reply,
+ (ndr_push_flags_fn_t)ndr_push_named_pipe_auth_rep);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ DEBUG(2, ("Error encoding structure: %s",
+ ndr_map_error2string(ndr_err)));
+ tevent_req_error(req, EIO);
+ return;
+ }
+
+ DEBUG(10, ("named_pipe_auth reply[%u]\n", (unsigned)out.length));
+ dump_data(11, out.data, out.length);
+
+ if (DEBUGLVL(10)) {
+ NDR_PRINT_DEBUG(named_pipe_auth_rep, &pipe_reply);
+ }
+
+ state->accept_status = pipe_reply.status;
+
+ state->out_iov.iov_base = out.data;
+ state->out_iov.iov_len = out.length;
+
+ subreq = tstream_writev_send(state, state->ev,
+ state->plain,
+ &state->out_iov, 1);
+ if (tevent_req_nomem(subreq, req)) {
+ DEBUG(0, ("no memory for tstream_writev_send"));
+ return;
+ }
+
+ tevent_req_set_callback(subreq, tstream_npa_accept_existing_done, req);
+}
+
+static void tstream_npa_accept_existing_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ int sys_errno;
+ int ret;
+
+ ret = tstream_writev_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, sys_errno);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+int _tstream_npa_accept_existing_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream,
+ struct tsocket_address **client,
+ char **_client_name,
+ struct tsocket_address **server,
+ char **server_name,
+ struct netr_SamInfo3 **info3,
+ DATA_BLOB *session_key,
+ DATA_BLOB *delegated_creds,
+ const char *location)
+{
+ struct tstream_npa_accept_state *state =
+ tevent_req_data(req, struct tstream_npa_accept_state);
+ struct tstream_npa *npas;
+ int ret;
+
+ ret = tsocket_simple_int_recv(req, perrno);
+ if (ret != 0) {
+ DEBUG(2, ("Failed to accept named pipe conection: %s\n",
+ strerror(*perrno)));
+ tevent_req_received(req);
+ return -1;
+ }
+
+ if (!NT_STATUS_IS_OK(state->accept_status)) {
+#if defined(EPROTONOSUPPORT)
+ *perrno = EPROTONOSUPPORT;
+#elif defined(EPROTO)
+ *perrno = EPROTO;
+#else
+ *perrno = EINVAL;
+#endif
+ DEBUG(2, ("Failed to accept named pipe conection: %s => %s\n",
+ nt_errstr(state->accept_status),
+ strerror(*perrno)));
+ tevent_req_received(req);
+ return -1;
+ }
+
+ *stream = tstream_context_create(mem_ctx,
+ &tstream_npa_ops,
+ &npas,
+ struct tstream_npa,
+ location);
+ if (!*stream) {
+ *perrno = ENOMEM;
+ tevent_req_received(req);
+ return -1;
+ }
+ ZERO_STRUCTP(npas);
+ npas->unix_stream = state->plain;
+ npas->file_type = state->file_type;
+
+ *client = talloc_move(mem_ctx, &state->client);
+ *_client_name = talloc_move(mem_ctx, &state->client_name);
+ *server = talloc_move(mem_ctx, &state->server);
+ *server_name = talloc_move(mem_ctx, &state->server_name);
+ *info3 = talloc_move(mem_ctx, &state->info3);
+ *session_key = state->session_key;
+ talloc_move(mem_ctx, &state->session_key.data);
+ *delegated_creds = state->delegated_creds;
+ talloc_move(mem_ctx, &state->delegated_creds.data);
+
+ tevent_req_received(req);
+ return 0;
+}
diff --git a/libcli/named_pipe_auth/npa_tstream.h b/libcli/named_pipe_auth/npa_tstream.h
index 7a19e10410..63845bed6a 100644
--- a/libcli/named_pipe_auth/npa_tstream.h
+++ b/libcli/named_pipe_auth/npa_tstream.h
@@ -56,4 +56,70 @@ int _tstream_npa_existing_socket(TALLOC_CTX *mem_ctx,
_tstream_npa_existing_socket(mem_ctx, fd, ft, stream, \
__location__)
+
+/**
+ * @brief Accepts a connection for authenticated named pipes
+ *
+ * @param[in] mem_ctx The memory context for the operation
+ * @param[in] ev The tevent_context for the operation
+ * @param[in] plain The plain tstream_context of the bsd unix
+ * domain socket.
+ * This must be valid for the whole life of the
+ * resulting npa tstream_context!
+ * @param[in] file_type The file_type, message mode or byte mode
+ * @param[in] device_state The reported device state
+ * @param[in] allocation_size The reported allocation size
+ *
+ * @return the tevent_req handle
+ */
+struct tevent_req *tstream_npa_accept_existing_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct tstream_context *plain,
+ uint16_t file_type,
+ uint16_t device_state,
+ uint64_t allocation_size);
+
+/**
+ * @brief The receive end of the previous async function
+ *
+ * @param[in] req The tevent_req handle
+ * @param[out] perrno Pointer to store the errno in case of error
+ * @param[in] mem_ctx The memory context for the results
+ * @param[out] stream The resulting stream
+ * @param[out] client The resulting client address
+ * @param[out] client_name The resulting client name
+ * @param[out] server The resulting server address
+ * @param[out] server_name The resulting server name
+ * @param[out] info3 The info3 auth for the connecting user.
+ * @param[out] session_key The resulting session key
+ * @param[out] delegated_creds Delegated credentials
+ *
+ * @return 0 if successful, -1 on failure with *perror filled.
+ */
+int _tstream_npa_accept_existing_recv(struct tevent_req *req,
+ int *perrno,
+ TALLOC_CTX *mem_ctx,
+ struct tstream_context **stream,
+ struct tsocket_address **client,
+ char **client_name,
+ struct tsocket_address **server,
+ char **server_name,
+ struct netr_SamInfo3 **info3,
+ DATA_BLOB *session_key,
+ DATA_BLOB *delegated_creds,
+ const char *location);
+#define tstream_npa_accept_existing_recv(req, perrno, \
+ mem_ctx, stream, \
+ client, client_name, \
+ server, server_name, \
+ info3, session_key, \
+ delegated_creds) \
+ _tstream_npa_accept_existing_recv(req, perrno, \
+ mem_ctx, stream, \
+ client, client_name, \
+ server, server_name, \
+ info3, session_key, \
+ delegated_creds, \
+ __location__)
+
#endif /* NPA_TSTREAM_H */