/*
Unix SMB/CIFS implementation.
helper functions for NAMED PIPE servers
Copyright (C) Stefan (metze) Metzmacher 2008
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include "includes.h"
#include
#include "smbd/service.h"
#include "param/param.h"
#include "auth/auth.h"
#include "auth/session.h"
#include "auth/auth_sam_reply.h"
#include "lib/socket/socket.h"
#include "lib/tsocket/tsocket.h"
#include "libcli/util/tstream.h"
#include "librpc/gen_ndr/ndr_named_pipe_auth.h"
#include "system/passwd.h"
#include "system/network.h"
#include "libcli/raw/smb.h"
#include "auth/credentials/credentials.h"
#include "auth/credentials/credentials_krb5.h"
#include "libcli/security/security.h"
#include "libcli/named_pipe_auth/npa_tstream.h"
struct named_pipe_socket {
const char *pipe_name;
const char *pipe_path;
const struct stream_server_ops *ops;
void *private_data;
};
static void named_pipe_accept_done(struct tevent_req *subreq);
static void named_pipe_accept(struct stream_connection *conn)
{
struct tstream_context *plain_tstream;
int fd;
struct tevent_req *subreq;
int ret;
/* Let tstream take over fd operations */
fd = socket_get_fd(conn->socket);
socket_set_flags(conn->socket, SOCKET_FLAG_NOCLOSE);
TALLOC_FREE(conn->event.fde);
TALLOC_FREE(conn->socket);
ret = tstream_bsd_existing_socket(conn, fd, &plain_tstream);
if (ret != 0) {
stream_terminate_connection(conn,
"named_pipe_accept: out of memory");
return;
}
subreq = tstream_npa_accept_existing_send(conn, conn->event.ctx,
plain_tstream,
FILE_TYPE_MESSAGE_MODE_PIPE,
0xff | 0x0400 | 0x0100,
4096);
if (subreq == NULL) {
stream_terminate_connection(conn,
"named_pipe_accept: "
"no memory for tstream_npa_accept_existing_send");
return;
}
tevent_req_set_callback(subreq, named_pipe_accept_done, conn);
}
static void named_pipe_accept_done(struct tevent_req *subreq)
{
struct stream_connection *conn = tevent_req_callback_data(subreq,
struct stream_connection);
struct named_pipe_socket *pipe_sock =
talloc_get_type(conn->private_data,
struct named_pipe_socket);
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;
union netr_Validation val;
struct auth_serversupplied_info *server_info;
struct auth_context *auth_context;
uint32_t session_flags = 0;
struct dom_sid *anonymous_sid;
const char *reason = NULL;
TALLOC_CTX *tmp_ctx;
NTSTATUS status;
int error;
int ret;
tmp_ctx = talloc_new(conn);
if (!tmp_ctx) {
reason = "Out of memory!\n";
goto out;
}
ret = tstream_npa_accept_existing_recv(subreq, &error, tmp_ctx,
&conn->tstream,
&client,
&client_name,
&server,
&server_name,
&info3,
&session_key,
&delegated_creds);
TALLOC_FREE(subreq);
if (ret != 0) {
reason = talloc_asprintf(conn,
"tstream_npa_accept_existing_recv()"
" failed: %s", strerror(error));
goto out;
}
DEBUG(10, ("Accepted npa connection from %s. "
"Client: %s (%s). Server: %s (%s)\n",
tsocket_address_string(conn->remote_address, tmp_ctx),
client_name, tsocket_address_string(client, tmp_ctx),
server_name, tsocket_address_string(server, tmp_ctx)));
if (info3) {
val.sam3 = info3;
status = make_server_info_netlogon_validation(conn,
val.sam3->base.account_name.string,
3, &val, &server_info);
if (!NT_STATUS_IS_OK(status)) {
reason = talloc_asprintf(conn,
"make_server_info_netlogon_validation "
"returned: %s", nt_errstr(status));
goto out;
}
status = auth_context_create(conn, conn->event.ctx,
conn->msg_ctx, conn->lp_ctx,
&auth_context);
if (!NT_STATUS_IS_OK(status)) {
reason = talloc_asprintf(conn,
"auth_context_create returned: %s",
nt_errstr(status));
goto out;
}
anonymous_sid = dom_sid_parse_talloc(auth_context,
SID_NT_ANONYMOUS);
if (anonymous_sid == NULL) {
talloc_free(auth_context);
reason = "Failed to parse Anonymous SID ";
goto out;
}
session_flags = AUTH_SESSION_INFO_DEFAULT_GROUPS;
if (!dom_sid_equal(anonymous_sid, server_info->account_sid)) {
session_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
}
/* setup the session_info on the connection */
status = auth_context->generate_session_info(conn,
auth_context,
server_info,
session_flags,
&conn->session_info);
talloc_free(auth_context);
if (!NT_STATUS_IS_OK(status)) {
reason = talloc_asprintf(conn,
"auth_generate_session_info "
"returned: %s", nt_errstr(status));
goto out;
}
}
if (session_key.length) {
conn->session_info->session_key = session_key;
talloc_steal(conn->session_info, session_key.data);
}
if (delegated_creds.length) {
struct cli_credentials *creds;
OM_uint32 minor_status;
gss_buffer_desc cred_token;
gss_cred_id_t cred_handle;
const char *error_string;
DEBUG(10, ("Delegated credentials supplied by client\n"));
cred_token.value = delegated_creds.data;
cred_token.length = delegated_creds.length;
ret = gss_import_cred(&minor_status,
&cred_token,
&cred_handle);
if (ret != GSS_S_COMPLETE) {
reason = "Internal error in gss_import_cred()";
goto out;
}
creds = cli_credentials_init(conn->session_info);
if (!creds) {
reason = "Out of memory in cli_credentials_init()";
goto out;
}
conn->session_info->credentials = creds;
cli_credentials_set_conf(creds, conn->lp_ctx);
/* Just so we don't segfault trying to get at a username */
cli_credentials_set_anonymous(creds);
ret = cli_credentials_set_client_gss_creds(creds,
conn->lp_ctx,
cred_handle,
CRED_SPECIFIED,
&error_string);
if (ret) {
reason = talloc_asprintf(conn,
"Failed to set pipe forwarded"
"creds: %s\n", error_string);
goto out;
}
/* This credential handle isn't useful for password
* authentication, so ensure nobody tries to do that */
cli_credentials_set_kerberos_state(creds,
CRED_MUST_USE_KERBEROS);
}
/*
* hand over to the real pipe implementation,
* now that we have setup the transport session_info
*/
conn->ops = pipe_sock->ops;
conn->private_data = pipe_sock->private_data;
conn->ops->accept_connection(conn);
DEBUG(10, ("named pipe connection [%s] established\n",
conn->ops->name));
talloc_free(tmp_ctx);
return;
out:
talloc_free(tmp_ctx);
if (!reason) {
reason = "Internal error";
}
stream_terminate_connection(conn, reason);
}
/*
called when a pipe socket becomes readable
*/
static void named_pipe_recv(struct stream_connection *conn, uint16_t flags)
{
stream_terminate_connection(conn, "named_pipe_recv: called");
}
/*
called when a pipe socket becomes writable
*/
static void named_pipe_send(struct stream_connection *conn, uint16_t flags)
{
stream_terminate_connection(conn, "named_pipe_send: called");
}
static const struct stream_server_ops named_pipe_stream_ops = {
.name = "named_pipe",
.accept_connection = named_pipe_accept,
.recv_handler = named_pipe_recv,
.send_handler = named_pipe_send,
};
NTSTATUS tstream_setup_named_pipe(TALLOC_CTX *mem_ctx,
struct tevent_context *event_context,
struct loadparm_context *lp_ctx,
const struct model_ops *model_ops,
const struct stream_server_ops *stream_ops,
const char *pipe_name,
void *private_data)
{
char *dirname;
struct named_pipe_socket *pipe_sock;
NTSTATUS status = NT_STATUS_NO_MEMORY;;
pipe_sock = talloc(mem_ctx, struct named_pipe_socket);
if (pipe_sock == NULL) {
goto fail;
}
/* remember the details about the pipe */
pipe_sock->pipe_name = talloc_strdup(pipe_sock, pipe_name);
if (pipe_sock->pipe_name == NULL) {
goto fail;
}
dirname = talloc_asprintf(pipe_sock, "%s/np", lpcfg_ncalrpc_dir(lp_ctx));
if (dirname == NULL) {
goto fail;
}
if (!directory_create_or_exist(dirname, geteuid(), 0700)) {
status = map_nt_error_from_unix(errno);
DEBUG(0,(__location__ ": Failed to create stream pipe directory %s - %s\n",
dirname, nt_errstr(status)));
goto fail;
}
if (strncmp(pipe_name, "\\pipe\\", 6) == 0) {
pipe_name += 6;
}
pipe_sock->pipe_path = talloc_asprintf(pipe_sock, "%s/%s", dirname,
pipe_name);
if (pipe_sock->pipe_path == NULL) {
goto fail;
}
talloc_free(dirname);
pipe_sock->ops = stream_ops;
pipe_sock->private_data = private_data;
status = stream_setup_socket(pipe_sock,
event_context,
lp_ctx,
model_ops,
&named_pipe_stream_ops,
"unix",
pipe_sock->pipe_path,
NULL,
NULL,
pipe_sock);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
return NT_STATUS_OK;
fail:
talloc_free(pipe_sock);
return status;
}