diff --git a/internal/flags/activate.go b/internal/flags/activate.go index 4abb4332..b9b211a9 100644 --- a/internal/flags/activate.go +++ b/internal/flags/activate.go @@ -29,6 +29,8 @@ func (f *Flags) handleActivateCommand() error { f.amtActivateCommand.BoolVar(&f.SkipIPRenew, "skipIPRenew", false, "skip DHCP renewal of the IP address if AMT becomes enabled") // for local activation in ACM mode need a few more items f.amtActivateCommand.StringVar(&f.configContent, "config", "", "specify a config file or smb: file share URL") + f.amtActivateCommand.StringVar(&f.configContentV2, "configv2", "", "specify a config file or smb: file share URL") + f.amtActivateCommand.StringVar(&f.configV2Key, "configv2key", "", "provide the 32 byte key to decrypt the config file") f.amtActivateCommand.StringVar(&f.LocalConfig.ACMSettings.AMTPassword, "amtPassword", f.lookupEnvOrString("AMT_PASSWORD", ""), "amt password") f.amtActivateCommand.StringVar(&f.LocalConfig.ACMSettings.ProvisioningCert, "provisioningCert", f.lookupEnvOrString("PROVISIONING_CERT", ""), "provisioning certificate") f.amtActivateCommand.StringVar(&f.LocalConfig.ACMSettings.ProvisioningCertPwd, "provisioningCertPwd", f.lookupEnvOrString("PROVISIONING_CERT_PASSWORD", ""), "provisioning certificate password") @@ -78,47 +80,110 @@ func (f *Flags) handleActivateCommand() error { fmt.Println("Warning: Overriding UUID prevents device from connecting to MPS") } } else { - if !f.UseCCM && !f.UseACM || f.UseCCM && f.UseACM { - fmt.Println("must specify -ccm or -acm, but not both") - return utils.InvalidParameterCombination - } + if f.configContentV2 != "" { + err := f.handleLocalConfigV2() + if err != nil { + return utils.FailedReadingConfiguration + } - err := f.handleLocalConfig() - if err != nil { - return utils.FailedReadingConfiguration + err = f.ValidateConfigV2() + if err != nil { + return err + } + + } else { + err := f.handleLocalConfigV1() + if err != nil { + return utils.FailedReadingConfiguration + } } + } + return nil +} + +func (f *Flags) handleLocalConfigV1() error { + if !f.UseCCM && !f.UseACM || f.UseCCM && f.UseACM { + fmt.Println("must specify -ccm or -acm, but not both") + return utils.InvalidParameterCombination + } + + err := f.handleLocalConfig() + if err != nil { + return utils.FailedReadingConfiguration + } + + // Gets optimized in rpc-go version 3 + if f.LocalConfig.CCMSettings.AMTPassword != "" { + f.LocalConfig.Password = f.LocalConfig.CCMSettings.AMTPassword + } - // Gets optimized in rpc-go version 3 - if f.LocalConfig.CCMSettings.AMTPassword != "" { - f.LocalConfig.Password = f.LocalConfig.CCMSettings.AMTPassword + if (f.LocalConfig.ACMSettings.AMTPassword == "" || f.LocalConfig.CCMSettings.AMTPassword == "") && f.Password == "" { + if rc := f.ReadNewPasswordTo(&f.Password, "New AMT Password"); rc != nil { + return rc } + } + + if f.Password != "" { + f.LocalConfig.ACMSettings.AMTPassword = f.Password + f.LocalConfig.Password = f.Password + } - if (f.LocalConfig.ACMSettings.AMTPassword == "" || f.LocalConfig.CCMSettings.AMTPassword == "") && f.Password == "" { - if rc := f.ReadNewPasswordTo(&f.Password, "New AMT Password"); rc != nil { - return rc + if f.UseACM { + v := reflect.ValueOf(f.LocalConfig.ACMSettings) + for i := 0; i < v.NumField(); i++ { + if v.Field(i).Interface() == "" { // not checking 0 since authenticantProtocol can and needs to be 0 for EAP-TLS + log.Error("Missing value for field: ", v.Type().Field(i).Name) + return utils.IncorrectCommandLineParameters } } + } - if f.Password != "" { - f.LocalConfig.ACMSettings.AMTPassword = f.Password - f.LocalConfig.Password = f.Password + if f.UUID != "" { + fmt.Println("-uuid cannot be use in local activation") + f.amtActivateCommand.Usage() + return utils.InvalidParameterCombination + } + + return nil +} + +func (f *Flags) ValidateConfigV2() error { + // Check if the Control Mode is set + switch f.LocalConfigV2.Configuration.AMTSpecific.ControlMode { + case "acmactivate": + f.UseACM = true + case "ccmactivate": + f.UseCCM = true + default: + log.Error("Invalid Control Mode") + return utils.IncorrectCommandLineParameters //ToDo: Add a new error type + } + + // Check if the AMT Password is set + if f.LocalConfigV2.Configuration.AMTSpecific.AdminPassword == "" { + log.Warn("AMT Password is not set") + if rc := f.ReadNewPasswordTo(&f.Password, "New AMT Password"); rc != nil { + return rc } + } + f.LocalConfig.ACMSettings.AMTPassword = f.LocalConfigV2.Configuration.AMTSpecific.AdminPassword + f.LocalConfig.Password = f.LocalConfigV2.Configuration.AMTSpecific.AdminPassword - if f.UseACM { - v := reflect.ValueOf(f.LocalConfig.ACMSettings) - for i := 0; i < v.NumField(); i++ { - if v.Field(i).Interface() == "" { // not checking 0 since authenticantProtocol can and needs to be 0 for EAP-TLS - log.Error("Missing value for field: ", v.Type().Field(i).Name) - return utils.IncorrectCommandLineParameters - } - } + if f.UseACM { + // Check if the Provisioning Certificate is set + if f.LocalConfigV2.Configuration.AMTSpecific.ProvisioningCert == "" { + log.Error("Provisioning Certificate is not set") + return utils.IncorrectCommandLineParameters //ToDo: Add a new error type } + f.LocalConfig.ACMSettings.ProvisioningCert = f.LocalConfigV2.Configuration.AMTSpecific.ProvisioningCert - if f.UUID != "" { - fmt.Println("-uuid cannot be use in local activation") - f.amtActivateCommand.Usage() - return utils.InvalidParameterCombination + // Check if the Provisioning Certificate Password is set + if f.LocalConfigV2.Configuration.AMTSpecific.ProvisioningCertPwd == "" { + log.Error("Provisioning Certificate Password is not set") + return utils.IncorrectCommandLineParameters //ToDo: Add a new error type } + f.LocalConfig.ACMSettings.ProvisioningCertPwd = f.LocalConfigV2.Configuration.AMTSpecific.ProvisioningCertPwd } + return nil } diff --git a/internal/flags/configure.go b/internal/flags/configure.go index 6e16b10c..c2b1688d 100644 --- a/internal/flags/configure.go +++ b/internal/flags/configure.go @@ -145,21 +145,23 @@ func (f *Flags) handleConfigureCommand() error { } f.Local = true - if f.Password == "" { - if f.LocalConfig.Password != "" { - f.Password = f.LocalConfig.Password + if f.SubCommand != utils.SubCommandSetAMTFeatures { + if f.Password == "" { + if f.LocalConfig.Password != "" { + f.Password = f.LocalConfig.Password + } else { + if err = f.ReadPasswordFromUser(); err != nil { + return utils.MissingOrIncorrectPassword + } + f.LocalConfig.Password = f.Password + } } else { - if err = f.ReadPasswordFromUser(); err != nil { + if f.LocalConfig.Password == "" { + f.LocalConfig.Password = f.Password + } else if f.LocalConfig.Password != f.Password { + log.Error("password does not match config file password") return utils.MissingOrIncorrectPassword } - f.LocalConfig.Password = f.Password - } - } else { - if f.LocalConfig.Password == "" { - f.LocalConfig.Password = f.Password - } else if f.LocalConfig.Password != f.Password { - log.Error("password does not match config file password") - return utils.MissingOrIncorrectPassword } } return nil @@ -215,10 +217,31 @@ func (f *Flags) handleSetAMTFeatures() error { f.flagSetAMTFeatures.BoolVar(&f.IDER, "ider", false, "Enables or Disables IDER (IDE Redirection)") f.flagSetAMTFeatures.StringVar(&f.Password, "password", f.lookupEnvOrString("AMT_PASSWORD", ""), "AMT password") + // V2 features + f.flagSetAMTFeatures.StringVar(&f.configContentV2, "configv2", "", "specify a config file or smb: file share URL") + f.flagSetAMTFeatures.StringVar(&f.configV2Key, "configv2key", "", "provide the 32 byte key to decrypt the config file") + if err = f.flagSetAMTFeatures.Parse(f.commandLineArgs[3:]); err != nil { f.printConfigurationUsage() return utils.IncorrectCommandLineParameters } + + if f.configContentV2 != "" { + err := f.handleLocalConfigV2() + if err != nil { + return utils.FailedReadingConfiguration + } + + // Set the values from the v2 config file. + if f.LocalConfigV2.Configuration.Redirection.UserConsent != "" { + f.UserConsent = f.LocalConfigV2.Configuration.Redirection.UserConsent + } + f.KVM = f.LocalConfigV2.Configuration.Redirection.Services.KVM + f.SOL = f.LocalConfigV2.Configuration.Redirection.Services.SOL + f.IDER = f.LocalConfigV2.Configuration.Redirection.Services.IDER + f.Password = f.LocalConfigV2.Configuration.AMTSpecific.AdminPassword + } + // Validate UserConsent if f.UserConsent != "" { f.UserConsent = strings.ToLower(f.UserConsent) @@ -241,6 +264,9 @@ func (f *Flags) handleMEBxPassword() error { f.flagSetMEBx.BoolVar(&f.JsonOutput, "json", false, "JSON output") f.flagSetMEBx.StringVar(&f.Password, "password", f.lookupEnvOrString("AMT_PASSWORD", ""), "AMT password") f.flagSetMEBx.StringVar(&f.MEBxPassword, "mebxpassword", f.lookupEnvOrString("MEBX_PASSWORD", ""), "MEBX password") + // V2 features + f.flagSetMEBx.StringVar(&f.configContentV2, "configv2", "", "specify a config file or smb: file share URL") + f.flagSetMEBx.StringVar(&f.configV2Key, "configv2key", "", "provide the 32 byte key to decrypt the config file") if len(f.commandLineArgs) > 3 { if err := f.flagSetMEBx.Parse(f.commandLineArgs[3:]); err != nil { @@ -249,6 +275,17 @@ func (f *Flags) handleMEBxPassword() error { } } + if f.configContentV2 != "" { + err := f.handleLocalConfigV2() + if err != nil { + return utils.FailedReadingConfiguration + } + + // Set the values from the v2 config file. + f.MEBxPassword = f.LocalConfigV2.Configuration.AMTSpecific.MEBXPassword + f.Password = f.LocalConfigV2.Configuration.AMTSpecific.AdminPassword + } + if f.Password == "" { if rc := f.ReadPasswordFromUser(); rc != nil { return rc @@ -308,6 +345,10 @@ func (f *Flags) handleConfigureTLS() error { fs.StringVar(&f.ConfigTLSInfo.EAUsername, "eaUsername", "", "Enterprise Assistant username") fs.StringVar(&f.ConfigTLSInfo.EAPassword, "eaPassword", "", "Enterprise Assistant password") + // V2 features + fs.StringVar(&f.configContentV2, "configv2", "", "specify a config file or smb: file share URL") + fs.StringVar(&f.configV2Key, "configv2key", "", "provide the 32 byte key to decrypt the config file") + if len(f.commandLineArgs) < (3 + 0) { fs.Usage() return utils.IncorrectCommandLineParameters @@ -320,6 +361,25 @@ func (f *Flags) handleConfigureTLS() error { fs.Usage() return utils.IncorrectCommandLineParameters } + + if f.configContentV2 != "" { + err := f.handleLocalConfigV2() + if err != nil { + return utils.FailedReadingConfiguration + } + mutualAuth := f.LocalConfigV2.Configuration.TLS.MutualAuthentication + enabled := f.LocalConfigV2.Configuration.TLS.Enabled + allowNonTLS := f.LocalConfigV2.Configuration.TLS.AllowNonTLS + mode := f.DetermineTLSMode(mutualAuth, enabled, allowNonTLS) + f.ConfigTLSInfo.TLSMode, _ = ParseTLSMode(mode) + // ToDo: No need check whether it has to include in config file or default value to 3 + f.ConfigTLSInfo.DelayInSeconds = 3 + f.ConfigTLSInfo.EAAddress = f.LocalConfigV2.Configuration.EnterpriseAssistant.URL + f.ConfigTLSInfo.EAUsername = f.LocalConfigV2.Configuration.EnterpriseAssistant.Username + f.ConfigTLSInfo.EAPassword = f.LocalConfigV2.Configuration.EnterpriseAssistant.Password + + } + if f.configContent != "" { err := f.handleLocalConfig() if err != nil { @@ -344,11 +404,32 @@ func (f *Flags) handleConfigureTLS() error { return nil } +func (f *Flags) DetermineTLSMode(mutualAuth, enabled, allowNonTLS bool) string { + switch { + case enabled && !allowNonTLS && !mutualAuth: + return "Server" + case enabled && allowNonTLS && !mutualAuth: + return "ServerAndNonTLS" + case enabled && !allowNonTLS && mutualAuth: + return "Mutual" + case enabled && allowNonTLS && mutualAuth: + return "MutualAndNonTLS" + case !enabled: + return "None" + default: + return "Unknown" + } +} + func (f *Flags) handleAddEthernetSettings() error { var configJson string var secretsFilePath string var secretConfig config.SecretConfig + // V2 features + f.flagSetAddEthernetSettings.StringVar(&f.configContentV2, "configv2", "", "specify a config file or smb: file share URL") + f.flagSetAddEthernetSettings.StringVar(&f.configV2Key, "configv2key", "", "provide the 32 byte key to decrypt the config file") + f.flagSetAddEthernetSettings.StringVar(&f.configContent, "config", "", "specify a config file or smb: file share URL") f.flagSetAddEthernetSettings.StringVar(&configJson, "configJson", "", "configuration as a JSON string") f.flagSetAddEthernetSettings.StringVar(&secretsFilePath, "secrets", "", "specify a secrets file ") @@ -396,6 +477,31 @@ func (f *Flags) handleAddEthernetSettings() error { f.LocalConfig.Ieee8021xConfigs = append(f.LocalConfig.Ieee8021xConfigs, ieee8021xCfg) f.LocalConfig.EnterpriseAssistant = eaSettings + if f.configContentV2 != "" && f.configV2Key != "" { + err := f.handleLocalConfigV2() + if err != nil { + return utils.FailedReadingConfiguration + } + f.LocalConfig.WiredConfig.DHCP = f.LocalConfigV2.Configuration.Network.Wired.DHCPEnabled + f.LocalConfig.WiredConfig.Static = f.LocalConfigV2.Configuration.Network.Wired.SharedStaticIP + f.LocalConfig.WiredConfig.IpSync = f.LocalConfigV2.Configuration.Network.Wired.IPSyncEnabled + f.LocalConfig.WiredConfig.IpAddress = f.LocalConfigV2.Configuration.Network.Wired.IPAddress + f.LocalConfig.WiredConfig.Subnetmask = f.LocalConfigV2.Configuration.Network.Wired.SubnetMask + f.LocalConfig.WiredConfig.Gateway = f.LocalConfigV2.Configuration.Network.Wired.DefaultGateway + f.LocalConfig.WiredConfig.PrimaryDNS = f.LocalConfigV2.Configuration.Network.Wired.PrimaryDNS + f.LocalConfig.WiredConfig.SecondaryDNS = f.LocalConfigV2.Configuration.Network.Wired.SecondaryDNS + f.LocalConfig.WiredConfig.Ieee8021xProfileName = "exists" + + f.LocalConfig.Ieee8021xConfigs[0].ProfileName = "exists" + f.LocalConfig.Ieee8021xConfigs[0].Username = f.LocalConfigV2.Configuration.Network.Wired.IEEE8021x.Username + f.LocalConfig.Ieee8021xConfigs[0].Password = f.LocalConfigV2.Configuration.Network.Wired.IEEE8021x.Password + f.LocalConfig.Ieee8021xConfigs[0].AuthenticationProtocol = f.LocalConfigV2.Configuration.Network.Wired.IEEE8021x.AuthenticationProtocol + f.LocalConfig.Ieee8021xConfigs[0].ClientCert = f.LocalConfigV2.Configuration.Network.Wired.IEEE8021x.ClientCert + f.LocalConfig.Ieee8021xConfigs[0].CACert = f.LocalConfigV2.Configuration.Network.Wired.IEEE8021x.CACert + f.LocalConfig.Ieee8021xConfigs[0].PrivateKey = f.LocalConfigV2.Configuration.Network.Wired.IEEE8021x.PrivateKey + + } + if f.configContent != "" || configJson != "" { err := f.handleLocalConfig() if err != nil { @@ -526,6 +632,11 @@ func (f *Flags) handleAddWifiSettings() error { var secretsFilePath string var wifiSecretConfig config.SecretConfig var configJson string + + // V2 features + f.flagSetAddEthernetSettings.StringVar(&f.configContentV2, "configv2", "", "specify a config file or smb: file share URL") + f.flagSetAddEthernetSettings.StringVar(&f.configV2Key, "configv2key", "", "provide the 32 byte key to decrypt the config file") + f.flagSetAddWifiSettings.StringVar(&f.configContent, "config", "", "specify a config file or smb: file share URL") f.flagSetAddWifiSettings.StringVar(&configJson, "configJson", "", "configuration as a JSON string") f.flagSetAddWifiSettings.StringVar(&secretsFilePath, "secrets", "", "specify a secrets file ") @@ -582,15 +693,54 @@ func (f *Flags) handleAddWifiSettings() error { f.LocalConfig.EnterpriseAssistant = eaSettings eaSettings.EAConfigured = false - err = f.handleLocalConfig() - if err != nil { - return utils.FailedReadingConfiguration - } - if configJson != "" { - err := json.Unmarshal([]byte(configJson), &f.LocalConfig) + if f.configContentV2 != "" && f.configV2Key != "" { + err := f.handleLocalConfigV2() if err != nil { - log.Error(err) - return utils.IncorrectCommandLineParameters + return utils.FailedReadingConfiguration + } + + for i, wifiCfg := range f.LocalConfigV2.Configuration.Network.Wireless.Profiles { + f.LocalConfig.WifiConfigs = []config.WifiConfig{} + f.LocalConfig.Ieee8021xConfigs = []config.Ieee8021xConfig{} + newWifiConfig := config.WifiConfig{ + ProfileName: fmt.Sprintf("profile%d", i+1), + SSID: wifiCfg.SSID, + Priority: wifiCfg.Priority, + AuthenticationMethod: f.getAuthenticationCode(wifiCfg.AuthenticationMethod), + EncryptionMethod: f.getEncrytionCode(wifiCfg.EncryptionMethod), + PskPassphrase: wifiCfg.Password, + } + + // Handle 802.1x configurations + if wifiCfg.IEEE8021x.Username != "" || wifiCfg.IEEE8021x.Password != "" || + wifiCfg.IEEE8021x.AuthenticationProtocol != 0 { + // Add corresponding 802.1x config + ieee8021xConfig := config.Ieee8021xConfig{ + ProfileName: newWifiConfig.ProfileName, + Username: wifiCfg.IEEE8021x.Username, + Password: wifiCfg.IEEE8021x.Password, + AuthenticationProtocol: wifiCfg.IEEE8021x.AuthenticationProtocol, + ClientCert: wifiCfg.IEEE8021x.ClientCert, + CACert: wifiCfg.IEEE8021x.CACert, + PrivateKey: wifiCfg.IEEE8021x.PrivateKey, + } + f.LocalConfig.Ieee8021xConfigs = append(f.LocalConfig.Ieee8021xConfigs, ieee8021xConfig) + } + + f.LocalConfig.WifiConfigs = append(f.LocalConfig.WifiConfigs, newWifiConfig) + } + + } else { + err = f.handleLocalConfig() + if err != nil { + return utils.FailedReadingConfiguration + } + if configJson != "" { + err := json.Unmarshal([]byte(configJson), &f.LocalConfig) + if err != nil { + log.Error(err) + return utils.IncorrectCommandLineParameters + } } } @@ -626,6 +776,48 @@ func (f *Flags) handleAddWifiSettings() error { return nil } +func (f *Flags) getAuthenticationCode(code string) int { + authType := strings.ToLower(strings.TrimSpace(code)) + + switch authType { + case "other": + return 1 + case "open system": + return 2 + case "shared key": + return 3 + case "wpa psk": + return 4 + case "wpa ieee 802.1x": + return 5 + case "wpa2 psk": + return 6 + case "wpa2 ieee 802.1x": + return 7 + default: + return 0 // unknown + } +} + +func (f *Flags) getEncrytionCode(code string) int { + encryptionType := strings.ToLower(strings.TrimSpace(code)) + + switch encryptionType { + case "other": + return 1 + case "wep": + return 2 + case "tkip": + return 3 + case "ccmp": + return 4 + case "none": + return 5 + default: + return 0 // unknown + } +} + func (f *Flags) mergeWifiSecrets(wifiSecretConfig config.SecretConfig) error { for _, secret := range wifiSecretConfig.Secrets { if secret.ProfileName == "" { diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 82880dab..bfc83349 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -7,6 +7,7 @@ package flags import ( "bytes" "encoding/base64" + "encoding/json" "flag" "fmt" "net" @@ -20,6 +21,9 @@ import ( "strings" "time" + configv2 "github.com/open-amt-cloud-toolkit/go-wsman-messages/v2/pkg/config" + "github.com/open-amt-cloud-toolkit/go-wsman-messages/v2/pkg/security" + "github.com/google/uuid" "github.com/ilyakaznacheev/cleanenv" @@ -76,8 +80,11 @@ type Flags struct { UseACM bool EchoPass bool configContent string + configContentV2 string + configV2Key string UUID string LocalConfig config.Config + LocalConfigV2 configv2.Configuration amtInfoCommand *flag.FlagSet amtActivateCommand *flag.FlagSet amtDeactivateCommand *flag.FlagSet @@ -345,3 +352,26 @@ func (f *Flags) handleLocalConfig() error { } return nil } + +func (f *Flags) handleLocalConfigV2() error { + if f.configV2Key == "" { + log.Error("config error: missing encryption key") + return utils.FailedReadingConfiguration + } + + security := security.Crypto{EncryptionKey: f.configV2Key} + content, err := security.ReadAndDecryptFile(f.configContentV2) + if err != nil { + log.Error("config error: ", err) + return err + } + + formattedContent, err := json.MarshalIndent(content, "", " ") + if err != nil { + log.Error("error formatting config content: ", err) + return err + } + fmt.Println(string(formattedContent)) + f.LocalConfigV2 = content + return nil +}