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