Jonathan Turner 9 лет назад
Родитель
Сommit
ab8bb5c8bf
10 измененных файлов с 307 добавлено и 100 удалено
  1. 1 0
      README.md
  2. 123 0
      client/client.go
  3. 72 23
      config/krb5conf.go
  4. 3 3
      crypto/EncryptionEngine.go
  5. 36 47
      debug.go
  6. 5 2
      keytab/keytab.go
  7. 41 12
      messages/KDCReq.go
  8. 2 11
      messages/constants.go
  9. 2 2
      testenv/krbclient-vagrant/krb5.conf
  10. 22 0
      types/constants.go

+ 1 - 0
README.md

@@ -13,6 +13,7 @@ This is work in progress and does not yet work...
 [text](https://www.ietf.org/rfc/rfc3961.txt) [html](https://tools.ietf.org/html/rfc3961)
 [text](https://www.ietf.org/rfc/rfc3961.txt) [html](https://tools.ietf.org/html/rfc3961)
 * RFC 3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5
 * RFC 3962 Advanced Encryption Standard (AES) Encryption for Kerberos 5
 [text](https://www.ietf.org/rfc/rfc3962.txt) [html](https://tools.ietf.org/html/rfc3962)
 [text](https://www.ietf.org/rfc/rfc3962.txt) [html](https://tools.ietf.org/html/rfc3962)
+* Kerberos Principal Name Canonicalization and Cross-Realm Referrals [text](https://www.ietf.org/rfc/rfc6806.txt) [html](https://tools.ietf.org/html/rfc6806.html)
 * [Microsoft PAC Validation](https://blogs.msdn.microsoft.com/openspecification/2009/04/24/understanding-microsoft-kerberos-pac-validation/)
 * [Microsoft PAC Validation](https://blogs.msdn.microsoft.com/openspecification/2009/04/24/understanding-microsoft-kerberos-pac-validation/)
 * [Microsoft Kerberos Protocol Extensions](https://msdn.microsoft.com/en-us/library/cc233855.aspx)
 * [Microsoft Kerberos Protocol Extensions](https://msdn.microsoft.com/en-us/library/cc233855.aspx)
 
 

+ 123 - 0
client/client.go

@@ -0,0 +1,123 @@
+package client
+
+import (
+	"fmt"
+	"github.com/jcmturner/gokrb5/config"
+	"math/rand"
+	"net"
+	"bytes"
+	"time"
+)
+
+func ASExchange() {
+
+}
+
+func SendToKDC(c *config.Config, b []byte) ([]byte, error) {
+	var rb []byte
+	var kdcs []string
+	for _, r := range c.Realms {
+		if r.Realm == c.LibDefaults.Default_realm {
+			kdcs = r.Kdc
+			break
+		}
+	}
+	if len(kdcs) < 1 {
+		return rb, fmt.Errorf("No KDCs defined in configuration for realm %v", c.LibDefaults.Default_realm)
+	}
+	var kdc string
+	if len(kdcs) > 1 {
+		//Select one of the KDCs at random
+		kdc = kdcs[rand.Intn(len(kdcs))]
+	} else {
+		kdc = kdcs[0]
+	}
+
+	if c.LibDefaults.Udp_preference_limit == 1 {
+		//1 means we should always use TCP
+		rb, errtcp := sendTCP(kdc, b)
+		if errtcp != nil {
+			return rb, fmt.Errorf("Failed to communicate with KDC %v via TDP (%v)", kdc, errtcp)
+		}
+		if len(rb) < 1 {
+			return rb, fmt.Errorf("No response data from KDC %v", kdc)
+		}
+		return rb, nil
+	}
+	if len(b) <= c.LibDefaults.Udp_preference_limit {
+		//Try UDP first, TCP second
+		rb, errudp := sendUDP(kdc, b)
+		if errudp != nil {
+			rb, errtcp := sendTCP(kdc, b)
+			if errtcp != nil {
+				return rb, fmt.Errorf("Failed to communicate with KDC %v via UDP (%v) and then via TDP (%v)", kdc, errudp, errtcp)
+			}
+		}
+		if len(rb) < 1 {
+			return rb, fmt.Errorf("No response data from KDC %v", kdc)
+		}
+		return rb, nil
+	}
+	//Try TCP first, UDP second
+	rb, errtcp := sendTCP(kdc, b)
+	if errtcp != nil {
+		rb, errudp := sendUDP(kdc, b)
+		if errudp != nil {
+			return rb, fmt.Errorf("Failed to communicate with KDC %v via TCP (%v) and then via UDP (%v)", kdc, errtcp, errudp)
+		}
+	}
+	if len(rb) < 1 {
+		return rb, fmt.Errorf("No response data from KDC %v", kdc)
+	}
+	return rb, nil
+}
+
+func sendUDP(kdc string, b []byte) ([]byte, error) {
+	var r []byte
+	udpAddr, err := net.ResolveUDPAddr("udp", kdc)
+	if err != nil {
+		return r, fmt.Errorf("Error resolving KDC address: %v", err)
+	}
+	conn, err := net.DialUDP("udp", nil, udpAddr)
+	if err != nil {
+		return r, fmt.Errorf("Error establishing connection to KDC: %v", err)
+	}
+	defer conn.Close()
+	conn.SetDeadline(time.Now().Add(time.Duration(5) * time.Second))
+	_, err = conn.Write(b)
+	if err != nil {
+		return r, fmt.Errorf("Error sending to KDC: %v", err)
+	}
+	udpbuf := make([]byte, 4096)
+	n, _, err := conn.ReadFrom(udpbuf)
+	r = udpbuf[:n]
+	if err != nil {
+		return r, fmt.Errorf("Sending over UDP failed: %v", err)
+	}
+	return r, nil
+}
+
+func sendTCP(kdc string, b []byte) ([]byte, error) {
+	var r []byte
+	tcpAddr, err := net.ResolveTCPAddr("tcp", kdc)
+	if err != nil {
+		return r, fmt.Errorf("Error resolving KDC address: %v", err)
+	}
+	conn, err := net.DialTCP("tcp", nil, tcpAddr)
+	if err != nil {
+		return r, fmt.Errorf("Error establishing connection to KDC: %v", err)
+	}
+	defer conn.Close()
+	conn.SetDeadline(time.Now().Add(time.Duration(5) * time.Second))
+	_, err = conn.Write(b)
+	if err != nil {
+		return r, fmt.Errorf("Error sending to KDC: %v", err)
+	}
+	tcpbuf := bytes.NewBuffer(make([]byte, 4096))
+	n, err := conn.ReadFrom(tcpbuf)
+	r = tcpbuf.Bytes()[:n]
+	if err != nil {
+		return r, fmt.Errorf("Sending over TCP failed: %v", err)
+	}
+	return r, nil
+}

+ 72 - 23
config/krb5conf.go

@@ -12,6 +12,8 @@ import (
 	"strconv"
 	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
+	"github.com/jcmturner/gokrb5/types"
+	"io"
 )
 )
 
 
 type Config struct {
 type Config struct {
@@ -23,10 +25,15 @@ type Config struct {
 	//Plugins
 	//Plugins
 }
 }
 
 
+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"
+)
+
+
 func NewConfig() *Config {
 func NewConfig() *Config {
 	d := make(DomainRealm)
 	d := make(DomainRealm)
 	return &Config{
 	return &Config{
-		LibDefaults: NewLibDefaults(),
+		LibDefaults: newLibDefaults(),
 		DomainRealm: d,
 		DomainRealm: d,
 	}
 	}
 }
 }
@@ -43,6 +50,8 @@ type LibDefaults struct {
 	Default_realm              string
 	Default_realm              string
 	Default_tgs_enctypes       []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
 	Default_tgs_enctypes       []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
 	Default_tkt_enctypes       []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
 	Default_tkt_enctypes       []string //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
+	Default_tgs_enctype_ids       []int //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
+	Default_tkt_enctype_ids       []int //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
 	Dns_canonicalize_hostname  bool     //default true
 	Dns_canonicalize_hostname  bool     //default true
 	Dns_lookup_kdc             bool     //default false
 	Dns_lookup_kdc             bool     //default false
 	Dns_lookup_realm             bool
 	Dns_lookup_realm             bool
@@ -55,7 +64,8 @@ type LibDefaults struct {
 	Kdc_timesync             int            //default 1
 	Kdc_timesync             int            //default 1
 	//kdc_req_checksum_type int //unlikely to implement as for very old KDCs
 	//kdc_req_checksum_type int //unlikely to implement as for very old KDCs
 	Noaddresses        bool //default true
 	Noaddresses        bool //default true
-	Permitted_enctypes []string
+	Permitted_enctypes []string  //default aes256-cts-hmac-sha1-96 aes128-cts-hmac-sha1-96 des3-cbc-sha1 arcfour-hmac-md5 camellia256-cts-cmac camellia128-cts-cmac des-cbc-crc des-cbc-md5 des-cbc-md4
+	Permitted_enctype_ids []int
 	//plugin_base_dir string //not supporting plugins
 	//plugin_base_dir string //not supporting plugins
 	Preferred_preauth_types []int         //default “17, 16, 15, 14”, which forces libkrb5 to attempt to use PKINIT if it is supported
 	Preferred_preauth_types []int         //default “17, 16, 15, 14”, which forces libkrb5 to attempt to use PKINIT if it is supported
 	Proxiable               bool          //default false
 	Proxiable               bool          //default false
@@ -68,7 +78,7 @@ type LibDefaults struct {
 	Verify_ap_req_nofail    bool          //default false
 	Verify_ap_req_nofail    bool          //default false
 }
 }
 
 
-func NewLibDefaults() *LibDefaults {
+func newLibDefaults() *LibDefaults {
 	usr, _ := user.Current()
 	usr, _ := user.Current()
 	opts := asn1.BitString{}
 	opts := asn1.BitString{}
 	opts.Bytes, _ = hex.DecodeString("00000010")
 	opts.Bytes, _ = hex.DecodeString("00000010")
@@ -95,7 +105,7 @@ func NewLibDefaults() *LibDefaults {
 	}
 	}
 }
 }
 
 
-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, "=") {
 			return fmt.Errorf("libdefaults configuration line invalid: %s", line)
 			return fmt.Errorf("libdefaults configuration line invalid: %s", line)
@@ -269,6 +279,9 @@ func (l *LibDefaults) ParseLines(lines []string) error {
 			continue
 			continue
 		}
 		}
 	}
 	}
+	l.Default_tgs_enctype_ids = parseETypes(l.Default_tgs_enctypes, l.Allow_weak_crypto)
+	l.Default_tkt_enctype_ids = parseETypes(l.Default_tkt_enctypes, l.Allow_weak_crypto)
+	l.Permitted_enctype_ids = parseETypes(l.Permitted_enctypes, l.Allow_weak_crypto)
 	return nil
 	return nil
 }
 }
 
 
@@ -283,7 +296,7 @@ type Realm struct {
 	Master_kdc     []string
 	Master_kdc     []string
 }
 }
 
 
-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
 	var kdc_final bool
 	var kdc_final bool
@@ -323,7 +336,7 @@ func (r *Realm) ParseLines(name string, lines []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func ParseRealms(lines []string) ([]Realm, error) {
+func parseRealms(lines []string) ([]Realm, error) {
 	var realms []Realm
 	var realms []Realm
 	start := -1
 	start := -1
 	var name string
 	var name string
@@ -346,7 +359,7 @@ func ParseRealms(lines []string) ([]Realm, error) {
 				return nil, errors.New("Invalid Realms section in configuration.")
 				return nil, errors.New("Invalid Realms section in configuration.")
 			}
 			}
 			var r Realm
 			var r Realm
-			r.ParseLines(name, lines[start+1:i])
+			r.parseLines(name, lines[start+1:i])
 			realms = append(realms, r)
 			realms = append(realms, r)
 			start = -1
 			start = -1
 		}
 		}
@@ -356,7 +369,7 @@ func ParseRealms(lines []string) ([]Realm, error) {
 
 
 type DomainRealm map[string]string
 type DomainRealm map[string]string
 
 
-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, "=") {
 			return fmt.Errorf("Realm configuration line invalid: %s", line)
 			return fmt.Errorf("Realm configuration line invalid: %s", line)
@@ -364,16 +377,16 @@ func (d *DomainRealm) ParseLines(lines []string) error {
 		p := strings.Split(line, "=")
 		p := strings.Split(line, "=")
 		domain := strings.Replace(strings.ToLower(p[0]), " ", "", -1)
 		domain := strings.Replace(strings.ToLower(p[0]), " ", "", -1)
 		realm := strings.Replace(p[1], " ", "", -1)
 		realm := strings.Replace(p[1], " ", "", -1)
-		d.AddMapping(domain, realm)
+		d.addMapping(domain, realm)
 	}
 	}
 	return nil
 	return nil
 }
 }
 
 
