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 11, 2023
1 parent 92a8845 commit 8f7fa78
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 53 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) utils.ReturnCode {
// 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) utils.ReturnCode {
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 utils.ReturnCode
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"
"os"
"rpc/internal/amt"
"rpc/pkg/utils"
Expand All @@ -12,12 +14,16 @@ import (
log "github.com/sirupsen/logrus"
)

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

func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
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 @@ -27,7 +33,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
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 @@ -38,7 +44,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
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 @@ -56,7 +62,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
if service.flags.AmtInfo.UUID {
result, err := amtCommand.GetUUID()
result, err := cmd.GetUUID()
if err != nil {
log.Error(err)
}
Expand All @@ -67,7 +73,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
if service.flags.AmtInfo.Mode {
result, err := amtCommand.GetControlMode()
result, err := cmd.GetControlMode()
if err != nil {
log.Error(err)
}
Expand All @@ -78,7 +84,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
if service.flags.AmtInfo.DNS {
result, err := amtCommand.GetDNSSuffix()
result, err := cmd.GetDNSSuffix()
if err != nil {
log.Error(err)
}
Expand All @@ -87,7 +93,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
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 @@ -109,7 +115,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}

if service.flags.AmtInfo.Ras {
result, err := amtCommand.GetRemoteAccessConnectionStatus()
result, err := cmd.GetRemoteAccessConnectionStatus()
if err != nil {
log.Error(err)
}
Expand All @@ -123,7 +129,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
if service.flags.AmtInfo.Lan {
wired, err := amtCommand.GetLANInterfaceSettings(false)
wired, err := cmd.GetLANInterfaceSettings(false)
if err != nil {
log.Error(err)
}
Expand All @@ -138,7 +144,7 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
println("MAC Address : " + wired.MACAddress)
}

wireless, err := amtCommand.GetLANInterfaceSettings(true)
wireless, err := cmd.GetLANInterfaceSettings(true)
if err != nil {
log.Error(err)
}
Expand All @@ -154,30 +160,72 @@ func (service *ProvisioningService) DisplayAMTInfo() utils.ReturnCode {
}
}
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 174 in internal/local/info.go

View check run for this annotation

Codecov / codecov/patch

internal/local/info.go#L174

Added line #L174 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 204 in internal/local/info.go

View check run for this annotation

Codecov / codecov/patch

internal/local/info.go#L203-L204

Added lines #L203 - L204 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 211 in internal/local/info.go

View check run for this annotation

Codecov / codecov/patch

internal/local/info.go#L211

Added line #L211 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 8f7fa78

Please sign in to comment.