Friday, May 9, 2014

Extending with Science! ... or files

Hey, say you've got like 40,000 files on your hands.  AND you've got a public, Enterprise SharePoint 2010 portal.  AND you've got a mandate to publish each one of those files to the world.  AND you just got kicked off your previous web host.  Watcha gonna do?


Well my friend, use SCIENCE!


OK, not really.  BUT, SharePoint is a Microsoft.NET application, hosted in Internet Information Services, so we can just add a virtual directory to the SharePoint web application and serve away.  Yes, but...


I tried doing just the above once, but didn't discover the secret sauce until just yesterday.  Here's the breakdown:

  • The 40,000 files are served anonymously (this is actually a good thing)
  • Directory access performance isn't really an issue, there aren't a million hits a month let alone a minute.
  • There's already a structured navigational approach to finding the files so there's no need to browse them or build a new catalog.
What do you need to do to spin up a virtual directory inside the SharePoint web app?  Well here's what I did and it's working find, thank you very much.

  1. Establish a virtual directory in your SharePoint web app.  If you've got multiple AAMs and/or multiple WFE's you'll have to repeat the following process for each.
  2. Once you've built the VDIR, double click it's Authentication option.   We only needed anonymous so I turned off everything else (More Later).
That's it.  You're up and running.  But wait you say, I tried that and it didn't work. Well it didn't work for me the second time either.  My first test I had access to the directory that was being access and was using an IE client on the same host as the server.  That three-headed-dog be sent home to Hades!
 
The change?  This time around, instead of using pass-through security on the VDIR, I used the connect as option and connect using an account that has access to the files.  That solved the Anonymous access problem I was having.  Another solution would be to grant the "Everyone" user access to the files.  But that's a problem in and of itself, plus if you're using a remote VDIR it probably won't work unless you make the file share wide open.

The other change?  Well you need to make sure you establish a web.config file in the directory  you're serving.  Just inheriting from the base file in SharePoint's home dir won't work.  Until I established a custom web.config, SharePoint tried to intercede and bump the URL up against the content database.  Not-found is the same as access denied in that world.  So in summary:

  • Use a delegation account to access the VDIR
  • Establish a web.config that explicitly sets the Anonymous access properties
 Well we just turned SharePoint into a regular IIS web application, and my client is happy.

Wednesday, May 7, 2014

SharePoint Powershell -- Awesomesauce

Introduced with SharePoint 2010 (but actually possible with older versions) we were given direct access to the SharePoint object model in a interpreted scripting environment.

With a simple script like this, I can list all of the lists and libraries in a SharePoint web application:

$wa = get-spwebapplication http://site.domain.com
foreach($site in $wa.Sites) {
    foreach($web in $site.AllWebs) {
        foreach($list in $web.Lists) {
            write-host $wa.Name $web.Title $web.Url $list.Title
        }
    }
}


Great, so what you can list a bunch of the content.  Tell me something I can't do with other tools...

OK, here's one.  I had a WSP that installed custom page layouts.  One of the page layouts got modified by a user using SharePoint designer, and even if I returned the page layout to the original state, upgrading or re-installing the WSP wouldn't overwrite the page layout in the content database.

Solution: Detach the each of the publishing pages from the page layout and  make a file that lists each of the publishing pages that were attached to the page layout.  Then remove all of the bad stuff using what ever tool you like and reattach the page layouts based on the data we saved in the file.

Check out this script, the first half of the process of detaching the page layouts. $detLog gets set outside the function and is the full page to the change log.  Just point the function at an SPWeb and a reference to new page layout (it needs to be a Microsoft.SharePoint.Publishing.PageLayout) and away it goes, cycling through the entire SPWeb and its children.  Oh, $comment gets set outside the function too.  It could be something snappy like "Detaching Page Layout for Upgrade."

 Function BFS-PubPage($web, $newlayout) {
    $web.Lists | foreach-object -process {
        $l = $_
        $l.Items | foreach-object -process {
            $i = $_
            if ([Microsoft.SharePoint.Publishing.PublishingPage]::IsPublishingPage($i)) {
                $pp = [Microsoft.SharePoint.Publishing.PublishingPage]::GetPublishingPage($i)
                if ($pp.Layout -ne $null) {
                
                    if ($pp.Layout.ServerRelativeUrl -eq "/_catalogs/masterpage/OffendingLayout1.aspx" `
                       -or $pp.Layout.ServerRelativeUrl -eq "/_catalogs/masterpage/OffendingLayout2.aspx") {
                        write-host -ForegroundColor DarkBlue ($web.Url + "/" + $pp.Url)
                        
                        $cl = $pp.Layout
                        
                        $pp.CheckOut()
                        $pp.Layout = $newlayout
                        $pp.Update()
                        $pp.CheckIn($comment)
                        $pf = $pp.ListItem.File
                        $pf.Publish($comment)
                        $pf.Approve($comment)
                        
                        add-content $detLog ($web.Url + "`t" + $i.Name + "`t" + $pp.Url + "`t" + $cl.ServerRelativeUrl)
                    }
                     else {
                        write-host -ForeGroundColor DarkYellow `
                             ($web.Url + "/" + $pp.Url +"`t" + $pp.Layout.ServerRelativeUrl)
                    }
                }
            }
            
        }
    }
    
    $web.Webs | foreach-object -process {
        if ($_ -ne $null) {
            BFS-PubPage $_ $newlayout
        }
    }
}


