1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
package ipmi
import (
"context"
"fmt"
)
// 13.22 RAKP Message 3
type RAKPMessage3 struct {
// Selected by remote console. Used by remote console to help match
// responses up with requests.
MessageTag uint8
// Identifies the status of the previous message.
RmcpStatusCode RmcpStatusCode
// The Managed System's Session ID for this session, returned by the managed system on the previous RMCP+ Open Session Response message.
ManagedSystemSessionID uint32
// An integrity check value over the relevant items specified by the RAKP
// authentication algorithm identified in RAKP Message 1.
// The size of this field depends on the specific authentication algorithm.
//
// This field may be 0 bytes (absent) for some algorithms (e.g. RAKP-none).
KeyExchangeAuthenticationCode []byte
}
type RAKPMessage4 struct {
authAlg AuthAlg
MessageTag uint8
RmcpStatusCode RmcpStatusCode
MgmtConsoleSessionID uint32
// An integrity check value over the relevant items specified by
// the RAKP authentication algorithm that was identified in RAKP Message 1.
//
// The size of this field depends on the specific authentication algorithm.
//
// For example, the RAKP-HMAC-SHA1 specifies that an HMACSHA1-96 algorithm be used for calculating this field.
// See Section 13.28
// Authentication, Integrity, and Confidentiality Algorithm Numbers for info on
// the algorithm to be used for this field.
//
// This field may be 0 bytes (absent) for some authentication algorithms (e.g. RAKP-none)
IntegrityCheckValue []byte
}
func (req *RAKPMessage3) Command() Command {
return CommandNone
}
func (req *RAKPMessage3) Pack() []byte {
var msg = make([]byte, 8+len(req.KeyExchangeAuthenticationCode))
packUint8(req.MessageTag, msg, 0)
packUint8(uint8(req.RmcpStatusCode), msg, 1)
packUint16(0, msg, 2) // reserved
packUint32L(req.ManagedSystemSessionID, msg, 4)
packBytes(req.KeyExchangeAuthenticationCode, msg, 8)
return msg
}
func (res *RAKPMessage4) Unpack(msg []byte) error {
authCodeLen := 0
switch res.authAlg {
case AuthAlgRAKP_None:
// nothing need to do
case AuthAlgRAKP_HMAC_MD5:
// need to copy 16 bytes
authCodeLen = 16
case AuthAlgRAKP_HMAC_SHA1:
// need to copy 12 bytes
authCodeLen = 12
case AuthAlgRAKP_HMAC_SHA256:
authCodeLen = 16
default:
}
if len(msg) < 8+authCodeLen {
return ErrUnpackedDataTooShortWith(len(msg), 8+authCodeLen)
}
res.MessageTag, _, _ = unpackUint8(msg, 0)
b1, _, _ := unpackUint8(msg, 1)
res.RmcpStatusCode = RmcpStatusCode(b1)
res.MgmtConsoleSessionID, _, _ = unpackUint32L(msg, 4)
res.IntegrityCheckValue, _, _ = unpackBytes(msg, 8, authCodeLen)
return nil
}
func (*RAKPMessage4) CompletionCodes() map[uint8]string {
// no command-specific cc
return map[uint8]string{}
}
func (res *RAKPMessage4) Format() string {
return fmt.Sprintf("%v", res)
}
// authAlg is used to parse the returned RAKPMessage4 message
func (c *Client) RAKPMessage3(ctx context.Context) (response *RAKPMessage4, err error) {
// create session integrity key
sik, err := c.generate_sik()
if err != nil {
err = fmt.Errorf("generate sik failed, err: %w", err)
return
}
c.session.v20.sik = sik
k1, err := c.generate_k1()
if err != nil {
err = fmt.Errorf("generate k1 failed, err: %w", err)
return
}
c.session.v20.k1 = k1
k2, err := c.generate_k2()
if err != nil {
err = fmt.Errorf("generate k2 failed, err: %w", err)
return
}
c.session.v20.k2 = k2
authCode, err := c.generate_rakp3_authcode()
if err != nil {
return nil, fmt.Errorf("generate rakp3 auth code failed, err: %w", err)
}
request := &RAKPMessage3{
MessageTag: 0,
RmcpStatusCode: RmcpStatusCode(c.session.v20.rakp2ReturnCode),
ManagedSystemSessionID: c.session.v20.bmcSessionID,
KeyExchangeAuthenticationCode: authCode,
}
response = &RAKPMessage4{
authAlg: c.session.v20.authAlg,
}
c.session.v20.state = SessionStateRakp3Sent
err = c.Exchange(ctx, request, response)
if err != nil {
return nil, err
}
if _, err = c.ValidateRAKP4(ctx, response); err != nil {
return nil, fmt.Errorf("validate rakp4 failed, err: %w", err)
}
c.session.v20.state = SessionStateActive
return response, nil
}
func (c *Client) ValidateRAKP4(ctx context.Context, response *RAKPMessage4) (bool, error) {
if response.RmcpStatusCode != RmcpStatusCodeNoErrors {
return false, fmt.Errorf("rakp4 status code not ok, %x", response.RmcpStatusCode)
}
// verify
if c.session.v20.consoleSessionID != response.MgmtConsoleSessionID {
return false, fmt.Errorf("session not activated")
}
authCode, err := c.generate_rakp4_authcode()
if err != nil {
return false, fmt.Errorf("generate rakp4 auth code failed, err: %w", err)
}
c.DebugBytes("rakp4 console computed authcode", authCode, 16)
c.DebugBytes("rakp4 bmc returned authcode", response.IntegrityCheckValue, 16)
if !isByteSliceEqual(response.IntegrityCheckValue, authCode) {
return false, fmt.Errorf("rakp4 returned integrity check not passed, console mac %0x, bmc mac: %0x", authCode, response.IntegrityCheckValue)
}
return true, nil
}
|