1
0
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:
Simo Sorce 2010-05-19 17:02:52 -04:00 committed by Stefan Metzmacher
parent 9e194cd673
commit b7159e6ffd
2 changed files with 521 additions and 0 deletions

View File

@ -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;
}

View File

@ -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 */