blob: a58c234178dc3a728484763624cc3ca8f9da54b6 [file] [log] [blame]
Scott Baker611f6bd2019-10-18 13:45:19 -07001package config
2
3import (
4 "fmt"
5 "math/rand"
6 "net"
7 "strconv"
8 "strings"
9
10 "gopkg.in/jcmturner/dnsutils.v1"
11)
12
13// GetKDCs returns the count of KDCs available and a map of KDC host names keyed on preference order.
14func (c *Config) GetKDCs(realm string, tcp bool) (int, map[int]string, error) {
15 if realm == "" {
16 realm = c.LibDefaults.DefaultRealm
17 }
18 kdcs := make(map[int]string)
19 var count int
20
21 // Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf.
22 if c.LibDefaults.DNSLookupKDC {
23 proto := "udp"
24 if tcp {
25 proto = "tcp"
26 }
27 c, addrs, err := dnsutils.OrderedSRV("kerberos", proto, realm)
28 if err != nil {
29 return count, kdcs, err
30 }
31 if len(addrs) < 1 {
32 return count, kdcs, fmt.Errorf("no KDC SRV records found for realm %s", realm)
33 }
34 count = c
35 for k, v := range addrs {
36 kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port))
37 }
38 } else {
39 // Get the KDCs from the krb5.conf an order them randomly for preference.
40 var ks []string
41 for _, r := range c.Realms {
42 if r.Realm == realm {
43 ks = r.KDC
44 break
45 }
46 }
47 count = len(ks)
48 if count < 1 {
49 return count, kdcs, fmt.Errorf("no KDCs defined in configuration for realm %s", realm)
50 }
51 kdcs = randServOrder(ks)
52 }
53 return count, kdcs, nil
54}
55
56// GetKpasswdServers returns the count of kpasswd servers available and a map of kpasswd host names keyed on preference order.
57// https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html#realms - see kpasswd_server section
58func (c *Config) GetKpasswdServers(realm string, tcp bool) (int, map[int]string, error) {
59 kdcs := make(map[int]string)
60 var count int
61
62 // Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf.
63 if c.LibDefaults.DNSLookupKDC {
64 proto := "udp"
65 if tcp {
66 proto = "tcp"
67 }
68 c, addrs, err := dnsutils.OrderedSRV("kpasswd", proto, realm)
69 if err != nil {
70 return count, kdcs, err
71 }
72 if c < 1 {
73 c, addrs, err = dnsutils.OrderedSRV("kerberos-adm", proto, realm)
74 if err != nil {
75 return count, kdcs, err
76 }
77 }
78 if len(addrs) < 1 {
79 return count, kdcs, fmt.Errorf("no kpasswd or kadmin SRV records found for realm %s", realm)
80 }
81 count = c
82 for k, v := range addrs {
83 kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port))
84 }
85 } else {
86 // Get the KDCs from the krb5.conf an order them randomly for preference.
87 var ks []string
88 var ka []string
89 for _, r := range c.Realms {
90 if r.Realm == realm {
91 ks = r.KPasswdServer
92 ka = r.AdminServer
93 break
94 }
95 }
96 if len(ks) < 1 {
97 for _, k := range ka {
98 h, _, err := net.SplitHostPort(k)
99 if err != nil {
100 continue
101 }
102 ks = append(ks, h+":464")
103 }
104 }
105 count = len(ks)
106 if count < 1 {
107 return count, kdcs, fmt.Errorf("no kpasswd or kadmin defined in configuration for realm %s", realm)
108 }
109 kdcs = randServOrder(ks)
110 }
111 return count, kdcs, nil
112}
113
114func randServOrder(ks []string) map[int]string {
115 kdcs := make(map[int]string)
116 count := len(ks)
117 i := 1
118 if count > 1 {
119 l := len(ks)
120 for l > 0 {
121 ri := rand.Intn(l)
122 kdcs[i] = ks[ri]
123 if l > 1 {
124 // Remove the entry from the source slice by swapping with the last entry and truncating
125 ks[len(ks)-1], ks[ri] = ks[ri], ks[len(ks)-1]
126 ks = ks[:len(ks)-1]
127 l = len(ks)
128 } else {
129 l = 0
130 }
131 i++
132 }
133 } else {
134 kdcs[i] = ks[0]
135 }
136 return kdcs
137}