diff options
Diffstat (limited to 'client/network.go')
-rw-r--r-- | client/network.go | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/client/network.go b/client/network.go new file mode 100644 index 0000000..b29928f --- /dev/null +++ b/client/network.go @@ -0,0 +1,92 @@ +package client + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "strings" + "time" + + krbClient "github.com/jcmturner/gokrb5/v8/client" + "github.com/jcmturner/gokrb5/v8/kadmin" +) + +// From here: https://github.com/jcmturner/gokrb5/blob/v8.4.4/v8/client/passwd.go#L51C1-L75C2 +// It would be really nice if this was public, but it isn't :( +func sendToKAdmin(cl *krbClient.Client, msg kadmin.Request) (r kadmin.Reply, err error) { + _, kps, err := cl.Config.GetKpasswdServers(cl.Credentials.Domain(), true) + if err != nil { + return + } + b, err := msg.Marshal() + if err != nil { + return + } + var rb []byte + rb, err = dialSendTCP(kps, b) + if err != nil { + return + } + err = r.Unmarshal(rb) + + return +} + +// Below are from here: https://github.com/jcmturner/gokrb5/blob/master/v8/client/network.go +// Likewise, it sucks that the change password API is so limited, and there is zero low-level exposure without copy-pasting code. +// dialSendTCP establishes a TCP connection to a KDC. +func dialSendTCP(kdcs map[int]string, b []byte) ([]byte, error) { + var errs []string + for i := 1; i <= len(kdcs); i++ { + conn, err := net.DialTimeout("tcp", kdcs[i], 5*time.Second) + if err != nil { + errs = append(errs, fmt.Sprintf("error establishing connection to %s: %v", kdcs[i], err)) + continue + } + if err := conn.SetDeadline(time.Now().Add(5 * time.Second)); err != nil { + errs = append(errs, fmt.Sprintf("error setting deadline on connection to %s: %v", kdcs[i], err)) + continue + } + // conn is guaranteed to be a TCPConn + rb, err := sendTCP(conn.(*net.TCPConn), b) + if err != nil { + errs = append(errs, fmt.Sprintf("error sending to %s: %v", kdcs[i], err)) + continue + } + return rb, nil + } + return nil, fmt.Errorf("error sending: %s", strings.Join(errs, "; ")) +} + +// sendTCP sends bytes to connection over TCP. +func sendTCP(conn *net.TCPConn, b []byte) ([]byte, error) { + defer conn.Close() + var r []byte + // RFC 4120 7.2.2 specifies the first 4 bytes indicate the length of the message in big endian order. + hb := make([]byte, 4, 4) + binary.BigEndian.PutUint32(hb, uint32(len(b))) + b = append(hb, b...) + + _, err := conn.Write(b) + if err != nil { + return r, fmt.Errorf("error sending to %s: %v", conn.RemoteAddr().String(), err) + } + + sh := make([]byte, 4, 4) + _, err = conn.Read(sh) + if err != nil { + return r, fmt.Errorf("error reading response size header: %v", err) + } + s := binary.BigEndian.Uint32(sh) + + rb := make([]byte, s, s) + _, err = io.ReadFull(conn, rb) + if err != nil { + return r, fmt.Errorf("error reading response: %v", err) + } + if len(rb) < 1 { + return r, fmt.Errorf("no response data from %s", conn.RemoteAddr().String()) + } + return rb, nil +} |