network.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. package client
  2. import (
  3. "encoding/binary"
  4. "fmt"
  5. "io"
  6. "net"
  7. "strings"
  8. "time"
  9. "github.com/jcmturner/gokrb5/v8/iana/errorcode"
  10. "github.com/jcmturner/gokrb5/v8/messages"
  11. )
  12. // SendToKDC performs network actions to send data to the KDC.
  13. func (cl *Client) sendToKDC(b []byte, realm string) ([]byte, error) {
  14. var rb []byte
  15. if cl.Config.LibDefaults.UDPPreferenceLimit == 1 {
  16. //1 means we should always use TCP
  17. rb, errtcp := cl.sendKDCTCP(realm, b)
  18. if errtcp != nil {
  19. if e, ok := errtcp.(messages.KRBError); ok {
  20. return rb, e
  21. }
  22. return rb, fmt.Errorf("communication error with KDC via TCP: %v", errtcp)
  23. }
  24. return rb, nil
  25. }
  26. if len(b) <= cl.Config.LibDefaults.UDPPreferenceLimit {
  27. //Try UDP first, TCP second
  28. rb, errudp := cl.sendKDCUDP(realm, b)
  29. if errudp != nil {
  30. if e, ok := errudp.(messages.KRBError); ok && e.ErrorCode != errorcode.KRB_ERR_RESPONSE_TOO_BIG {
  31. // Got a KRBError from KDC
  32. // If this is not a KRB_ERR_RESPONSE_TOO_BIG we will return immediately otherwise will try TCP.
  33. return rb, e
  34. }
  35. // Try TCP
  36. r, errtcp := cl.sendKDCTCP(realm, b)
  37. if errtcp != nil {
  38. if e, ok := errtcp.(messages.KRBError); ok {
  39. // Got a KRBError
  40. return r, e
  41. }
  42. return r, fmt.Errorf("failed to communicate with KDC. Attempts made with UDP (%v) and then TCP (%v)", errudp, errtcp)
  43. }
  44. rb = r
  45. }
  46. return rb, nil
  47. }
  48. //Try TCP first, UDP second
  49. rb, errtcp := cl.sendKDCTCP(realm, b)
  50. if errtcp != nil {
  51. if e, ok := errtcp.(messages.KRBError); ok {
  52. // Got a KRBError from KDC so returning and not trying UDP.
  53. return rb, e
  54. }
  55. rb, errudp := cl.sendKDCUDP(realm, b)
  56. if errudp != nil {
  57. if e, ok := errudp.(messages.KRBError); ok {
  58. // Got a KRBError
  59. return rb, e
  60. }
  61. return rb, fmt.Errorf("failed to communicate with KDC. Attempts made with TCP (%v) and then UDP (%v)", errtcp, errudp)
  62. }
  63. }
  64. return rb, nil
  65. }
  66. // sendKDCUDP sends bytes to the KDC via UDP.
  67. func (cl *Client) sendKDCUDP(realm string, b []byte) ([]byte, error) {
  68. var r []byte
  69. _, kdcs, err := cl.Config.GetKDCs(realm, false)
  70. if err != nil {
  71. return r, err
  72. }
  73. r, err = dialSendUDP(kdcs, b)
  74. if err != nil {
  75. return r, err
  76. }
  77. return checkForKRBError(r)
  78. }
  79. // dialSendUDP establishes a UDP connection to a KDC.
  80. func dialSendUDP(kdcs map[int]string, b []byte) ([]byte, error) {
  81. var errs []string
  82. for i := 1; i <= len(kdcs); i++ {
  83. udpAddr, err := net.ResolveUDPAddr("udp", kdcs[i])
  84. if err != nil {
  85. errs = append(errs, fmt.Sprintf("error resolving KDC address: %v", err))
  86. continue
  87. }
  88. conn, err := net.DialTimeout("udp", udpAddr.String(), 5*time.Second)
  89. if err != nil {
  90. errs = append(errs, fmt.Sprintf("error setting dial timeout on connection to %s: %v", kdcs[i], err))
  91. continue
  92. }
  93. if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
  94. errs = append(errs, fmt.Sprintf("error setting deadline on connection to %s: %v", kdcs[i], err))
  95. continue
  96. }
  97. // conn is guaranteed to be a UDPConn
  98. rb, err := sendUDP(conn.(*net.UDPConn), b)
  99. if err != nil {
  100. errs = append(errs, fmt.Sprintf("error sneding to %s: %v", kdcs[i], err))
  101. continue
  102. }
  103. return rb, nil
  104. }
  105. return nil, fmt.Errorf("error sending to a KDC: %s", strings.Join(errs, "; "))
  106. }
  107. // sendUDP sends bytes to connection over UDP.
  108. func sendUDP(conn *net.UDPConn, b []byte) ([]byte, error) {
  109. var r []byte
  110. defer conn.Close()
  111. _, err := conn.Write(b)
  112. if err != nil {
  113. return r, fmt.Errorf("error sending to (%s): %v", conn.RemoteAddr().String(), err)
  114. }
  115. udpbuf := make([]byte, 4096)
  116. n, _, err := conn.ReadFrom(udpbuf)
  117. r = udpbuf[:n]
  118. if err != nil {
  119. return r, fmt.Errorf("sending over UDP failed to %s: %v", conn.RemoteAddr().String(), err)
  120. }
  121. if len(r) < 1 {
  122. return r, fmt.Errorf("no response data from %s", conn.RemoteAddr().String())
  123. }
  124. return r, nil
  125. }
  126. // sendKDCTCP sends bytes to the KDC via TCP.
  127. func (cl *Client) sendKDCTCP(realm string, b []byte) ([]byte, error) {
  128. var r []byte
  129. _, kdcs, err := cl.Config.GetKDCs(realm, true)
  130. if err != nil {
  131. return r, err
  132. }
  133. r, err = dialSendTCP(kdcs, b)
  134. if err != nil {
  135. return r, err
  136. }
  137. return checkForKRBError(r)
  138. }
  139. // dialKDCTCP establishes a TCP connection to a KDC.
  140. func dialSendTCP(kdcs map[int]string, b []byte) ([]byte, error) {
  141. var errs []string
  142. for i := 1; i <= len(kdcs); i++ {
  143. tcpAddr, err := net.ResolveTCPAddr("tcp", kdcs[i])
  144. if err != nil {
  145. errs = append(errs, fmt.Sprintf("error resolving KDC address: %v", err))
  146. continue
  147. }
  148. conn, err := net.DialTimeout("tcp", tcpAddr.String(), 5*time.Second)
  149. if err != nil {
  150. errs = append(errs, fmt.Sprintf("error setting dial timeout on connection to %s: %v", kdcs[i], err))
  151. continue
  152. }
  153. if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil {
  154. errs = append(errs, fmt.Sprintf("error setting deadline on connection to %s: %v", kdcs[i], err))
  155. continue
  156. }
  157. // conn is guaranteed to be a TCPConn
  158. rb, err := sendTCP(conn.(*net.TCPConn), b)
  159. if err != nil {
  160. errs = append(errs, fmt.Sprintf("error sneding to %s: %v", kdcs[i], err))
  161. continue
  162. }
  163. return rb, nil
  164. }
  165. return nil, fmt.Errorf("error sending to a KDC: %s", strings.Join(errs, "; "))
  166. }
  167. // sendTCP sends bytes to connection over TCP.
  168. func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) {
  169. defer conn.Close()
  170. var r []byte
  171. // RFC 4120 7.2.2 specifies the first 4 bytes indicate the length of the message in big endian order.
  172. hb := make([]byte, 4, 4)
  173. binary.BigEndian.PutUint32(hb, uint32(len(b)))
  174. b = append(hb, b...)
  175. _, err := conn.Write(b)
  176. if err != nil {
  177. return r, fmt.Errorf("error sending to KDC (%s): %v", conn.RemoteAddr().String(), err)
  178. }
  179. sh := make([]byte, 4, 4)
  180. _, err = conn.Read(sh)
  181. if err != nil {
  182. return r, fmt.Errorf("error reading response size header: %v", err)
  183. }
  184. s := binary.BigEndian.Uint32(sh)
  185. rb := make([]byte, s, s)
  186. _, err = io.ReadFull(conn, rb)
  187. if err != nil {
  188. return r, fmt.Errorf("error reading response: %v", err)
  189. }
  190. if len(rb) < 1 {
  191. return r, fmt.Errorf("no response data from KDC %s", conn.RemoteAddr().String())
  192. }
  193. return rb, nil
  194. }
  195. // checkForKRBError checks if the response bytes from the KDC are a KRBError.
  196. func checkForKRBError(b []byte) ([]byte, error) {
  197. var KRBErr messages.KRBError
  198. if err := KRBErr.Unmarshal(b); err == nil {
  199. return b, KRBErr
  200. }
  201. return b, nil
  202. }