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