summaryrefslogtreecommitdiffhomepage
path: root/posture/serial_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'posture/serial_linux.go')
-rw-r--r--posture/serial_linux.go82
1 files changed, 82 insertions, 0 deletions
diff --git a/posture/serial_linux.go b/posture/serial_linux.go
new file mode 100644
index 000000000..ca464cad5
--- /dev/null
+++ b/posture/serial_linux.go
@@ -0,0 +1,82 @@
+//go:build linux
+
+package posture
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/digitalocean/go-smbios"
+)
+
+// GetByte retrieves a 8-bit unsigned integer at the given offset.
+func GetByte(s *smbios.Structure, offset 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 := offset - 4
+ if index >= len(s.Formatted) {
+ return 0
+ }
+
+ return s.Formatted[index]
+}
+
+// GetStringOrEmpty retrieves a string at the given offset.
+// Returns an empty string if no string was present.
+func GetStringOrEmpty(s *smbios.Structure, offset int) (string, error) {
+ index := GetByte(s, offset)
+
+ if index == 0 || int(index) > len(s.Strings) {
+ return errors.New("offset does not exist in smbios structure")
+ }
+
+ str := s.Strings[index-1]
+ trimmed := strings.TrimSpace(str)
+
+ // Convert to lowercase to address multiple formats:
+ // - "To Be Filled By O.E.M."
+ // - "To be filled by O.E.M."
+ if strings.ToLower(trimmed) == "to be filled by o.e.m." {
+ return errors.New("data is not provided by O.E.M.")
+ }
+
+ return trimmed
+}
+
+// System Information (Type 1) structure
+// https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
+// Page 34
+const (
+ sysInfoHeaderType = 1
+ serialNumberOffset = 0x07
+)
+
+func getSerialNumber() (string, error) {
+ // Find SMBIOS data in operating system-specific location.
+ rc, _, err := smbios.Stream()
+ if err != nil {
+ return "", 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 "", fmt.Errorf("failed to decode dmi/smbios structures: %w", err)
+ }
+
+ for _, s := range ss {
+ if s.Header.Type == sysInfoHeaderType {
+ serial, err := GetStringFromSmbiosStructure(s, serialNumberOffset)
+ if err != nil {
+ return "", fmt.Errorf("could not read serial from dmi/smbios structure: %w", err)
+ }
+
+ return serial, nil
+ }
+ }
+
+ return "", fmt.Errorf("could not read serial from dmi/smbios structure: no data found")
+}