You probably noticed, but this script uses a different way to implement the for-each loops.  Instead of using the C# style of the loop, it uses the object pipe method.  I'm guessing one method is probably more efficient than the other, but  you never know.

Tuesday, May 6, 2014

Windows Installer Don'ts

Ok, so I'm a software professional (or at least I thought I was).

A few weeks back my laptop started to complain that I was running out of disk space.  I've only got 500 GB on board, and with a couple of VM's for SharePoint, SQL Server 2012, and various FOSS OSes, I really was running out of diskspace.

Know that Image of Windows Server 2003 running SQL Server 2005 and MOSS 2007?  Well that was too important to purge, so what did I do?  I dug out the handy du command and started building up a hog report.  Where was all of that space going...

  • c:\MSOCache (hidden) - 2.1 GB
  • c:\Windows\Installer (not hidden, but not shown either) - 17.2 GB
  • c:\users\mbattey\AppData\Local\Temp (AppData hidden) - 1.85 GB
  • c:\ProgramData\Microsoft (ProgramData hidden) - 4.7 GG
Hey, I'm a smart guy, right?  What the heck are all of those Windows Installer files doing still hanging around on my computer?   I'll just delete those.  MSOCache? Stupid Microsoft leaving crap everywhere.  Gone!  Temporary files?  Fried!

Ya bad idea.

Windows Update was immediately broken.  There were a handful of updates to Visual Studio (2005, 2008, 2010) waiting to be installed as well as updates for Office.  None of these would go.  Every time they ran, the MSI tool would ask for a GUID named directory off the root of one of the drive partitions, which of course didn't exist.  Trying to run a Repair from Programs and Features failed miserably with the same result.  In fact, the icons for all of the Office Documents, Adobe Reader PDFs, and a bunch of others disappeared as well.  (Methinks that Adobe and MS got lazy and weren't moving all of the DLL's out to "Program Files" like they tell everyone else to do).

So after manually removing all of the remnants from Office following a nice showcase from Microsoft (the girl reading the script sounded nice, but had trouble pronouncing RegEdit and Suite, which came out more like "reg-it" and suit), writing a custom registry cleaner to delete stuff from HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall, HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall, and HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Installer\Products, and finally correcting individual packages with MSIZap, I'm kinda sorta back to normal.

Normal as in I'm back up to all that disk use, and Office and Visual Studio will run updates, mostly.  I do highly recommend MSIZap when you've got the GUID of a a package that won't install, because it's gone missing.  It will root out all of the registry and local files related to the product and in the cases I needed it let a patch go through unperturbed.

Thursday, April 17, 2014

SPTrustedRootAuthority -- Most important internal class for Claims Based Authentication

In wisdom unknown to me the SPTrustedRootAuthority (TRA) class and its manager SPTrustedRootAuthorityManager (TRA manager, both in Microsoft.SharePoint.Administration) are internal and sealed.  Big statement, probably nobody cares but me, so hear me out.  What if you wanted to add on to Central Administration so that CBA could be maitained 100% through the UI?  Can't do it with the classes on hand, because we can't link directly to those classes.

SharePoint maintains an internal Certificate Authority (CA) which just happens to be managed by the SPTrustedRootAuthorityManager.  Microsoft graciously provided us with a form in Central Admin and Power Shell commands to work with the list of certificates (see get-command -noun SPTrustedRootAuthority), but the only way to work one is through then PS command scripts.  This is OK, but it seems like SharePoint team took the easy way out and didn't finish the GUI in Central Admin.

So what's a boy (or girl for that matter) to do?  I went and found all of the PS Cmdlets that parallel the steps in the Claims Walkthrough, just so I wouldn't have to write a Forms app just to setup the SPTrustedLoginProvider.   That was great for me, but I'm a consultant these days and I need to get my clients up and running on this stuff.  If I can barely remember the sequence of command, how could they.  Especially when you want to add new Known Claim Values when a new set of secureables comes out.


