forked from tailscale/tailscale
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathserialnumber_notmacos.go
143 lines (115 loc) · 3.54 KB
/
serialnumber_notmacos.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
// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Build on Windows, Linux and *BSD
//go:build windows || (linux && !android) || freebsd || openbsd || dragonfly || netbsd
package posture
import (
"errors"
"fmt"
"strings"
"github.com/digitalocean/go-smbios/smbios"
"tailscale.com/types/logger"
"tailscale.com/util/multierr"
)
// getByteFromSmbiosStructure retrieves a 8-bit unsigned integer at the given specOffset.
func getByteFromSmbiosStructure(s *smbios.Structure, specOffset int) uint8 {
// the `Formatted` byte slice is missing the first 4 bytes of the structure that are stripped out as header info.
// so we need to subtract 4 from the offset mentioned in the SMBIOS documentation to get the right value.
index := specOffset - 4
if index >= len(s.Formatted) || index < 0 {
return 0
}
return s.Formatted[index]
}
// getStringFromSmbiosStructure retrieves a string at the given specOffset.
// Returns an empty string if no string was present.
func getStringFromSmbiosStructure(s *smbios.Structure, specOffset int) (string, error) {
index := getByteFromSmbiosStructure(s, specOffset)
if index == 0 || int(index) > len(s.Strings) {
return "", errors.New("specified offset does not exist in smbios structure")
}
str := s.Strings[index-1]
trimmed := strings.TrimSpace(str)
return trimmed, nil
}
// Product Table (Type 1) structure
// https://web.archive.org/web/20220126173219/https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
// Page 34 and onwards.
const (
// Serial is present at the same offset in all IDs
serialNumberOffset = 0x07
productID = 1
baseboardID = 2
chassisID = 3
)
var (
idToTableName = map[int]string{
1: "product",
2: "baseboard",
3: "chassis",
}
validTables []string
numOfTables int
)
func init() {
for _, table := range idToTableName {
validTables = append(validTables, table)
}
numOfTables = len(validTables)
}
// serialFromSmbiosStructure extracts a serial number from a product,
// baseboard or chassis SMBIOS table.
func serialFromSmbiosStructure(s *smbios.Structure) (string, error) {
id := s.Header.Type
if (id != productID) && (id != baseboardID) && (id != chassisID) {
return "", fmt.Errorf(
"cannot get serial table type %d, supported tables are %v",
id,
validTables,
)
}
serial, err := getStringFromSmbiosStructure(s, serialNumberOffset)
if err != nil {
return "", fmt.Errorf(
"failed to get serial from %s table: %w",
idToTableName[int(s.Header.Type)],
err,
)
}
return serial, nil
}
func GetSerialNumbers(logf logger.Logf) ([]string, error) {
// Find SMBIOS data in operating system-specific location.
rc, _, err := smbios.Stream()
if err != nil {
return nil, fmt.Errorf("failed to open dmi/smbios stream: %w", err)
}
defer rc.Close()
// Decode SMBIOS structures from the stream.
d := smbios.NewDecoder(rc)
ss, err := d.Decode()
if err != nil {
return nil, fmt.Errorf("failed to decode dmi/smbios structures: %w", err)
}
serials := make([]string, 0, numOfTables)
errs := make([]error, 0, numOfTables)
for _, s := range ss {
switch s.Header.Type {
case productID, baseboardID, chassisID:
serial, err := serialFromSmbiosStructure(s)
if err != nil {
errs = append(errs, err)
continue
}
serials = append(serials, serial)
}
}
err = multierr.New(errs...)
// if there were no serial numbers, check if any errors were
// returned and combine them.
if len(serials) == 0 && err != nil {
return nil, err
}
logf("got serial numbers %v (errors: %s)", serials, err)
return serials, nil
}