github.com/psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/upstreamproxy/go-ntlm/ntlm/av_pairs.go (about)

     1  //Copyright 2013 Thomson Reuters Global Resources. BSD License please see License file for more information
     2  
     3  package ntlm
     4  
     5  import (
     6  	"bytes"
     7  	"encoding/binary"
     8  	"encoding/hex"
     9  	"errors"
    10  	"fmt"
    11  )
    12  
    13  type AvPairType uint16
    14  
    15  // MS-NLMP - 2.2.2.1 AV_PAIR
    16  const (
    17  	// Indicates that this is the last AV_PAIR in the list. AvLen MUST be 0. This type of information MUST be present in the AV pair list.
    18  	MsvAvEOL AvPairType = iota
    19  	// The server's NetBIOS computer name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list.
    20  	MsvAvNbComputerName
    21  	// The server's NetBIOS domain name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list.
    22  	MsvAvNbDomainName
    23  	// The fully qualified domain name (FQDN (1)) of the computer. The name MUST be in Unicode, and is not null-terminated.
    24  	MsvAvDnsComputerName
    25  	// The FQDN (2) of the domain. The name MUST be in Unicode, and is not null-terminate.
    26  	MsvAvDnsDomainName
    27  	// The FQDN (2) of the forest. The name MUST be in Unicode, and is not null-terminated.<11>
    28  	MsvAvDnsTreeName
    29  	// A 32-bit value indicating server or client configuration.
    30  	// 0x00000001: indicates to the client that the account authentication is constrained.
    31  	// 0x00000002: indicates that the client is providing message integrity in the MIC field (section 2.2.1.3) in the AUTHENTICATE_MESSAGE.<12>
    32  	// 0x00000004: indicates that the client is providing a target SPN generated from an untrusted source.<13>
    33  	MsvAvFlags
    34  	// A FILETIME structure ([MS-DTYP] section 2.3.1) in little-endian byte order that contains the server local time.<14>
    35  	MsvAvTimestamp
    36  	//A Restriction_Encoding (section 2.2.2.2) structure. The Value field contains a structure representing the integrity level of the security principal, as well as a MachineID created at computer startup to identify the calling machine.<15>
    37  	MsAvRestrictions
    38  	// The SPN of the target server. The name MUST be in Unicode and is not null-terminated.<16>
    39  	MsvAvTargetName
    40  	// annel bindings hash. The Value field contains an MD5 hash ([RFC4121] section 4.1.1.2) of a gss_channel_bindings_struct ([RFC2744] section 3.11).
    41  	// An all-zero value of the hash is used to indicate absence of channel bindings.<17>
    42  	MsvChannelBindings
    43  )
    44  
    45  // Helper struct that contains a list of AvPairs with helper methods for running through them
    46  type AvPairs struct {
    47  	List []AvPair
    48  }
    49  
    50  func (p *AvPairs) AddAvPair(avId AvPairType, bytes []byte) {
    51  	a := &AvPair{AvId: avId, AvLen: uint16(len(bytes)), Value: bytes}
    52  	p.List = append(p.List, *a)
    53  }
    54  
    55  func ReadAvPairs(data []byte) (*AvPairs, error) {
    56  	pairs := new(AvPairs)
    57  
    58  	// Get the number of AvPairs and allocate enough AvPair structures to hold them
    59  	offset := 0
    60  	for i := 0; len(data) > 0 && i < 11; i++ {
    61  		pair, err := ReadAvPair(data, offset)
    62  		if err != nil {
    63  			return nil, err
    64  		}
    65  		offset = offset + 4 + int(pair.AvLen)
    66  		pairs.List = append(pairs.List, *pair)
    67  		if pair.AvId == MsvAvEOL {
    68  			break
    69  		}
    70  	}
    71  
    72  	return pairs, nil
    73  }
    74  
    75  func (p *AvPairs) Bytes() (result []byte) {
    76  	totalLength := 0
    77  	for i := range p.List {
    78  		a := p.List[i]
    79  		totalLength = totalLength + int(a.AvLen) + 4
    80  	}
    81  
    82  	result = make([]byte, 0, totalLength)
    83  	for i := range p.List {
    84  		a := p.List[i]
    85  		result = append(result, a.Bytes()...)
    86  	}
    87  
    88  	return result
    89  }
    90  
    91  func (p *AvPairs) String() string {
    92  	var buffer bytes.Buffer
    93  
    94  	buffer.WriteString(fmt.Sprintf("Av Pairs (Total %d pairs)\n", len(p.List)))
    95  
    96  	for i := range p.List {
    97  		buffer.WriteString(p.List[i].String())
    98  		buffer.WriteString("\n")
    99  	}
   100  
   101  	return buffer.String()
   102  }
   103  
   104  func (p *AvPairs) Find(avType AvPairType) (result *AvPair) {
   105  	for i := range p.List {
   106  		pair := p.List[i]
   107  		if avType == pair.AvId {
   108  			result = &pair
   109  			break
   110  		}
   111  	}
   112  	return
   113  }
   114  
   115  func (p *AvPairs) ByteValue(avType AvPairType) (result []byte) {
   116  	pair := p.Find(avType)
   117  	if pair != nil {
   118  		result = pair.Value
   119  	}
   120  	return
   121  }
   122  
   123  func (p *AvPairs) StringValue(avType AvPairType) (result string) {
   124  	pair := p.Find(avType)
   125  	if pair != nil {
   126  		result = pair.UnicodeStringValue()
   127  	}
   128  	return
   129  }
   130  
   131  // AvPair as described by MS-NLMP
   132  type AvPair struct {
   133  	AvId  AvPairType
   134  	AvLen uint16
   135  	Value []byte
   136  }
   137  
   138  func ReadAvPair(data []byte, offset int) (*AvPair, error) {
   139  
   140  	// [Psiphon]
   141  	// Don't panic on malformed remote input.
   142  	if len(data) < offset+4 {
   143  		return nil, errors.New("invalid AvPair")
   144  	}
   145  
   146  	pair := new(AvPair)
   147  	pair.AvId = AvPairType(binary.LittleEndian.Uint16(data[offset : offset+2]))
   148  	pair.AvLen = binary.LittleEndian.Uint16(data[offset+2 : offset+4])
   149  
   150  	// [Psiphon]
   151  	// Don't panic on malformed remote input.
   152  	if len(data) < offset+4+int(pair.AvLen) {
   153  		return nil, errors.New("invalid AvPair")
   154  	}
   155  
   156  	pair.Value = data[offset+4 : offset+4+int(pair.AvLen)]
   157  	return pair, nil
   158  }
   159  
   160  func (a *AvPair) UnicodeStringValue() string {
   161  	return utf16ToString(a.Value)
   162  }
   163  
   164  func (a *AvPair) Bytes() (result []byte) {
   165  	result = make([]byte, 4, a.AvLen+4)
   166  	result[0] = byte(a.AvId)
   167  	result[1] = byte(a.AvId >> 8)
   168  	result[2] = byte(a.AvLen)
   169  	result[3] = byte(a.AvLen >> 8)
   170  	result = append(result, a.Value...)
   171  	return
   172  }
   173  
   174  func (a *AvPair) String() string {
   175  	var outString string
   176  
   177  	switch a.AvId {
   178  	case MsvAvEOL:
   179  		outString = "MsvAvEOL"
   180  	case MsvAvNbComputerName:
   181  		outString = "MsAvNbComputerName: " + a.UnicodeStringValue()
   182  	case MsvAvNbDomainName:
   183  		outString = "MsvAvNbDomainName: " + a.UnicodeStringValue()
   184  	case MsvAvDnsComputerName:
   185  		outString = "MsvAvDnsComputerName: " + a.UnicodeStringValue()
   186  	case MsvAvDnsDomainName:
   187  		outString = "MsvAvDnsDomainName: " + a.UnicodeStringValue()
   188  	case MsvAvDnsTreeName:
   189  		outString = "MsvAvDnsTreeName: " + a.UnicodeStringValue()
   190  	case MsvAvFlags:
   191  		outString = "MsvAvFlags: " + hex.EncodeToString(a.Value)
   192  	case MsvAvTimestamp:
   193  		outString = "MsvAvTimestamp: " + hex.EncodeToString(a.Value)
   194  	case MsAvRestrictions:
   195  		outString = "MsAvRestrictions: " + hex.EncodeToString(a.Value)
   196  	case MsvAvTargetName:
   197  		outString = "MsvAvTargetName: " + a.UnicodeStringValue()
   198  	case MsvChannelBindings:
   199  		outString = "MsvChannelBindings: " + hex.EncodeToString(a.Value)
   200  	default:
   201  		outString = fmt.Sprintf("unknown pair type: '%d'", a.AvId)
   202  	}
   203  
   204  	return outString
   205  }