summaryrefslogtreecommitdiff
path: root/client/network.go
diff options
context:
space:
mode:
authorAria <me@aria.rip>2023-10-01 22:55:08 +0100
committerAria <me@aria.rip>2023-10-01 22:55:08 +0100
commit7045a630996e08c18beb7c3d39e1b2752c8a4ba4 (patch)
treee539793ccb588433f44cc0e1b52821cb6953a85a /client/network.go
parentca56d194c605ef7ba09f46eff11374b492f83804 (diff)
a working version with only changing passwords
Diffstat (limited to 'client/network.go')
-rw-r--r--client/network.go92
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
+}