mirror of
https://github.com/samba-team/samba.git
synced 2025-03-27 22:50:26 +03:00
named_pipe_auth: implement tstream_npa_accept_existing_send/recv
Pair-programmed-with: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
9e194cd673
commit
b7159e6ffd
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user