Well I took it upon myself to create my own user interface to manage CBA trust providers and developed it into a set of 14 hive application pages, in the ADMIN folder (/_admin/TrustConfig, etc.) so that they would only be available through Central Admin and not through a normal site.   Configuring Trust is an administrative task after all...


So everybody knows when you setup a new CBA Trust you do the following:

  1. Get an X.509 certificate w/o the private key attached in a DER file.
  2. Load the certificate into SharePoint's Trusted Authority Manager (CA or PS usually works)
  3. Create new SPTrustedLoginProvider that references the certificate.
There's some subtleties though.  A X.509 certificate can be used for only one SPTrustedLoginProvider at a time.  Off hand I can't remember if it throws an error when you create the provider or if it error when you try and use it.  Either way, I remember the error message isn't very helpful!  The cert has to be loaded in SP's private CA ahead of time too.  So, why doesn't allow you to just reference the cert's Subject & Issuer?  May because it's important that you have all of the bits in the cert on hand? Maybe, its like the old days, when you couldn't deposit a picture of a check, you had to hand it over to the teller.

Anyway, that was only one problem, when you first setup the Login Provider.  You need to come back to one and update the Known Claim Values, maybe add a new Claim Type now and again too.  But try describing that over the phone (you may be better at than I am).

Solution, add those application pages to Central Admin.  But you'll need to use some reflection magic to get a hold of that data.  Now if you review my code I'm about to post, you may notice that there may have been more direct ways to get to the data, like accessing the TRA manager directly, or calling the "Certificate" property on the TRA.  I'm not saying my code is perfect, but it got me there.

What, you're about to see, is what the GOF would call Adapter Classes.  The first TrustedRootAuthority wraps the SPTrustedRootAuthority to provide access to the X509Certificate2 and the second, RootAuthority, wraps the SPCmdletGetTrustedRootAuthority Power Shell Cmdlet to get the set of all installed certificates.

These additions don't let you install new certificates, but that wouldn't be too hard once you've gotten this far.  They do allow you to get a hold of all of the installed certs so that you can pick and choose the certificate you want to add to your newly minted SPTrustedLoginProvider.



class TrustedRootAuthority
{
    static Type traType;
    static TrustedRootAuthority() {
       Assembly a = Assembly.Load("Microsoft.SharePoint, Version=14.0.0.0, " 
                              +"Culture=neutral, PublicKeyToken=71e9bce111e9429c");
       traType = a.GetType("Microsoft.SharePoint.Administration.SPTrustedRootAuthority",
                              true, true);
    }

    object tra;
    public TrustedRootAuthority(object tra) {
       this.tra = tra;
    }

    public X509Certificate2 Certificate {
       get {
           return traType.InvokeMember("m_Certificate", BindingFlags.NonPublic | 
                            BindingFlags.GetField | BindingFlags.Instance, null,
                            tra, null) as X509Certificate2;
       }
    }

}
      
class RootAuthority
{
    static ConstructorInfo ctor;
    static MethodInfo rdo;
    static RootAuthority() {
       Assembly a = Assembly.Load("Microsoft.SharePoint.PowerShell, Version=14.0.0.0, "+
                                  "Culture=neutral, PublicKeyToken=71e9bce111e9429c");
       Type t = a.GetType("Microsoft.SharePoint.PowerShell.SPCmdletGetTrustedRootAuthority",
                                  true, true);
       ctor = t.GetConstructor(new Type[0]);
       rdo =  t.GetMethod("RetrieveDataObjects", BindingFlags.NonPublic 
                                     | BindingFlags.Instance);
    }

    private object tra;

    public RootAuthority() {
        tra = ctor.Invoke(null);
    }

    public IEnumerable<TrustedRootAuthority> RetrieveDataObjects() {
        IEnumerable src = rdo.Invoke(tra, null) as IEnumerable;
        List<TrustedRootAuthority> ret = new List<TrustedRootAuthority>();
        foreach (object o in src) {
            ret.Add(new TrustedRootAuthority(o));
        }
        return ret;
    }
}




Tuesday, April 15, 2014

More on Quasigroup Block Ciphers

During the course of acquiring my Masters in Computer Science, I had the opportunity to do some crypto research.  It's probably landed me on a couple of watch lists, but what the heck, YOLO.  I still can fly to Nashville and just get tagged for my nephew's Play-Doh Christmas present in my carry-ons.

So, how to list research publications? Anti-chronologically? Randomly? No names just links?  On your curriculum vita it's newest to oldest, so AC it is.
Improved QGBC

PRNG Feedback Generator
Battey, "The Quasigroup Block Cipher and its Analysis," Graduate Thesis, University of Nebraska at Omaha, 2014.



Battey & Parakh, “Cryptanalysis of the Quasigroup Block Cipher,” Proceedings of  ACM SE’2014, Kennesaw, Georgia, USA,  2014.




