The branch, master has been updated via ddd8ae51f8c smb2_server: do async shutdown for pending multi-channel requests via a90ac47d88d smbXsrv_session: add a smbXsrv_session_disconnect_xconn() helper via de0e6dfdb84 smb2_server: call smbXsrv_connection_disconnect_transport() early on network errors via 26ba013e279 smb2_server: add and use a function that calculated the remaining channels via a87c9a92df6 smb2_server: let smbd_server_connection_terminate_ex() call smbXsrv_connection_disconnect_transport() via d8ab88e77f8 s3:smbd: split out smbXsrv_connection_disconnect_transport() via 0cec96526bf smb2_server: make sure we detect stale smbXsrv_connection pointers in smbXsrv_channel_global via 2ac0f835458 smb2_server: update inline comment for max channels via e26b55a232b smbXsrv_client: make sure that we store a valid blob via 73cc25fa7b1 smbXsrv_client: fix debug message in smbXsrv_client_create() from 004e7a1fee7 s4/rpc_server/dnsserver: Allow parsing of dnsProperty to fail gracefully
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit ddd8ae51f8c7a8f35754d0b281c74cb36e7d6bbd Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 14:55:52 2019 +0200 smb2_server: do async shutdown for pending multi-channel requests We have wait until all pending requests are done before we can TALLOC_FREE() the connection structure. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> Autobuild-User(master): Günther Deschner <g...@samba.org> Autobuild-Date(master): Fri May 15 10:26:29 UTC 2020 on sn-devel-184 commit a90ac47d88d08dfd396e956f9e24d9fcc0de3c5d Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 12:11:00 2019 +0200 smbXsrv_session: add a smbXsrv_session_disconnect_xconn() helper This removes the connection references from the session channel array for each session that's used on the connection. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit de0e6dfdb84cfe13b04d6ec7a09b18f001c61db4 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 14:56:40 2019 +0200 smb2_server: call smbXsrv_connection_disconnect_transport() early on network errors It's good to remember the first error we got and makes sure we don't try any further io on the connection. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit 26ba013e279769b39e6bac510cb4b2d6d801ac98 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 14:49:59 2019 +0200 smb2_server: add and use a function that calculated the remaining channels This is useful for debugging, but also simplies the following changes, where client->connections may hold disconnected connections until all pending requests are finished. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit a87c9a92df675173fc28404819d8dc0303eb81af Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 14:30:17 2019 +0200 smb2_server: let smbd_server_connection_terminate_ex() call smbXsrv_connection_disconnect_transport() If the connection is broken mark it as invalid and close the socket. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit d8ab88e77f8bbd29d9222348eb4f9a290a4f3694 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 14:26:20 2019 +0200 s3:smbd: split out smbXsrv_connection_disconnect_transport() It's good to have an isolated function that just disconnects the lower layer transport and remembers the first error status. This will be used in more placed in the following commits. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit 0cec96526bf4d3209caf36c4a19632ff5d5dd112 Author: Stefan Metzmacher <me...@samba.org> Date: Fri Oct 4 10:02:56 2019 +0200 smb2_server: make sure we detect stale smbXsrv_connection pointers in smbXsrv_channel_global Pointer values can be reused (yes, I hit that during my testing!). Introduce a channel_id to identify connections and also add some timestamps to make debugging easier. This makes smbXsrv_session_find_channel() much more robust. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit 2ac0f8354585ac2cd617d53bfc9678769c8698fc Author: Günther Deschner <g...@samba.org> Date: Wed Jan 24 17:14:59 2018 +0100 smb2_server: update inline comment for max channels All Windows versions have the limit of 32 channels. Signed-off-by: Guenther Deschner <g...@samba.org> Signed-off-by: Stefan Metzmacher <me...@samba.org> commit e26b55a232b1402b5d42ce0ab9e3d79b4f4319d9 Author: Stefan Metzmacher <me...@samba.org> Date: Thu May 7 06:49:24 2020 -0700 smbXsrv_client: make sure that we store a valid blob This fixes a regression introduced by 14182350f8397d27d7642dae595dc52691f0acfe ("librpc ndr: ndr_pull_advance check for unsigned overflow.") BUG: https://bugzilla.samba.org/show_bug.cgi?id=14236 ndr_push_smbXsrv_client_global0() is happy with pushing NULL pointers for r->{local_address,remote_address,remote_name}, while the IDL doesn't allow it. In turn ndr_pull_smbXsrv_client_global0() no longer ignores the error. This means multi-channel connections were broken, and we paniced on a NULL pointer. It's really sad that we still don't have automated tests for it. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> commit 73cc25fa7b126938fd161a01aef327c3fd85eef1 Author: Stefan Metzmacher <me...@samba.org> Date: Thu May 7 06:54:37 2020 -0700 smbXsrv_client: fix debug message in smbXsrv_client_create() Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Guenther Deschner <g...@samba.org> ----------------------------------------------------------------------- Summary of changes: source3/librpc/idl/smbXsrv.idl | 3 + source3/smbd/globals.h | 8 +- source3/smbd/process.c | 26 +++++- source3/smbd/server_exit.c | 28 +++--- source3/smbd/smb2_server.c | 201 +++++++++++++++++++++++++++++++++++++++-- source3/smbd/smb2_sesssetup.c | 1 + source3/smbd/smbXsrv_client.c | 22 ++++- source3/smbd/smbXsrv_session.c | 141 ++++++++++++++++++++++++++++- 8 files changed, 397 insertions(+), 33 deletions(-) Changeset truncated at 500 lines: diff --git a/source3/librpc/idl/smbXsrv.idl b/source3/librpc/idl/smbXsrv.idl index 4d9249fb3bb..bdab9345342 100644 --- a/source3/librpc/idl/smbXsrv.idl +++ b/source3/librpc/idl/smbXsrv.idl @@ -141,6 +141,7 @@ interface smbXsrv */ [ignore] struct smbXsrv_connection *connections; boolean8 server_multi_channel_enabled; + hyper next_channel_id; } smbXsrv_client; typedef union { @@ -200,6 +201,8 @@ interface smbXsrv typedef struct { server_id server_id; + hyper channel_id; + NTTIME creation_time; [charset(UTF8),string] char local_address[]; [charset(UTF8),string] char remote_address[]; [charset(UTF8),string] char remote_name[]; diff --git a/source3/smbd/globals.h b/source3/smbd/globals.h index 79086b3c81c..d3b6ac2ffe6 100644 --- a/source3/smbd/globals.h +++ b/source3/smbd/globals.h @@ -220,6 +220,8 @@ NTSTATUS smbd_calculate_access_mask(connection_struct *conn, void smbd_notify_cancel_by_smbreq(const struct smb_request *smbreq); +void smbXsrv_connection_disconnect_transport(struct smbXsrv_connection *xconn, + NTSTATUS status); void smbd_server_connection_terminate_ex(struct smbXsrv_connection *xconn, const char *reason, const char *location); @@ -231,7 +233,7 @@ bool smbd_is_smb2_header(const uint8_t *inbuf, size_t size); bool smbd_smb2_is_compound(const struct smbd_smb2_request *req); NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd, - struct smbXsrv_connection **_xconn); + NTTIME now, struct smbXsrv_connection **_xconn); NTSTATUS reply_smb2002(struct smb_request *req, uint16_t choice); NTSTATUS reply_smb20ff(struct smb_request *req, uint16_t choice); @@ -347,6 +349,8 @@ struct smbXsrv_connection { struct smbXsrv_client *client; + NTTIME connect_time; + uint64_t channel_id; const struct tsocket_address *local_address; const struct tsocket_address *remote_address; const char *remote_hostname; @@ -547,7 +551,9 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn, struct smbXsrv_session **_session); NTSTATUS smbXsrv_session_add_channel(struct smbXsrv_session *session, struct smbXsrv_connection *conn, + NTTIME now, struct smbXsrv_channel_global0 **_c); +NTSTATUS smbXsrv_session_disconnect_xconn(struct smbXsrv_connection *xconn); NTSTATUS smbXsrv_session_update(struct smbXsrv_session *session); struct smbXsrv_channel_global0; NTSTATUS smbXsrv_session_find_channel(const struct smbXsrv_session *session, diff --git a/source3/smbd/process.c b/source3/smbd/process.c index 955f13938b4..161672bd516 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -3720,7 +3720,7 @@ const char *smbXsrv_connection_dbg(const struct smbXsrv_connection *xconn) } NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd, - struct smbXsrv_connection **_xconn) + NTTIME now, struct smbXsrv_connection **_xconn) { TALLOC_CTX *frame = talloc_stackframe(); struct smbXsrv_connection *xconn; @@ -3750,6 +3750,10 @@ NTSTATUS smbd_add_connection(struct smbXsrv_client *client, int sock_fd, return NT_STATUS_NO_MEMORY; } talloc_steal(frame, xconn); + xconn->connect_time = now; + if (client->next_channel_id != 0) { + xconn->channel_id = client->next_channel_id++; + } xconn->transport.sock = sock_fd; smbd_echo_init(xconn); @@ -3994,7 +3998,7 @@ void smbd_process(struct tevent_context *ev_ctx, smbd_setup_sig_hup_handler(sconn); } - status = smbd_add_connection(client, sock_fd, &xconn); + status = smbd_add_connection(client, sock_fd, now, &xconn); if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_ACCESS_DENIED)) { /* * send a negative session response "not listening on calling @@ -4024,6 +4028,24 @@ void smbd_process(struct tevent_context *ev_ctx, exit_server_cleanly("tsocket_strdup() failed"); } + client->global->local_address = + tsocket_address_string(sconn->local_address, + client->global); + if (client->global->local_address == NULL) { + exit_server_cleanly("tsocket_address_string() failed"); + } + client->global->remote_address = + tsocket_address_string(sconn->remote_address, + client->global); + if (client->global->remote_address == NULL) { + exit_server_cleanly("tsocket_address_string() failed"); + } + client->global->remote_name = + talloc_strdup(client->global, sconn->remote_hostname); + if (client->global->remote_name == NULL) { + exit_server_cleanly("tsocket_strdup() failed"); + } + if (tsocket_address_is_inet(sconn->local_address, "ip")) { locaddr = tsocket_address_inet_addr_string( sconn->local_address, diff --git a/source3/smbd/server_exit.c b/source3/smbd/server_exit.c index f361aaf0bb7..e3efa993159 100644 --- a/source3/smbd/server_exit.c +++ b/source3/smbd/server_exit.c @@ -81,6 +81,17 @@ static void exit_server_common(enum server_exit_reason how, struct smbXsrv_connection *xconn = NULL; struct smbd_server_connection *sconn = NULL; struct messaging_context *msg_ctx = global_messaging_context(); + NTSTATUS disconnect_status; + + switch (how) { + case SERVER_EXIT_NORMAL: + disconnect_status = NT_STATUS_LOCAL_DISCONNECT; + break; + case SERVER_EXIT_ABNORMAL: + default: + disconnect_status = NT_STATUS_INTERNAL_ERROR; + break; + } if (client != NULL) { sconn = client->sconn; @@ -100,19 +111,12 @@ static void exit_server_common(enum server_exit_reason how, for (; xconn != NULL; xconn = xconn->next) { /* * This is typically the disconnect for the only - * (or with multi-channel last) connection of the client + * (or with multi-channel last) connection of the client. + * + * smbXsrv_connection_disconnect_transport() might be called already, + * but calling it again is a no-op. */ - if (NT_STATUS_IS_OK(xconn->transport.status)) { - switch (how) { - case SERVER_EXIT_ABNORMAL: - xconn->transport.status = NT_STATUS_INTERNAL_ERROR; - break; - case SERVER_EXIT_NORMAL: - xconn->transport.status = NT_STATUS_LOCAL_DISCONNECT; - break; - } - } - DO_PROFILE_INC(disconnect); + smbXsrv_connection_disconnect_transport(xconn, disconnect_status); } change_to_root_user(); diff --git a/source3/smbd/smb2_server.c b/source3/smbd/smb2_server.c index 718f0941532..c11cc66a710 100644 --- a/source3/smbd/smb2_server.c +++ b/source3/smbd/smb2_server.c @@ -1106,20 +1106,190 @@ static NTSTATUS smbd_smb2_request_setup_out(struct smbd_smb2_request *req) return NT_STATUS_OK; } +void smbXsrv_connection_disconnect_transport(struct smbXsrv_connection *xconn, + NTSTATUS status) +{ + if (!NT_STATUS_IS_OK(xconn->transport.status)) { + return; + } + + xconn->transport.status = status; + TALLOC_FREE(xconn->transport.fde); + if (xconn->transport.sock != -1) { + xconn->transport.sock = -1; + } + DO_PROFILE_INC(disconnect); +} + +static size_t smbXsrv_client_valid_connections(struct smbXsrv_client *client) +{ + struct smbXsrv_connection *xconn = NULL; + size_t num_ok = 0; + + for (xconn = client->connections; xconn != NULL; xconn = xconn->next) { + if (NT_STATUS_IS_OK(xconn->transport.status)) { + num_ok++; + } + } + + return num_ok; +} + +struct smbXsrv_connection_shutdown_state { + struct tevent_queue *wait_queue; +}; + +static void smbXsrv_connection_shutdown_wait_done(struct tevent_req *subreq); + +static struct tevent_req *smbXsrv_connection_shutdown_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct smbXsrv_connection *xconn) +{ + struct tevent_req *req = NULL; + struct smbXsrv_connection_shutdown_state *state = NULL; + struct tevent_req *subreq = NULL; + size_t len = 0; + struct smbd_smb2_request *preq = NULL; + NTSTATUS status; + + /* + * The caller should have called + * smbXsrv_connection_disconnect_transport() before. + */ + SMB_ASSERT(!NT_STATUS_IS_OK(xconn->transport.status)); + + req = tevent_req_create(mem_ctx, &state, + struct smbXsrv_connection_shutdown_state); + if (req == NULL) { + return NULL; + } + + status = smbXsrv_session_disconnect_xconn(xconn); + if (tevent_req_nterror(req, status)) { + return tevent_req_post(req, ev); + } + + state->wait_queue = tevent_queue_create(state, "smbXsrv_connection_shutdown_queue"); + if (tevent_req_nomem(state->wait_queue, req)) { + return tevent_req_post(req, ev); + } + + for (preq = xconn->smb2.requests; preq != NULL; preq = preq->next) { + /* + * The connection is gone so we + * don't need to take care of + * any crypto + */ + preq->session = NULL; + preq->do_signing = false; + preq->do_encryption = false; + preq->preauth = NULL; + + if (preq->subreq != NULL) { + tevent_req_cancel(preq->subreq); + } + + /* + * Now wait until the request is finished. + * + * We don't set a callback, as we just want to block the + * wait queue and the talloc_free() of the request will + * remove the item from the wait queue. + */ + subreq = tevent_queue_wait_send(preq, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + } + + len = tevent_queue_length(state->wait_queue); + if (len == 0) { + tevent_req_done(req); + return tevent_req_post(req, ev); + } + + /* + * Now we add our own waiter to the end of the queue, + * this way we get notified when all pending requests are finished + * and send to the socket. + */ + subreq = tevent_queue_wait_send(state, ev, state->wait_queue); + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, smbXsrv_connection_shutdown_wait_done, req); + + return req; +} + +static void smbXsrv_connection_shutdown_wait_done(struct tevent_req *subreq) +{ + struct tevent_req *req = + tevent_req_callback_data(subreq, + struct tevent_req); + + tevent_queue_wait_recv(subreq); + TALLOC_FREE(subreq); + + tevent_req_done(req); +} + +static NTSTATUS smbXsrv_connection_shutdown_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +static void smbd_server_connection_terminate_done(struct tevent_req *subreq) +{ + struct smbXsrv_connection *xconn = + tevent_req_callback_data(subreq, + struct smbXsrv_connection); + struct smbXsrv_client *client = xconn->client; + NTSTATUS status; + + status = smbXsrv_connection_shutdown_recv(subreq); + if (!NT_STATUS_IS_OK(status)) { + exit_server("smbXsrv_connection_shutdown_recv failed"); + } + + DLIST_REMOVE(client->connections, xconn); + TALLOC_FREE(xconn); +} + void smbd_server_connection_terminate_ex(struct smbXsrv_connection *xconn, const char *reason, const char *location) { struct smbXsrv_client *client = xconn->client; + size_t num_ok = 0; + + /* + * Make sure that no new request will be able to use this session. + * + * smbXsrv_connection_disconnect_transport() might be called already, + * but calling it again is a no-op. + */ + smbXsrv_connection_disconnect_transport(xconn, + NT_STATUS_CONNECTION_DISCONNECTED); + + num_ok = smbXsrv_client_valid_connections(client); + + DBG_DEBUG("conn[%s] num_ok[%zu] reason[%s] at %s\n", + smbXsrv_connection_dbg(xconn), num_ok, + reason, location); - DEBUG(10,("smbd_server_connection_terminate_ex: conn[%s] reason[%s] at %s\n", - smbXsrv_connection_dbg(xconn), reason, location)); + if (num_ok != 0) { + struct tevent_req *subreq = NULL; - if (client->connections->next != NULL) { - /* TODO: cancel pending requests */ - DLIST_REMOVE(client->connections, xconn); - TALLOC_FREE(xconn); - DO_PROFILE_INC(disconnect); + subreq = smbXsrv_connection_shutdown_send(client, + client->raw_ev_ctx, + xconn); + if (subreq == NULL) { + exit_server("smbXsrv_connection_shutdown_send failed"); + } + tevent_req_set_callback(subreq, + smbd_server_connection_terminate_done, + xconn); return; } @@ -3794,6 +3964,8 @@ static NTSTATUS smbd_smb2_flush_send_queue(struct smbXsrv_connection *xconn) talloc_free(e->mem_ctx); if (!NT_STATUS_IS_OK(status)) { + smbXsrv_connection_disconnect_transport(xconn, + status); return status; } continue; @@ -3816,7 +3988,10 @@ static NTSTATUS smbd_smb2_flush_send_queue(struct smbXsrv_connection *xconn) return NT_STATUS_OK; } if (err != 0) { - return map_nt_error_from_unix_common(err); + status = map_nt_error_from_unix_common(err); + smbXsrv_connection_disconnect_transport(xconn, + status); + return status; } ok = iov_advance(&e->vector, &e->count, ret); @@ -3903,7 +4078,10 @@ again: ret = recvmsg(xconn->transport.sock, &msg, 0); if (ret == 0) { /* propagate end of file */ - return NT_STATUS_END_OF_FILE; + status = NT_STATUS_END_OF_FILE; + smbXsrv_connection_disconnect_transport(xconn, + status); + return status; } err = socket_error_from_errno(ret, errno, &retry); if (retry) { @@ -3912,7 +4090,10 @@ again: return NT_STATUS_OK; } if (err != 0) { - return map_nt_error_from_unix_common(err); + status = map_nt_error_from_unix_common(err); + smbXsrv_connection_disconnect_transport(xconn, + status); + return status; } if (ret < state->vector.iov_len) { diff --git a/source3/smbd/smb2_sesssetup.c b/source3/smbd/smb2_sesssetup.c index 3fa1a15910e..f9c8819327c 100644 --- a/source3/smbd/smb2_sesssetup.c +++ b/source3/smbd/smb2_sesssetup.c @@ -912,6 +912,7 @@ static struct tevent_req *smbd_smb2_session_setup_send(TALLOC_CTX *mem_ctx, status = smbXsrv_session_add_channel(smb2req->session, smb2req->xconn, + now, &c); if (!NT_STATUS_IS_OK(status)) { tevent_req_nterror(req, status); diff --git a/source3/smbd/smbXsrv_client.c b/source3/smbd/smbXsrv_client.c index 02754a88dd5..c6114d4b8dc 100644 --- a/source3/smbd/smbXsrv_client.c +++ b/source3/smbd/smbXsrv_client.c @@ -294,6 +294,13 @@ NTSTATUS smb2srv_client_lookup_global(struct smbXsrv_client *client, return NT_STATUS_OBJECTID_NOT_FOUND; } + if (global == NULL) { + /* + * most likely ndr_pull_struct_blob() failed + */ + return NT_STATUS_INTERNAL_DB_CORRUPTION; + } + *_global = global; return NT_STATUS_OK; } @@ -375,6 +382,10 @@ static NTSTATUS smbXsrv_client_global_store(struct smbXsrv_client_global0 *globa * store the information in the old format. */ + SMB_ASSERT(global->local_address != NULL); + SMB_ASSERT(global->remote_address != NULL); + SMB_ASSERT(global->remote_name != NULL); + if (global->db_rec == NULL) { return NT_STATUS_INTERNAL_ERROR; } @@ -512,7 +523,9 @@ NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx, client->msg_ctx = msg_ctx; client->server_multi_channel_enabled = lp_server_multi_channel_support(); - + if (client->server_multi_channel_enabled) { + client->next_channel_id = 1; + } client->table = talloc_move(client, &table); table = client->table; @@ -539,7 +552,7 @@ NTSTATUS smbXsrv_client_create(TALLOC_CTX *mem_ctx, }; struct GUID_txt_buf buf; - DBG_DEBUG("client_guid[%s] stored\n", + DBG_DEBUG("client_guid[%s] created\n", GUID_buf_string(&global->client_guid, &buf)); NDR_PRINT_DEBUG(smbXsrv_clientB, &client_blob); } @@ -662,7 +675,10 @@ static void smbXsrv_client_connection_pass_loop(struct tevent_req *subreq) DBG_ERR("got connection sockfd[%d]\n", sock_fd); NDR_PRINT_DEBUG(smbXsrv_connection_passB, &pass_blob); - status = smbd_add_connection(client, sock_fd, &xconn); + status = smbd_add_connection(client, + sock_fd, + pass_info0->initial_connect_time, + &xconn); if (!NT_STATUS_IS_OK(status)) { close(sock_fd); sock_fd = -1; diff --git a/source3/smbd/smbXsrv_session.c b/source3/smbd/smbXsrv_session.c index ba542702213..8eaa9fdcbab 100644 --- a/source3/smbd/smbXsrv_session.c +++ b/source3/smbd/smbXsrv_session.c @@ -1293,7 +1293,7 @@ NTSTATUS smbXsrv_session_create(struct smbXsrv_connection *conn, global->creation_time = now; global->expiration_time = GENSEC_EXPIRE_TIME_INFINITY; - status = smbXsrv_session_add_channel(session, conn, &channel); + status = smbXsrv_session_add_channel(session, conn, now, &channel); if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(session); return status; -- Samba Shared Repository