| package config |
| |
| import ( |
| "fmt" |
| "math/rand" |
| "net" |
| "strconv" |
| "strings" |
| |
| "gopkg.in/jcmturner/dnsutils.v1" |
| ) |
| |
| // GetKDCs returns the count of KDCs available and a map of KDC host names keyed on preference order. |
| func (c *Config) GetKDCs(realm string, tcp bool) (int, map[int]string, error) { |
| if realm == "" { |
| realm = c.LibDefaults.DefaultRealm |
| } |
| kdcs := make(map[int]string) |
| var count int |
| |
| // Get the KDCs from the krb5.conf. |
| var ks []string |
| for _, r := range c.Realms { |
| if r.Realm != realm { |
| continue |
| } |
| ks = r.KDC |
| } |
| count = len(ks) |
| |
| if count > 0 { |
| // Order the kdcs randomly for preference. |
| kdcs = randServOrder(ks) |
| return count, kdcs, nil |
| } |
| |
| if !c.LibDefaults.DNSLookupKDC { |
| return count, kdcs, fmt.Errorf("no KDCs defined in configuration for realm %s", realm) |
| } |
| |
| // Use DNS to resolve kerberos SRV records. |
| proto := "udp" |
| if tcp { |
| proto = "tcp" |
| } |
| index, addrs, err := dnsutils.OrderedSRV("kerberos", proto, realm) |
| if err != nil { |
| return count, kdcs, err |
| } |
| if len(addrs) < 1 { |
| return count, kdcs, fmt.Errorf("no KDC SRV records found for realm %s", realm) |
| } |
| count = index |
| for k, v := range addrs { |
| kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port)) |
| } |
| return count, kdcs, nil |
| } |
| |
| // GetKpasswdServers returns the count of kpasswd servers available and a map of kpasswd host names keyed on preference order. |
| // https://web.mit.edu/kerberos/krb5-latest/doc/admin/conf_files/krb5_conf.html#realms - see kpasswd_server section |
| func (c *Config) GetKpasswdServers(realm string, tcp bool) (int, map[int]string, error) { |
| kdcs := make(map[int]string) |
| var count int |
| |
| // Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf. |
| if c.LibDefaults.DNSLookupKDC { |
| proto := "udp" |
| if tcp { |
| proto = "tcp" |
| } |
| c, addrs, err := dnsutils.OrderedSRV("kpasswd", proto, realm) |
| if err != nil { |
| return count, kdcs, err |
| } |
| if c < 1 { |
| c, addrs, err = dnsutils.OrderedSRV("kerberos-adm", proto, realm) |
| if err != nil { |
| return count, kdcs, err |
| } |
| } |
| if len(addrs) < 1 { |
| return count, kdcs, fmt.Errorf("no kpasswd or kadmin SRV records found for realm %s", realm) |
| } |
| count = c |
| for k, v := range addrs { |
| kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port)) |
| } |
| } else { |
| // Get the KDCs from the krb5.conf an order them randomly for preference. |
| var ks []string |
| var ka []string |
| for _, r := range c.Realms { |
| if r.Realm == realm { |
| ks = r.KPasswdServer |
| ka = r.AdminServer |
| break |
| } |
| } |
| if len(ks) < 1 { |
| for _, k := range ka { |
| h, _, err := net.SplitHostPort(k) |
| if err != nil { |
| continue |
| } |
| ks = append(ks, h+":464") |
| } |
| } |
| count = len(ks) |
| if count < 1 { |
| return count, kdcs, fmt.Errorf("no kpasswd or kadmin defined in configuration for realm %s", realm) |
| } |
| kdcs = randServOrder(ks) |
| } |
| return count, kdcs, nil |
| } |
| |
| func randServOrder(ks []string) map[int]string { |
| kdcs := make(map[int]string) |
| count := len(ks) |
| i := 1 |
| if count > 1 { |
| l := len(ks) |
| for l > 0 { |
| ri := rand.Intn(l) |
| kdcs[i] = ks[ri] |
| if l > 1 { |
| // Remove the entry from the source slice by swapping with the last entry and truncating |
| ks[len(ks)-1], ks[ri] = ks[ri], ks[len(ks)-1] |
| ks = ks[:len(ks)-1] |
| l = len(ks) |
| } else { |
| l = 0 |
| } |
| i++ |
| } |
| } else { |
| kdcs[i] = ks[0] |
| } |
| return kdcs |
| } |