diff options
author | Simo Sorce <ssorce@redhat.com> | 2010-05-19 17:02:52 -0400 |
---|---|---|
committer | Stefan Metzmacher <metze@samba.org> | 2010-05-26 09:23:47 +0200 |
commit | b7159e6ffd9f2c907301ce4d3e6ba3ff01c8a1f6 (patch) | |
tree | afc12dc4e7584b0649fe4a06c0385cd4da9cd92e /libcli/named_pipe_auth | |
parent | 9e194cd673a0d0a56cd064bab38cbf30b468999d (diff) | |
download | samba-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>
Diffstat (limited to 'libcli/named_pipe_auth')
-rw-r--r-- | libcli/named_pipe_auth/npa_tstream.c | 455 | ||||
-rw-r--r-- | libcli/named_pipe_auth/npa_tstream.h | 66 |
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 */ |