-
Notifications
You must be signed in to change notification settings - Fork 6
/
ipmi_session.go
147 lines (129 loc) · 4.58 KB
/
ipmi_session.go
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
package virtualbmc
import (
"bytes"
"encoding/binary"
"fmt"
"io"
)
type authenticationType uint8
const (
authTypeNone = authenticationType(0x00)
authTypeMD2 = authenticationType(0x01)
authTypeMD5 = authenticationType(0x02)
authTypeStraightPasswordKey = authenticationType(0x04)
authTypeOEM = authenticationType(0x05)
authTypeRMCPPlus = authenticationType(0x06)
)
type ipmiSession struct {
authType authenticationType
}
// ipmiSessionWrapper represents ipmi RMCPPlusSession header v1.5 format
// BMCs that support ipmi v2.0/RMCP+ must support the Get Channel Authentication Capabilities command in both the ipmi v1.5 and v2.0 packet formats
// It is recommended that a remote console uses the ipmi v1.5 formats until it has confirmed ipmi v2.0 support
type ipmiSessionWrapper struct {
AuthenticationType authenticationType
SequenceNumber uint32
SessionId uint32
AuthenticationCode [16]byte
MessageLen uint8
}
func newIPMISession(buf io.Reader) (*ipmiSession, error) {
var authType authenticationType
if err := binary.Read(buf, binary.LittleEndian, &authType); err != nil {
return nil, fmt.Errorf("failed to read authentication type: %w", err)
}
return &ipmiSession{authType: authType}, nil
}
// handle handles both ipmi v1.5 and v2.0 packet formats and dispatches layers below
func (r *ipmiSession) handle(buf io.Reader, machine Machine, session *rmcpPlusSessionHolder, bmcUser *bmcUserHolder) ([]byte, error) {
rmcpPlus, err := isRMCPPlusFormat(r.authType)
if err != nil {
return nil, err
}
if rmcpPlus {
rmcpPlus, err := newRMCPPlus(buf, r.authType, session, bmcUser)
if err != nil {
return nil, err
}
return rmcpPlus.handle(buf, machine)
}
wrapper, err := deserializeIPMISessionWrapper(buf, r.authType)
if err != nil {
return nil, err
}
ipmi, err := newIPMI(buf, int(wrapper.MessageLen), machine, session)
if err != nil {
return nil, err
}
res, err := ipmi.handle()
if err != nil {
return nil, err
}
responseWrapper := ipmiSessionWrapper{
AuthenticationType: r.authType,
SequenceNumber: wrapper.SequenceNumber,
SessionId: wrapper.SessionId,
MessageLen: uint8(len(res)),
}
obuf := bytes.Buffer{}
if err := serializeIPMISessionWrapper(&obuf, responseWrapper); err != nil {
return nil, err
}
if err := binary.Write(&obuf, binary.LittleEndian, res); err != nil {
return nil, fmt.Errorf("failed to write ipmi response body: %w", err)
}
return obuf.Bytes(), nil
}
func isRMCPPlusFormat(authType authenticationType) (bool, error) {
switch authType {
case authTypeRMCPPlus:
return true, nil
case authTypeNone:
return false, nil
case authTypeMD2:
case authTypeMD5:
case authTypeStraightPasswordKey:
case authTypeOEM:
default:
}
return false, fmt.Errorf("unsupported authentication type %d", authType)
}
func deserializeIPMISessionWrapper(buf io.Reader, authType authenticationType) (*ipmiSessionWrapper, error) {
wrapper := &ipmiSessionWrapper{}
wrapper.AuthenticationType = authType
if err := binary.Read(buf, binary.LittleEndian, &wrapper.SequenceNumber); err != nil {
return nil, fmt.Errorf("failed to read SequenceNumber: %w", err)
}
if err := binary.Read(buf, binary.LittleEndian, &wrapper.SessionId); err != nil {
return nil, fmt.Errorf("failed to read SessionId: %w", err)
}
if wrapper.SessionId != 0x00 {
if err := binary.Read(buf, binary.LittleEndian, &wrapper.AuthenticationCode); err != nil {
return nil, fmt.Errorf("failed to read AuthenticationCode: %w", err)
}
}
if err := binary.Read(buf, binary.LittleEndian, &wrapper.MessageLen); err != nil {
return nil, fmt.Errorf("failed to read MessageLen: %w", err)
}
return wrapper, nil
}
func serializeIPMISessionWrapper(buf *bytes.Buffer, wrapper ipmiSessionWrapper) error {
if err := binary.Write(buf, binary.LittleEndian, wrapper.AuthenticationType); err != nil {
return fmt.Errorf("failed to write authenticationType: %w", err)
}
if err := binary.Write(buf, binary.LittleEndian, wrapper.SequenceNumber); err != nil {
return fmt.Errorf("failed to write SequenceNumber: %w", err)
}
if err := binary.Write(buf, binary.LittleEndian, wrapper.SessionId); err != nil {
return fmt.Errorf("failed to write SessionId: %w", err)
}
if wrapper.SessionId != 0 {
if err := binary.Write(buf, binary.LittleEndian, wrapper.AuthenticationCode); err != nil {
return fmt.Errorf("failed to write AuthenticationCode: %w", err)
}
}
if err := binary.Write(buf, binary.LittleEndian, wrapper.MessageLen); err != nil {
return fmt.Errorf("failed to write MessageLen: %w", err)
}
return nil
}