-func (d *DomainRealm) AddMapping(domain, realm string) {
+func (d *DomainRealm) addMapping(domain, realm string) {
 	(*d)[domain] = realm
 	(*d)[domain] = realm
 }
 }
 
 
-func (d *DomainRealm) DeleteMapping(domain, realm string) {
+func (d *DomainRealm) deleteMapping(domain, realm string) {
 	delete(*d, domain)
 	delete(*d, domain)
 }
 }
 
 
@@ -383,38 +396,51 @@ func Load(cfgPath string) (*Config, error) {
 		return nil, errors.New("Configuration file could not be openned: " + cfgPath + " " + err.Error())
 		return nil, errors.New("Configuration file could not be openned: " + cfgPath + " " + err.Error())
 	}
 	}
 	defer fh.Close()
 	defer fh.Close()
-	c := NewConfig()
+	scanner := bufio.NewScanner(fh)
+	return NewConfigFromScanner(scanner)
+}
+
+func NewConfigFromString(s string) (*Config, error){
+	reader := strings.NewReader(s)
+	return NewConfigFromReader(reader)
+}
+
+func NewConfigFromReader(r io.Reader) (*Config, error){
+	scanner := bufio.NewScanner(r)
+	return NewConfigFromScanner(scanner)
+}
 
 
-	fs := bufio.NewScanner(fh)
+func NewConfigFromScanner(scanner *bufio.Scanner) (*Config, error){
+	c := NewConfig()
 	sections := make(map[int]string)
 	sections := make(map[int]string)
 	var section_line_num []int
 	var section_line_num []int
 	var lines []string
 	var lines []string
-	for fs.Scan() {
+	for scanner.Scan() {
 		// Skip comments and blank lines
 		// Skip comments and blank lines
-		if matched, _ := regexp.MatchString(`\s*(#|;|\n)`, fs.Text()); matched {
+		if matched, _ := regexp.MatchString(`\s*(#|;|\n)`, scanner.Text()); matched {
 			continue
 			continue
 		}
 		}
-		if matched, _ := regexp.MatchString(`\s*\[libdefaults\]\s*`, fs.Text()); matched {
+		if matched, _ := regexp.MatchString(`\s*\[libdefaults\]\s*`, scanner.Text()); matched {
 			sections[len(lines)] = "libdefaults"
 			sections[len(lines)] = "libdefaults"
 			section_line_num = append(section_line_num, len(lines))
 			section_line_num = append(section_line_num, len(lines))
 			continue
 			continue
 		}
 		}
-		if matched, _ := regexp.MatchString(`\s*\[realms\]\s*`, fs.Text()); matched {
+		if matched, _ := regexp.MatchString(`\s*\[realms\]\s*`, scanner.Text()); matched {
 			sections[len(lines)] = "realms"
 			sections[len(lines)] = "realms"
 			section_line_num = append(section_line_num, len(lines))
 			section_line_num = append(section_line_num, len(lines))
 			continue
 			continue
 		}
 		}
-		if matched, _ := regexp.MatchString(`\s*\[domain_realm\]\s*`, fs.Text()); matched {
+		if matched, _ := regexp.MatchString(`\s*\[domain_realm\]\s*`, scanner.Text()); matched {
 			sections[len(lines)] = "domain_realm"
 			sections[len(lines)] = "domain_realm"
 			section_line_num = append(section_line_num, len(lines))
 			section_line_num = append(section_line_num, len(lines))
 			continue
 			continue
 		}
 		}
-		if matched, _ := regexp.MatchString(`\s*\[.*\]\s*`, fs.Text()); matched {
+		if matched, _ := regexp.MatchString(`\s*\[.*\]\s*`, scanner.Text()); matched {
 			sections[len(lines)] = "unknown_section"
 			sections[len(lines)] = "unknown_section"
 			section_line_num = append(section_line_num, len(lines))
 			section_line_num = append(section_line_num, len(lines))
 			continue
 			continue
 		}
 		}
-		lines = append(lines, fs.Text())
+		lines = append(lines, scanner.Text())
 	}
 	}
 	for i, start := range section_line_num {
 	for i, start := range section_line_num {
 		var end int
 		var end int
@@ -425,18 +451,18 @@ func Load(cfgPath string) (*Config, error) {
 		}
 		}
 		switch section := sections[start]; section {
 		switch section := sections[start]; section {
 		case "libdefaults":
 		case "libdefaults":
-			err := c.LibDefaults.ParseLines(lines[start:end])
+			err := c.LibDefaults.parseLines(lines[start:end])
 			if err != nil {
 			if err != nil {
 				return nil, fmt.Errorf("Error processing libdefaults section: %v", err)
 				return nil, fmt.Errorf("Error processing libdefaults section: %v", err)
 			}
 			}
 		case "realms":
 		case "realms":
-			realms, err := ParseRealms(lines[start:end])
+			realms, err := parseRealms(lines[start:end])
 			if err != nil {
 			if err != nil {
 				return nil, fmt.Errorf("Error processing realms section: %v", err)
 				return nil, fmt.Errorf("Error processing realms section: %v", err)
 			}
 			}
 			c.Realms = realms
 			c.Realms = realms
 		case "domain_realm":
 		case "domain_realm":
-			c.DomainRealm.ParseLines(lines[start:end])
+			err := c.DomainRealm.parseLines(lines[start:end])
 			if err != nil {
 			if err != nil {
 				return nil, fmt.Errorf("Error processing domaain_realm section: %v", err)
 				return nil, fmt.Errorf("Error processing domaain_realm section: %v", err)
 			}
 			}
@@ -447,6 +473,29 @@ func Load(cfgPath string) (*Config, error) {
 	return c, nil
 	return c, nil
 }
 }
 
 
+func parseETypes(s []string, w bool) ([]int) {
+	var eti []int
+	for _, et := range s {
+		if !w {
+			var weak bool
+			for _, wet := range strings.Fields(WEAK_ETYPE_LIST){
+				if et == wet {
+					weak = true
+					break
+				}
+			}
+			if weak {
+				continue
+			}
+		}
+		i := types.KrbDictionary.ETypesByName[et]
+		if i != 0 {
+			eti = append(eti, i)
+		}
+	}
+	return eti
+}
+
 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)

+ 3 - 3
crypto/EncryptionEngine.go

@@ -163,12 +163,12 @@ func GetKeyFromPassword(passwd string, cn types.PrincipalName, realm string, ety
 	var patype int
 	var patype int
 	for _, pa := range pas {
 	for _, pa := range pas {
 		switch pa.PADataType {
 		switch pa.PADataType {
-		case 3:
+		case types.PA_PW_SALT:
 			if patype > pa.PADataType {
 			if patype > pa.PADataType {
 				continue
 				continue
 			}
 			}
 			salt = string(pa.PADataValue)
 			salt = string(pa.PADataValue)
-		case 11:
+		case types.PA_ETYPE_INFO:
 			if patype > pa.PADataType {
 			if patype > pa.PADataType {
 				continue
 				continue
 			}
 			}
@@ -184,7 +184,7 @@ func GetKeyFromPassword(passwd string, cn types.PrincipalName, realm string, ety
 				}
 				}
 			}
 			}
 			salt = string(et[0].Salt)
 			salt = string(et[0].Salt)
-		case 19:
+		case types.PA_ETYPE_INFO2:
 			if patype > pa.PADataType {
 			if patype > pa.PADataType {
 				continue
 				continue
 			}
 			}

+ 36 - 47
debug.go

@@ -1,74 +1,63 @@
 package main
 package main
 
 
 import (
 import (
-	"encoding/hex"
 	"fmt"
 	"fmt"
-	"github.com/jcmturner/gokrb5/keytab"
-	"github.com/jcmturner/gokrb5/messages"
-	"github.com/jcmturner/gokrb5/types"
-	"net"
 	"os"
 	"os"
-	"time"
+	"github.com/jcmturner/gokrb5/config"
+	"github.com/jcmturner/gokrb5/messages"
+	"github.com/jcmturner/gokrb5/client"
+	"encoding/hex"
+	"github.com/jcmturner/gokrb5/keytab"
 )
 )
 
 
 const ktab = "05020000004b0001000b544553542e474f4b5242350009746573747573657231000000015898e0770100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de900000001"
 const ktab = "05020000004b0001000b544553542e474f4b5242350009746573747573657231000000015898e0770100120020bbdc430aab7e2d4622a0b6951481453b0962e9db8e2f168942ad175cda6d9de900000001"
+const krb5conf =`[libdefaults]
+  default_realm = TEST.GOKRB5
+  dns_lookup_realm = false
+  dns_lookup_kdc = false
+  ticket_lifetime = 24h
+  forwardable = yes
+  default_tkt_enctypes = aes256-cts-hmac-sha1-96
+
+[realms]
+ TEST.GOKRB5 = {
+  kdc = 10.80.88.88:88
+  admin_server = 10.80.88.88:749
+  default_domain = test.gokrb5
+ }
+
+[domain_realm]
+ .test.gokrb5 = TEST.GOKRB5
+ test.gokrb5 = TEST.GOKRB5`
 
 
 func main() {
 func main() {
-	udpAddr, _ := net.ResolveUDPAddr("udp", "10.80.88.88:88")
-	realm := "TEST.GOKRB5"
-
-	conn, _ := net.DialUDP("udp", nil, udpAddr)
-	defer conn.Close()
 
 
-	var pas types.PADataSequence
-	pa := types.PAData{
-		PADataType: 149,
+	c, err := config.NewConfigFromString(krb5conf)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error creating config: %v", err)
 	}
 	}
-	pas = append(pas, pa)
-
-	a := messages.NewASReq()
-	a.PAData = pas
-	a.ReqBody.Realm = realm
-	a.ReqBody.CName.NameString = []string{"testuser1"}
-	a.ReqBody.SName.NameType = 2
-	a.ReqBody.SName.NameString = []string{"krbtgt", realm}
-	a.ReqBody.Till = time.Now().Add(10 * time.Hour)
-	a.ReqBody.Nonce = 2069991465
-	a.ReqBody.EType = []int{18}
+	fmt.Fprintf(os.Stdout, "Config: %+v\n", *c)
+	a := messages.NewASReq(c, "testuser1")
 	fmt.Fprintf(os.Stdout, "AS_REQ: %+v\n", a)
 	fmt.Fprintf(os.Stdout, "AS_REQ: %+v\n", a)
 	b, err := a.Marshal()
 	b, err := a.Marshal()
 	if err != nil {
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "Error marshalling AS_REQ: %v\n", err)
 		fmt.Fprintf(os.Stderr, "Error marshalling AS_REQ: %v\n", err)
 	}
 	}
-
-	_, _ = conn.Write(b)
-
-	buf := make([]byte, 4096)
-	n, _, err := conn.ReadFrom(buf)
-	var r messages.ASRep
-	var p messages.ASRep
-	r.Unmarshal(buf[:n])
-	p.Unmarshal(buf[:n])
-	fmt.Fprintf(os.Stdout, "AS_REP: %+v\n", r)
-
+	rb, err := client.SendToKDC(c, b)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error sending to KDC: %v\n", err)
+	}
+	var ar messages.ASRep
+	ar.Unmarshal(rb)
 	kb, _ := hex.DecodeString(ktab)
 	kb, _ := hex.DecodeString(ktab)
 	kt, err := keytab.Parse(kb)
 	kt, err := keytab.Parse(kb)
 	if err != nil {
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "KT load err: %v\n\n", err)
 		fmt.Fprintf(os.Stderr, "KT load err: %v\n\n", err)
 	}
 	}
