Tuesday, July 24, 2012

Adding domain user as local admin immediatly after domain join

Here's a way to add a domain user as a local admin immediatly after joining the domain, without rebooting first:
  1. using System;  
  2. using System.Runtime.InteropServices;  
  3. using System.Security.Principal;  
  4. using System.Text;  
  5.   
  6. class NativeMethods  
  7. {  
  8.     public const int LOGON32_LOGON_INTERACTIVE = 2;  
  9.     public const int LOGON32_LOGON_NETWORK = 3;  
  10.     public const int LOGON32_LOGON_BATCH = 4;  
  11.     public const int LOGON32_LOGON_SERVICE = 5;  
  12.     public const int LOGON32_LOGON_UNLOCK = 7;  
  13.     public const int LOGON32_LOGON_NETWORK_CLEARTEXT = 8;  
  14.     public const int LOGON32_LOGON_NEW_CREDENTIALS = 9;  
  15.   
  16.     public enum SID_NAME_USE  
  17.     {  
  18.         SidTypeUser = 1,  
  19.         SidTypeGroup,  
  20.         SidTypeDomain,  
  21.         SidTypeAlias,  
  22.         SidTypeWellKnownGroup,  
  23.         SidTypeDeletedAccount,  
  24.         SidTypeInvalid,  
  25.         SidTypeUnknown,  
  26.         SidTypeComputer,  
  27.     }  
  28.   
  29.     public struct LOCALGROUP_MEMBERS_INFO_0  
  30.     {  
  31.         public IntPtr PSID;  
  32.     }  
  33.   
  34.     [DllImport("kernel32.dll")]  
  35.     public extern static bool CloseHandle(IntPtr hToken);  
  36.   
  37.     [DllImport("advapi32.DLL", SetLastError = true)]  
  38.     public static extern int LogonUser(  
  39.         string lpszUsername,  
  40.         string lpszDomain,  
  41.         string lpszPassword,  
  42.         int dwLogonType,  
  43.         int dwLogonProvider,  
  44.         out IntPtr phToken);  
  45.   
  46.     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  47.     public static extern bool LookupAccountName(  
  48.         string lpSystemName,  
  49.         string lpAccountName,  
  50.         [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,  
  51.         ref uint cbSid,  
  52.         StringBuilder ReferencedDomainName,  
  53.         ref uint cchReferencedDomainName,  
  54.         out SID_NAME_USE peUse);  
  55.   
  56.     [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  57.     public static extern bool LookupAccountSid(  
  58.         string lpSystemName,  
  59.         [MarshalAs(UnmanagedType.LPArray)] byte[] lpSid,  
  60.         StringBuilder lpName,  
  61.         ref uint cchName,  
  62.         StringBuilder lpReferencedDomainName,  
  63.         ref uint cchReferencedDomainName,  
  64.         out SID_NAME_USE peUse);  
  65.   
  66.     [DllImport("netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]  
  67.     public static extern int NetLocalGroupAddMembers(  
  68.         string servername,  
  69.         string groupname,  
  70.         uint level,  
  71.         ref LOCALGROUP_MEMBERS_INFO_0 buf,  
  72.         uint totalentries);  
  73. }  
  74.   
  75. public class AddAdminUserHelper  
  76. {  
  77.     public static void AddAdminUser(string domain, string username, string password)  
  78.     {  
  79.         // Get built in administrators account name  
  80.         StringBuilder adminGroupName = new StringBuilder();  
  81.         uint adminGroupNameCapacity = (uint)adminGroupName.Capacity;  
  82.         StringBuilder referencedDomainName = new StringBuilder();  
  83.         uint referencedDomainNameCapacity = (uint)referencedDomainName.Capacity;  
  84.         NativeMethods.SID_NAME_USE eUse;  
  85.         byte[] adminGroupSid = new byte[] { 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2 };  
  86.         if (!NativeMethods.LookupAccountSid(  
  87.             null,  
  88.             adminGroupSid,  
  89.             adminGroupName,  
  90.             ref adminGroupNameCapacity,  
  91.             referencedDomainName,  
  92.             ref referencedDomainNameCapacity,  
  93.             out eUse))  
  94.         {  
  95.             Console.WriteLine("LookupAccountSid failed with error " + Marshal.GetLastWin32Error());  
  96.             return;  
  97.         }  
  98.   
  99.         // Get a security token needed to be able to afterwards query for the user's SID  
  100.         IntPtr token = IntPtr.Zero;  
  101.         if (NativeMethods.LogonUser(  
  102.             username,  
  103.             domain,  
  104.             password,  
  105.             NativeMethods.LOGON32_LOGON_NEW_CREDENTIALS,  
  106.             0,  
  107.             out token) == 0)  
  108.         {  
  109.             Console.WriteLine("LogonUser failed with error " + Marshal.GetLastWin32Error());  
  110.             return;  
  111.         }  
  112.   
  113.         // Get user's SID  
  114.         byte[] userSid = new byte[1024];  
  115.         uint userSidLength = (uint)userSid.Length;  
  116.         referencedDomainName = new StringBuilder();  
  117.         referencedDomainNameCapacity = (uint)referencedDomainName.Capacity;  
  118.         NativeMethods.SID_NAME_USE peUse;  
  119.         using (WindowsImpersonationContext context = WindowsIdentity.Impersonate(token))  
  120.         {  
  121.             if (!NativeMethods.LookupAccountName(  
  122.                 domain,  
  123.                 username,  
  124.                 userSid,  
  125.                 ref userSidLength,  
  126.                 referencedDomainName,  
  127.                 ref referencedDomainNameCapacity,  
  128.                 out peUse))  
  129.             {  
  130.                 Console.WriteLine("LookupAccountName failed with error " + Marshal.GetLastWin32Error());  
  131.                 return;  
  132.             }  
  133.         }  
  134.         NativeMethods.CloseHandle(token);  
  135.   
  136.         // Add user's SID to local admins group  
  137.         IntPtr userSidNative = Marshal.AllocHGlobal(userSid.Length);  
  138.         Marshal.Copy(userSid, 0, userSidNative, (int)userSid.Length);  
  139.         NativeMethods.LOCALGROUP_MEMBERS_INFO_0 info0;  
  140.         info0.PSID = userSidNative;  
  141.         int r = NativeMethods.NetLocalGroupAddMembers(  
  142.             null,  
  143.             adminGroupName.ToString(),  
  144.             0,  
  145.             ref info0,  
  146.             1);  
  147.         Marshal.FreeHGlobal(userSidNative);  
  148.         if (r != 0)  
  149.         {  
  150.             Console.WriteLine("NetLocalGroupAddMembers failed by returning " + r);  
  151.             return;  
  152.         }  
  153.     }  
  154. }  

Thursday, July 12, 2012

Join a domain and rename in one reboot using WMI

If you don't take special care in your script, then renaming a machine and then joining it to a Windows domain will join it using the old name. Apparently there's an undocumented flag 0x400 on JoinDomainOrWorkGroup. At least I think it started working when I added that flag... Here's a PowerShell snippet that's working for me now:
$computer = Get-WmiObject Win32_ComputerSystem
$cred = Get-Credential
$computer.Rename("newmachinename", $NULL, $NULL)
$computer.JoinDomainOrWorkGroup( `
 "mydomain", `
 ($cred.GetNetworkCredential()).Password, `
 $cred.UserName, `
 $NULL, `
 0x1 + 0x2 + 0x20 + 0x400)

Sunday, July 08, 2012

Windows Shares vs. FTP vs. FTPS vs. SFTP

Conducted this little test between a Windows 7 machine and Windows XP machine on a 100 Mbit switch. 

Used FileZilla server for FTP and FTPS server, FileZilla as FTP and FTPS client, Cygwin OpenSSH as SFTP server and WinSCP as SFTP client.

Was surprised to see how much overhead there was on FTPS, but it could also be something specific to FileZilla.

Windows sharesFTP FTPSSFTP
2700 files, 200Mb4,17 Mb/sec
48 secs
4,76 Mb/sec
42 secs
0,61 Mb/sec
326 secs
3,03 Mb/sec
66 secs
1 file, 550Mb9,82 Mb/sec
56 secs
11,22 Mb/sec
49 secs
11,00 Mb/sec
50 secs
6,25 Mb/sec
88 secs

Friday, July 06, 2012

Finding all machines a user owns in Active Directory

Here's a quick and dirty way to find all machines a user owns in Active Directory. Performance is not good though, as the objects are processed client side. I was not able to find any way of doing server side queries dealing with the ntSecurityDescriptor field. Please let me know if you find a way!

Adjust the DN strings as needed.

  1. using System;  
  2. using System.DirectoryServices;  
  3.   
  4. namespace ConsoleApplication1  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             DateTime startTime = DateTime.Now;  
  11.   
  12.             DirectoryEntry d = new DirectoryEntry("LDAP://ou=UserAccounts,dc=contoso,dc=com");  
  13.             DirectorySearcher s = new DirectorySearcher(d, "(&(objectClass=user)(cn=John Smith))",  
  14.                 new string[] { "cn""objectsid" });  
  15.   
  16.             string sid = BitConverter.ToString(  
  17.                     (byte[])s.FindOne().Properties["objectsid"][0])  
  18.                 .Replace("-""");  
  19.   
  20.             int count = 0;  
  21.             d = new DirectoryEntry("LDAP://ou=Machines,dc=contoso,dc=com");  
  22.             s = new DirectorySearcher(d, "(&(objectCategory=computer)(objectClass=computer)(cn=*))",  
  23.                 new string[] { "cn""ntSecurityDescriptor" });  
  24.             s.PageSize = 500;  
  25.             s.SecurityMasks = SecurityMasks.Owner;  
  26.             foreach (SearchResult r in s.FindAll())  
  27.             {  
  28.                 count++;  
  29.                 string cn = (string)r.Properties["cn"][0];  
  30.                 string sd = BitConverter.ToString((byte[])r.Properties["ntsecuritydescriptor"][0]).Replace("-""");  
  31.   
  32.                 if (sd.Contains(sid))  
  33.                     Console.WriteLine(cn);  
  34.             }  
  35.   
  36.             Console.WriteLine();  
  37.             Console.WriteLine("Checked " + count + " objects");  
  38.             Console.WriteLine("Query finished in " + (DateTime.Now - startTime));  
  39.         }  
  40.     }  
  41. }  

Tuesday, June 26, 2012

Compiling Bootstrap LESS on Windows

The current version of Bootstrap can neither be compiled on WinLess nor lessc.exe as of writing. I found that these days the easiest is actually to run the Windows version of Node.js. Standalone single .exe versions of Node.js are available. Go download the less.js source and you can make yourself a "lessjs.cmd" with something like the following:
d:\utils\node.exe d:\utils\lesscss\bin\lessc %1

Monday, April 30, 2012

Django as a micro framework, including models

The following can be used as a base when you want to avoid the normal structure Django imposes on your project, and you just want everything in a single file.

It is based on similar attempts by Ivan Sagalaev and Fahrzin Hemmati (especially this gist), and a post on stack overflow.

A thing that I consider an improvement over Fahrzin Hemmati's version is that models don't need the explicit app_label and __module__.

  1. #  
  2. # Django as a microframework skeleton, by Allan Boll, 1 May 2012  
  3. # Based on http://softwaremaniacs.org/blog/2011/01/07/django-micro-framework/en/  
  4. # and https://gist.github.com/2219751  
  5. #  
  6.   
  7. #  
  8. # Settings  
  9. #  
  10. appname = 'app'  
  11. from django.conf import settings  
  12. if not settings.configured:  
  13.     settings.configure(  
  14.         SITE_ID=1,  
  15.         TEMPLATE_DIRS=['.'],  
  16.         DATABASES = {  
  17.             'default':{  
  18.                 'ENGINE':'django.db.backends.sqlite3',  
  19.                 'NAME''db',  
  20.             }  
  21.         },  
  22.         DEBUG=True,  
  23.         TEMPLATE_DEBUG=True,  
  24.         ROOT_URLCONF = __name__,  
  25.         STATIC_URL='/static/',  
  26.         STATICFILES_DIRS=['./static/'],  
  27.         INSTALLED_APPS=(  
  28.             'django.contrib.auth',  
  29.             'django.contrib.contenttypes',  
  30.             'django.contrib.sessions',  
  31.             'django.contrib.messages',  
  32.             'django.contrib.staticfiles',  
  33.             'django.contrib.admin',   
  34.         )  
  35.     )  
  36.   
  37. #  
  38. # Models  
  39. #  
  40. from django.db import models  
  41.   
  42. # Workaround to allow models in current file  
  43. import sys  
  44. sys.modules[appname+'.'] = sys.modules[__name__]  
  45. old_module_name = sys.modules[__name__].__name__  
  46. sys.modules[__name__].__name__ = appname+'.'  
  47.   
  48. class SomeModel(models.Model):  
  49.     field_name = models.CharField(max_length=10)  
  50.   
  51. # Continuation of workaround to allow models in current file  
  52. sys.modules[__name__].__name__ = old_module_name  
  53.   
  54.   
  55. #  
  56. # Views  
  57. #  
  58. from django.shortcuts import render  
  59.   
  60. def index(request):  
  61.     out = []  
  62.     for obj in SomeModel.objects.all():  
  63.         out.append(obj.field_name)  
  64.     return render(request, 'index.html', {'s'', '.join(out)})  
  65.   
  66.   
  67. #  
  68. # URL configuration  
  69. #  
  70. from django.conf.urls.defaults import patterns, url, include  
  71.   
  72. urlpatterns = patterns('',  
  73.     (r'^$', index),  
  74. )  
  75.   
  76.   
  77. #  
  78. # Admin site  
  79. #  
  80. from django.contrib import admin  
  81. from django.contrib.auth.admin import UserAdmin, GroupAdmin  
  82. from django.contrib.auth.models import User, Group  
  83.   
  84. admin.site = admin.AdminSite()  
  85. admin.site.register(User, UserAdmin)  
  86. admin.site.register(Group, GroupAdmin)  
  87. admin.site.register(SomeModel)  
  88.   
  89. urlpatterns = patterns('',  
  90.     url(r'^admin/', include(admin.site.urls))  
  91. ) + urlpatterns  
  92.   
  93.   
  94. #  
  95. # Running  
  96. #  
  97. if __name__=='__main__':  
  98.     # Monkey patch get_app to allow models in current file  
  99.     get_app_orig = models.get_app  
  100.     def get_app(app_label,*a, **kw):  
  101.         if app_label==appname:  
  102.             return sys.modules[__name__]  
  103.         return get_app_orig(app_label, *a, **kw)  
  104.     models.get_app = get_app  
  105.   
  106.     # Add models in current file to global list of apps  
  107.     models.loading.cache.app_store[type(appname+'.models',(),{'__file__':__file__})] = appname  
  108.   
  109.     from django.core import management  
  110.     management.execute_from_command_line()  

Friday, February 10, 2012

Sublime Text default to empty file

I recently began using the editor Sublime Text. Previously I was using Notepad++ which is also a fine editor, but not as "futuristic" as Sublime Text. I had gotten used to Notepad++'s behaviour of defaulting to an empty file when all tabs are closed, and found that this behaviour was easy to achieve in Sublime Text as well, thanks to its amazing API. To get the behaviour, drop the following Python source into something like Data/Packages/User/default_to_empty_file.py.

  1. import sublime, sublime_plugin  
  2.   
  3. class DefaultToEmptyFile(sublime_plugin.EventListener):  
  4.     def on_close(self, view):  
  5.         for window in sublime.windows():  
  6.             if window == None:  
  7.                 continue  
  8.   
  9.             if len(window.views()) <= 1:  
  10.                 window.new_file()  
  11.   
  12.     def on_load(self, view):  
  13.         window = view.window()  
  14.         if window == None:  
  15.             return  
  16.   
  17.         views = window.views()  
  18.         if len(views) == 2:  
  19.             for v in views:  
  20.                 if v != view and v.file_name() == None and v.size() == 0:  
  21.                     window.focus_view(v)  
  22.                     window.run_command('close_file')