network.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package client
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "gopkg.in/jcmturner/dnsutils.v1"
  8. "gopkg.in/jcmturner/gokrb5.v2/iana/errorcode"
  9. "gopkg.in/jcmturner/gokrb5.v2/messages"
  10. "io"
  11. "math/rand"
  12. "net"
  13. "strconv"
  14. "strings"
  15. "time"
  16. )
  17. func (cl *Client) resolveKDC(realm string, tcp bool) (int, map[int]string, error) {
  18. kdcs := make(map[int]string)
  19. var count int
  20. // Use DNS to resolve kerberos SRV records if configured to do so in krb5.conf.
  21. if cl.Config.LibDefaults.DNSLookupKDC {
  22. proto := "udp"
  23. if tcp {
  24. proto = "tcp"
  25. }
  26. c, addrs, err := dnsutils.OrderedSRV("kerberos", proto, realm)
  27. if err != nil {
  28. return count, kdcs, err
  29. }
  30. if len(addrs) < 1 {
  31. return count, kdcs, fmt.Errorf("no KDC SRV records found for realm %s", realm)
  32. }
  33. count = c
  34. for k, v := range addrs {
  35. kdcs[k] = strings.TrimRight(v.Target, ".") + ":" + strconv.Itoa(int(v.Port))
  36. }
  37. } else {
  38. // Get the KDCs from the krb5.conf an order them randomly for preference.
  39. var ks []string
  40. for _, r := range cl.Config.Realms {
  41. if r.Realm == realm {
  42. ks = r.KDC
  43. break
  44. }
  45. }
  46. count = len(ks)
  47. if count < 1 {
  48. return count, kdcs, fmt.Errorf("no KDCs defined in configuration for realm %s", realm)
  49. }
  50. i := 1
  51. if count > 1 {
  52. l := len(ks)
  53. for l > 0 {
  54. ri := rand.Intn(l)
  55. kdcs[i] = ks[ri]
  56. if l > 1 {
  57. // Remove the entry from the source slice by swapping with the last entry and truncating
  58. ks[len(ks)-1], ks[ri] = ks[ri], ks[len(ks)-1]
  59. ks = ks[:len(ks)-1]
  60. l = len(ks)
  61. } else {
  62. l = 0
  63. }
  64. i += 1
  65. }
  66. } else {
  67. kdcs[i] = ks[0]
  68. }
  69. }
  70. return count, kdcs, nil
  71. }
  72. // SendToKDC performs network actions to send data to the KDC.
  73. func (cl *Client) SendToKDC(b []byte, realm string) ([]byte, error) {
  74. var rb []byte
  75. if cl.Config.LibDefaults.UDPPreferenceLimit == 1 {
  76. //1 means we should always use TCP
  77. rb, errtcp := cl.sendTCP(realm, b)
  78. if errtcp != nil {
  79. if e, ok := errtcp.(messages.KRBError); ok {
  80. return rb, e
  81. }
  82. return rb, fmt.Errorf("communication error with KDC via TCP: %v", errtcp)
  83. }
  84. return rb, nil
  85. }
  86. if len(b) <= cl.Config.LibDefaults.UDPPreferenceLimit {
  87. //Try UDP first, TCP second
  88. rb, errudp := cl.sendUDP(realm, b)
  89. if errudp != nil {
  90. if e, ok := errudp.(messages.KRBError); ok && e.ErrorCode != errorcode.KRB_ERR_RESPONSE_TOO_BIG {
  91. // Got a KRBError from KDC
  92. // If this is not a KRB_ERR_RESPONSE_TOO_BIG we will return immediately otherwise will try TCP.
  93. return rb, e
  94. }
  95. // Try TCP
  96. r, errtcp := cl.sendTCP(realm, b)
  97. if errtcp != nil {
  98. if e, ok := errtcp.(messages.KRBError); ok {
  99. // Got a KRBError
  100. return r, e
  101. }
  102. return r, fmt.Errorf("failed to communicate with KDC. Attempts made with UDP (%v) and then TCP (%v)", errudp, errtcp)
  103. }
  104. rb = r
  105. }
  106. return rb, nil
  107. }
  108. //Try TCP first, UDP second
  109. rb, errtcp := cl.sendTCP(realm, b)
  110. if errtcp != nil {
  111. if e, ok := errtcp.(messages.KRBError); ok {
  112. // Got a KRBError from KDC so returning and not trying UDP.
  113. return rb, e
  114. }
  115. rb, errudp := cl.sendUDP(realm, b)
  116. if errudp != nil {
  117. if e, ok := errudp.(messages.KRBError); ok {
  118. // Got a KRBError
  119. return rb, e
  120. }
  121. return rb, fmt.Errorf("failed to communicate with KDC. Attempts made with TCP (%v) and then UDP (%v)", errtcp, errudp)
  122. }
  123. }
  124. return rb, nil
  125. }
  126. func dialKDCUDP(count int, kdcs map[int]string) (conn *net.UDPConn, err error) {
  127. i := 1
  128. for i <= count {
  129. udpAddr, e := net.ResolveUDPAddr("udp", kdcs[i])
  130. if e != nil {
  131. err = fmt.Errorf("error resolving KDC address: %v", e)
  132. return
  133. }
  134. conn, err = net.DialUDP("udp", nil, udpAddr)
  135. if err == nil {
  136. conn.SetDeadline(time.Now().Add(time.Duration(5 * time.Second)))
  137. return
  138. }
  139. i += 1
  140. }
  141. err = errors.New("error in getting a UDP connection to any of the KDCs")
  142. return
  143. }
  144. func dialKDCTCP(count int, kdcs map[int]string) (conn *net.TCPConn, err error) {
  145. i := 1
  146. for i <= count {
  147. tcpAddr, e := net.ResolveTCPAddr("tcp", kdcs[i])
  148. if e != nil {
  149. err = fmt.Errorf("error resolving KDC address: %v", e)
  150. return
  151. }
  152. conn, err = net.DialTCP("tcp", nil, tcpAddr)
  153. if err == nil {
  154. conn.SetDeadline(time.Now().Add(time.Duration(5 * time.Second)))
  155. return
  156. }
  157. i += 1
  158. }
  159. err = errors.New("error in getting a TCP connection to any of the KDCs")
  160. return
  161. }
  162. // Send the bytes to the KDC over UDP.
  163. func (cl *Client) sendUDP(realm string, b []byte) ([]byte, error) {
  164. var r []byte
  165. count, kdcs, err := cl.resolveKDC(realm, false)
  166. if err != nil {
  167. return r, err
  168. }
  169. conn, err := dialKDCUDP(count, kdcs)
  170. if err != nil {
  171. return r, err
  172. }
  173. defer conn.Close()
  174. _, err = conn.Write(b)
  175. if err != nil {
  176. return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err)
  177. }
  178. udpbuf := make([]byte, 4096)
  179. n, _, err := conn.ReadFrom(udpbuf)
  180. r = udpbuf[:n]
  181. if err != nil {
  182. return r, fmt.Errorf("sending over UDP failed to %s: %v", conn.RemoteAddr().String(), err)
  183. }
  184. if len(r) < 1 {
  185. return r, fmt.Errorf("no response data from KDC %s", conn.RemoteAddr().String())
  186. }
  187. return checkForKRBError(r)
  188. }
  189. // Send the bytes to the KDC over TCP.
  190. func (cl *Client) sendTCP(realm string, b []byte) ([]byte, error) {
  191. var r []byte
  192. count, kdcs, err := cl.resolveKDC(realm, true)
  193. if err != nil {
  194. return r, err
  195. }
  196. conn, err := dialKDCTCP(count, kdcs)
  197. if err != nil {
  198. return r, err
  199. }
  200. defer conn.Close()
  201. /*
  202. RFC https://tools.ietf.org/html/rfc4120#section-7.2.2
  203. Each request (KRB_KDC_REQ) and response (KRB_KDC_REP or KRB_ERROR)
  204. sent over the TCP stream is preceded by the length of the request as
  205. 4 octets in network byte order. The high bit of the length is
  206. reserved for future expansion and MUST currently be set to zero. If
  207. a KDC that does not understand how to interpret a set high bit of the
  208. length encoding receives a request with the high order bit of the
  209. length set, it MUST return a KRB-ERROR message with the error
  210. KRB_ERR_FIELD_TOOLONG and MUST close the TCP stream.
  211. NB: network byte order == big endian
  212. */
  213. var buf bytes.Buffer
  214. binary.Write(&buf, binary.BigEndian, uint32(len(b)))
  215. b = append(buf.Bytes(), b...)
  216. _, err = conn.Write(b)
  217. if err != nil {
  218. return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err)
  219. }
  220. sh := make([]byte, 4, 4)
  221. _, err = conn.Read(sh)
  222. if err != nil {
  223. return r, fmt.Errorf("error reading response size header: %v", err)
  224. }
  225. s := binary.BigEndian.Uint32(sh)
  226. rb := make([]byte, s, s)
  227. _, err = io.ReadFull(conn, rb)
  228. if err != nil {
  229. return r, fmt.Errorf("error reading response: %v", err)
  230. }
  231. if len(rb) < 1 {
  232. return r, fmt.Errorf("no response data from KDC %s", conn.RemoteAddr().String())
  233. }
  234. return checkForKRBError(rb)
  235. }
  236. func checkForKRBError(b []byte) ([]byte, error) {
  237. var KRBErr messages.KRBError
  238. if err := KRBErr.Unmarshal(b); err == nil {
  239. return b, KRBErr
  240. }
  241. return b, nil
  242. }