580 lines
25 KiB
C#

//----------------------------------------------------------------------------
//
// Copyright (C) Intel Corporation, 2011 - 2014 All Rights Reserved.
//
// File: UserConsentApi.cs
//
// Contents: Api code for Intel(R) Active Management Technology
// (Intel® AMT) User Consent.
//
// Notes: This sample application demonstrates various commands of user
// consent flow for AMT 6.1 and above FW versions.
//
//----------------------------------------------------------------------------
using System;
using Connection;
using Intel.Management.Wsman;
using System.Threading;
using System.Collections.Generic;
using Utils;
using Common.Utils;
namespace UserConsent
{
public class UserConsentApi : Connection_setup
{
#region ENUMS
/// <summary>
/// User input flags.
/// </summary>
public enum YesNoFlag
{
YES,
NO
};
/// <summary>
/// System power states.
/// </summary>
public enum PowerState
{
Other = 1,
On,
LightSleep,
DeepSleep,
SoftPowerCycle,
HardOff,
Hibernate,
SoftOff,
HardPowerCycle,
MasterBusReset,
DiagnosticInterrupt
};
/// <summary>
/// Indicates the state of OptIn (User Consent).
/// </summary>
public enum OptInState
{
/// <summary>
/// OptIn is required for sessions affected by OptInPolicy.
/// </summary>
NotStarted,
/// <summary>
/// A console has required an optIn code, but it was not displayed to the user yet.
/// </summary>
Requested,
/// <summary>
/// The optIn code was displayed to the user.
/// </summary>
Displayed,
/// <summary>
/// The optIn code was successfully entered by the IT.
/// </summary>
Received,
/// <summary>
/// Intel AMT had a KVM or Redirection open session.
/// </summary>
InSession
};
/// <summary>
/// Indicates the user consent policy.
/// </summary>
public enum OptInPolicy : uint
{
/// <summary>
/// The optIn is not required for any of the features.
/// </summary>
NONE,
/// <summary>
/// The optIn is required only for KVM.
/// </summary>
KVM,
/// <summary>
/// The optIn is required for all operations.
/// </summary>
ALL = UInt32.MaxValue
}
/// <summary>
/// Default screen values.
/// </summary>
public enum DefaultScreen
{
PrimaryScreen,
SecondaryScreen
//to do -- will need to include Third Screen option as well..
};
#endregion
#region CONSTANTS
private const uint MAX_NUMBER_OF_TRIALS = 3;
private const uint PT_STATUS_SUCCESS = 0;
private const uint PT_STATUS_UNSPECIFIED_ERROR = 2;
private const string STR_UNSPECIFIED_ERROR = "Requested operation is not allowed in Intel AMT system's current state.";
// OptInService Functions Statuses
Dictionary<uint, string> StatusCodes = new Dictionary<uint, string>()
{
{0, "PT_STATUS_SUCCESS"},
{1, "PT_STATUS_INTERNAL_ERROR"},
{2, "PT_STATUS_INVALID_STATE"},
{3, "PT_STATUS_BLOCKED"},
{2066, "PT_STATUS_INVALID_CREDENTIALS"}
};
// Yes No questions
private string ASK_FOR_SWITCH_SCREEN = "Would you like to switch monitors?";
private string ASK_FOR_SAMPLE_REBOOT = "Would you like to reboot the system?";
#endregion
#region PRIVATE_DATA_MEMBERS
public static CmdLineArguments Params = new CmdLineArguments();
IWsmanItem returnValue;
#endregion PRIVATE_DATA_MEMBERS
#region CONSTRUCTORS
// Creating the connection to the WSMan Client.
// Inheriting Connection details from Connection_setup class.
// Convert password to secure string to comply with wsman dll which supports passwords in SecureString
// format only.
public UserConsentApi(string ip, string username, string pwd, bool krb, MpsManager proxy, bool acceptSelfSignedCertificate = false)
: base(ip, username, pwd.ConvertToSecureString(), krb, proxy, acceptSelfSignedCertificate)
{
}
// Convert password to secure string to comply with wsman dll which supports passwords in SecureString
// format only.
public UserConsentApi(string ip, string username, string pwd, string clientCert, bool krb, MpsManager proxy, bool acceptSelfSignedCertificate = false)
: base(ip, username, pwd.ConvertToSecureString(), clientCert, krb, proxy, acceptSelfSignedCertificate)
{
}
#endregion CONSTRUCTORS
#region PUBLIC_FUNCTIONS
/// <summary>
/// Get user consent policy.
/// </summary>
/// <returns>The current policy</returns>
public OptInPolicy GetUserConsentPolicy()
{
// Get the IPS_OptInService using the Intel AMT as a reference.
IManagedReference optInServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_OptInService WHERE Name='Intel(r) AMT OptIn Service'");
IManagedInstance optInServiceInstance = optInServiceRef.Get();
IWsmanItem optInRequired = optInServiceInstance.GetProperty("OptInRequired");
OptInPolicy policy = OptInPolicy.NONE;
// The optIn is not required for any of the features. (Property does not exist.)
if (optInRequired != null)//service.ContainsField("OptInRequired"))
{
// Indicates the OptIn (User Consent) Policy for Redirection operations,including KVM and IDER,
// or setting of boot options.
// This value is Read Only if the system was configured in Client Control Mode and Read-Write
// in Admin Control Mode and CanModifyOptInPolicy=true.
// The allowed values in Admin Control Mode are: None (opt-in not required for any of the features),
// KVM, or All (KVM+IDER+Boot options). Possible values in Client Control Mode: All.
policy = (OptInPolicy)(Convert.ToUInt64(optInRequired.ToString()));
}
return policy;
}
/// <summary>
/// Get user consent state - mainly examines OptInState.
/// </summary>
/// <returns>The current state</returns>
public OptInState GetUserConsentState()
{
// Traversing the IPS_OptInService using the Intel AMT as a reference.
IManagedReference optInServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_OptInService WHERE Name='Intel(r) AMT OptIn Service'");
IManagedInstance optInServiceInstance = optInServiceRef.Get();
IWsmanItem optInState = optInServiceInstance.GetProperty("OptInState");
// Return the optIn state as enum parameter.
OptInState state = (OptInState)(Convert.ToUInt64(optInState.ToString()));
return state;
}
/// <summary>
/// Get the system power state.
/// </summary>
/// <returns>The current power state</returns>
public PowerState GetPowerState()
{
// Create a reference to the CIM_ComputerSystem instance.
IManagedReference computerSysRef = wsmanClient.NewReference("SELECT * FROM CIM_ComputerSystem WHERE Name='ManagedSystem'");
IManagedReference associatedPowerMgmtRef = wsmanClient.NewReference("CIM_AssociatedPowerManagementService");
associatedPowerMgmtRef.AddSelector("UserOfService", computerSysRef);
//Traverse to the CIM_AssociatedPowerManagementService instances that are connected to CIM_ComputerSystem instance.
int powerState;
foreach (IWsmanItem associatedPowerMgmtItem in associatedPowerMgmtRef.Enumerate("http://schemas.dmtf.org/wbem/wsman/1/wsman/SelectorFilter", null))
{
// For each instance, check if it is associated to the CIM_PowerManagementService instance.
if (associatedPowerMgmtItem.Object.GetProperty("ServiceProvided").IsA("CIM_PowerManagementService"))
{
IWsmanItem associatedPowerMgmtInstance = associatedPowerMgmtItem;
powerState = Convert.ToInt16(associatedPowerMgmtInstance.Object.GetProperty("PowerState").ToString());
return (PowerState)(powerState);
}
}
return 0;
}
/// <summary>
/// Getting the user consent status.
/// </summary>
public IManagedInstance DisplayUserConsentStatus()
{
Console.WriteLine("Getting User Consent Status.");
IManagedReference optInServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_OptInService WHERE Name='Intel(r) AMT OptIn Service'");
IManagedInstance optInServiceInstance = optInServiceRef.Get();
IWsmanItem optInRequired = optInServiceInstance.GetProperty("OptInRequired");
IWsmanItem optInState = optInServiceInstance.GetProperty("OptInState");
IWsmanItem optInDisplayTimeout = optInServiceInstance.GetProperty("OptInDisplayTimeout");
IWsmanItem optInCodeTimeout = optInServiceInstance.GetProperty("OptInCodeTimeout");
Console.WriteLine("User Consent Policy is: " + (OptInPolicy)(Convert.ToUInt64(optInRequired.ToString())));
Console.WriteLine("User Consent State is: " + (OptInState)(Convert.ToUInt64(optInState.ToString())));
Console.WriteLine("User Consent code Timeout is : " + optInCodeTimeout.ToString());
Console.WriteLine("User Consent Display Timeout is : " + optInDisplayTimeout.ToString());
return optInServiceInstance;
}
/// <summary>
/// Request an optIn code. (Intel AMT generates code internally.)
/// </summary>
public void StartUserConsent()
{
Console.WriteLine("Starting User Consent Process...");
IManagedReference optInServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_OptInService WHERE Name='Intel(r) AMT OptIn Service'");
IManagedInstance optInServiceInstance = optInServiceRef.Get();
IWsmanItem optInState = optInServiceInstance.GetProperty("OptInState");
if (optInState.ToString().Equals("0")) // OptInState = 0 ==> NotStarted
{
// Starting the user consent service.
// Intel AMT generates a random code and displays the consent form to the user.
IManagedInstance inputObject = optInServiceRef.CreateMethodInput("StartOptIn");
IManagedInstance outputObject = optInServiceRef.InvokeMethod(inputObject);
returnValue = outputObject.GetProperty("ReturnValue");
if (Convert.ToUInt64(returnValue.ToString()) != PT_STATUS_SUCCESS)
{
throw new Exception("Error occurred in StartUserConsent. Intel AMT status code: " + StatusCodes[Convert.ToUInt32(returnValue.ToString())]);
}
Params.MessageDisplay_Color("Success.", ConsoleColor.Green);
}
if (optInState.ToString().Equals("1")) // OptInState == 1 ==> Requested
{
Console.WriteLine("User Consent has been requested.");
}
if (optInState.ToString().Equals("3")) // OptInState == 3 ==> Received
{
Console.WriteLine("User Consent has been received.");
}
}
/// <summary>
/// Cancel a previous optIn code request.
/// </summary>
public void StopUserConsent()
{
Console.WriteLine("Canceling User Consent Process...");
// Getting the service instance.
IManagedReference optInServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_OptInService WHERE Name='Intel(r) AMT OptIn Service'");
IManagedInstance inputObject = optInServiceRef.CreateMethodInput("CancelOptIn");
IManagedInstance outputObject = optInServiceRef.InvokeMethod(inputObject);
returnValue = outputObject.GetProperty("ReturnValue");
if (Convert.ToUInt64(returnValue.ToString()) != PT_STATUS_SUCCESS)
{
throw new Exception("Error occurred in StopUserConsent. Intel AMT status code: " + StatusCodes[Convert.ToUInt32(returnValue.ToString())]);
}
Params.MessageDisplay_Color("Success.", ConsoleColor.Green);
}
/// <summary>
/// Send the consent code to Intel AMT.
/// </summary>
public void SendConsentCode()
{
// Count the number of trials to send the consent code.
uint codeTrials = 0;
while (codeTrials != MAX_NUMBER_OF_TRIALS)
{
uint optInCode;
// Asking for user consent code to send - waiting for input.
Console.WriteLine("Please enter user consent code:");
string strCode = Console.ReadLine();
// Parsing the user input to uint code (validate the input).
uint.TryParse(strCode, out optInCode);
if (optInCode != 0 || uint.Parse(strCode) == 0)
{
// When caller exceeded the number of allowed SendConsent retries, there is no indication that the
// state machine has reset. Therefore we are checking the OptInState, after each SendConsent failure,
// to figure out if it can send an additional retry.
// Getting the service instance for getting the user consent state.
IManagedReference optInServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_OptInService WHERE Name='Intel(r) AMT OptIn Service'");
IManagedInstance optInServiceInstance = optInServiceRef.Get();
IWsmanItem OptIn_state = optInServiceInstance.GetProperty("OptInState");
if (OptIn_state.ToString().Equals("2")) // OptInState = 2 ==>Displayed
{
Console.WriteLine("Sending Consent Code.. ");
IManagedInstance inputObject = optInServiceRef.CreateMethodInput("SendOptInCode");
inputObject.SetProperty("OptInCode", optInCode.ToString());
IManagedInstance outputObject = optInServiceRef.InvokeMethod(inputObject);
returnValue = outputObject.GetProperty("ReturnValue");
if (Convert.ToUInt64(returnValue.ToString()) != PT_STATUS_SUCCESS)
{
Params.MessageDisplay_Color("Failed to send user consent code. Try again.", ConsoleColor.Red);
codeTrials++;
}
else
{
Params.MessageDisplay_Color("Success.", ConsoleColor.Green);
break;
}
}
}
else
{
throw new InvalidCastException("An illegal value was entered. Please provide a valid Code(six digits).");
}
}
if (Convert.ToUInt64(returnValue.ToString()) != PT_STATUS_SUCCESS)
{
Params.MessageDisplay_Color("Maximal number of trials was reached - code is expired.", ConsoleColor.Red);
throw new Exception("Error occurred in SendConsentCode. Intel AMT status code: " + StatusCodes[Convert.ToUInt32(returnValue.ToString())]);
}
}
/// <summary>
/// Change the default monitor.
/// </summary>
public void SetDefaultMonitor()
{
Console.WriteLine("Setting Default Monitor...");
uint value;
IManagedReference secIOServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_SecIOService WHERE Name='SecIO'");
IManagedInstance secIOServiceInstance = secIOServiceRef.Get();
value = uint.Parse(secIOServiceInstance.GetProperty("DefaultScreen").ToString());
if (value == 0)
{
secIOServiceInstance.SetProperty("DefaultScreen", "1");
}
else
{
// Starting with Release 8.0, Intel AMT supports up to three screens, so the value can be 0, 1, or 2.
if (UtilitiesMethods.GetCoreVersion(wsmanClient).CompareTo("8.0.0.0") >= 0)
{
if (value == 1)
secIOServiceInstance.SetProperty("DefaultScreen", "2");
if (value == 2)
secIOServiceInstance.SetProperty("DefaultScreen", "0");
}
else
{
secIOServiceInstance.SetProperty("DefaultScreen", "0");
}
}
secIOServiceRef.Put(secIOServiceInstance);
Params.MessageDisplay_Color("Success.", ConsoleColor.Green);
}
/// <summary>
/// Gets the current default monitor.
/// </summary>
public void GetDefaultMonitor()
{
IManagedReference secIOServiceRef = wsmanClient.NewReference("SELECT * FROM IPS_SecIOService WHERE Name='SecIO'");
IManagedInstance secIOServiceInstance = secIOServiceRef.Get();
Console.WriteLine("The current default monitor: " + secIOServiceInstance.GetProperty("DefaultScreen").ToString());
}
/// <summary>
/// Run full user consent flow.
/// See the attached diagram (UserConsentFlowDiagram.bmp) that depicts the flow performed by the full user consent flow option.
/// </summary>
public void RunFullUserConsentFlow()
{
// -------------------------------------
// STEP 1: Check the system power state.
// -------------------------------------
if (GetPowerState() != (PowerState.On))
{
// If system state is Sx - close the program.
Params.MessageDisplay_Color("This flow does not support the current power state.", ConsoleColor.Red);
return;
}
// -------------------------------------
// STEP 2: Check the user consent state.
// -------------------------------------
DisplayUserConsentStatus();
// Check the system policy:
// Policy is "NONE" - indicates that user consent is not required.
if (GetUserConsentPolicy() == OptInPolicy.NONE)
{
Params.MessageDisplay_Color("User consent is not required for any operation", ConsoleColor.Yellow);
return;
}
// Get user consent state.
OptInState state = GetUserConsentState();
// Check the system state:
if (state != OptInState.NotStarted)
{
// The state is received - the code was entered successfully by the IT.
if (state == OptInState.Received)
{
Params.MessageDisplay_Color("User consent was already obtained.", ConsoleColor.Yellow);
return;
}
if (state == OptInState.InSession)
{
Params.MessageDisplay_Color("User Consent is not required. KVM or Redirection session is currently opened.", ConsoleColor.Yellow);
return;
}
// The state should be displayed (The code was displayed to the user), or requested (The console required an opt-in code)
else
{
string errorMessage = "Full flow cannot be executed in the current user-consent state. Resetting the state using cancel option.";
Params.MessageDisplay_Color(errorMessage, ConsoleColor.Yellow);
// Cancel the user consent (reset OptInState to "NotStarted").
StopUserConsent();
}
}
// ------- -------------------------------
// STEP 3: Start the user consent process.
// ---------------------------------------
StartUserConsent();
// The "Requested" state is ambiguous. SW cannot determine, based on this state alone, whether it should
// initiate a reboot to display MEBx code, or keep waiting for Intel AMT to display Sprite screen.
// A "Requested" state indicates to SW that a reboot is required for MEBx only after 15 seconds from StartConsent()
// The Sleep of 15 seconds (15000 milliseconds) is required since Intel AMT FW will only determine
// if it can use Sprite screen after a few seconds.
Thread.Sleep(15000);
// ---------------------------
// STEP 4: Display the Sprite.
// ---------------------------
// Intel AMT FW detects whether integrated gfx can be used for consent and prioritizes it over MEBx method.
// If the state is not "Displayed" - indicates that Discrete gfx are present in the platform
// and displays the sprite over MEBx.
// Check user consent state.
if (GetUserConsentState() != OptInState.Displayed)
{
Console.WriteLine("User Consent form will be displayed via MEBx.");
if ((YesNoFlag)Params.AskYesNo(ASK_FOR_SAMPLE_REBOOT) == YesNoFlag.YES)
{
BootSystem(PowerState.SoftPowerCycle);
}
}
// The DefaultScreen property controls on which screen the Sprite message is displayed.
// It is used by the IT to switch between screens if the local user does not see the Consent form.
if ((YesNoFlag)Params.AskYesNo(ASK_FOR_SWITCH_SCREEN) == YesNoFlag.YES)
{
SetDefaultMonitor();
}
// -----------------------------
// STEP 5: Send the consent code.
// ------------------------------
// Get the user consent code and send it.
SendConsentCode();
//Perform the task that required user consent...!
//Stop the Consent Flow
StopUserConsent();
}
#endregion
#region UTILS_FUNCTIONS
/// <summary>
/// Reboot the system.
/// </summary>
public void BootSystem(PowerState requestedPower)
{
Console.WriteLine("Booting the system... ");
IManagedReference powerManagementRef = wsmanClient.NewReference("SELECT * FROM CIM_PowerManagementService WHERE Name='Intel(r) AMT Power Management Service'");
IManagedInstance inputObject = powerManagementRef.CreateMethodInput("RequestPowerStateChange");
inputObject.SetProperty("PowerState", (uint)requestedPower); // 5 = SoftPowerCycle - other options can also be set, based on the value that is being passed.
/*
* The supported PowerState values are:
* 2 (Power Up), 5 (Power Cycle), 8 (Power Down), 10 (Master Bus Reset)
*
* ValueMap={2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}
Values={Power On, Sleep - Light, Sleep - Deep, Power Cycle (Off Soft), Power Off - Hard, Hibernate, Power Off - Soft,
* Power Cycle (Off Hard), Master Bus Reset, Diagnostic Interrupt (NMI), Power Off - Soft Graceful, Power Off - Hard Graceful,
* Master Bus Reset Graceful, Power Cycle (Off - Soft Graceful), Power Cycle (Off - Hard Graceful)}
*/
IManagedReference computerSystemRef = wsmanClient.NewReference("SELECT * FROM CIM_ComputerSystem WHERE Name='ManagedSystem'");
inputObject.SetProperty("ManagedElement", computerSystemRef);
IManagedInstance outputObject = powerManagementRef.InvokeMethod(inputObject);
IWsmanItem return_value = outputObject.GetProperty("ReturnValue");
if (Convert.ToUInt64(return_value.ToString()) != PT_STATUS_SUCCESS)
{
throw new Exception("Failed to change power state. Intel AMT status code: " + StatusCodes[Convert.ToUInt32(return_value.ToString())]);
}
}
#endregion
}
}