Skip to content

Commit

Permalink
experimental impersonation feature
Browse files Browse the repository at this point in the history
  • Loading branch information
mookid8000 committed Dec 18, 2018
1 parent dd761e9 commit a624183
Show file tree
Hide file tree
Showing 9 changed files with 117 additions and 9 deletions.
Empty file.
Binary file added .vs/GoCommando/v15/Server/sqlite3/storage.ide
Binary file not shown.
Binary file not shown.
Binary file added .vs/GoCommando/v15/Server/sqlite3/storage.ide-wal
Binary file not shown.
1 change: 1 addition & 0 deletions Beverage/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Beverage Utility
Copyright (c) 2015 El Duderino
------------------------------")]
[SupportImpersonation]
class Program
{
static void Main()
Expand Down
103 changes: 97 additions & 6 deletions GoCommando/Go.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security;
using GoCommando.Internals;
using Switch = GoCommando.Internals.Switch;
// ReSharper disable ArgumentsStyleNamedExpression

namespace GoCommando
{
Expand Down Expand Up @@ -36,15 +41,32 @@ static void Run(ICommandFactory commandFactory)
{
try
{
var bannerAttribute = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType
.GetCustomAttribute<BannerAttribute>();
var declaringType = Assembly.GetEntryAssembly()?.EntryPoint?.DeclaringType;
var supportImpersonation = declaringType?.GetCustomAttribute<SupportImpersonationAttribute>() != null;

if (supportImpersonation)
{
var args = Environment.GetCommandLineArgs().Skip(1).ToList();
var settings = new Settings();
var arguments = Parse(args, settings);

var impersonateNow = arguments.Switches.Any(s => s.Key == "username");

if (impersonateNow)
{
Impersonate(arguments);
return;
}
}

var bannerAttribute = declaringType?.GetCustomAttribute<BannerAttribute>();

if (bannerAttribute != null)
{
Console.WriteLine(bannerAttribute.BannerText);
}

InnerRun(commandFactory);
InnerRun(commandFactory, supportImpersonation);
}
catch (GoCommandoException friendlyException)
{
Expand All @@ -66,6 +88,75 @@ static void Run(ICommandFactory commandFactory)
}
}

static void Impersonate(Arguments arguments)
{
var username = arguments.Switches.First(s => s.Key == "username");
var password = arguments.Switches.FirstOrDefault(s => s.Key == "password")
?? throw new ArgumentException("Please remember to also specify the -password switch when you use the -username switch");
var domain = arguments.Switches.FirstOrDefault(s => s.Key == "domain")?.Value;

var keysToRemove = new[] { "username", "password", "domain" };

Impersonate(username.Value, password.Value, domain, arguments.Switches.Where(s => !keysToRemove.Contains(s.Key)), arguments.Command);
}

static void Impersonate(string username, string password, string domain, IEnumerable<Switch> switches, string command)
{
var commandLineArgs = string.Join(" ", switches.Select(s => $"-{s.Key} {EnsureQuoted(s.Value)}"));
var ohSoSecureString = new SecureString();

foreach (var @char in password) { ohSoSecureString.AppendChar(@char); }

var processStartInfo = new ProcessStartInfo
{
FileName = $"{Assembly.GetEntryAssembly().GetName().Name}.exe",
Arguments = $"{command} {string.Join(" ", commandLineArgs)}",
WorkingDirectory = Environment.CurrentDirectory,

UserName = username,
Domain = domain,
Password = ohSoSecureString,

UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
};

var process = new Process
{
StartInfo = processStartInfo,
};

void Write(string str, string prefix = "") => Console.WriteLine(string.IsNullOrWhiteSpace(str) ? "" : $"{prefix} {str}");

process.OutputDataReceived += (s, ea) => Write(ea.Data);
process.ErrorDataReceived += (s, ea) => Write(ea.Data, "ERR");

try
{
Console.WriteLine($"Invoking command as {username}...");

process.Start();

process.BeginOutputReadLine();
process.BeginErrorReadLine();

process.WaitForExit();
}
catch (Exception exception)
{
throw new ApplicationException("An error occurred when running the command under impersonation", exception);
}
}

static string EnsureQuoted(string str)
{
const string doubleQuote = "\"";
return str.StartsWith(doubleQuote) && str.EndsWith(doubleQuote) ? str : $"\"{str}\"";
}


static TCommandFactory CreateCommandFactory<TCommandFactory>() where TCommandFactory : ICommandFactory, new()
{
try
Expand All @@ -89,12 +180,12 @@ static void FailAndExit(Exception customExitCodeException, int exitCode)
Environment.ExitCode = exitCode;
}

static void InnerRun(ICommandFactory commandFactory)
static void InnerRun(ICommandFactory commandFactory, bool supportImpersonation)
{
var args = Environment.GetCommandLineArgs().Skip(1).ToList();
var settings = new Settings();
var arguments = Parse(args, settings);
var commandTypes = GetCommands(commandFactory, settings);
var commandTypes = GetCommands(commandFactory, settings, supportImpersonation: supportImpersonation);

var helpSwitch = arguments.Switches.FirstOrDefault(s => s.Key == "?")
?? arguments.Switches.FirstOrDefault(s => s.Key == "help");
Expand Down Expand Up @@ -255,7 +346,7 @@ static string FormatExamples(Parameter parameter, Settings settings)
";
}

internal static List<CommandInvoker> GetCommands(ICommandFactory commandFactory, Settings settings)
internal static List<CommandInvoker> GetCommands(ICommandFactory commandFactory, Settings settings, bool supportImpersonation)
{
var commandAttributes = Assembly.GetEntryAssembly().GetTypes()
.Select(t => new
Expand Down
1 change: 1 addition & 0 deletions GoCommando/GoCommando.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Internals\Settings.cs" />
<Compile Include="Internals\Switch.cs" />
<Compile Include="SupportImpersonationAttribute.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
9 changes: 6 additions & 3 deletions GoCommando/Internals/CommandInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ class CommandInvoker
readonly Settings _settings;
readonly ICommand _commandInstance;
readonly Action<ICommand> _releaser;
readonly List<Parameter> _parameters;

public CommandInvoker(string command, Type type, Settings settings, string group = null, ICommandFactory commandFactory = null)
public CommandInvoker(string command, Type type, Settings settings, string @group = null,
ICommandFactory commandFactory = null)
: this(command, settings, CreateInstance(type, GetFactoryMethod(commandFactory)), group, GetReleaseMethod(commandFactory))
{
}
Expand All @@ -28,7 +30,8 @@ public CommandInvoker(string command, Settings settings, ICommand commandInstanc

Command = command;
Group = group;
Parameters = GetParameters(Type);

_parameters = GetParameters(Type).ToList();
}

static void DefaultReleaseMethod(ICommand command)
Expand Down Expand Up @@ -110,7 +113,7 @@ static TAttribute GetSingleAttributeOrNull<TAttribute>(PropertyInfo p) where TAt

public Type Type => _commandInstance.GetType();

public IEnumerable<Parameter> Parameters { get; }
public IEnumerable<Parameter> Parameters => _parameters;

public string Description => Type.GetCustomAttribute<DescriptionAttribute>()?.DescriptionText ??
"(no help text for this command)";
Expand Down
12 changes: 12 additions & 0 deletions GoCommando/SupportImpersonationAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace GoCommando
{
/// <summary>
/// Apply this attribute to the class that has your <code>Main</code> method in order to support impersonation
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class SupportImpersonationAttribute : Attribute
{
}
}

0 comments on commit a624183

Please sign in to comment.