Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ccm activation on tlsonly platforms #745

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified build.sh
100755 → 100644
Empty file.
33 changes: 33 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ func runRPC(args []string) error {
if err != nil {
return err
}
// Update TLS enforcement and Current Activation Mode, helps decide how to connect to LMS
err = updateConnectionSettings(flags)
if err != nil {
return err
}
if flags.Local {
err = local.ExecuteCommand(flags)
} else {
Expand Down Expand Up @@ -85,6 +90,34 @@ func main() {
}
}

func updateConnectionSettings(flags *flags.Flags) error {
// Check if TLS is Mandatory for LMS connection
resp, err := flags.AmtCommand.GetChangeEnabled()
flags.LocalTlsEnforced = false
if err != nil {
if err.Error() == "wait timeout while sending data" {
log.Trace("Operation timed out while sending data. This may occur on systems with AMT version 11 and below.")
return nil
} else {
log.Error(err)
return err
}
}

if resp.IsTlsEnforcedOnLocalPorts() {
flags.LocalTlsEnforced = true
log.Trace("TLS is enforced on local ports")
}

// Check the current provisioning mode
flags.ControlMode, err = flags.AmtCommand.GetControlMode()
if err != nil {
return err
}

return nil
}

func handleErrorAndExit(err error) {
if customErr, ok := err.(utils.CustomError); ok {
if err != utils.HelpRequested {
Expand Down
139 changes: 139 additions & 0 deletions internal/config/lmsTls.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*********************************************************************
* Copyright (c) Intel Corporation 2025
* SPDX-License-Identifier: Apache-2.0
**********************************************************************/

package config

import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"errors"
"strconv"

"rpc/internal/amt"
"strings"

log "github.com/sirupsen/logrus"
)

// CreateTLSConfig generates a TLS configuration based on the provided mode.
func GetTLSConfig(mode *int) *tls.Config {
if *mode == 0 { // Pre-provisioning mode
return &tls.Config{
InsecureSkipVerify: true,
VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
const (
prodChainLength = 6
selfSignedChainLength = 1
)
if len(rawCerts) == prodChainLength {
allowedLeafCNs := []string{
"iAMT-RCFG", "iAMT CSME IDevID RCFG", "AMT RCFG",
}
allowedOUPrefixes := []string{
"ODCA 2 CSME P", "On Die CSME P", "ODCA 2 CSME", "On Die CSME",
}
const (
leafLevel = 0
odcaCertLevel = 3
rootLevel = 5
)
for i, rawCert := range rawCerts {
cert, err := x509.ParseCertificate(rawCert)
if err != nil {
log.Error("Failed to parse certificate", i, ":", err)
return err
}
// log.Tracef("Subject: %s, Issuer: %s, Serial Number: %s, Not Before: %s, Not After: %s", cert.Subject, cert.Issuer, cert.SerialNumber, cert.NotBefore, cert.NotAfter)
// Validate the leaf certificate
if i == leafLevel {
if !isAllowedLeafCN(cert.Subject.CommonName, allowedLeafCNs) {
log.Error("Leaf certificate CN is not allowed: ", cert.Subject.CommonName)
return errors.New("leaf certificate CN is not allowed: " + cert.Subject.CommonName)
}
}
// Validate 4th certificate (CSME ROM ODCA Certificate)
if i == odcaCertLevel {
if !strings.Contains(cert.Subject.CommonName, "ROM CA") {
log.Error("4th certificate Common Name does not contain 'ROM CA'")
return errors.New("4th certificate Common Name does not contain 'ROM CA'")
}
// Check that organizationalUnitName Attribute (OU) of odcaCertLevel must have a prefix equal to either ODCA 2 CSME P or On Die CSME P
validOU := false
for _, ou := range cert.Issuer.OrganizationalUnit {
for _, prefix := range allowedOUPrefixes {
if strings.HasPrefix(ou, prefix) {
validOU = true
break
}
}
if validOU {
break
}
}
if !validOU {
log.Error("4th certificate Organizational Unit does not have a valid prefix")
return errors.New("4th certificate Organizational Unit does not have a valid prefix")
}
// TODO: Check system UPID matches with first 20 bytes of sha256 hash of the 4th certificate
}
// check if last intermediate certificate is not signed by trusted root certificate
if i == rootLevel {
// Base64 encoded DER certificate without line breaks ttps://tsci.intel.com/content/OnDieCA/certs/OnDie_CA_RootCA_Certificate.cer
certBase64 := `MIICujCCAj6gAwIBAgIUPLLiHTrwySRtWxR4lxKLlu7MJ7wwDAYIKoZIzj0EAwMFADCBiTELMAkGA1UEBgwCVVMxCzAJBgNVBAgMAkNBMRQwEgYDVQQHDAtTYW50YSBDbGFyYTEaMBgGA1UECgwRSW50ZWwgQ29ycG9yYXRpb24xIzAhBgNVBAsMGk9uRGllIENBIFJvb3QgQ2VydCBTaWduaW5nMRYwFAYDVQQDDA13d3cuaW50ZWwuY29tMB4XDTE5MDQwMzAwMDAwMFoXDTQ5MTIzMTIzNTk1OVowgYkxCzAJBgNVBAYMAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMSMwIQYDVQQLDBpPbkRpZSBDQSBSb290IENlcnQgU2lnbmluZzEWMBQGA1UEAwwNd3d3LmludGVsLmNvbTB2MBAGByqGSM49AgEGBSuBBAAiA2IABK8SfB2UflvXZqb5Kc3+lokrABHWazvNER2axPURP64HILkXChPB0OEX5hLB7Okw7Dy6oFqB5tQVDupgfvUX/SgYBEaDdG5rCVFrGAis6HX5TA2ewQmj14r2ncHBgnppB6NjMGEwHwYDVR0jBBgwFoAUtFjJ9uQIQKPyWMg5eG6ujgqNnDgwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLRYyfbkCECj8ljIOXhuro4KjZw4MAwGCCqGSM49BAMDBQADaAAwZQIxAP9B4lFF86uvpHmkcp61cWaU565ayE3p7ezu9haLE/lPLh5hFQfmTi1nm/sG3JEXMQIwNpKfHoDmUTrUyezhhfv3GG+1CqBXstmCYH40buj9jKW3pHWc71s9arEmPWli7I8U`
// Decode the Base64 certificate string
certBytes, err := base64.StdEncoding.DecodeString(certBase64)
if err != nil {
log.Error("Failed to decode base64 certificate: ", err)
return err
}
// Parse the DER certificate into an x509.Certificate
prodRootCert, err := x509.ParseCertificate(certBytes)
if err != nil {
log.Error("Failed to parse root certificate: ", err)
return err
}
err = cert.CheckSignatureFrom(prodRootCert)
if err != nil {
log.Error("last certificate in the chain is not signed by a trusted root certificate: ", err)
return err
}
}
// TODO: check CRL for each certificate in the chain
}
} else if len(rawCerts) == selfSignedChainLength { // this is for the scenario where AMT transitions from Preprovisioning mode to ACM/CCM
controlMode, err := amt.NewAMTCommand().GetControlMode()
if err != nil {
log.Error("Failed to get control mode: ", err)
return err
}
if controlMode != 0 {
log.Trace("AMT has transitioned to mode: ", controlMode)
*mode = controlMode
return nil
}
} else {
return errors.New("unexpected number of certificates received from AMT: " + strconv.Itoa(len(rawCerts)))
}
return nil
},
}
}

// Default for ACM or CCM
log.Trace("Setting default TLS Config for ACM/CCM mode")
return &tls.Config{
InsecureSkipVerify: true,
}
}

func isAllowedLeafCN(cn string, allowedCNs []string) bool {
for _, allowed := range allowedCNs {
if cn == allowed {
return true
}
}
return false
}
6 changes: 4 additions & 2 deletions internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ type Flags struct {
flagSetEnableWifiPort *flag.FlagSet
flagSetMEBx *flag.FlagSet
flagSetAMTFeatures *flag.FlagSet
amtCommand amt.AMTCommand
AmtCommand amt.AMTCommand
netEnumerator NetEnumerator
IpConfiguration IPConfiguration
HostnameInfo HostnameInfo
Expand All @@ -115,6 +115,8 @@ type Flags struct {
KVM bool
SOL bool
IDER bool
LocalTlsEnforced bool
ControlMode int
}

func NewFlags(args []string, pr utils.PasswordReader) *Flags {
Expand Down Expand Up @@ -142,7 +144,7 @@ func NewFlags(args []string, pr utils.PasswordReader) *Flags {
flags.flagSetMEBx = flag.NewFlagSet(utils.SubCommandSetMEBx, flag.ContinueOnError)
flags.flagSetAMTFeatures = flag.NewFlagSet(utils.SubCommandSetAMTFeatures, flag.ContinueOnError)

flags.amtCommand = amt.NewAMTCommand()
flags.AmtCommand = amt.NewAMTCommand()
flags.netEnumerator = NetEnumerator{}
flags.netEnumerator.Interfaces = net.Interfaces
flags.netEnumerator.InterfaceAddrs = (*net.Interface).Addrs
Expand Down
2 changes: 1 addition & 1 deletion internal/flags/maintenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func (f *Flags) handleMaintenanceSyncIP() error {
return nil
}

amtLanIfc, err := f.amtCommand.GetLANInterfaceSettings(false)
amtLanIfc, err := f.AmtCommand.GetLANInterfaceSettings(false)
if err != nil {
log.Error(err)
return utils.AMTConnectionFailed
Expand Down
40 changes: 27 additions & 13 deletions internal/lm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,60 @@
package lm

import (
"crypto/tls"
"errors"
"io"
"net"

"strings"
"time"

"rpc/internal/config"

log "github.com/sirupsen/logrus"
)

// LMConnection is struct for managing connection to LMS
type LMSConnection struct {
Connection net.Conn
address string
port string
data chan []byte
errors chan error
Connection net.Conn
address string
port string
useTls bool
data chan []byte
errors chan error
controlMode int
}

func NewLMSConnection(address string, port string, data chan []byte, errors chan error) *LMSConnection {
func NewLMSConnection(address string, port string, useTls bool, data chan []byte, errors chan error, mode int) *LMSConnection {

lms := &LMSConnection{
address: address,
port: port,
data: data,
errors: errors,
address: address,
port: port,
useTls: useTls,
data: data,
errors: errors,
controlMode: mode,
}
return lms
}

func (lms *LMSConnection) Initialize() error {
return errors.New("not implemented")
}

// Connect initializes TCP connection to LMS
func (lms *LMSConnection) Connect() error {
log.Debug("connecting to lms")
var err error
if lms.Connection == nil {
lms.Connection, err = net.Dial("tcp4", lms.address+":"+lms.port)
if lms.useTls {
log.Debug("connecting to lms over tls...")
lms.Connection, err = tls.Dial("tcp4", lms.address+":"+lms.port, config.GetTLSConfig(&lms.controlMode))
} else {
log.Debug("connecting to lms...")
lms.Connection, err = net.Dial("tcp4", lms.address+":"+lms.port)
}
if err != nil {
// handle error
log.Error("error connecting to LMS: ", err)
return err
}
}
Expand Down
17 changes: 8 additions & 9 deletions internal/local/activate.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/pem"
"errors"
"rpc/internal/config"
"rpc/pkg/utils"
"strings"

Expand All @@ -23,12 +25,8 @@ import (
)

func (service *ProvisioningService) Activate() error {

controlMode, err := service.amtCommand.GetControlMode()
if err != nil {
return utils.ActivationFailedGetControlMode
}
if controlMode != 0 {
// Check if the device is already activated
if service.flags.ControlMode != 0 {
log.Error("Device is already activated")
return utils.UnableToActivate
}
Expand All @@ -38,9 +36,9 @@ func (service *ProvisioningService) Activate() error {
return err
}

tlsConfig := &tls.Config{}
if tlsEnforced {
log.Error("TLS is enforced on local ports, unable to activate")
return utils.UnsupportedAMTVersion
tlsConfig = config.GetTLSConfig(&service.flags.ControlMode)
}

// for local activation, wsman client needs local system account credentials
Expand All @@ -49,7 +47,8 @@ func (service *ProvisioningService) Activate() error {
log.Error(err)
return utils.AMTConnectionFailed
}
service.interfacedWsmanMessage.SetupWsmanClient(lsa.Username, lsa.Password, log.GetLevel() == log.TraceLevel)

service.interfacedWsmanMessage.SetupWsmanClient(lsa.Username, lsa.Password, tlsEnforced, tlsConfig, log.GetLevel() == log.TraceLevel)

if service.flags.UseACM {
err = service.ActivateACM()
Expand Down
20 changes: 14 additions & 6 deletions internal/local/amt/wsman.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package amt

import (
cryptotls "crypto/tls"
"encoding/base64"
"net"
"rpc/pkg/utils"
Expand Down Expand Up @@ -34,7 +35,7 @@ import (
)

type WSMANer interface {
SetupWsmanClient(username string, password string, logAMTMessages bool)
SetupWsmanClient(username string, password string, useTLS bool, tlsConfig *cryptotls.Config, logAMTMessages bool)
Unprovision(int) (setupandconfiguration.Response, error)
GetGeneralSettings() (general.Response, error)
HostBasedSetupService(digestRealm string, password string) (hostbasedsetup.Response, error)
Expand Down Expand Up @@ -96,21 +97,28 @@ func NewGoWSMANMessages(lmsAddress string) *GoWSMANMessages {
}
}

func (g *GoWSMANMessages) SetupWsmanClient(username string, password string, logAMTMessages bool) {
func (g *GoWSMANMessages) SetupWsmanClient(username string, password string, useTLS bool, tlsConfig *cryptotls.Config, logAMTMessages bool) {
clientParams := client.Parameters{
Target: g.target,
Username: username,
Password: password,
UseDigest: true,
UseTLS: false,
UseTLS: useTLS,
TlsConfig: tlsConfig,
LogAMTMessages: logAMTMessages,
}
logrus.Info("Attempting to connect to LMS...")
port := utils.LMSPort

var (
con net.Conn // Declared to hold the connection object
err error
)
if clientParams.UseTLS {
port = client.TLSPort
con, err = cryptotls.Dial("tcp", utils.LMSAddress+":"+client.TLSPort, clientParams.TlsConfig)
} else {
con, err = net.Dial("tcp", utils.LMSAddress+":"+client.NonTLSPort)
}
con, err := net.Dial("tcp4", utils.LMSAddress+":"+port)

if err != nil {
logrus.Info("Failed to connect to LMS, using local transport instead.")
clientParams.Transport = NewLocalTransport()
Expand Down
Loading
Loading