forked from microsoft/fsharplu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSecurity.fs
111 lines (92 loc) · 4.04 KB
/
Security.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/// Security-related functions
module Microsoft.FSharpLu.Security
#nowarn "9"
open System.Security
open System.Runtime.InteropServices
/// Decrypt a secure string
let convertToUnsecureString (secureString:SecureString) =
let unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString)
try
Marshal.PtrToStringUni(unmanagedString)
finally
if unmanagedString <> System.IntPtr.Zero then
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString)
/// Create a secure string
/// Converted from http://blogs.msdn.com/b/fpintos/archive/2009/06/12/how-to-properly-convert-securestring-to-string.aspx,
/// courtesy of F# Discussion <[email protected]>.
let convertToSecureString (s: string) =
if s = null then
raise <| new System.ArgumentNullException("s")
let gcHandle = GCHandle.Alloc(s, GCHandleType.Pinned)
try
let secureString = new SecureString(NativeInterop.NativePtr.ofNativeInt (gcHandle.AddrOfPinnedObject()), s.Length)
secureString.MakeReadOnly()
secureString
finally
gcHandle.Free()
/// Split login of the form "DOMAIN\alias" into domain and alias parts
let private splitLogin (login:string) =
let aliasPos = login.LastIndexOf('\\')
if aliasPos > 0 then
let domain = login.Substring(0, aliasPos)
let user = login.Substring(aliasPos + 1)
domain, user
else
"", login
/// Equality test for AD identities
let private sameIdentity (d1,a1) (d2, a2) =
let c x y = System.String.Compare(x, y, System.StringComparison.OrdinalIgnoreCase) = 0
c a1 a2 && c d1 d2
////
//// Identity impersonation.
//// Ported from https://msdn.microsoft.com/en-us/library/w070t6ka(v=vs.110).aspx
////
open System
open System.Security.Principal
open Microsoft.Win32.SafeHandles
open System.Runtime.ConstrainedExecution
open System.Security.Permissions
type SafeTokenHandle private () =
inherit SafeHandleZeroOrMinusOneIsInvalid(true)
[<DllImport("kernel32.dll")>]
[<ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)>]
[<SuppressUnmanagedCodeSecurity>]
static extern [<MarshalAs(UnmanagedType.Bool)>] bool CloseHandle(IntPtr handle)
override x.ReleaseHandle() =
CloseHandle(x.handle)
[<DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)>]
extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, [<Out>] SafeTokenHandle& phToken)
type PROVIDER =
| DEFAULT = 0
type LOGON32 =
| LOGON_INTERACTIVE = 2
| LOGON_NETWORK = 3
| LOGON_BATCH = 4
| LOGON_SERVICE = 5
| LOGON_UNLOCK = 7
| LOGON_NETWORK_CLEARTEXT = 8
| LOGON_NEW_CREDENTIALS = 9
[<PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")>]
let public impersonate (log:Logger.Logger<_,_>) logonType alias domain getPwd f =
let mutable safeTokenHandle = Unchecked.defaultof<_>
let logonType = defaultArg logonType LOGON32.LOGON_INTERACTIVE
let returnValue = LogonUser(alias, domain, getPwd(), (int)logonType, (int)PROVIDER.DEFAULT, &safeTokenHandle)
if not returnValue then
let ret = Marshal.GetLastWin32Error()
log.write "[WARNING] LogonUser failed with error code : 0x%X" ret
raise <| new ComponentModel.Win32Exception(ret)
use x = safeTokenHandle
log.write "Impersonating %s\%s from %s" domain alias (WindowsIdentity.GetCurrent().Name)
use newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle())
let result =
(use impersonatedUser = newId.Impersonate()
f())
log.write "Reimpersonated as %s" (WindowsIdentity.GetCurrent().Name)
result
[<PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")>]
let public impersonateIfNecessary log logonType alias domain getPwd f =
let currentLogin = WindowsIdentity.GetCurrent().Name |> splitLogin
if not <| sameIdentity currentLogin (domain, alias) then
impersonate log logonType alias domain getPwd f
else
f()