Skip to content

Commit

Permalink
feat: amtinfo display user certificates
Browse files Browse the repository at this point in the history
  • Loading branch information
tim-shockley committed Sep 8, 2023
1 parent ae42b5b commit 65bb76b
Show file tree
Hide file tree
Showing 7 changed files with 274 additions and 47 deletions.
28 changes: 22 additions & 6 deletions internal/flags/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,33 @@ type AmtInfoFlags struct {
Mode bool
DNS bool
Cert bool
UserCert bool
Ras bool
Lan bool
Hostname bool
}

// TODO: write unit tests
func (f *Flags) handleAMTInfo(amtInfoCommand *flag.FlagSet) int {
// runs locally
f.Local = true

amtInfoCommand.BoolVar(&f.AmtInfo.Ver, "ver", false, "BIOS Version")
amtInfoCommand.BoolVar(&f.AmtInfo.Bld, "bld", false, "Build Number")
amtInfoCommand.BoolVar(&f.AmtInfo.Sku, "sku", false, "Product SKU")
amtInfoCommand.BoolVar(&f.AmtInfo.UUID, "uuid", false, "Unique Identifier")
amtInfoCommand.BoolVar(&f.AmtInfo.Mode, "mode", false, "Current Control Mode")
amtInfoCommand.BoolVar(&f.AmtInfo.DNS, "dns", false, "Domain Name Suffix")
amtInfoCommand.BoolVar(&f.AmtInfo.Cert, "cert", false, "Certificate Hashes")
amtInfoCommand.BoolVar(&f.AmtInfo.Cert, "cert", false, "System Certificate Hashes (and User Certificates if AMT password is provided)")
amtInfoCommand.BoolVar(&f.AmtInfo.UserCert, "userCert", false, "User Certificates only. AMT password is required")
amtInfoCommand.BoolVar(&f.AmtInfo.Ras, "ras", false, "Remote Access Status")
amtInfoCommand.BoolVar(&f.AmtInfo.Lan, "lan", false, "LAN Settings")
amtInfoCommand.BoolVar(&f.AmtInfo.Hostname, "hostname", false, "OS Hostname")
amtInfoCommand.StringVar(&f.Password, "password", f.lookupEnvOrString("AMT_PASSWORD", ""), "AMT Password")

if err := amtInfoCommand.Parse(f.commandLineArgs[2:]); err != nil {
return utils.IncorrectCommandLineParameters
}

// runs locally
f.Local = true

defaultFlagCount := 2
if f.JsonOutput {
defaultFlagCount = defaultFlagCount + 1
Expand All @@ -49,10 +51,24 @@ func (f *Flags) handleAMTInfo(amtInfoCommand *flag.FlagSet) int {
f.AmtInfo.UUID = true
f.AmtInfo.Mode = true
f.AmtInfo.DNS = true
f.AmtInfo.Cert = false
f.AmtInfo.Ras = true
f.AmtInfo.Lan = true
f.AmtInfo.Hostname = true
}

// no password - same behavior only cert hashes
// with password - shows user certs too
// (don't break previous behavior)
if f.AmtInfo.Cert && f.Password != "" {
f.AmtInfo.UserCert = true
}

// user certs need the amt password for the local wsman connection
if f.AmtInfo.UserCert && f.Password == "" {
if _, errCode := f.ReadPasswordFromUser(); errCode != 0 {
return utils.MissingOrIncorrectPassword
}
}

return utils.Success
}
92 changes: 92 additions & 0 deletions internal/flags/info_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package flags

import (
"github.com/stretchr/testify/assert"
"rpc/pkg/utils"
"strings"
"testing"
)

func TestParseFlagsAmtInfo(t *testing.T) {
defaultFlags := AmtInfoFlags{
Ver: true,
Bld: true,
Sku: true,
UUID: true,
Mode: true,
DNS: true,
Ras: true,
Lan: true,
Hostname: true,
}

tests := map[string]struct {
cmdLine string
wantResult int
wantFlags AmtInfoFlags
userInput string
}{
"expect success for basic command": {
cmdLine: "./rpc amtinfo -json",
wantResult: utils.Success,
wantFlags: defaultFlags,
},
"expect IncorrectCommandLineParameters on Parse error": {
cmdLine: "./rpc amtinfo -balderdash",
wantResult: utils.IncorrectCommandLineParameters,
wantFlags: AmtInfoFlags{},
},
"expect only cert flag with no password on command line": {
cmdLine: "./rpc amtinfo -cert",
wantResult: utils.Success,
wantFlags: AmtInfoFlags{
Cert: true,
},
},
"expect both cert flags with no password on command line": {
cmdLine: "./rpc amtinfo -cert -password testPassword",
wantResult: utils.Success,
wantFlags: AmtInfoFlags{
Cert: true,
UserCert: true,
},
},
"expect MissingOrIncorrectPassword for userCert": {
cmdLine: "./rpc amtinfo -userCert",
wantResult: utils.MissingOrIncorrectPassword,
wantFlags: AmtInfoFlags{
UserCert: true,
},
},
"expect Success for userCert with password": {
cmdLine: "./rpc amtinfo -userCert -password testPassword",
wantResult: utils.Success,
wantFlags: AmtInfoFlags{
UserCert: true,
},
},
"expect Success for userCert with password input": {
cmdLine: "./rpc amtinfo -userCert",
wantResult: utils.Success,
wantFlags: AmtInfoFlags{
UserCert: true,
},
userInput: "testPassword",
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
args := strings.Fields(tc.cmdLine)
if tc.userInput != "" {
defer userInput(t, tc.userInput)()
}
flags := NewFlags(args)
gotResult := flags.ParseFlags()
assert.Equal(t, tc.wantResult, gotResult)
assert.Equal(t, true, flags.Local)
assert.Equal(t, utils.CommandAMTInfo, flags.Command)
assert.Equal(t, tc.wantFlags, flags.AmtInfo)
})
}
}
98 changes: 73 additions & 25 deletions internal/local/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package local
import (
"encoding/json"
"fmt"
"github.com/open-amt-cloud-toolkit/go-wsman-messages/pkg/amt/publickey"
"github.com/open-amt-cloud-toolkit/go-wsman-messages/pkg/amt/publicprivate"
log "github.com/sirupsen/logrus"
"os"
"rpc/internal/amt"
Expand All @@ -11,12 +13,16 @@ import (
"strings"
)

type PrivateKeyPairReference struct {
KeyPair publicprivate.KeyPair
AssociatedCerts []string
}

func (service *ProvisioningService) DisplayAMTInfo() int {
dataStruct := make(map[string]interface{})

amtCommand := amt.NewAMTCommand()
cmd := service.amtCommand
if service.flags.AmtInfo.Ver {
result, err := amtCommand.GetVersionDataFromME("AMT", service.flags.AMTTimeoutDuration)
result, err := cmd.GetVersionDataFromME("AMT", service.flags.AMTTimeoutDuration)
if err != nil {
log.Error(err)
}
Expand All @@ -26,7 +32,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.Bld {
result, err := amtCommand.GetVersionDataFromME("Build Number", service.flags.AMTTimeoutDuration)
result, err := cmd.GetVersionDataFromME("Build Number", service.flags.AMTTimeoutDuration)
if err != nil {
log.Error(err)
}
Expand All @@ -37,7 +43,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.Sku {
result, err := amtCommand.GetVersionDataFromME("Sku", service.flags.AMTTimeoutDuration)
result, err := cmd.GetVersionDataFromME("Sku", service.flags.AMTTimeoutDuration)
if err != nil {
log.Error(err)
}
Expand All @@ -55,7 +61,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.UUID {
result, err := amtCommand.GetUUID()
result, err := cmd.GetUUID()
if err != nil {
log.Error(err)
}
Expand All @@ -66,7 +72,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.Mode {
result, err := amtCommand.GetControlMode()
result, err := cmd.GetControlMode()
if err != nil {
log.Error(err)
}
Expand All @@ -77,7 +83,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.DNS {
result, err := amtCommand.GetDNSSuffix()
result, err := cmd.GetDNSSuffix()
if err != nil {
log.Error(err)
}
Expand All @@ -86,7 +92,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
if !service.flags.JsonOutput {
println("DNS Suffix : " + string(result))
}
result, err = amtCommand.GetOSDNSSuffix()
result, err = cmd.GetOSDNSSuffix()
if err != nil {
log.Error(err)
}
Expand All @@ -108,7 +114,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}

if service.flags.AmtInfo.Ras {
result, err := amtCommand.GetRemoteAccessConnectionStatus()
result, err := cmd.GetRemoteAccessConnectionStatus()
if err != nil {
log.Error(err)
}
Expand All @@ -122,7 +128,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.Lan {
wired, err := amtCommand.GetLANInterfaceSettings(false)
wired, err := cmd.GetLANInterfaceSettings(false)
if err != nil {
log.Error(err)
}
Expand All @@ -137,7 +143,7 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
println("MAC Address : " + wired.MACAddress)
}

wireless, err := amtCommand.GetLANInterfaceSettings(true)
wireless, err := cmd.GetLANInterfaceSettings(true)
if err != nil {
log.Error(err)
}
Expand All @@ -153,30 +159,72 @@ func (service *ProvisioningService) DisplayAMTInfo() int {
}
}
if service.flags.AmtInfo.Cert {
result, err := amtCommand.GetCertificateHashes()
result, err := cmd.GetCertificateHashes()
if err != nil {
log.Error(err)
}
certs := make(map[string]interface{})
sysCertMap := map[string]amt.CertHashEntry{}
for _, v := range result {
certs[v.Name] = v
sysCertMap[v.Name] = v
}
dataStruct["certificateHashes"] = certs
dataStruct["certificateHashes"] = sysCertMap
if !service.flags.JsonOutput {
println("Certificate Hashes :")
for _, v := range result {
print(v.Name + " (")
if v.IsDefault {
print("Default,")
if len(result) == 0 {
fmt.Println("---No Certificate Hashes Found---")

Check warning on line 173 in internal/local/info.go

View check run for this annotation

Codecov / codecov/patch

internal/local/info.go#L173

Added line #L173 was not covered by tests
} else {
fmt.Println("---Certificate Hashes---")
}
for k, v := range sysCertMap {
fmt.Printf("%s", k)
if v.IsDefault && v.IsActive {
fmt.Printf(" (Default, Active)")
} else if v.IsDefault {
fmt.Printf(" (Default)")
} else if v.IsActive {
fmt.Printf(" (Active)")
}
if v.IsActive {
print("Active)")
fmt.Println()
fmt.Println(" " + v.Algorithm + ": " + v.Hash)
}
}
}
if service.flags.AmtInfo.UserCert {
service.setupWsmanClient("admin", service.flags.Password)
var userCerts []publickey.PublicKeyCertificate
service.GetPublicKeyCerts(&userCerts)
userCertMap := map[string]publickey.PublicKeyCertificate{}
for i := range userCerts {
c := userCerts[i]
name := GetTokenFromKeyValuePairs(c.Subject, "CN")
// CN is not required by spec, but should work
// just in case, provide something accurate
if name == "" {
name = c.InstanceID
}

Check warning on line 203 in internal/local/info.go

View check run for this annotation

Codecov / codecov/patch

internal/local/info.go#L202-L203

Added lines #L202 - L203 were not covered by tests
userCertMap[name] = c
}
dataStruct["publicKeyCerts"] = userCertMap

if !service.flags.JsonOutput {
if len(userCertMap) == 0 {
fmt.Println("---No Public Key Certs Found---")

Check warning on line 210 in internal/local/info.go

View check run for this annotation

Codecov / codecov/patch

internal/local/info.go#L210

Added line #L210 was not covered by tests
} else {
fmt.Println("---Public Key Certs---")
}
for k, c := range userCertMap {
fmt.Printf("%s", k)
if c.TrustedRootCertficate && c.ReadOnlyCertificate {
fmt.Printf(" (TrustedRoot, ReadOnly)")
} else if c.TrustedRootCertficate {
fmt.Printf(" (TrustedRoot)")
} else if c.ReadOnlyCertificate {
fmt.Printf(" (ReadOnly)")
}
println()
println(" " + v.Algorithm + ": " + v.Hash)
fmt.Println()
}
}
}

if service.flags.JsonOutput {
outBytes, err := json.MarshalIndent(dataStruct, "", " ")
output := string(outBytes)
Expand Down
Loading

0 comments on commit 65bb76b

Please sign in to comment.