PageRenderTime 50ms CodeModel.GetById 2ms app.highlight 42ms RepoModel.GetById 1ms app.codeStats 0ms

/Visual Studio 2008/VBUACSelfElevation/MainForm.vb

#
Visual Basic | 464 lines | 237 code | 57 blank | 170 comment | 1 complexity | f1151439c1b336cbedcf5feac84aafff MD5 | raw file
  1'********************************* Module Header ***********************************\
  2' Module Name:  MainForm.vb
  3' Project:      VBUACSelfElevation
  4' Copyright (c) Microsoft Corporation.
  5' 
  6' User Account Control (UAC) is a new security component in Windows Vista and newer 
  7' operating systems. With UAC fully enabled, interactive administrators normally run 
  8' with least user privileges. This example demonstrates how to check the privilege 
  9' level of the current process, and how to self-elevate the process by giving 
 10' explicit consent with the Consent UI. 
 11' 
 12' This source is subject to the Microsoft Public License.
 13' See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
 14' All other rights reserved.
 15' 
 16' THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
 17' EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF 
 18' MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 19'***********************************************************************************/
 20
 21#Region "Imports directives"
 22
 23Imports Microsoft.Win32.SafeHandles
 24Imports System.Runtime.InteropServices
 25Imports System.Security.Principal
 26Imports System.ComponentModel
 27
 28#End Region
 29
 30
 31Public Class MainForm
 32
 33
 34#Region "Helper Functions for Admin Privileges and Elevation Status"
 35
 36    ''' <summary>
 37    ''' The function checks whether the primary access token of the process belongs 
 38    ''' to user account that is a member of the local Administrators group, even if 
 39    ''' it currently is not elevated.
 40    ''' </summary>
 41    ''' <returns>
 42    ''' Returns True if the primary access token of the process belongs to user 
 43    ''' account that is a member of the local Administrators group. Returns False 
 44    ''' if the token does not.
 45    ''' </returns>
 46    ''' <exception cref="System.ComponentModel.Win32Exception">
 47    ''' When any native Windows API call fails, the function throws a Win32Exception 
 48    ''' with the last error code.
 49    ''' </exception>
 50    Friend Function IsUserInAdminGroup() As Boolean
 51        Dim fInAdminGroup As Boolean = False
 52        Dim hToken As SafeTokenHandle = Nothing
 53        Dim hTokenToCheck As SafeTokenHandle = Nothing
 54        Dim pElevationType As IntPtr = IntPtr.Zero
 55        Dim pLinkedToken As IntPtr = IntPtr.Zero
 56        Dim cbSize As Integer = 0
 57
 58        Try
 59            ' Open the access token of the current process for query and duplicate.
 60            If (Not NativeMethods.OpenProcessToken(Process.GetCurrentProcess.Handle, _
 61                NativeMethods.TOKEN_QUERY Or NativeMethods.TOKEN_DUPLICATE, hToken)) Then
 62                Throw New Win32Exception
 63            End If
 64
 65            ' Determine whether system is running Windows Vista or later operating 
 66            ' systems (major version >= 6) because they support linked tokens, but 
 67            ' previous versions (major version < 6) do not.
 68            If (Environment.OSVersion.Version.Major >= 6) Then
 69                ' Running Windows Vista or later (major version >= 6). 
 70                ' Determine token type: limited, elevated, or default. 
 71
 72                ' Allocate a buffer for the elevation type information.
 73                cbSize = 4  ' Size of TOKEN_ELEVATION_TYPE
 74                pElevationType = Marshal.AllocHGlobal(cbSize)
 75                If (pElevationType = IntPtr.Zero) Then
 76                    Throw New Win32Exception
 77                End If
 78
 79                ' Retrieve token elevation type information.
 80                If (Not NativeMethods.GetTokenInformation(hToken, _
 81                    TOKEN_INFORMATION_CLASS.TokenElevationType, _
 82                    pElevationType, cbSize, cbSize)) Then
 83                    Throw New Win32Exception
 84                End If
 85
 86                ' Marshal the TOKEN_ELEVATION_TYPE enum from native to .NET.
 87                Dim elevType As TOKEN_ELEVATION_TYPE = Marshal.ReadInt32(pElevationType)
 88
 89                ' If limited, get the linked elevated token for further check.
 90                If (elevType = TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited) Then
 91                    ' Allocate a buffer for the linked token.
 92                    cbSize = IntPtr.Size
 93                    pLinkedToken = Marshal.AllocHGlobal(cbSize)
 94                    If (pLinkedToken = IntPtr.Zero) Then
 95                        Throw New Win32Exception
 96                    End If
 97
 98                    ' Get the linked token.
 99                    If (Not NativeMethods.GetTokenInformation(hToken, _
100                        TOKEN_INFORMATION_CLASS.TokenLinkedToken, _
101                        pLinkedToken, cbSize, cbSize)) Then
102                        Throw New Win32Exception
103                    End If
104
105                    ' Marshal the linked token value from native to .NET.
106                    Dim hLinkedToken As IntPtr = Marshal.ReadIntPtr(pLinkedToken)
107                    hTokenToCheck = New SafeTokenHandle(hLinkedToken)
108                End If
109            End If
110
111            ' CheckTokenMembership requires an impersonation token. If we just got a 
112            ' linked token, it already is an impersonation token.  If we did not get 
113            ' a linked token, duplicate the original into an impersonation token for 
114            ' CheckTokenMembership.
115            If (hTokenToCheck Is Nothing) Then
116                If (Not NativeMethods.DuplicateToken(hToken, _
117                    SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, _
118                    hTokenToCheck)) Then
119                    Throw New Win32Exception
120                End If
121            End If
122
123            ' Check if the token to be checked contains admin SID.
124            Dim id As New WindowsIdentity(hTokenToCheck.DangerousGetHandle)
125            Dim principal As New WindowsPrincipal(id)
126            fInAdminGroup = principal.IsInRole(WindowsBuiltInRole.Administrator)
127
128        Finally
129            ' Centralized cleanup for all allocated resources. 
130            If (Not hToken Is Nothing) Then
131                hToken.Close()
132                hToken = Nothing
133            End If
134            If (Not hTokenToCheck Is Nothing) Then
135                hTokenToCheck.Close()
136                hTokenToCheck = Nothing
137            End If
138            If (pElevationType <> IntPtr.Zero) Then
139                Marshal.FreeHGlobal(pElevationType)
140                pElevationType = IntPtr.Zero
141            End If
142            If (pLinkedToken <> IntPtr.Zero) Then
143                Marshal.FreeHGlobal(pLinkedToken)
144                pLinkedToken = IntPtr.Zero
145            End If
146        End Try
147
148        Return fInAdminGroup
149    End Function
150
151
152    ''' <summary>
153    ''' The function checks whether the current process is run as administrator.
154    ''' In other words, it dictates whether the primary access token of the 
155    ''' process belongs to user account that is a member of the local 
156    ''' Administrators group and it is elevated.
157    ''' </summary>
158    ''' <returns>
159    ''' Returns True if the primary access token of the process belongs to user 
160    ''' account that is a member of the local Administrators group and it is 
161    ''' elevated. Returns False if the token does not.
162    ''' </returns>
163    Friend Function IsRunAsAdmin() As Boolean
164        Dim principal As New WindowsPrincipal(WindowsIdentity.GetCurrent)
165        Return principal.IsInRole(WindowsBuiltInRole.Administrator)
166    End Function
167
168
169    ''' <summary>
170    ''' The function gets the elevation information of the current process. It 
171    ''' dictates whether the process is elevated or not. Token elevation is only 
172    ''' available on Windows Vista and newer operating systems, thus 
173    ''' IsProcessElevated throws a C++ exception if it is called on systems prior 
174    ''' to Windows Vista. It is not appropriate to use this function to determine 
175    ''' whether a process is run as administartor.
176    ''' </summary>
177    ''' <returns>
178    ''' Returns True if the process is elevated. Returns False if it is not.
179    ''' </returns>
180    ''' <exception cref="System.ComponentModel.Win32Exception">
181    ''' When any native Windows API call fails, the function throws a Win32Exception
182    ''' with the last error code.
183    ''' </exception>
184    ''' <remarks>
185    ''' TOKEN_INFORMATION_CLASS provides TokenElevationType to check the elevation 
186    ''' type (TokenElevationTypeDefault / TokenElevationTypeLimited / 
187    ''' TokenElevationTypeFull) of the process. It is different from TokenElevation 
188    ''' in that, when UAC is turned off, elevation type always returns 
189    ''' TokenElevationTypeDefault even though the process is elevated (Integrity 
190    ''' Level == High). In other words, it is not safe to say if the process is 
191    ''' elevated based on elevation type. Instead, we should use TokenElevation.
192    ''' </remarks>
193    Friend Function IsProcessElevated() As Boolean
194        Dim fIsElevated As Boolean = False
195        Dim hToken As SafeTokenHandle = Nothing
196        Dim cbTokenElevation As Integer = 0
197        Dim pTokenElevation As IntPtr = IntPtr.Zero
198
199        Try
200            ' Open the access token of the current process with TOKEN_QUERY.
201            If (Not NativeMethods.OpenProcessToken(Process.GetCurrentProcess.Handle, _
202                NativeMethods.TOKEN_QUERY, hToken)) Then
203                Throw New Win32Exception
204            End If
205
206            ' Allocate a buffer for the elevation information.
207            cbTokenElevation = Marshal.SizeOf(GetType(TOKEN_ELEVATION))
208            pTokenElevation = Marshal.AllocHGlobal(cbTokenElevation)
209            If (pTokenElevation = IntPtr.Zero) Then
210                Throw New Win32Exception
211            End If
212
213            ' Retrieve token elevation information.
214            If (Not NativeMethods.GetTokenInformation(hToken, _
215                TOKEN_INFORMATION_CLASS.TokenElevation, _
216                pTokenElevation, cbTokenElevation, cbTokenElevation)) Then
217                ' When the process is run on operating systems prior to 
218                ' Windows Vista, GetTokenInformation returns false with the 
219                ' ERROR_INVALID_PARAMETER error code because 
220                ' TokenIntegrityLevel is not supported on those OS's.
221                Throw New Win32Exception
222            End If
223
224            ' Marshal the TOKEN_ELEVATION struct from native to .NET
225            Dim elevation As TOKEN_ELEVATION = Marshal.PtrToStructure( _
226            pTokenElevation, GetType(TOKEN_ELEVATION))
227
228            ' TOKEN_ELEVATION.TokenIsElevated is a non-zero value if the 
229            ' token has elevated privileges; otherwise, a zero value.
230            fIsElevated = (elevation.TokenIsElevated <> 0)
231
232        Finally
233            ' Centralized cleanup for all allocated resources.
234            If (Not hToken Is Nothing) Then
235                hToken.Close()
236                hToken = Nothing
237            End If
238            If (pTokenElevation <> IntPtr.Zero) Then
239                Marshal.FreeHGlobal(pTokenElevation)
240                pTokenElevation = IntPtr.Zero
241                cbTokenElevation = 0
242            End If
243        End Try
244
245        Return fIsElevated
246    End Function
247
248
249    ''' <summary>
250    ''' The function gets the integrity level of the current process. Integrity 
251    ''' level is only available on Windows Vista and newer operating systems, thus 
252    ''' GetProcessIntegrityLevel throws a C++ exception if it is called on systems 
253    ''' prior to Windows Vista.
254    ''' </summary>
255    ''' <returns>
256    ''' Returns the integrity level of the current process. It is usually one of 
257    ''' these values:
258    ''' 
259    '''    SECURITY_MANDATORY_UNTRUSTED_RID - means untrusted level. It is used by 
260    '''    processes started by the Anonymous group. Blocks most write access.
261    '''    (SID: S-1-16-0x0)
262    '''    
263    '''    SECURITY_MANDATORY_LOW_RID - means low integrity level. It is used by 
264    '''    Protected Mode Internet Explorer. Blocks write acess to most objects 
265    '''    (such as files and registry keys) on the system. (SID: S-1-16-0x1000)
266    ''' 
267    '''    SECURITY_MANDATORY_MEDIUM_RID - means medium integrity level. It is used 
268    '''    by normal applications being launched while UAC is enabled. 
269    '''    (SID: S-1-16-0x2000)
270    '''    
271    '''    SECURITY_MANDATORY_HIGH_RID - means high integrity level. It is used by 
272    '''    administrative applications launched through elevation when UAC is 
273    '''    enabled, or normal applications if UAC is disabled and the user is an 
274    '''    administrator. (SID: S-1-16-0x3000)
275    '''    
276    '''    SECURITY_MANDATORY_SYSTEM_RID - means system integrity level. It is used 
277    '''    by services and other system-level applications (such as Wininit, 
278    '''    Winlogon, Smss, etc.)  (SID: S-1-16-0x4000)
279    ''' 
280    ''' </returns>
281    ''' <exception cref="System.ComponentModel.Win32Exception">
282    ''' When any native Windows API call fails, the function throws a Win32Exception 
283    ''' with the last error code.
284    ''' </exception>
285    Friend Function GetProcessIntegrityLevel() As Integer
286        Dim IL As Integer = -1
287        Dim hToken As SafeTokenHandle = Nothing
288        Dim cbTokenIL As Integer = 0
289        Dim pTokenIL As IntPtr = IntPtr.Zero
290
291        Try
292            ' Open the access token of the current process with TOKEN_QUERY.
293            If (Not NativeMethods.OpenProcessToken(Process.GetCurrentProcess.Handle, _
294                NativeMethods.TOKEN_QUERY, hToken)) Then
295                Throw New Win32Exception
296            End If
297
298            ' Then we must query the size of the integrity level information 
299            ' associated with the token. Note that we expect GetTokenInformation to 
300            ' return False with the ERROR_INSUFFICIENT_BUFFER error code because we 
301            ' have given it a null buffer. On exit cbTokenIL will tell the size of 
302            ' the group information.
303            If (Not NativeMethods.GetTokenInformation(hToken, _
304                TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, _
305                IntPtr.Zero, 0, cbTokenIL)) Then
306                Dim err As Integer = Marshal.GetLastWin32Error
307                If (err <> NativeMethods.ERROR_INSUFFICIENT_BUFFER) Then
308                    ' When the process is run on operating systems prior to Windows 
309                    ' Vista, GetTokenInformation returns false with the 
310                    ' ERROR_INVALID_PARAMETER error code because TokenIntegrityLevel 
311                    ' is not supported on those OS's.
312                    Throw New Win32Exception(err)
313                End If
314            End If
315
316            ' Now we allocate a buffer for the integrity level information.
317            pTokenIL = Marshal.AllocHGlobal(cbTokenIL)
318            If (pTokenIL = IntPtr.Zero) Then
319                Throw New Win32Exception
320            End If
321
322            ' Now we ask for the integrity level information again. This may fail if 
323            ' an administrator has added this account to an additional group between 
324            ' our first call to GetTokenInformation and this one.
325            If (Not NativeMethods.GetTokenInformation(hToken, _
326                TOKEN_INFORMATION_CLASS.TokenIntegrityLevel, _
327                pTokenIL, cbTokenIL, cbTokenIL)) Then
328                Throw New Win32Exception
329            End If
330
331            ' Marshal the TOKEN_MANDATORY_LABEL struct from native to .NET object.
332            Dim tokenIL As TOKEN_MANDATORY_LABEL = Marshal.PtrToStructure( _
333            pTokenIL, GetType(TOKEN_MANDATORY_LABEL))
334
335            ' Integrity Level SIDs are in the form of S-1-16-0xXXXX. (e.g. 
336            ' S-1-16-0x1000 stands for low integrity level SID). There is one and 
337            ' only one subauthority.
338            Dim pIL As IntPtr = NativeMethods.GetSidSubAuthority(tokenIL.Label.Sid, 0)
339            IL = Marshal.ReadInt32(pIL)
340
341        Finally
342            ' Centralized cleanup for all allocated resources. 
343            If (Not hToken Is Nothing) Then
344                hToken.Close()
345                hToken = Nothing
346            End If
347            If (pTokenIL <> IntPtr.Zero) Then
348                Marshal.FreeHGlobal(pTokenIL)
349                pTokenIL = IntPtr.Zero
350                cbTokenIL = 0
351            End If
352        End Try
353
354        Return IL
355    End Function
356
357#End Region
358
359
360    Private Sub MainForm_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _
361    Handles MyBase.Load
362
363        ' Get and display whether the primary access token of the process belongs 
364        ' to user account that is a member of the local Administrators group even 
365        ' if it currently is not elevated (IsUserInAdminGroup).
366        Try
367            Dim fInAdminGroup As Boolean = Me.IsUserInAdminGroup
368            Me.lbInAdminGroup.Text = fInAdminGroup.ToString
369        Catch ex As Exception
370            Me.lbInAdminGroup.Text = "N/A"
371            MessageBox.Show(ex.Message, "An error occurred in IsUserInAdminGroup", _
372                            MessageBoxButtons.OK, MessageBoxIcon.Error)
373        End Try
374
375        ' Get and display whether the process is run as administrator or not 
376        ' (IsRunAsAdmin).
377        Try
378            Dim fIsRunAsAdmin As Boolean = Me.IsRunAsAdmin
379            Me.lbIsRunAsAdmin.Text = fIsRunAsAdmin.ToString
380        Catch ex As Exception
381            Me.lbIsRunAsAdmin.Text = "N/A"
382            MessageBox.Show(ex.Message, "An error occurred in IsRunAsAdmin", _
383                            MessageBoxButtons.OK, MessageBoxIcon.Error)
384        End Try
385
386        ' Get and display the process elevation information (IsProcessElevated) 
387        ' and integrity level (GetProcessIntegrityLevel). The information is not 
388        ' available on operating systems prior to Windows Vista.
389        If (Environment.OSVersion.Version.Major >= 6) Then
390            ' Running Windows Vista or later (major version >= 6). 
391
392            Try
393                ' Get and display the process elevation information.
394                Dim fIsElevated As Boolean = Me.IsProcessElevated
395                Me.lbIsElevated.Text = fIsElevated.ToString
396
397                ' Update the Self-elevate button to show the UAC shield icon on 
398                ' the UI if the process is not elevated.
399                Me.btnElevate.FlatStyle = FlatStyle.System
400                NativeMethods.SendMessage(Me.btnElevate.Handle, NativeMethods.BCM_SETSHIELD, _
401                                         0, IIf(fIsElevated, IntPtr.Zero, New IntPtr(1)))
402            Catch ex As Exception
403                Me.lbIsElevated.Text = "N/A"
404                MessageBox.Show(ex.Message, "An error occurred in IsProcessElevated", _
405                                MessageBoxButtons.OK, MessageBoxIcon.Error)
406            End Try
407
408            Try
409                ' Get and display the process integrity level.
410                Dim IL As Integer = Me.GetProcessIntegrityLevel
411                Select Case IL
412                    Case NativeMethods.SECURITY_MANDATORY_UNTRUSTED_RID
413                        Me.lbIntegrityLevel.Text = "Untrusted"
414                    Case NativeMethods.SECURITY_MANDATORY_LOW_RID
415                        Me.lbIntegrityLevel.Text = "Low"
416                    Case NativeMethods.SECURITY_MANDATORY_MEDIUM_RID
417                        Me.lbIntegrityLevel.Text = "Medium"
418                    Case NativeMethods.SECURITY_MANDATORY_HIGH_RID
419                        Me.lbIntegrityLevel.Text = "High"
420                    Case NativeMethods.SECURITY_MANDATORY_SYSTEM_RID
421                        Me.lbIntegrityLevel.Text = "System"
422                    Case Else
423                        Me.lbIntegrityLevel.Text = "Unknown"
424                End Select
425            Catch ex As Exception
426                Me.lbIntegrityLevel.Text = "N/A"
427                MessageBox.Show(ex.Message, "An error occurred in GetProcessIntegrityLevel!", _
428                                MessageBoxButtons.OK, MessageBoxIcon.Hand)
429            End Try
430
431        Else
432            Me.lbIsElevated.Text = "N/A"
433            Me.lbIntegrityLevel.Text = "N/A"
434        End If
435
436    End Sub
437
438
439    Private Sub btnElevate_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
440    Handles btnElevate.Click
441        ' Elevate the process if it is not run as administrator.
442        If (Not Me.IsRunAsAdmin) Then
443            ' Launch itself as administrator
444            Dim proc As New ProcessStartInfo
445            proc.UseShellExecute = True
446            proc.WorkingDirectory = Environment.CurrentDirectory
447            proc.FileName = Application.ExecutablePath
448            proc.Verb = "runas"
449
450            Try
451                Process.Start(proc)
452            Catch
453                ' The user refused the elevation.
454                ' Do nothing and return directly ...
455                Return
456            End Try
457
458            Application.Exit()  ' Quit itself
459        Else
460            MessageBox.Show("The process is running as administrator", "UAC")
461        End If
462    End Sub
463
464End Class