Jonathan Turner 9 лет назад
Родитель
Сommit
3edf314ac6
9 измененных файлов с 65 добавлено и 5 удалено
  1. 6 5
      asn1tools/tools.go
  2. 2 0
      client/ASExchange.go
  3. 2 0
      client/TGSExchange.go
  4. 12 0
      client/cache.go
  5. 6 0
      client/client.go
  6. 3 0
      client/network.go
  7. 1 0
      client/session.go
  8. 25 0
      config/krb5conf.go
  9. 8 0
      credentials/credentials.go

+ 6 - 5
asn1tools/tools.go

@@ -1,10 +1,10 @@
 package asn1tools
 package asn1tools
 
 
 
 
-// ASN1 Length octets.
+// Get the ASN1 encoded bytes for the length 'l'.
 // There are two forms: short (for lengths between 0 and 127), and long definite (for lengths between 0 and 2^1008 -1).
 // There are two forms: short (for lengths between 0 and 127), and long definite (for lengths between 0 and 2^1008 -1).
-// Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
-// Long form. Two to 127 octets. Bit 8 of first octet has value "1" and bits 7-1 give the number of additional length octets. Second and following octets give the length, base 256, most significant digit first.
+// Short form: One octet. Bit 8 has value "0" and bits 7-1 give the length.
+// Long form: Two to 127 octets. Bit 8 of first octet has value "1" and bits 7-1 give the number of additional length octets. Second and following octets give the length, base 256, most significant digit first.
 func MarshalLengthBytes(l int) []byte {
 func MarshalLengthBytes(l int) []byte {
 	if l <= 127 {
 	if l <= 127 {
 		return []byte{byte(l)}
 		return []byte{byte(l)}
@@ -22,6 +22,7 @@ func MarshalLengthBytes(l int) []byte {
 	return append([]byte{byte(128 + len(b))}, b...)
 	return append([]byte{byte(128 + len(b))}, b...)
 }
 }
 
 
+// Get the length of a slice of ASN1 encoded bytes from the ASN1 length header it contains.
 func GetLengthFromASN(b []byte) int {
 func GetLengthFromASN(b []byte) int {
 	if int(b[1]) <= 127 {
 	if int(b[1]) <= 127 {
 		return int(b[1])
 		return int(b[1])
@@ -37,8 +38,8 @@ func GetLengthFromASN(b []byte) int {
 	return l
 	return l
 }
 }
 
 
-// The Marshal method of golang's asn1 package does not enable you to configure to wrap the output in an application tag.
-// This method adds that wrapping tag
+// The Marshal method of golang's asn1 package does not enable you to define wrapping the output in an application tag.
+// This method adds that wrapping tag.
 func AddASNAppTag(b []byte, tag int) []byte {
 func AddASNAppTag(b []byte, tag int) []byte {
 	// The ASN1 wrapping consists of 2 bytes:
 	// The ASN1 wrapping consists of 2 bytes:
 	// 1st byte -> Identifier Octet - Application Tag
 	// 1st byte -> Identifier Octet - Application Tag

+ 2 - 0
client/ASExchange.go

@@ -11,10 +11,12 @@ import (
 	"sort"
 	"sort"
 )
 )
 
 
+// Login the client with the KDC via an AS exchange
 func (cl *Client) Login() error {
 func (cl *Client) Login() error {
 	return cl.ASExchange()
 	return cl.ASExchange()
 }
 }
 
 
+// Perform an AS exchange for the client to retrieve a TGT
 func (cl *Client) ASExchange() error {
 func (cl *Client) ASExchange() error {
 	if !cl.IsConfigured() {
 	if !cl.IsConfigured() {
 		return errors.New("Client is not configured correctly.")
 		return errors.New("Client is not configured correctly.")

+ 2 - 0
client/TGSExchange.go

@@ -6,6 +6,8 @@ import (
 	"github.com/jcmturner/gokrb5/messages"
 	"github.com/jcmturner/gokrb5/messages"
 )
 )
 
 
+// Perform a TGS exchange to retrieve a ticket to the specified SPN.
+// The ticket retrieved is added to the client's cache
 func (cl *Client) TGSExchange(spn string) error {
 func (cl *Client) TGSExchange(spn string) error {
 	if cl.Session == nil {
 	if cl.Session == nil {
 		return errors.New("Error client does not have a session. Client needs to login first")
 		return errors.New("Error client does not have a session. Client needs to login first")

+ 12 - 0
client/cache.go

@@ -8,10 +8,12 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+// Client ticket cache
 type Cache struct {
 type Cache struct {
 	Entries map[string]CacheEntry
 	Entries map[string]CacheEntry
 }
 }
 
 
+// Ticket cache entry
 type CacheEntry struct {
 type CacheEntry struct {
 	Ticket    types.Ticket
 	Ticket    types.Ticket
 	AuthTime  time.Time
 	AuthTime  time.Time
@@ -20,17 +22,21 @@ type CacheEntry struct {
 	AutoRenew bool
 	AutoRenew bool
 }
 }
 
 
+// Create a new client ticket cache.
 func NewCache() *Cache {
 func NewCache() *Cache {
 	return &Cache{
 	return &Cache{
 		Entries: map[string]CacheEntry{},
 		Entries: map[string]CacheEntry{},
 	}
 	}
 }
 }
 
 
+// Get a cache entry that matches the SPN.
 func (c *Cache) GetEntry(spn string) (CacheEntry, bool) {
 func (c *Cache) GetEntry(spn string) (CacheEntry, bool) {
 	e, ok := (*c).Entries[spn]
 	e, ok := (*c).Entries[spn]
 	return e, ok
 	return e, ok
 }
 }
 
 
+// Get a ticket from the cache for the SPN.
+// Only a ticket that is currently valid will be returned.
 func (c *Cache) GetTicket(spn string) (types.Ticket, bool) {
 func (c *Cache) GetTicket(spn string) (types.Ticket, bool) {
 	if e, ok := c.GetEntry(spn); ok {
 	if e, ok := c.GetEntry(spn); ok {
 		//If within time window of ticket return it
 		//If within time window of ticket return it
@@ -42,6 +48,7 @@ func (c *Cache) GetTicket(spn string) (types.Ticket, bool) {
 	return tkt, false
 	return tkt, false
 }
 }
 
 
+// Renew a ticket in the cache for the specified SPN.
 func (c *Cache) RenewEntry(spn string) error {
 func (c *Cache) RenewEntry(spn string) error {
 	if e, ok := c.GetEntry(spn); ok {
 	if e, ok := c.GetEntry(spn); ok {
 		return e.Renew()
 		return e.Renew()
@@ -49,6 +56,7 @@ func (c *Cache) RenewEntry(spn string) error {
 	return fmt.Errorf("No entry for this SPN: %s", spn)
 	return fmt.Errorf("No entry for this SPN: %s", spn)
 }
 }
 
 
+// Add a ticket to the cache
 func (c *Cache) AddEntry(tkt types.Ticket, authTime, endTime, renewTill time.Time) {
 func (c *Cache) AddEntry(tkt types.Ticket, authTime, endTime, renewTill time.Time) {
 	(*c).Entries[strings.Join(tkt.SName.NameString, "/")] = CacheEntry{
 	(*c).Entries[strings.Join(tkt.SName.NameString, "/")] = CacheEntry{
 		Ticket:    tkt,
 		Ticket:    tkt,
@@ -58,18 +66,22 @@ func (c *Cache) AddEntry(tkt types.Ticket, authTime, endTime, renewTill time.Tim
 	}
 	}
 }
 }
 
 
+// Remove the cache entry for the defined SPN
 func (c *Cache) RemoveEntry(spn string) {
 func (c *Cache) RemoveEntry(spn string) {
 	delete(c.Entries, spn)
 	delete(c.Entries, spn)
 }
 }
 
 
+// Enable background auto renew of the ticket for the specified SPN
 func (c *Cache) EnableAutoRenew(spn string) error {
 func (c *Cache) EnableAutoRenew(spn string) error {
 	return nil
 	return nil
 }
 }
 
 
+// Disable background auto renew of the ticket for the specified SPN
 func (c *Cache) DisableAutoRenew(spn string) error {
 func (c *Cache) DisableAutoRenew(spn string) error {
 	return nil
 	return nil
 }
 }
 
 
+// Renew the cache entry
 func (e *CacheEntry) Renew() error {
 func (e *CacheEntry) Renew() error {
 	if time.Now().After(e.RenewTill) {
 	if time.Now().After(e.RenewTill) {
 		return errors.New("Past renew till time. Cannot renew.")
 		return errors.New("Past renew till time. Cannot renew.")

+ 6 - 0
client/client.go

@@ -6,6 +6,7 @@ import (
 	"github.com/jcmturner/gokrb5/keytab"
 	"github.com/jcmturner/gokrb5/keytab"
 )
 )
 
 
+// Client struct
 type Client struct {
 type Client struct {
 	Credentials *credentials.Credentials
 	Credentials *credentials.Credentials
 	Config      *config.Config
 	Config      *config.Config
@@ -13,6 +14,7 @@ type Client struct {
 	Cache       *Cache
 	Cache       *Cache
 }
 }
 
 
+// Create a new client with a password credential
 func NewClientWithPassword(username, password string) Client {
 func NewClientWithPassword(username, password string) Client {
 	creds := credentials.NewCredentials(username)
 	creds := credentials.NewCredentials(username)
 	return Client{
 	return Client{
@@ -22,6 +24,7 @@ func NewClientWithPassword(username, password string) Client {
 	}
 	}
 }
 }
 
 
+// Create a new client with a keytab credential
 func NewClientWithKeytab(username string, kt keytab.Keytab) Client {
 func NewClientWithKeytab(username string, kt keytab.Keytab) Client {
 	creds := credentials.NewCredentials(username)
 	creds := credentials.NewCredentials(username)
 	return Client{
 	return Client{
@@ -31,11 +34,13 @@ func NewClientWithKeytab(username string, kt keytab.Keytab) Client {
 	}
 	}
 }
 }
 
 
+// Set the Kerberos configuration for the client
 func (cl *Client) WithConfig(cfg *config.Config) *Client {
 func (cl *Client) WithConfig(cfg *config.Config) *Client {
 	cl.Config = cfg
 	cl.Config = cfg
 	return cl
 	return cl
 }
 }
 
 
+// Load the Kerberos configuration for the client from file path specified
 func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
 func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
 	cfg, err := config.Load(cfgPath)
 	cfg, err := config.Load(cfgPath)
 	if err != nil {
 	if err != nil {
@@ -45,6 +50,7 @@ func (cl *Client) LoadConfig(cfgPath string) (*Client, error) {
 	return cl, nil
 	return cl, nil
 }
 }
 
 
+// Has the client got sufficient values required
 func (cl *Client) IsConfigured() bool {
 func (cl *Client) IsConfigured() bool {
 	if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
 	if !cl.Credentials.HasPassword() && !cl.Credentials.HasKeytab() {
 		return false
 		return false

+ 3 - 0
client/network.go

@@ -8,6 +8,7 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+// Send bytes to the KDC
 func (cl *Client) SendToKDC(b []byte) ([]byte, error) {
 func (cl *Client) SendToKDC(b []byte) ([]byte, error) {
 	var rb []byte
 	var rb []byte
 	var kdcs []string
 	var kdcs []string
@@ -67,6 +68,7 @@ func (cl *Client) SendToKDC(b []byte) ([]byte, error) {
 	return rb, nil
 	return rb, nil
 }
 }
 
 
+// Send the bytes to the KDC over UDP
 func sendUDP(kdc string, b []byte) ([]byte, error) {
 func sendUDP(kdc string, b []byte) ([]byte, error) {
 	var r []byte
 	var r []byte
 	udpAddr, err := net.ResolveUDPAddr("udp", kdc)
 	udpAddr, err := net.ResolveUDPAddr("udp", kdc)
@@ -92,6 +94,7 @@ func sendUDP(kdc string, b []byte) ([]byte, error) {
 	return r, nil
 	return r, nil
 }
 }
 
 
+// Send the bytes to the KDC over TCP
 func sendTCP(kdc string, b []byte) ([]byte, error) {
 func sendTCP(kdc string, b []byte) ([]byte, error) {
 	var r []byte
 	var r []byte
 	tcpAddr, err := net.ResolveTCPAddr("tcp", kdc)
 	tcpAddr, err := net.ResolveTCPAddr("tcp", kdc)

+ 1 - 0
client/session.go

@@ -5,6 +5,7 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+// Client session struct
 type Session struct {
 type Session struct {
 	AuthTime             time.Time
 	AuthTime             time.Time
 	EndTime              time.Time
 	EndTime              time.Time

+ 25 - 0
config/krb5conf.go

@@ -16,6 +16,9 @@ import (
 	"time"
 	"time"
 )
 )
 
 
+// Implements KRB5 client and service configuration as described at https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html
+
+// Struct representing the KRB5 configuration
 type Config struct {
 type Config struct {
 	LibDefaults *LibDefaults
 	LibDefaults *LibDefaults
 	Realms      []Realm
 	Realms      []Realm
@@ -25,10 +28,12 @@ type Config struct {
 	//Plugins
 	//Plugins
 }
 }
 
 
+// List of encryption types that have been deemed weak.
 const (
 const (
 	WEAK_ETYPE_LIST = "des-cbc-crc des-cbc-md4 des-cbc-md5 des-cbc-raw des3-cbc-raw des-hmac-sha1 arcfour-hmac-exp rc4-hmac-exp arcfour-hmac-md5-exp des"
 	WEAK_ETYPE_LIST = "des-cbc-crc des-cbc-md4 des-cbc-md5 des-cbc-raw des3-cbc-raw des-hmac-sha1 arcfour-hmac-exp rc4-hmac-exp arcfour-hmac-md5-exp des"
 )
 )
 
 
+// Create a new config struct
 func NewConfig() *Config {
 func NewConfig() *Config {
 	d := make(DomainRealm)
 	d := make(DomainRealm)
 	return &Config{
 	return &Config{
@@ -37,6 +42,7 @@ func NewConfig() *Config {
 	}
 	}
 }
 }
 
 
+// Struct representing the [libdefaults] section of the configuration
 type LibDefaults struct {
 type LibDefaults struct {
 	Allow_weak_crypto bool //default false
 	Allow_weak_crypto bool //default false
 	// ap_req_checksum_type int //unlikely to support this
 	// ap_req_checksum_type int //unlikely to support this
@@ -77,6 +83,7 @@ type LibDefaults struct {
 	Verify_ap_req_nofail    bool          //default false
 	Verify_ap_req_nofail    bool          //default false
 }
 }
 
 
+// Create a new LibDefaults struct
 func newLibDefaults() *LibDefaults {
 func newLibDefaults() *LibDefaults {
 	usr, _ := user.Current()
 	usr, _ := user.Current()
 	opts := asn1.BitString{}
 	opts := asn1.BitString{}
@@ -104,6 +111,7 @@ func newLibDefaults() *LibDefaults {
 	}
 	}
 }
 }
 
 
+// Parse the lines of the [libdefaults] section of the configuration into the LibDefaults struct
 func (l *LibDefaults) parseLines(lines []string) error {
 func (l *LibDefaults) parseLines(lines []string) error {
 	for _, line := range lines {
 	for _, line := range lines {
 		if !strings.Contains(line, "=") {
 		if !strings.Contains(line, "=") {
@@ -284,6 +292,7 @@ func (l *LibDefaults) parseLines(lines []string) error {
 	return nil
 	return nil
 }
 }
 
 
+// Struct representing an entry in the [realms] section of the configuration
 type Realm struct {
 type Realm struct {
 	Realm        string
 	Realm        string
 	Admin_server []string
 	Admin_server []string
@@ -295,6 +304,7 @@ type Realm struct {
 	Master_kdc     []string
 	Master_kdc     []string
 }
 }
 
 
+// Parse the lines of a [realms] entry into the Realm struct
 func (r *Realm) parseLines(name string, lines []string) error {
 func (r *Realm) parseLines(name string, lines []string) error {
 	r.Realm = name
 	r.Realm = name
 	var admin_server_final bool
 	var admin_server_final bool
@@ -335,6 +345,7 @@ func (r *Realm) parseLines(name string, lines []string) error {
 	return nil
 	return nil
 }
 }
 
 
+// Parse the lines of the [realms] section of the configuration into an slice of Realm structs
 func parseRealms(lines []string) ([]Realm, error) {
 func parseRealms(lines []string) ([]Realm, error) {
 	var realms []Realm
 	var realms []Realm
 	start := -1
 	start := -1
@@ -366,8 +377,10 @@ func parseRealms(lines []string) ([]Realm, error) {
 	return realms, nil
 	return realms, nil
 }
 }
 
 
+// Mapping of domains to realms representing the [domain_realm] section of the configuration
 type DomainRealm map[string]string
 type DomainRealm map[string]string
 
 
+// Parse the lines of the [domain_realm] section of the configuration and add to the mapping
 func (d *DomainRealm) parseLines(lines []string) error {
 func (d *DomainRealm) parseLines(lines []string) error {
 	for _, line := range lines {
 	for _, line := range lines {
 		if !strings.Contains(line, "=") {
 		if !strings.Contains(line, "=") {
@@ -381,14 +394,18 @@ func (d *DomainRealm) parseLines(lines []string) error {
 	return nil
 	return nil
 }
 }
 
 
+// Add a domain to realm mapping
 func (d *DomainRealm) addMapping(domain, realm string) {
 func (d *DomainRealm) addMapping(domain, realm string) {
 	(*d)[domain] = realm
 	(*d)[domain] = realm
 }
 }
 
 
+// Delete a domain to realm mapping
 func (d *DomainRealm) deleteMapping(domain, realm string) {
 func (d *DomainRealm) deleteMapping(domain, realm string) {
 	delete(*d, domain)
 	delete(*d, domain)
 }
 }
 
 
+// Resolve the realm for the specified domain name from the domain to realm mapping.
+// The most specific mapping is returned.
 func (c *Config) ResolveRealm(domainName string) string {
 func (c *Config) ResolveRealm(domainName string) string {
 	domainName = strings.TrimSuffix(domainName, ".")
 	domainName = strings.TrimSuffix(domainName, ".")
 	periods := strings.Count(domainName, ".") + 1
 	periods := strings.Count(domainName, ".") + 1
@@ -401,6 +418,7 @@ func (c *Config) ResolveRealm(domainName string) string {
 	return c.LibDefaults.Default_realm
 	return c.LibDefaults.Default_realm
 }
 }
 
 
+// Load the KRB5 configuration from the specified file path
 func Load(cfgPath string) (*Config, error) {
 func Load(cfgPath string) (*Config, error) {
 	fh, err := os.Open(cfgPath)
 	fh, err := os.Open(cfgPath)
 	if err != nil {
 	if err != nil {
@@ -411,16 +429,19 @@ func Load(cfgPath string) (*Config, error) {
 	return NewConfigFromScanner(scanner)
 	return NewConfigFromScanner(scanner)
 }
 }
 
 
+// Create a new Config struct from a string
 func NewConfigFromString(s string) (*Config, error) {
 func NewConfigFromString(s string) (*Config, error) {
 	reader := strings.NewReader(s)
 	reader := strings.NewReader(s)
 	return NewConfigFromReader(reader)
 	return NewConfigFromReader(reader)
 }
 }
 
 
+// Create a new Config struct from an io.Reader
 func NewConfigFromReader(r io.Reader) (*Config, error) {
 func NewConfigFromReader(r io.Reader) (*Config, error) {
 	scanner := bufio.NewScanner(r)
 	scanner := bufio.NewScanner(r)
 	return NewConfigFromScanner(scanner)
 	return NewConfigFromScanner(scanner)
 }
 }
 
 
+// Create a new Config struct from a bufio.Scanner
 func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error) {
 func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error) {
 	c := NewConfig()
 	c := NewConfig()
 	sections := make(map[int]string)
 	sections := make(map[int]string)
@@ -484,6 +505,7 @@ func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error) {
 	return c, nil
 	return c, nil
 }
 }
 
 
+// Parse a space delimited list of ETypes into a list of EType numbers optionally filtering out weak ETypes
 func parseETypes(s []string, w bool) []int {
 func parseETypes(s []string, w bool) []int {
 	var eti []int
 	var eti []int
 	for _, et := range s {
 	for _, et := range s {
@@ -507,6 +529,7 @@ func parseETypes(s []string, w bool) []int {
 	return eti
 	return eti
 }
 }
 
 
+// Parse a time duration string in the configuration to a golang time.Duration.
 func parseDuration(s string) (time.Duration, error) {
 func parseDuration(s string) (time.Duration, error) {
 	s = strings.Replace(s, " ", "", -1)
 	s = strings.Replace(s, " ", "", -1)
 	d, err := time.ParseDuration(s)
 	d, err := time.ParseDuration(s)
@@ -539,6 +562,7 @@ func parseDuration(s string) (time.Duration, error) {
 	return time.Duration(0), errors.New("Invalid time duration value")
 	return time.Duration(0), errors.New("Invalid time duration value")
 }
 }
 
 
+// Parse possible boolean values to golang bool
 func parseBoolean(s string) (bool, error) {
 func parseBoolean(s string) (bool, error) {
 	s = strings.Replace(s, " ", "", -1)
 	s = strings.Replace(s, " ", "", -1)
 	v, err := strconv.ParseBool(s)
 	v, err := strconv.ParseBool(s)
@@ -558,6 +582,7 @@ func parseBoolean(s string) (bool, error) {
 	return false, errors.New("Invalid boolean value")
 	return false, errors.New("Invalid boolean value")
 }
 }
 
 
+// Parse array of strings but stop if an asterisk is placed at the end of a line
 func appendUntilFinal(s *[]string, value string, final *bool) {
 func appendUntilFinal(s *[]string, value string, final *bool) {
 	if *final {
 	if *final {
 		return
 		return

+ 8 - 0
credentials/credentials.go

@@ -2,12 +2,16 @@ package credentials
 
 
 import "github.com/jcmturner/gokrb5/keytab"
 import "github.com/jcmturner/gokrb5/keytab"
 
 
+// Credentials struct for a user.
+// Contains either a keytab, password or both.
+// Keytabs are used over passwords if both are defined.
 type Credentials struct {
 type Credentials struct {
 	Username string
 	Username string
 	Keytab   keytab.Keytab
 	Keytab   keytab.Keytab
 	Password string
 	Password string
 }
 }
 
 
+// Create a new Credentials struct
 func NewCredentials(username string) Credentials {
 func NewCredentials(username string) Credentials {
 	return Credentials{
 	return Credentials{
 		Username: username,
 		Username: username,
@@ -15,16 +19,19 @@ func NewCredentials(username string) Credentials {
 	}
 	}
 }
 }
 
 
+// Set the Keytab in the Credentials struct
 func (c *Credentials) WithKeytab(kt keytab.Keytab) *Credentials {
 func (c *Credentials) WithKeytab(kt keytab.Keytab) *Credentials {
 	c.Keytab = kt
 	c.Keytab = kt
 	return c
 	return c
 }
 }
 
 
+// Set the password in the Credentials struct
 func (c *Credentials) WithPassword(password string) *Credentials {
 func (c *Credentials) WithPassword(password string) *Credentials {
 	c.Password = password
 	c.Password = password
 	return c
 	return c
 }
 }
 
 
+// Query if the Credentials has a keytab defined
 func (c *Credentials) HasKeytab() bool {
 func (c *Credentials) HasKeytab() bool {
 	if len(c.Keytab.Entries) > 0 {
 	if len(c.Keytab.Entries) > 0 {
 		return true
 		return true
@@ -32,6 +39,7 @@ func (c *Credentials) HasKeytab() bool {
 	return false
 	return false
 }
 }
 
 
+// Query if the Credentials has a password defined
 func (c *Credentials) HasPassword() bool {
 func (c *Credentials) HasPassword() bool {
 	if c.Password != "" {
 	if c.Password != "" {
 		return true
 		return true