1286 lines · 55.4 KB · C#
RawDownload
//******************************************************************************************************//  CustomActions.cs - Gbtc////  Copyright © 2013, Grid Protection Alliance.  All Rights Reserved.////  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See//  the NOTICE file distributed with this work for additional information regarding copyright ownership.//  The GPA licenses this file to you under the MIT License (MIT), the "License"; you may//  not use this file except in compliance with the License. You may obtain a copy of the License at:////      http://www.opensource.org/licenses/MIT////  Unless agreed to in writing, the subject software distributed under the License is distributed on an//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the//  License for the specific language governing permissions and limitations.////  Code Modification History://  ----------------------------------------------------------------------------------------------------//  04/08/2013 - Stephen C. Wills//       Generated original version of source code. Portions of code derived from Code Project article://       http://www.codeproject.com/Articles/6164/A-ServiceInstaller-Extension-That-Enables-Recovery//       written by Neil Baliga////****************************************************************************************************** using System;using System.Collections;using System.Collections.Generic;using System.Diagnostics;using System.IO;using System.IO.Compression;using System.Linq;using System.Reflection;using System.Runtime.InteropServices;using System.Security.Principal;using System.Text;using System.Text.RegularExpressions;using System.Threading;using System.Windows.Forms;using System.Xml.Linq;using System.Xml.XPath;using GSF.Data;using GSF.Identity;using GSF.Interop;using GSF.Security.Cryptography;using GSF.ServiceProcess;using Microsoft.Deployment.WindowsInstaller; namespace GSF.InstallerActions{    /// <summary>    /// Class that contains custom actions for a WiX installer.    /// </summary>    public class CustomActions    {        /// <summary>        /// Custom action to attempt to authenticate the service account.        /// </summary>        /// <param name="session">Session object containing data from the installer.</param>        /// <returns>Result of the custom action.</returns>        [CustomAction]        public static ActionResult AuthenticateServiceAccountAction(Session session)        {            Logger logger = new Logger(session);             string serviceDomain = string.Empty;            string serviceUser = string.Empty;             logger.Log("Begin AuthenticateServiceAccountAction");             string serviceAccount = GetPropertyValue(session, "ServiceAccount");            string servicePassword = GetPropertyValue(session, "ServicePassword");            string[] splitServiceAccount = serviceAccount.Split('\\');             switch (splitServiceAccount.Length)            {                case 1:                    serviceDomain = UserInfo.CurrentUserID.Split('\\')[0];                    serviceUser = splitServiceAccount[0];                    break;                 case 2:                    serviceDomain = splitServiceAccount[0];                    serviceUser = splitServiceAccount[1];                    break;            }             bool isSystemAccount =                 serviceAccount.Equals("LocalSystem", StringComparison.OrdinalIgnoreCase) ||                serviceAccount.StartsWith(@"NT AUTHORITY\", StringComparison.OrdinalIgnoreCase) ||                serviceAccount.StartsWith(@"NT SERVICE\", StringComparison.OrdinalIgnoreCase);             bool isManagedServiceAccount = serviceAccount.EndsWith("$", StringComparison.Ordinal);             if (isSystemAccount)            {                session["SERVICEPASSWORD"] = string.Empty;                session["SERVICEAUTHENTICATED"] = "yes";            }            else if (isManagedServiceAccount)            {                using (UserInfo serviceAccountInfo = new UserInfo(serviceAccount))                {                    bool isManagedServiceAccountValid = serviceAccountInfo.Exists &&                                                        !serviceAccountInfo.AccountIsDisabled &&                                                        !serviceAccountInfo.AccountIsLockedOut &&                                                        serviceAccountInfo.GetUserPropertyValue("msDS-HostServiceAccountBL").Split(',')[0].                                                            Equals("CN=" + Environment.MachineName, StringComparison.CurrentCultureIgnoreCase);                     if (isManagedServiceAccountValid)                    {                        session["SERVICEPASSWORD"] = string.Empty;                        session["SERVICEAUTHENTICATED"] = "yes";                    }                    else                    {                        session["SERVICEAUTHENTICATED"] = null;                    }                }            }            else            {                IPrincipal servicePrincipal = UserInfo.AuthenticateUser(serviceDomain, serviceUser, servicePassword);                session["SERVICEAUTHENTICATED"] = servicePrincipal?.Identity.IsAuthenticated == true ? "yes" : null;            }             logger.Log("End AuthenticateServiceAccountAction");             return ActionResult.Success;        }         /// <summary>        /// Custom action to write to an XML file.        /// </summary>        /// <param name="session">Session object containing data from the installer.</param>        /// <returns>Result of the custom action.</returns>        [CustomAction]        public static ActionResult XmlFileAction(Session session)        {            Logger logger = new Logger(session);             logger.Log("Begin XmlFileAction");             string filePath = GetPropertyValue(session, "FilePath");            string mode = GetPropertyValue(session, "Mode").ToUpper();            string mappings = GetPropertyValue(session, "PropertyMappings");             if (File.Exists(filePath))            {                // ReSharper disable PossibleMultipleEnumeration                try                {                    XDocument document = XDocument.Load(filePath);                     foreach (KeyValuePair<string, string> mapping in mappings.ParseKeyValuePairs())                    {                        IEnumerable query = (IEnumerable)document.XPathEvaluate(mapping.Value);                         if (mode != "WRITE")                        {                            session[mapping.Key] = query.OfType<XElement>().Select(element => element.Value)                                .Concat(query.OfType<XAttribute>().Select(attribute => attribute.Value))                                .FirstOrDefault();                        }                        else                        {                            string value = GetPropertyValue(session, mapping.Key);                             query.OfType<XAttribute>().ToList().ForEach(attribute =>                            {                                if (!string.IsNullOrEmpty(value))                                    attribute.Value = value;                                else                                    attribute.Remove();                            });                             query.OfType<XElement>().ToList().ForEach(element =>                            {                                if (!string.IsNullOrEmpty(value))                                    element.Value = value;                                else                                    element.Remove();                            });                             document.Save(filePath);                        }                    }                }                catch (Exception ex)                {                    string action = mode == "WRITE" ? "update" : "read";                    string message = $"Failed to {action} XML file: {ex.Message}";                    logger.Log(InstallMessage.Error, message);                    logger.Log(EventLogEntryType.Error, ex);                    logger.Log($"FilePath = {filePath}");                    logger.Log($"PropertyMappings = {mappings}");                    return ActionResult.Failure;                }                // ReSharper restore PossibleMultipleEnumeration            }             logger.Log("End XmlFileAction");             return ActionResult.Success;        }         [CustomAction]        public static ActionResult ConnectionStringAction(Session session)        {            Logger logger = new Logger(session);             logger.Log("Begin ConnectionStringAction");             string connectionString = GetPropertyValue(session, "ConnectionString");            string mappings = GetPropertyValue(session, "PropertyMappings");            Dictionary<string, string> settings = connectionString.ParseKeyValuePairs();             foreach (KeyValuePair<string, string> kvp in mappings.ParseKeyValuePairs())            {                if (settings.TryGetValue(kvp.Value, out string setting))                    session[kvp.Key] = setting;                else                    session[kvp.Key] = null;            }             logger.Log("End ConnectionStringAction");             return ActionResult.Success;        }         /// <summary>        /// Custom action to load any previously defined CompanyName, CompanyAcronym and SystemName settings from the configuration file of an installed service.        /// </summary>        /// <param name="session">Session object containing data from the installer.</param>        /// <returns>Result of the custom action.</returns>        [CustomAction]        public static ActionResult LoadExistingCompanyInfoAction(Session session)        {            Logger logger = new Logger(session);             logger.Log("Begin LoadExistingCompanyInfoAction");             string serviceName = GetPropertyValue(session, "SERVICENAME");            string configPath = Path.Combine(GetPropertyValue(session, "INSTALLFOLDER"), $"{serviceName}.exe.config");             void setPropertyValue(XElement systemSettings, string settingName, string propertyName)            {                XElement setting = systemSettings.Elements("add").FirstOrDefault(element => string.Compare(element.Attributes("name").FirstOrDefault()?.Value, settingName, StringComparison.OrdinalIgnoreCase) == 0);                string value = setting?.Attributes("value").FirstOrDefault()?.Value;                 if (!string.IsNullOrEmpty(value))                    session[propertyName] = value;            }             if (File.Exists(configPath))            {                XDocument config = XDocument.Load(configPath);                XElement systemSettings = config.Descendants("systemSettings").FirstOrDefault();                 if (systemSettings != null)                {                    setPropertyValue(systemSettings, "CompanyName", "COMPANYNAME");                    setPropertyValue(systemSettings, "CompanyAcronym", "COMPANYACRONYM");                    setPropertyValue(systemSettings, "SystemName", "SYSTEMNAME");                }            }             logger.Log("End LoadExistingCompanyInfoAction");             return ActionResult.Success;        }         /// <summary>        /// Custom action to write CompanyName, CompanyAcronym and SystemName settings to the configuration file of an installed service.        /// </summary>        /// <param name="session">Session object containing data from the installer.</param>        /// <returns>Result of the custom action.</returns>        [CustomAction]        public static ActionResult CompanyInfoAction(Session session)        {            const string DefaultCompanyName = "Grid Protection Alliance";            const string DefaultCompanyAcronym = "GPA";            const string DefaultSystemName = " ";             Logger logger = new Logger(session);             logger.Log("Begin CompanyInfoAction");             string validAcronym(string propertyValue)            {                try                {                    return Regex.Replace(propertyValue.Trim().ToUpperInvariant(), @"[^A-Z0-9\-!_\.@#\$]+", "_", RegexOptions.Compiled);                }                catch (Exception ex)                {                    logger.Log(EventLogEntryType.Warning, ex);                    return propertyValue;                }            }             string serviceName = GetPropertyValue(session, "SERVICENAME");            string configPath = Path.Combine(GetPropertyValue(session, "INSTALLDIR"), $"{serviceName}.exe.config");            string companyName = GetPropertyValue(session, "COMPANYNAME") ?? DefaultCompanyName;            string companyAcronym = validAcronym(GetPropertyValue(session, "COMPANYACRONYM") ?? DefaultCompanyAcronym);            string systemName = validAcronym(GetPropertyValue(session, "SYSTEMNAME") ?? DefaultSystemName);             if (File.Exists(configPath))            {                XDocument config = XDocument.Load(configPath);                 foreach (XElement systemSettings in config.Descendants("systemSettings"))                {                    // Search for existing CompanyName settings and update their values                    foreach (XElement companyNameElement in systemSettings.Elements("add").Where(element => element.Attributes("name").Any(nameAttribute => string.Compare(nameAttribute.Value, "CompanyName", StringComparison.OrdinalIgnoreCase) == 0)))                        companyNameElement.Attributes("value").ToList().ForEach(valueAttribute => valueAttribute.Value = companyName);                     // Search for existing CompanyAcronym settings and update their values                    foreach (XElement companyAcronymElement in systemSettings.Elements("add").Where(element => element.Attributes("name").Any(nameAttribute => string.Compare(nameAttribute.Value, "CompanyAcronym", StringComparison.OrdinalIgnoreCase) == 0)))                        companyAcronymElement.Attributes("value").ToList().ForEach(valueAttribute => valueAttribute.Value = companyAcronym);                     // Search for existing SystemName settings and update their values                    foreach (XElement systemNameElement in systemSettings.Elements("add").Where(element => element.Attributes("name").Any(nameAttribute => string.Compare(nameAttribute.Value, "SystemName", StringComparison.OrdinalIgnoreCase) == 0)))                        systemNameElement.Attributes("value").ToList().ForEach(valueAttribute => valueAttribute.Value = systemName);                     // Add CompanyName setting if no such setting exists                    if (!systemSettings.Elements("add").Any(element => element.Attributes("name").Any(nameAttribute => string.Compare(nameAttribute.Value, "CompanyName", StringComparison.OrdinalIgnoreCase) == 0)))                    {                        systemSettings.Add(new XElement("add",                            new XAttribute("name", "CompanyName"),                            new XAttribute("value", companyName),                            new XAttribute("description", $"The name of the company who owns this instance of the {serviceName}."),                            new XAttribute("encrypted", "false")                        ));                    }                     // Add CompanyAcronym setting if no such setting exists                    if (!systemSettings.Elements("add").Any(element => element.Attributes("name").Any(nameAttribute => string.Compare(nameAttribute.Value, "CompanyAcronym", StringComparison.OrdinalIgnoreCase) == 0)))                    {                        systemSettings.Add(new XElement("add",                            new XAttribute("name", "CompanyAcronym"),                            new XAttribute("value", companyAcronym),                            new XAttribute("description", $"The acronym representing the company who owns this instance of the {serviceName}."),                            new XAttribute("encrypted", "false")                        ));                    }                     // Add SystemName setting if no such setting exists                    if (!systemSettings.Elements("add").Any(element => element.Attributes("name").Any(nameAttribute => string.Compare(nameAttribute.Value, "SystemName", StringComparison.OrdinalIgnoreCase) == 0)))                    {                        systemSettings.Add(new XElement("add",                            new XAttribute("name", "SystemName"),                            new XAttribute("value", systemName),                            new XAttribute("description", $"Name of system for this instance of the {serviceName} that will be prefixed to system level tags, when defined. Value should follow tag naming conventions, e.g., no spaces and all upper case."),                            new XAttribute("encrypted", "false")                        ));                    }                }                 config.Save(configPath);            }             logger.Log("End CompanyInfoAction");             return ActionResult.Success;        }         /// <summary>        /// Custom action to configure various failure settings of an installed service.        /// </summary>        /// <param name="session">Session object containing data from the installer.</param>        /// <returns>Result of the custom action.</returns>        [CustomAction]        public static ActionResult ConfigureServiceAction(Session session)        {            Logger logger = new Logger(session);             logger.Log("Begin ConfigureServiceAction");            UpdateServiceConfig(session, logger);            logger.Log("End ConfigureServiceAction");             return ActionResult.Success;        }         /// <summary>        /// Custom action to set up the group and necessary permissions for the service account.        /// </summary>        /// <param name="session">Session object containing data from the installer.</param>        /// <returns>Result of the custom action.</returns>        [CustomAction]        public static ActionResult ServiceAccountAction(Session session)        {            Logger logger = new Logger(session);             logger.Log("Begin ServiceAccountAction");             // Get properties from the installer session            string serviceName = GetPropertyValue(session, "SERVICENAME");            string serviceAccount = GetPropertyValue(session, "SERVICEACCOUNT");            string servicePorts = GetPropertyValue(session, "HTTPSERVICEPORTS");            string serviceAdminsGroupName = $"{serviceName} Admins";             // Create service admins group            try            {                logger.Log($"Creating {serviceAdminsGroupName} group...");                UserInfo.CreateLocalGroup(serviceAdminsGroupName, $"Members in this group have the necessary rights to administrate the {serviceName} service.");                logger.Log($"Done creating {serviceAdminsGroupName} group...");            }            catch (Exception ex)            {                logger.Log(InstallMessage.Error, $"Failed to create {serviceAdminsGroupName} group!");                logger.Log(EventLogEntryType.Error, ex);            }             void addServiceAccountToGroup(string groupName)            {                try                {                    logger.Log($"Adding {serviceAccount} user to {groupName} group...");                    UserInfo.AddUserToLocalGroup(groupName, serviceAccount);                    logger.Log($"Done adding {serviceAccount} user to {groupName} group.");                }                catch (InvalidOperationException ex)                {                    // This exception is thrown when group doesn't exist, just treat this as a warning                    logger.Log(EventLogEntryType.Warning, ex);                }                catch (Exception ex)                {                    logger.Log(InstallMessage.Error, $"Failed to add {serviceAccount} user to {groupName} group!");                    logger.Log(EventLogEntryType.Error, ex);                }            }             // Determine if we're dealing with the local system account            bool isLocalSystem = serviceAccount == "LocalSystem";             // Don't attempt grant rights to the local system account because it already has them            if (!isLocalSystem)            {                // Create service admins group and add service account to that group as well as the Performance Log Users group                addServiceAccountToGroup(serviceAdminsGroupName);                addServiceAccountToGroup("Performance Monitor Users");                addServiceAccountToGroup("Performance Log Users");                 try                {                    logger.Log($"Adding {serviceAccount} user right to logon as a service...");                    AddPrivileges(serviceAccount, WindowsApi.SE_SERVICE_LOGON_NAME);                    logger.Log($"Done adding {serviceAccount} user right to logon as a service...");                }                catch (Exception ex)                {                    logger.Log(InstallMessage.Error, $"Failed to add {serviceAccount} user right to logon as a service!");                    logger.Log(EventLogEntryType.Error, ex);                }                 try                {                    logger.Log($"Adding {serviceAccount} user right to change system time...");                    AddPrivileges(serviceAccount, WindowsApi.SE_SYSTEMTIME_NAME);                    logger.Log($"Done adding {serviceAccount} user right to change system time...");                }                catch (Exception ex)                {                    // This right is only needed for updating system clock, so its treated as a warning                    logger.Log(EventLogEntryType.Warning, ex);                }            }             // Attempt to grant rights to start and stop the service            try            {                string accountSID = UserInfo.UserNameToSID(serviceAccount);                string groupSID = UserInfo.GroupNameToSID(serviceAdminsGroupName);                string acl;                 using (Process process = new Process())                {                    process.StartInfo.FileName = "sc";                    process.StartInfo.Arguments = $"sdshow {serviceName}";                    process.StartInfo.UseShellExecute = false;                    process.StartInfo.RedirectStandardOutput = true;                    process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;                    process.Start();                     acl = process.StandardOutput.ReadToEnd().Trim();                    process.WaitForExit();                     if (process.ExitCode != 0)                        throw new Exception($"Non-zero exit code ({process.ExitCode}) returned from 'sc sdshow {serviceName}'.");                }                 int index = acl.IndexOf("S:", StringComparison.Ordinal);                 // Don't attempt grant rights to the local system account because it already has them                if (!isLocalSystem && accountSID.StartsWith("S-") && !acl.Contains(accountSID))                    acl = acl.Insert(index, $"(A;;RPWPCR;;;{accountSID})"); 

Lines 1–500 of 1286.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t