youtubebeat/vendor/github.com/elastic/beats/packetbeat/protos/tls/extensions.go

274 lines
7.2 KiB
Go

// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package tls
import (
"fmt"
"strconv"
"github.com/elastic/beats/libbeat/common"
"github.com/elastic/beats/libbeat/logp"
)
// ExtensionID is the 16-bit identifier for an extension
type ExtensionID uint16
// Extensions stores the data from parsed extensions
type Extensions struct {
Parsed common.MapStr
Raw map[ExtensionID][]byte
InOrder []ExtensionID
}
type extensionParser func(reader bufferView) interface{}
type extension struct {
label string
parser extensionParser
saveRaw bool
}
const (
// ExtensionSupportedGroups identifies the supported group extension
ExtensionSupportedGroups ExtensionID = 10
// ExtensionEllipticCurvePointsFormats identifies the points formats extension
ExtensionEllipticCurvePointsFormats = 11
)
var extensionMap = map[uint16]extension{
0: {"server_name_indication", parseSni, false},
1: {"max_fragment_length", parseMaxFragmentLen, false},
2: {"client_certificate_url", expectEmpty, false},
3: {"trusted_ca_keys", ignoreContent, false},
4: {"truncated_hmac", expectEmpty, false},
5: {"status_request", ignoreContent, false},
6: {"user_mapping", ignoreContent, false},
7: {"client_authz", ignoreContent, false},
8: {"server_authz", ignoreContent, false},
9: {"cert_type", parseCertType, false},
10: {"supported_groups", parseSupportedGroups, true},
11: {"ec_points_formats", parseEcPoints, true},
12: {"srp", parseSrp, false},
13: {"signature_algorithms", parseSignatureSchemes, false},
16: {"application_layer_protocol_negotiation", parseALPN, false},
35: {"session_ticket", parseTicket, false},
0xff01: {"renegotiation_info", ignoreContent, false},
}
// ParseExtensions returns an Extensions object parsed from the supplied buffer
func ParseExtensions(buffer bufferView) Extensions {
var extensionsLength uint16
if !buffer.read16Net(0, &extensionsLength) || extensionsLength == 0 {
// No extensions
return Extensions{}
}
limit := 2 + int(extensionsLength)
result := Extensions{
Parsed: common.MapStr{},
Raw: make(map[ExtensionID][]byte),
}
var unknown []string
for base := 2; base < limit; {
var code, length uint16
if !buffer.read16Net(base, &code) || !buffer.read16Net(base+2, &length) {
logp.Warn("failed parsing extensions")
return Extensions{}
}
extBuffer := buffer.subview(base+4, int(length))
base += 4 + int(length)
// Skip GREASE extensions
if isGreaseValue(code) {
continue
}
result.InOrder = append(result.InOrder, ExtensionID(code))
label, parsed, saveRaw := parseExtension(code, extBuffer)
if parsed != nil {
result.Parsed[label] = parsed
} else {
unknown = append(unknown, label)
}
if saveRaw {
result.Raw[ExtensionID(code)] = extBuffer.readBytes(0, extBuffer.length())
}
}
if len(unknown) != 0 {
result.Parsed["_unparsed_"] = unknown
}
return result
}
func parseExtension(code uint16, buffer bufferView) (string, interface{}, bool) {
if ext, ok := extensionMap[code]; ok {
parsed := ext.parser(buffer)
return ext.label, parsed, ext.saveRaw
}
return strconv.Itoa(int(code)), nil, false
}
func parseSni(buffer bufferView) interface{} {
var listLength uint16
if !buffer.read16Net(0, &listLength) {
return nil
}
var hosts []string
for pos, limit := 2, 2+int(listLength); pos+3 <= limit; {
var nameType uint8
var nameLen uint16
var host string
if !buffer.read8(pos, &nameType) || !buffer.read16Net(pos+1, &nameLen) ||
limit < pos+3+int(nameLen) || !buffer.readString(pos+3, int(nameLen), &host) {
logp.Warn("SNI hostname list truncated")
break
}
if nameType == 0 {
hosts = append(hosts, host)
}
pos += 3 + int(nameLen)
}
return hosts
}
func parseMaxFragmentLen(buffer bufferView) interface{} {
var val uint8
if buffer.length() == 1 && buffer.read8(0, &val) {
if val > 0 && val < 5 {
return fmt.Sprintf("2^%d", 8+val)
}
return fmt.Sprintf("(unknown:%d)", val)
}
return nil
}
func ignoreContent(_ bufferView) interface{} {
return nil
}
func expectEmpty(buffer bufferView) interface{} {
if buffer.length() != 0 {
return fmt.Sprintf("(expected empty: found %d bytes)", buffer.length())
}
return ""
}
func parseCertType(buffer bufferView) interface{} {
var value uint8
var types []string
pos, limit := 0, buffer.length()
if limit > 1 {
buffer.read8(0, &value)
pos = 1
if int(value)+1 < limit {
limit = 1 + int(value)
}
}
for ; pos < limit && buffer.read8(pos, &value); pos++ {
var label string
switch value {
case 0:
label = "X.509"
case 1:
label = "OpenPGP"
case 2:
label = "RawPubKey"
default:
label = fmt.Sprintf("(unknown:%d)", value)
}
types = append(types, label)
}
return types
}
func parseSupportedGroups(buffer bufferView) interface{} {
var value uint16
if !buffer.read16Net(0, &value) || int(value)+2 != buffer.length() {
return nil
}
var groups []string
for pos := 0; buffer.read16Net(pos+2, &value); pos += 2 {
if !isGreaseValue(value) {
groups = append(groups, pointsGroup(value).String())
}
}
return groups
}
func parseEcPoints(buffer bufferView) interface{} {
var value, length uint8
if !buffer.read8(0, &length) || int(length)+1 != buffer.length() {
return nil
}
var formats []string
for pos := 0; pos < int(length) && buffer.read8(1+pos, &value); pos++ {
formats = append(formats, ecPointsFormat(value).String())
}
return formats
}
func parseSrp(buffer bufferView) interface{} {
var length uint8
if !buffer.read8(0, &length) || int(length)+1 > buffer.length() {
return nil
}
var user string
if !buffer.readString(1, int(length), &user) {
return nil
}
return user
}
func parseSignatureSchemes(buffer bufferView) interface{} {
var value uint16
if !buffer.read16Net(0, &value) || int(value)+2 != buffer.length() {
return nil
}
var groups []string
for pos := 2; buffer.read16Net(pos, &value); pos += 2 {
groups = append(groups, signatureScheme(value).String())
}
return groups
}
func parseTicket(buffer bufferView) interface{} {
if buffer.length() > 0 {
return fmt.Sprintf("(%d bytes)", buffer.length())
}
return ""
}
func parseALPN(buffer bufferView) interface{} {
var length uint16
if !buffer.read16Net(0, &length) || int(length)+2 != buffer.length() {
return nil
}
var strlen uint8
var proto string
var protos []string
for pos := 2; buffer.read8(pos, &strlen); {
if !buffer.readString(pos+1, int(strlen), &proto) {
return nil
}
protos = append(protos, proto)
pos += 1 + int(strlen)
}
return protos
}