-	fmt.Fprintf(os.Stdout, "KT: %+v", kt)
-	err = r.DecryptEncPartWithKeytab(kt)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "\nDecrypt err: %v\n", err)
-	} else {
-		fmt.Fprintf(os.Stdout, "\n\nAS REP decrypted with keytab: %+v\n", r)
-	}
-
-	pswd := "passwordvalue"
-	err = p.DecryptEncPartWithPassword(pswd)
+	err = ar.DecryptEncPartWithKeytab(kt)
 	if err != nil {
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "\nDecrypt err: %v\n", err)
 		fmt.Fprintf(os.Stderr, "\nDecrypt err: %v\n", err)
 	} else {
 	} else {
-		fmt.Fprintf(os.Stdout, "\nAS REP decrypted with passwd: %+v\n", p)
+		fmt.Fprintf(os.Stdout, "\n\nAS REP decrypted with keytab: %+v\n", ar)
 	}
 	}
 }
 }

+ 5 - 2
keytab/keytab.go

@@ -36,6 +36,7 @@ type KeyBlock struct {
 	KeyMaterial []byte
 	KeyMaterial []byte
 }
 }
 
 
+//Create new, empty Keytab type
 func NewKeytab() Keytab {
 func NewKeytab() Keytab {
 	var e []KeytabEntry
 	var e []KeytabEntry
 	return Keytab{
 	return Keytab{
@@ -91,6 +92,7 @@ func newKeyBlock() KeyBlock {
 	}
 	}
 }
 }
 
 
+//Load a keytab file into Keytab type
 func Load(ktPath string) (kt Keytab, err error) {
 func Load(ktPath string) (kt Keytab, err error) {
 	k, err := ioutil.ReadFile(ktPath)
 	k, err := ioutil.ReadFile(ktPath)
 	if err != nil {
 	if err != nil {
@@ -99,6 +101,7 @@ func Load(ktPath string) (kt Keytab, err error) {
 	return Parse(k)
 	return Parse(k)
 }
 }
 
 
+//Parse byte slice of keytab data into Keytab type
 func Parse(b []byte) (kt Keytab, err error) {
 func Parse(b []byte) (kt Keytab, err error) {
 	//The first byte of the file always has the value 5
 	//The first byte of the file always has the value 5
 	if int8(b[0]) != 5 {
 	if int8(b[0]) != 5 {
@@ -115,7 +118,7 @@ func Parse(b []byte) (kt Keytab, err error) {
 	//Version 1 of the file format uses native byte order for integer representations. Version 2 always uses big-endian byte order
 	//Version 1 of the file format uses native byte order for integer representations. Version 2 always uses big-endian byte order
 	var endian binary.ByteOrder
 	var endian binary.ByteOrder
 	endian = binary.BigEndian
 	endian = binary.BigEndian
-	if kt.Version == 1 && IsNativeEndianLittle() {
+	if kt.Version == 1 && isNativeEndianLittle() {
 		endian = binary.LittleEndian
 		endian = binary.LittleEndian
 	}
 	}
 	/*
 	/*
@@ -222,7 +225,7 @@ func read_Bytes(b []byte, p *int, s int, e *binary.ByteOrder) []byte {
 	return r
 	return r
 }
 }
 
 
-func IsNativeEndianLittle() bool {
+func isNativeEndianLittle() bool {
 	var x int = 0x012345678
 	var x int = 0x012345678
 	var p unsafe.Pointer = unsafe.Pointer(&x)
 	var p unsafe.Pointer = unsafe.Pointer(&x)
 	var bp *[4]byte = (*[4]byte)(p)
 	var bp *[4]byte = (*[4]byte)(p)

+ 41 - 12
messages/KDCReq.go

@@ -10,7 +10,8 @@ import (
 	"github.com/jcmturner/gokrb5/types"
 	"github.com/jcmturner/gokrb5/types"
 	"github.com/jcmturner/gokrb5/types/asnAppTag"
 	"github.com/jcmturner/gokrb5/types/asnAppTag"
 	"time"
 	"time"
-	"encoding/hex"
+	"github.com/jcmturner/gokrb5/config"
+	"math/rand"
 )
 )
 
 
 type marshalKDCReq struct {
 type marshalKDCReq struct {
@@ -61,23 +62,51 @@ type KDCReqBody struct {
 	AdditionalTickets []types.Ticket      `asn1:"explicit,optional,tag:11"`
 	AdditionalTickets []types.Ticket      `asn1:"explicit,optional,tag:11"`
 }
 }
 
 
-func NewASReq() ASReq {
-	opts := asn1.BitString{}
-	opts.Bytes, _ = hex.DecodeString("40000010")
-	opts.BitLength = len(opts.Bytes) * 8
-	pn := types.PrincipalName{NameType: 1}
+func NewASReq(c *config.Config, username string) ASReq {
+	pas := types.PADataSequence{
+		types.PAData{
+			PADataType: types.PA_REQ_ENC_PA_REP,
+		},
+	}
+	nonce := int(rand.Int31())
+	t := time.Now()
 
 
-	return ASReq{
-		PVNO:    5,
-		MsgType: types.KrbDictionary.MsgTypesByName["KRB_AS_REQ"],
+	a := ASReq{
+		PVNO:    PVNO,
+		MsgType: KRB_AS_REQ,
+		PAData: pas,
 		ReqBody: KDCReqBody{
 		ReqBody: KDCReqBody{
-			KDCOptions: opts,
-			CName: pn,
-			SName: pn,
+			KDCOptions: c.LibDefaults.Kdc_default_options,
+			Realm: c.LibDefaults.Default_realm,
+			CName: types.PrincipalName{
+				NameType: types.KRB_NT_PRINCIPAL,
+				NameString: []string{username},
+			},
+			SName: types.PrincipalName{
+				NameType: types.KRB_NT_SRV_INST,
+				NameString: []string{"krbtgt", c.LibDefaults.Default_realm},
+			},
+			Till: t.Add(c.LibDefaults.Ticket_lifetime),
+			Nonce: nonce,
+			EType: c.LibDefaults.Default_tkt_enctype_ids,
 		},
 		},
 	}
 	}
+	if c.LibDefaults.Forwardable {
+		types.SetFlag(&a.ReqBody.KDCOptions, types.Forwardable)
+	}
+	if c.LibDefaults.Canonicalize {
+		types.SetFlag(&a.ReqBody.KDCOptions, types.Canonicalize)
+	}
+	if c.LibDefaults.Proxiable {
+		types.SetFlag(&a.ReqBody.KDCOptions, types.Proxiable)
+	}
+	if c.LibDefaults.Renew_lifetime != 0 {
+		a.ReqBody.RTime = t.Add(c.LibDefaults.Renew_lifetime)
+	}
+	return a
 }
 }
 
 
+
 func (k *ASReq) Unmarshal(b []byte) error {
 func (k *ASReq) Unmarshal(b []byte) error {
 	var m marshalKDCReq
 	var m marshalKDCReq
 	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))
 	_, err := asn1.UnmarshalWithParams(b, &m, fmt.Sprintf("application,explicit,tag:%v", asnAppTag.ASREQ))

+ 2 - 11
messages/constants.go

@@ -1,6 +1,8 @@
 package messages
 package messages
 
 
 const (
 const (
+	PVNO = 5
+
 	//Key usage numbers
 	//Key usage numbers
 	USAGE_AS_REQ_PA_ENC_TIMESTAMP                        = 1
 	USAGE_AS_REQ_PA_ENC_TIMESTAMP                        = 1
 	USAGE_KDC_REP_TICKET                                 = 2
 	USAGE_KDC_REP_TICKET                                 = 2
@@ -44,17 +46,6 @@ const (
 	KRB_CRED       = 22 //Private (encrypted) message to forward credentials
 	KRB_CRED       = 22 //Private (encrypted) message to forward credentials
 	KRB_ERROR      = 30 //Error response
 	KRB_ERROR      = 30 //Error response
 
 
-	//Name types
-	KRB_NT_UNKNOWN        = 0  //Name type not known
-	KRB_NT_PRINCIPAL      = 1  //Just the name of the principal as in DCE,  or for users
-	KRB_NT_SRV_INST       = 2  //Service and other unique instance (krbtgt)
-	KRB_NT_SRV_HST        = 3  //Service with host name as instance (telnet, rcommands)
-	KRB_NT_SRV_XHST       = 4  //Service with host as remaining components
-	KRB_NT_UID            = 5  //Unique ID
-	KRB_NT_X500_PRINCIPAL = 6  //Encoded X.509 Distinguished name [RFC2253]
-	KRB_NT_SMTP_NAME      = 7  //Name in form of SMTP email name (e.g., user@example.com)
-	KRB_NT_ENTERPRISE     = 10 //Enterprise name; may be mapped to principal name
-
 	//Error codes
 	//Error codes
 	KDC_ERR_NONE                          = 0  //No error
 	KDC_ERR_NONE                          = 0  //No error
 	KDC_ERR_NAME_EXP                      = 1  //Client's entry in database has expired
 	KDC_ERR_NAME_EXP                      = 1  //Client's entry in database has expired

+ 2 - 2
testenv/krbclient-vagrant/krb5.conf

@@ -13,8 +13,8 @@
 
 
 [realms]
 [realms]
  __REALM__ = {
  __REALM__ = {
-  kdc = kdc.test.gokrb5:88
-  admin_server = kdc.test.gokrb5:749
+  kdc = 10.80.88.88:88
+  admin_server = 10.80.88.88:749
   default_domain = test.gokrb5
   default_domain = test.gokrb5
  }
  }
 
 

+ 22 - 0
types/constants.go

@@ -0,0 +1,22 @@
+package types
+
+const (
+	//PA Types
+	PA_TGS_REQ        = 1
+	PA_ENC_TIMESTAMP  = 2
+	PA_PW_SALT        = 3
+	PA_ETYPE_INFO     = 11
+	PA_ETYPE_INFO2    = 19
+	PA_REQ_ENC_PA_REP = 149 //RFC6806 Section 11
+
+	//Name types
+	KRB_NT_UNKNOWN        = 0  //Name type not known
+	KRB_NT_PRINCIPAL      = 1  //Just the name of the principal as in DCE,  or for users
+	KRB_NT_SRV_INST       = 2  //Service and other unique instance (krbtgt)
+	KRB_NT_SRV_HST        = 3  //Service with host name as instance (telnet, rcommands)
+	KRB_NT_SRV_XHST       = 4  //Service with host as remaining components
+	KRB_NT_UID            = 5  //Unique ID
+	KRB_NT_X500_PRINCIPAL = 6  //Encoded X.509 Distinguished name [RFC2253]
+	KRB_NT_SMTP_NAME      = 7  //Name in form of SMTP email name (e.g., user@example.com)
+	KRB_NT_ENTERPRISE     = 10 //Enterprise name; may be mapped to principal name
+)