QGBC Activity
Battey & Parakh, “A New Quasigroup Based Random Number Generator,” 2013 Internal Conference on Security & Management, pp. 235-241,  Las Vegas, Nevada, 2013.



Battey & Parakh, “An Efficient Quasigroup Block Cipher,” Wireless Personal Communications, Nov 2013, Vol. 73, Issue 1, pp 63-76.



Battey & Parakh, “Efficient Quasigroup Block Cipher for Sensor Networks,” 21st International Conference on Computer Communication and Networks (ICCCN), pp 1-5, Munich, Germany, 2012.

Monday, April 14, 2014

SharePoint Menu Item to Change your AD Password

It totally slays me why Microsoft hasn't included a feature to change your Active Directory password directly through SharePoint, especially when they position the system as an extranet portal.  Maybe it's because they want you to invest in UAG or something.

Anyway, adding such a feature is pretty straight forward.  You'll need to do the following high level steps:

1) Create a WebPart (Visual, SharePoint, or otherwise)
2) Create a page to host the web part
3) Add a CustomAction in an Elements file

So when I when through this process (1) was a Visual WebPart in Visual Studio 2010; (2) was part of a Files Module and deploys to the /StyleLibrary of the site; and (3) references a WebControl that is in the same namespace as my ChagePassword (1) VWP.

I'm going to assume that you know how to create a VWP and deploy it.  And, I'll assume you know how to create an ASPX page as part of a SharePoint project.  Also I'll assume, for now, you know all about the System.DirectoryServices.AccountManagement API, so that you can do all that work on your own.  In another post, I'll cover the Account Management API and cover some of the topics related to Kerberos and how that enhances the ability to work with the Active Directory API's in general.

Right now, we'll concentrate on how you'd deploy the ASPX page to the /StyleLibrary and how you deploy a custom action to the Standard Menu.  First off, I called my password change page ChagnePassword.ASPX and my new custom action ChangePasswordMenuItem, and I deployed my custom resources in a folder called AccountManagement.  Original, I know.

Ok, lets first start with the menu item WebControl.



public class ChangePasswordMenuItem : WebControl
{
       protected override void CreateChildControls() {
              base.CreateChildControls();

              MenuItemTemplate menuItem = new MenuItemTemplate() {
                     Text = "Change Password",
                     Description = "Change your Login Password",
                     Sequence = 1,
                     ClientOnClickNavigateUrl =  
                            SPContext.Current.Site.Url.ToString() +  
                            "/Style Library/AccountManagement/ChangePassword.aspx?Source=" + 
                            SPHttpUtility.UrlKeyValueEncode( 
                                          SPUtility.OriginalServerRelativeRequestUrl),
                     ImageUrl = SPContext.Current.Site.Url.ToString() + 
                            "/Style Library/AccountManagement/cp.png"
              };

              Controls.Add(menuItem);
       }
}

When this control is loaded, it creates a new menu item to be placed in the Standard Menu, otherwise know as the "User Menu," that one in the top right-hand corner.  The reason it's in this menu instead of another menu?  There's an addition you need to manually make to one of the Elements.xml files in your VS2010 SharePoint project.



<Elements xmlns="http://schemas.microsoft.com/sharepoint/" >
       <CustomAction
              Id="MyChangePassword"
              Location="Microsoft.SharePoint.StandardMenu"
              GroupId="PersonalActions"
              ControlAssembly="$SharePoint.Project.AssemblyFullName$"
              ControlClass="<NAMESPACE>.ChangePasswordMenuItem">
       </CustomAction>
</Elements>

The CustomAction tag in the elements file causes SharePoint to register a new menu item in the Standard Menu.  You can cut and past my code here, but you'll need to replace <NAMESPACE> with the namespace you're using in your project.

Next, you'll need to add a destination for the custom ASPX page you develop.  Remember I called mine ChangePassword.aspx?  We'll it's also in a project folders called SupportComponents\Style Library\AccountManagement, and I created a custom icon for my menu item that looks like a pair of keys on a key ring.  I actually modified a couple of other SharePoint icons to come up with this one.  It's pictured in the image at the top of this post.


<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
   <Module Name="SupportComponents" RootWebOnly="TRUE" Url="Style Library/AccountManagement">
      <File Path="SupportComponents\Style Library\AccountManagement\ChangePassword.aspx"
            Url="ChangePassword.aspx" />
      <File Path="SupportComponents\Style Library\AccountManagement\CP.png"  
            Url="CP.png" />
   </Module> 
</Elements>

This element file modification will copy both of my files to the ~/Style Library/AccountManagement folder.

So, once this project is deployed, we now have a custom action in the Standard Menu, that references a page in the Style Library, and that page hosts our web part that allows the user to change his Active Directory Password.