Vincent Le Toux
2020-Oct-12 16:59 UTC
[Samba] Lookup sid with libsmbclient (invoked from c# on mono)
Typical example: Here is some search & export for libsmbclient: adiant at ubuntu:/usr/lib/x86_64-linux-gnu$ nm -D libsmbclient.so.0 |grep cli_rpc_pipe_open_noauth_transport <nothing> adiant at ubuntu:/usr/lib/x86_64-linux-gnu$ nm -D libsmbclient.so.0 |grep cli_rpc_pipe_open U cli_rpc_pipe_open_noauth adiant at ubuntu:/usr/lib/x86_64-linux-gnu$ nm -D libsmbclient.so.0 |grep lsa U ndr_table_lsarpc U rpccli_lsa_lookup_names U rpccli_lsa_lookup_sids U rpccli_lsa_open_policy So I need to call rpccli_lsa_open_policy you need struct rpc_pipe_client *lsa_pipe (ex: https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/lib/netapi/localgroup.c ) This structure is transformed from struct cli_state *cli, into struct rpc_pipe_client *rpccli; you may call cli_rpc_pipe_open_noauth_transport to do that ( https://gitlab.com/samba-team/devel/samba/-/blob/master/examples/winexe/winexe.c#L458 ) cli_rpc_pipe_open_noauth_transport is not exported. maybe i should use cli_rpc_pipe_open_noauth which is closed, but exported. Also another example. Sometimes the cli api is called, sometimes the dcerpc one. Typical example here: https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/lib/netapi/localgroup.c#L1028-1045 Do you understand my problem ? I have to explore all the possible ways to build that call stack by starting from the end. That's why I'm asking if there is an easier way to proceed. br Vincent Le lun. 12 oct. 2020 ? 18:36, Vincent Le Toux <vincent.letoux at gmail.com> a ?crit :> This wrapper, I know how to make it in c# > (Rewriting stdin and stdout is not possible because I?m discovering one > sid after having examined another one) > > What I?m missing is the api calls (in order) that I need to do to create > this connection. > Many exports that rpcclient is using are not included in libsmbdlient. > I can find the final rpc function, but not the functions to establish the > right context. > Rpcclient is using cli_ functions that are not found in other examples. > > I need to know which c api to call in order to build the binding that the > c function you quote needs (the first input arg) > > Br > Vincent > > Le lun. 12 oct. 2020 ? 18:23, Aur?lien Aptel <aaptel at suse.com> a ?crit : > >> Vincent Le Toux via samba <samba at lists.samba.org> writes: >> >> > Indeed, rpcclient is the program I looked for the first time. >> >> > >> >> > I cannot invoke it in a command line because I have at least 1000 SID to >> >> > resolve >> >> > (I discover them one by one so I'll have to run rpcclient at least 1000 >> >> > times) >> >> >> >> You can pass multiples SID at a time to these commands. But you would >> >> have to batch the resolving in your app. >> >> >> >> > >> >> > I looked at rpcclient source code, but there is no easy function such as >> >> > "connect" that can be used easily from libsmbclient >> >> > >> >> > Thinking about Smb_negox for example. I cannot just call >> structure->member >> >> > because I'll to translate all structure. >> >> > Working with pointer (IntPtr in c#) is much simpler. >> >> >> >> You can write a very simple C wrapper and call the wrapper from C#. >> >> >> >> So you would have: >> >> >> >> rpcwrapper.c: >> >> void* init_connection(const char *user, const char *pw) >> >> { >> >> // establish connection and return handle >> >> } >> >> >> >> const char* name_to_sid(void *con, const char *name) >> >> { >> >> // use con to resolve and return sid >> >> } >> >> >> >> const char* sid_to_name(void *con, const char *sid) >> >> { >> >> // use con to resolve and return name >> >> } >> >> >> >> void free_connection(void *con) >> >> { >> >> // release con handle >> >> } >> >> >> >> This wrapper would link against the same libs as rpcclient binary and >> >> would compile to librpcwrapper.so. From C you can #include all the >> >> struct definitions you need or copy them from the source code. >> >> >> >> Then from C# you just load librpcwrapper.so and call those simple >> functions. >> >> >> >> Cheers, >> >> -- >> >> Aur?lien Aptel / SUSE Labs Samba Team >> >> GPG: 1839 CB5F 9F5B FB9B AA97 8C99 03C8 A49B 521B D5D3 >> >> SUSE Software Solutions Germany GmbH, Maxfeldstr >> <https://www.google.com/maps/search/tions+Germany+GmbH,+Maxfeldstr?entry=gmail&source=g>. >> 5, 90409 N?rnberg, DE >> >> GF: Felix Imend?rffer, Mary Higgins, Sri Rasiah HRB 247165 (AG M?nchen) >> >> -- > --- > Vincent >-- --- Vincent
Aurélien Aptel
2020-Oct-13 10:23 UTC
[Samba] Lookup sid with libsmbclient (invoked from c# on mono)
Vincent Le Toux <vincent.letoux at gmail.com> writes:> Typical example: > Here is some search & export for libsmbclient: > adiant at ubuntu:/usr/lib/x86_64-linux-gnu$ nm -D libsmbclient.so.0 |grep > cli_rpc_pipe_open_noauth_transport > <nothing> > adiant at ubuntu:/usr/lib/x86_64-linux-gnu$ nm -D libsmbclient.so.0 |grep > cli_rpc_pipe_open > U cli_rpc_pipe_open_noauth > adiant at ubuntu:/usr/lib/x86_64-linux-gnu$ nm -D libsmbclient.so.0 |grep lsa > U ndr_table_lsarpc > U rpccli_lsa_lookup_names > U rpccli_lsa_lookup_sids > U rpccli_lsa_open_policy > > So I need to call rpccli_lsa_open_policy you need struct > rpc_pipe_client *lsa_pipe > (ex: > https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/lib/netapi/localgroup.c > ) > This structure is transformed from struct cli_state *cli, into struct > rpc_pipe_client *rpccli; you may call cli_rpc_pipe_open_noauth_transport > to do that ( > https://gitlab.com/samba-team/devel/samba/-/blob/master/examples/winexe/winexe.c#L458 > ) > cli_rpc_pipe_open_noauth_transport is not exported.It is exported but you're not looking at the right shared lib. You can use ldd(1) to list all the libs rpcclient is linked against. $ ldd /usr/bin/rpcclient | perl -nE 'if(m{(/\S+)} && -f $1){system "nm -o -D --defined-only $1";}' | grep rpc_pipe_open /usr/lib64/samba/libmsrpc3-samba4.so:0000000000010dd0 T cli_rpc_pipe_open_bind_schannel /usr/lib64/samba/libmsrpc3-samba4.so:000000000000d5d0 T cli_rpc_pipe_open_generic_auth /usr/lib64/samba/libmsrpc3-samba4.so:000000000000d3b0 T cli_rpc_pipe_open_noauth /usr/lib64/samba/libmsrpc3-samba4.so:000000000000d150 T cli_rpc_pipe_open_noauth_transport /usr/lib64/samba/libmsrpc3-samba4.so:0000000000013920 T cli_rpc_pipe_open_schannel_with_creds /usr/lib64/samba/libmsrpc3-samba4.so:000000000000d3c0 T cli_rpc_pipe_open_with_creds /usr/lib64/samba/libmsrpc3-samba4.so:000000000000c230 T rpc_pipe_open_ncalrpc /usr/lib64/samba/libmsrpc3-samba4.so:000000000000cb80 T rpc_pipe_open_tcp> maybe i should use cli_rpc_pipe_open_noauth which is closed, but exported. > > Also another example. Sometimes the cli api is called, sometimes the dcerpc > one. > Typical example here: > https://gitlab.com/samba-team/devel/samba/-/blob/master/source3/lib/netapi/localgroup.c#L1028-1045 > > Do you understand my problem ? > I have to explore all the possible ways to build that call stack by > starting from the end. > That's why I'm asking if there is an easier way to proceed.As I said earlier, the easiest way is to build this wrapper lib in C by copying code from rpcclient, and then calling the wrapper from C#. I don't think there's a lib that only does lookup thru a simple function. You can also try to use ltrace(1) to trace all calls to external symbols rpcclient is making while calling the lookup command. That should make the job easier if you don't want to copy the rpcclient code: $ ltrace -fo out rpcclient -U administrator%mypassword -c 'lookupnames user1' //192.168.2.110 user1 S-1-5-21-596735176-1287999152-3436313279-1104 (User: 1) $ grep -v -e ' malloc(' -e ' strequal(' -e 'rep_memset_' out 20582 _talloc_stackframe(0x5559133fbee8, 0x7ffff4a0f998, 0x7ffff4a0f9d0, 32) = 0x55591472dbf0 20582 smb_init_locale(0x7ffff4a0f860, 0x55591347b1a0, 0x55591472db80, 0) = 0x555914732c30 20582 zero_sockaddr(0x7ffff4a0f660, 0x7f3e4c71b46c, 1, 0) = 2 20582 setlinebuf(0x7f3e4dc36500) = <void> 20582 setup_logging(0x555913401189, 3, 0, 0) = 1 20582 lp_set_cmdline(0x5559133fc8b3, 0x5559134010c4, 0x7f3e4e0da668, 0x746e65696c636370) = 1 20582 poptGetContext(0x555913401189, 6, 0x7ffff4a0f998, 0x7ffff4a0f6e0) = 0x55591473b310 20582 poptSetOtherOptionHelp(0x55591473b310, 0x5559133fd270, 0x7f3e4d985b11, 0) = 0x5559147328d0 20582 poptGetNextOpt(0x55591473b310, 0x5559133fd270, 30, 17) = 99 20582 poptGetNextOpt(0x55591473b310, 0x55591473bae0, 17, 0) = 0xffffffff 20582 poptGetArg(0x55591473b310, 0x55591474a120, 0x5559146fd010, 0) = 0x7ffff4a1108b 20582 poptGetArg(0x55591473b310, 0x55591474a120, 0, 1) = 0 20582 poptFreeContext(0x55591473b310, 0x55591474a120, 1, 1) = 0 20582 popt_burn_cmdline_password(6, 0x7ffff4a0f998, 0x5559146fd010, 2) = 1 20582 get_dyn_CONFIGFILE(0x7f3e4e991197, 0x7ffff4a1108c, 10, 5) = 0x7f3e4e8a8053 20582 cmdline_messaging_context(0x7f3e4e8a8053, 0x7ffff4a1108c, 10, 5) = 0 20582 init_names(0, 0x7ffff4a0f4b0, 0x555914739fd0, 0x7f3e4db425d7) = 1 20582 dcerpc_parse_binding(0x55591472dbf0, 0x7ffff4a1108d, 0x7ffff4a0f650, 1) = 0 20582 dcerpc_binding_get_transport(0x555914732f30, 0x55591474a120, 0x5559146fd010, 1) = 0 20582 dcerpc_binding_set_transport(0x555914732f30, 1, 0x5559146fd010, 1) = 0 20582 dcerpc_binding_get_string_option(0x555914732f30, 0x5559133fd2b5, 0, 0x1000100) = 0x55591474bab0 20582 dcerpc_binding_get_flags(0x555914732f30, 0x5559133fd2b5, 0, 0xeffffbf0) = 0 20582 popt_get_cmdline_auth_info(0x555914732f30, 0x5559133fd2b5, 0, 0) = 0x55591473b880 20582 get_cmdline_auth_info_signing_state(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0xffffffff 20582 popt_get_cmdline_auth_info(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0x55591473b880 20582 get_cmdline_auth_info_use_kerberos(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0 20582 popt_get_cmdline_auth_info(0x5559146fd300, 0x5559133fd2b5, 0, 0) = 0x55591473b880 20582 get_cmdline_auth_info_use_ccache(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0 20582 popt_get_cmdline_auth_info(0x5559146fd300, 0x5559133fd2b5, 0, 0) = 0x55591473b880 20582 get_cmdline_auth_info_use_pw_nt_hash(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0 20582 popt_get_cmdline_auth_info(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0x55591473b880 20582 get_cmdline_auth_info_domain(0x55591473b880, 0x5559133fd2b5, 0, 0) = 0x555914746fa0 20582 popt_get_cmdline_auth_info(0x5559146fd300, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x55591473b880 20582 get_cmdline_auth_info_password(0x55591473b880, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x55591473bbe0 20582 popt_get_cmdline_auth_info(0x5559146fd300, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x55591473b880 20582 get_cmdline_auth_info_domain(0x55591473b880, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x555914746fa0 20582 popt_get_cmdline_auth_info(0x5559146fd300, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x55591473b880 20582 get_cmdline_auth_info_username(0x55591473b880, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x55591474b660 20582 lp_netbios_name(0x5559146fd300, 0x5559133fd2b5, 0x7f3e4e14d197, 0) = 0x55591473c210 20582 cli_full_connection(0x7ffff4a0f658, 0x55591473c210, 0x55591474bab0, 0) = 0 20582 popt_get_cmdline_auth_info(7, 0x55591473b670, 0x55591474bb40, 0) = 0x55591473b880 20582 get_cmdline_auth_info_smb_encrypt(0x55591473b880, 0x55591473b670, 0x55591474bb40, 0) = 0 20582 cli_set_timeout(0x555914735d40, 10000, 0x55591474bb40, 0) = 0x4e20 20582 dcerpc_binding_get_transport(0x555914732f30, 0, 0x55591474b520, 0) = 1 20582 strchr_m(0x55591473bcc0, 59, 0x55591474b520, 0) = 0 20582 strdup("lookupnames user1") = 0x555914733450 20582 popt_get_cmdline_auth_info(0x555914733450, 0x55591473bcc0, 18, 48) = 0x55591473b880 20582 poptParseArgvString(0x555914733450, 0x7ffff4a0f5d4, 0x7ffff4a0f5d8, 48) = 0 20582 _talloc_stackframe(0x555913449510, 0, 0, 0) = 0x55591474bc20 20582 cli_rpc_pipe_open_noauth_transport(0x555914735d40, 1, 0x7f3e4e694fe0, 0x55591347f1b0) = 0 20582 rpccli_set_timeout(0x555914734480, 10000, 0, 0) = 10000 20582 rpccli_lsa_open_policy(0x555914734480, 0x55591474bc20, 1, 0x2000000) = 0 20582 rpccli_lsa_lookup_names(0x555914734480, 0x55591474bc20, 0x7ffff4a0f480, 1) = 0 20582 sid_type_lookup(1, 1104, 0, 0x55591475e6c0) = 0x7f3e4eb07305 20582 dom_sid_str_buf(0x555914761c00, 0x7ffff4a0f4a0, 0x7f3e4eb109a0, 1) = 0x7ffff4a0f4a0 20582 __printf_chk(1, 0x5559133fcb55, 0x55591474ac24, 0x7ffff4a0f4a0) = 62 20582 dcerpc_lsa_Close(0x55591474efc0, 0x55591474bc20, 0x7ffff4a0f480, 0x7ffff4a0f46c) = 0 20582 _talloc_free(0x55591474bc20, 0x5559134498a8, 0x5559146fd010, 0) = 0 20582 free(0x55591474ac00) = <void> 20582 free(0x555914733450) = <void> 20582 cli_shutdown(0x555914735d40, 0x555914733450, 0x5559146fd010, 5) = 0 20582 popt_free_cmdline_auth_info(32, 0x555914735ce0, 0x5559146fd010, 3) = 0 20582 netlogon_creds_cli_close_global_db(7, 0, 0x7f3e4dc35a40, 7) = 0 20582 _talloc_free(0x55591472dbf0, 0x5559133fbff0, 0x7f3e4dc35a40, 7) = 0 20582 +++ exited (status 0) +++ Cheers, -- Aur?lien Aptel / SUSE Labs Samba Team GPG: 1839 CB5F 9F5B FB9B AA97 8C99 03C8 A49B 521B D5D3 SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 N?rnberg, DE GF: Felix Imend?rffer, Mary Higgins, Sri Rasiah HRB 247165 (AG M?nchen)
Vincent Le Toux
2020-Oct-18 12:48 UTC
[Samba] Lookup sid with libsmbclient (invoked from c# on mono)
Hi, There is some additional work to do (close lsa handle), but the far the code below is working. Let me share it with you in case somebody will have the same need that me. br Vincent LE TOUX using Microsoft.Win32.SafeHandles; using System; using System.ComponentModel; using System.Diagnostics; using System.Net; using System.Runtime.InteropServices; using System.Security.Principal; namespace PingCastle.ADWS { internal class SambaSidResolver : IDisposable { const int SECURITY_MAX_SID_SIZE = 68; [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] static internal extern IntPtr _talloc_stackframe(string context); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern IntPtr user_auth_info_init(IntPtr mem_ctx); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern void set_cmdline_auth_info_domain(IntPtr auth_info, string domain); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern void set_cmdline_auth_info_password(IntPtr auth_info, string password); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern void set_cmdline_auth_info_username(IntPtr auth_info, string password); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern int cli_full_connection(out SambaConnectionHandle output_cli, string my_name, string dest_host, IntPtr dest_ss, int port, string service, string service_type, string user, string domain, string password, int flags, int signing_state); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern void ndr_table_lsarpc(); internal delegate void RPCTable(); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern int cli_rpc_pipe_open_noauth(SambaConnectionHandle cli, RPCTable table, out IntPtr presult); internal struct policy_handle { public UInt32 handle_type; public Guid uuid; } [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern int rpccli_lsa_open_policy(IntPtr cli, IntPtr mem_ctx, bool sec_qos, uint des_access, ref policy_handle pol); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern int rpccli_lsa_lookup_sids(IntPtr cli, IntPtr mem_ctx, ref policy_handle pol, int num_sids, byte[] sids, out SambaTallocHandle pdomains, out SambaTallocHandle pnames, out SambaTallocHandle ptypes); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern int rpccli_lsa_lookup_names(IntPtr cli, IntPtr mem_ctx, ref policy_handle pol, int num_sids, string[] names, out SambaTallocHandle pdomains, int level, out SambaTallocHandle sids, out SambaTallocHandle ptypes); [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern void lp_set_cmdline(string i, string j); public static string LogLevel { get; set; } IntPtr memoryContext; static object lockobject = new object(); //in NetworkCredential credential; string remoteserver; // lsa connections state SambaConnectionHandle connectionHandle; IntPtr rpcHandle; policy_handle policy = new policy_handle(); public SambaSidResolver(NetworkCredential credential, string remoteserver) { if (credential == null) throw new NotImplementedException("Credential required"); memoryContext = _talloc_stackframe("PingCastle"); this.credential = credential; this.remoteserver = remoteserver; } private IntPtr BuildAuthInfo() { Trace.WriteLine(@"BuildAuthInfo"); var auth = user_auth_info_init(memoryContext); set_cmdline_auth_info_domain(auth, credential.Domain); set_cmdline_auth_info_username(auth, credential.UserName); set_cmdline_auth_info_password(auth, credential.Password); return auth; } private int ConnectWithFull() { Trace.WriteLine(@"Before ConnectoWithFull"); return cli_full_connection(out connectionHandle, "PingCastle", remoteserver, IntPtr.Zero, 0, "IPC$", "IPC", credential.UserName, credential.Domain, credential.Password, 0, -2); } private void ConnectToLsa() { Trace.WriteLine(@"ConnectToLsa Init"); if (!string.IsNullOrEmpty(LogLevel)) { lp_set_cmdline("log level", LogLevel); } lp_set_cmdline("client ipc signing", "required"); var r = ConnectWithFull(); if (r != 0) { throw new Win32Exception(r, "Unable to ConnectWithFull"); } r = cli_rpc_pipe_open_noauth(connectionHandle, ndr_table_lsarpc, out rpcHandle); if (r != 0) { throw new Win32Exception(r, "Unable to cli_rpc_pipe_open_noauth"); } r = rpccli_lsa_open_policy(rpcHandle, memoryContext, true, (uint)(1L << 25), ref policy); if (r != 0) { throw new Win32Exception(r, "Unable to rpccli_lsa_open_policy"); } Trace.WriteLine(@"ConnectToLsa OK"); } private void DisconnectFromLsa() { connectionHandle.Close(); } public string ConvertSIDToName(string sidstring, out string referencedDomain) { lock (lockobject) { if (rpcHandle == IntPtr.Zero) ConnectToLsa(); referencedDomain = null; var sids = new byte[SECURITY_MAX_SID_SIZE * 1]; var sid = new SecurityIdentifier(sidstring); sid.GetBinaryForm(sids, 1 * 0); SambaTallocHandle domainsIntPtr; SambaTallocHandle namesIntPtr; SambaTallocHandle typesIntPtr; var status = rpccli_lsa_lookup_sids(rpcHandle, memoryContext, ref policy, 1, sids, out domainsIntPtr, out namesIntPtr, out typesIntPtr); if (status != 0) { Trace.WriteLine(@"Error " + status + " when translating " + sidstring + " on " + remoteserver); return sidstring; } var domains1 Marshal.ReadIntPtr(domainsIntPtr.DangerousGetHandle()); referencedDomain = Marshal.PtrToStringAnsi(domains1); var names1 Marshal.ReadIntPtr(namesIntPtr.DangerousGetHandle()); var name = Marshal.PtrToStringAnsi(names1); domainsIntPtr.Close(); namesIntPtr.Close(); typesIntPtr.Close(); if (String.IsNullOrEmpty(referencedDomain)) return name; else return referencedDomain + "\\" + name; } } public SecurityIdentifier ConvertNameToSid(string nameToResolve) { lock (lockobject) { if (rpcHandle == IntPtr.Zero) ConnectToLsa(); SambaTallocHandle domainsIntPtr; SambaTallocHandle sidsIntPtr; SambaTallocHandle typesIntPtr; var status = rpccli_lsa_lookup_names(rpcHandle, memoryContext, ref policy, 1, new string[] { nameToResolve }, out domainsIntPtr, 1, out sidsIntPtr, out typesIntPtr); if (status != 0) { Trace.WriteLine(@"Error " + status + " when translating " + nameToResolve + " on " + remoteserver); return null; } var domains1 Marshal.ReadIntPtr(domainsIntPtr.DangerousGetHandle()); var referencedDomain = Marshal.PtrToStringAnsi(domains1); var sid = new SecurityIdentifier(sidsIntPtr.DangerousGetHandle()); sidsIntPtr.Close(); domainsIntPtr.Close(); typesIntPtr.Close(); return sid; } } public void Dispose() { if (rpcHandle != IntPtr.Zero) DisconnectFromLsa(); } } internal class SambaTallocHandle : SafeHandleZeroOrMinusOneIsInvalid { [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] static internal extern void _talloc_free(IntPtr context); public SambaTallocHandle() : base(true) { } public SambaTallocHandle(IntPtr handle) : base(true) { SetHandle(handle); } protected override bool ReleaseHandle() { _talloc_free(this.handle); return true; } } internal class SambaConnectionHandle : SafeHandleZeroOrMinusOneIsInvalid { [DllImport("libsmbclient.so.0", CharSet = CharSet.Ansi)] internal static extern void cli_shutdown(IntPtr cli); public SambaConnectionHandle() : base(true) { } public SambaConnectionHandle(IntPtr handle) : base(true) { SetHandle(handle); } protected override bool ReleaseHandle() { cli_shutdown(this.handle); return true; } } }