// Like GosaDecrypt() but operates in-place on buf. // Returns true if decryption successful and false if not. // If false is returned, the buffer contents may be destroyed, but only // if further decryption attempts with other keys would be pointless anyway, // because of some fatal condition (such as the data not being a multiple of // the cipher's block size). func GosaDecryptBuffer(buf *bytes.Buffer, key string) bool { buf.TrimSpace() if buf.Len() < 11 { return false } // minimum length of unencrypted <xml></xml> data := buf.Bytes() if string(data[0:5]) == "<xml>" { return true } // Fixes the following: // * gosa-si bug in the following line: // if( $client_answer =~ s/session_id=(\d+)$// ) { // This leaves the "." before "session_id" which breaks base64 // * new gosa-si protocol has ";IP:PORT" appended to message // which also breaks base64 for semicolon_period := 0; semicolon_period < len(data); semicolon_period++ { if data[semicolon_period] == ';' || data[semicolon_period] == '.' { buf.Trim(0, semicolon_period) data = buf.Bytes() break } } aescipher, _ := aes.NewCipher([]byte(util.Md5sum(key))) crypter := cipher.NewCBCDecrypter(aescipher, config.InitializationVector) cryptotest := make([]byte, (((3*aes.BlockSize)+2)/3)<<2) n := copy(cryptotest, data) cryptotest = cryptotest[0:n] cryptotest = util.Base64DecodeInPlace(cryptotest) n = (len(cryptotest) / aes.BlockSize) * aes.BlockSize cryptotest = cryptotest[0:n] crypter.CryptBlocks(cryptotest, cryptotest) if !strings.Contains(string(cryptotest), "<xml>") { return false } data = util.Base64DecodeInPlace(data) buf.Trim(0, len(data)) data = buf.Bytes() if buf.Len()%aes.BlockSize != 0 { // this condition is fatal => further decryption attempts are pointless buf.Reset() return false } crypter = cipher.NewCBCDecrypter(aescipher, config.InitializationVector) crypter.CryptBlocks(data, data) buf.TrimSpace() // removes 0 padding, too return true }
func faiConnection(conn *net.TCPConn) { defer conn.Close() var err error err = conn.SetKeepAlive(true) if err != nil { util.Log(0, "ERROR! SetKeepAlive: %v", err) } var buf bytes.Buffer defer buf.Reset() readbuf := make([]byte, 4096) n := 1 for n != 0 { n, err = conn.Read(readbuf) if err != nil && err != io.EOF { util.Log(0, "ERROR! Read: %v", err) } if n == 0 && err == nil { util.Log(0, "ERROR! Read 0 bytes but no error reported") } // Find complete lines terminated by '\n' and process them. for start := 0; ; { eol := start for ; eol < n; eol++ { if readbuf[eol] == '\n' { break } } // no \n found, append to buf and continue reading if eol == n { buf.Write(readbuf[start:n]) break } // append to rest of line to buffered contents buf.Write(readbuf[start:eol]) start = eol + 1 buf.TrimSpace() util.Log(2, "DEBUG! FAI monitor message from %v: %v", conn.RemoteAddr(), buf.String()) buf.Reset() } } if buf.Len() != 0 { util.Log(2, "DEBUG! Incomplete FAI monitor message (i.e. not terminated by \"\\n\") from %v: %v", conn.RemoteAddr(), buf.String()) } }
func testBuffer() { var b bytes.Buffer check(b.String(), "") // String() on fresh variable b.Reset() // Reset() on fresh variable check(b.String(), "") // String() after Reset() b.Reset() // Reset() after Reset() check(b.String(), "") check(b.Len(), 0) // same tests as above with pointer b2 := &bytes.Buffer{} check(b2.String(), "") b2.Reset() check(b2.String(), "") b2.Reset() check(b2.String(), "") check(b2.Len(), 0) b2.WriteString("Dies ist ein Test!") check(b2.String(), "Dies ist ein Test!") check(b2.Len(), 18) n, err := b.Write(nil) check(n, 0) check(err, nil) check(b.String(), "") n, err = b.Write([]byte{}) check(n, 0) check(err, nil) check(b.String(), "") check(b.Pointer(), nil) check(b.Capacity(), 0) check(b.Len(), 0) func() { defer func() { check(recover(), bytes.ErrTooLarge) }() b.Grow(-1) }() n, err = b.Write([]byte{'a'}) check(n, 1) check(err, nil) check(b.String(), "a") check(b.Capacity() >= 1, true) check(b.Len(), 1) check(b.Pointer() != nil, true) check(b.Grow(11), 1) check(b.Capacity() >= 12, true) c := b.Capacity() p := b.Pointer() check(b.Grow(11), 1) // should not cause actual growth check(b.Pointer(), p) check(b.Capacity(), c) check(b.Len(), 1) ((*[2]byte)(b.Pointer()))[1] = 'z' check(b.Contains("z"), false) n, err = b.WriteString("Hallo") check(n, 5) check(err, nil) check(b.String(), "aHallo") check(b.Pointer(), p) check(b.Capacity(), c) check(b.Len(), 6) b.Reset() check(b.String(), "") check(b.Pointer(), nil) check(b.Capacity(), 0) check(b.Contains(""), true) check(b.Contains("a"), false) b.WriteString("Hallo") b.WriteByte(' ') b.Write([]byte{'d', 'i', 'e', 's'}) b.WriteByte(' ') b.WriteString("ist ") b.WriteString("ein ") b.Write([]byte("Test")) check(b.String(), "Hallo dies ist ein Test") check(b.Contains("Hallo dies ist ein Test"), true) check(b.Contains("Test"), true) check(b.Contains("Hallo"), true) check(b.Contains("allo"), true) check(b.Contains(""), true) check(b.Split(" "), []string{"Hallo", "dies", "ist", "ein", "Test"}) check(b.Split("X"), []string{"Hallo dies ist ein Test"}) check(b.Split("Hallo dies ist ein Test"), []string{"", ""}) check(b.Split("H"), []string{"", "allo dies ist ein Test"}) check(b.Split("Test"), []string{"Hallo dies ist ein ", ""}) check(b.Split("es"), []string{"Hallo di", " ist ein T", "t"}) b.Reset() b.WriteString(" \n\t Hallo \t\v\n") check(b.Len(), 15) p = b.Pointer() b.TrimSpace() check(b.String(), "Hallo") check(b.Len(), 5) check(b.Pointer(), p) b.Reset() b.WriteString(" \n\t \t\v\n") check(b.Len(), 10) b.TrimSpace() check(b.Pointer(), nil) check(b.Len(), 0) check(b.Capacity(), 0) b.TrimSpace() check(b.Pointer(), nil) check(b.Len(), 0) check(b.Capacity(), 0) b.Reset() b.WriteString(" \n\t Hallo") check(b.Len(), 10) p = b.Pointer() b.TrimSpace() check(b.String(), "Hallo") check(b.Len(), 5) check(b.Pointer(), p) b.Reset() b.WriteString("Hallo \t\v\n") check(b.Len(), 10) p = b.Pointer() b.TrimSpace() check(b.String(), "Hallo") check(b.Len(), 5) check(b.Pointer(), p) b.Reset() b.WriteString(" ") check(b.Len(), 1) b.TrimSpace() check(b.Pointer(), nil) check(b.Len(), 0) check(b.Capacity(), 0) b.Reset() b.WriteString("Der Cottbuser Postkutscher kotzt in den Cottbuser Postkotzkasten") n = b.Len() c = b.Capacity() p = b.Pointer() b.Trim(-10, 2000) check(b.Len(), n) check(b.Capacity(), c) check(b.Pointer(), p) b.Trim(2000, -10) check(b.Len(), 0) check(b.Capacity(), 0) check(b.Pointer(), nil) b.WriteString("Der Cottbuser Postkutscher kotzt in den Cottbuser Postkotzkasten") b.Trim(4, 4) check(b.Len(), 0) check(b.Capacity(), 0) check(b.Pointer(), nil) b.WriteString("Der Cottbuser Postkutscher kotzt in den Cottbuser Postkotzkasten") n = b.Len() c = b.Capacity() p = b.Pointer() b.Trim(0, b.Len()-6) check(b.Len(), n-6) check(b.Capacity(), c) check(b.Pointer(), p) check(b.String(), "Der Cottbuser Postkutscher kotzt in den Cottbuser Postkotz") b.Trim(27, b.Len()) check(b.Len(), n-6-27) check(b.Capacity(), c) check(b.Pointer(), p) check(b.String(), "kotzt in den Cottbuser Postkotz") b.Trim(1, b.Len()-1) check(b.Len(), n-6-27-2) check(b.Capacity(), c) check(b.Pointer(), p) check(b.String(), "otzt in den Cottbuser Postkot") b.Reset() b.Write0(-1) b.Write0(-100) b.Write0(0) check(b.Len(), 0) check(b.Capacity(), 0) check(b.Pointer(), nil) b.Write0(1) check(b.Len(), 1) check(b.Capacity(), 1) check(b.Bytes(), []byte{0}) b.WriteByte(111) b.Write0(1) b.WriteByte(222) b.Write0(2) b.WriteByte(99) check(b.Len(), 7) check(b.Bytes(), []byte{0, 111, 0, 222, 0, 0, 99}) b2.Reset() slices := [][]byte{} total := 0 numfakeerrs := 0 for total < 100000 { c = rand.Intn(30000) total += c sl := make([]byte, c) for i := range sl { sl[i] = byte(rand.Intn(256)) } slices = append(slices, sl) b2.Write(sl) if total/30000 > numfakeerrs { slices = append(slices, nil) numfakeerrs++ } } check(numfakeerrs, 3) slcopy := make([][]byte, len(slices)) copy(slcopy, slices) slret := &sliceReturner{Slices: slcopy} b.Reset() check(b.Capacity(), 0) check(b.Len(), 0) check(b.Pointer(), nil) n = 0 for i := 0; i < numfakeerrs; i++ { n64, err := b.ReadFrom(slret) n += int(n64) check(err, FakeError) } n64, err := b.ReadFrom(slret) n += int(n64) check(err, nil) check(n, total) check(b.Capacity() > b.Len(), true) check(b.Len(), total) contents := b.Bytes() contents2 := b2.Bytes() check(len(contents), len(contents2)) n = 0 for i := range contents { if contents[i] != contents2[i] { break } n++ } check(n, total) b2.Reset() for i := range slices { for k := range slices[i] { slices[i][k] = 11 } n, err = b.Read(slices[i]) check(n, len(slices[i])) check(err, nil) b2.Write(slices[i]) } check(b2.Len(), total) n, err = b.Read(slices[0]) check(n, 0) check(err, io.EOF) contents = b.Bytes() contents2 = b2.Bytes() check(len(contents), len(contents2)) n = 0 for i := range contents { if contents[i] != contents2[i] { break } n++ } check(n, total) b.WriteString("foo") foo := make([]byte, 10) n, err = b.Read(foo) check(n, 3) check(err, nil) check(string(foo[0:3]), "foo") n64, err = b.Seek(6700, 0) check(n64, 6700) check(err, nil) n64, err = b.Seek(-6000, 1) check(n64, 700) check(err, nil) n64, err = b.Seek(815, 1) check(n64, 1515) check(err, nil) n, err = b.Read(foo) check(n, len(foo)) check(err, nil) check(foo, b2.Bytes()[1515:1515+len(foo)]) n64, err = b.Seek(-3, 2) check(n64, total) check(err, nil) n, err = b.Read(foo) check(n, 3) check(err, nil) check(string(foo[0:3]), "foo") n64, err = b.Seek(999999, 0) check(n64, b.Len()) check(err, nil) n64, err = b.Seek(-3, 1) check(n64, total) check(err, nil) n, err = b.Read(foo) check(n, 3) check(err, nil) check(string(foo[0:3]), "foo") n64, err = b.Seek(-815, 0) check(n64, 0) check(err, nil) n, err = b.Read(foo) check(n, len(foo)) check(err, nil) check(foo, b2.Bytes()[0:len(foo)]) }
// Handles one or more messages received over conn. Each message is a single // line terminated by \n. The message may be encrypted as by security.GosaEncrypt(). func handle_request(tcpconn *net.TCPConn) { defer tcpconn.Close() defer atomic.AddInt32(&ActiveConnections, -1) // defer util.Log(2, "DEBUG! Connection to %v closed", tcpconn.RemoteAddr()) // util.Log(2, "DEBUG! Connection from %v", tcpconn.RemoteAddr()) var err error err = tcpconn.SetKeepAlive(true) if err != nil { util.Log(0, "ERROR! SetKeepAlive: %v", err) } var buf bytes.Buffer defer buf.Reset() readbuf := make([]byte, 4096) var conn net.Conn conn = tcpconn n := 1 if config.TLSServerConfig != nil { // If TLS is required, we need to see a STARTTLS before the timeout. // If TLS is optional we need to accept idle connections for backwards compatibility if config.TLSRequired { conn.SetDeadline(time.Now().Add(config.TimeoutTLS)) } for i := range starttls { n, err = conn.Read(readbuf[0:1]) if n == 0 { if i != 0 { // Do not log an error for a port scan that just opens a connection and closes it immediately util.Log(0, "ERROR! Read error while looking for STARTTLS from %v: %v", conn.RemoteAddr(), err) } return } buf.Write(readbuf[0:1]) if readbuf[0] == '\r' && starttls[i] == '\n' { // Read the \n that must follow \r (we don't support lone CR line endings) conn.Read(readbuf[0:1]) // ignore error. It will pop up again further down the line. } if readbuf[0] != starttls[i] { if config.TLSRequired { util.Log(0, "ERROR! No STARTTLS from %v, but TLS is required", conn.RemoteAddr()) util.WriteAll(conn, []byte(message.ErrorReply("STARTTLS is required to connect"))) return } break } if readbuf[0] == '\n' { buf.Reset() // purge STARTTLS\n from buffer conn = tls.Server(conn, config.TLSServerConfig) } } } context := security.ContextFor(conn) if context == nil { return } for n != 0 { //util.Log(2, "DEBUG! Receiving from %v", conn.RemoteAddr()) n, err = conn.Read(readbuf) if err != nil && err != io.EOF { util.Log(0, "ERROR! Read: %v", err) } if err == io.EOF { util.Log(2, "DEBUG! Connection closed by %v", conn.RemoteAddr()) } if n == 0 && err == nil { util.Log(0, "ERROR! Read 0 bytes but no error reported") } // Find complete lines terminated by '\n' and process them. for start := 0; ; { eol := start for ; eol < n; eol++ { if readbuf[eol] == '\n' { break } } // no \n found, append to buf and continue reading if eol == n { buf.Write(readbuf[start:n]) break } // append to rest of line to buffered contents buf.Write(readbuf[start:eol]) start = eol + 1 buf.TrimSpace() // process the message and get a reply (if applicable) if buf.Len() > 0 { // ignore empty lines request_start := time.Now() reply, disconnect := message.ProcessEncryptedMessage(&buf, context) buf.Reset() request_time := time.Since(request_start) RequestProcessingTimes.Push(request_time) request_time -= RequestProcessingTimes.Next().(time.Duration) atomic.AddInt64(&message.RequestProcessingTime, int64(request_time)) if reply.Len() > 0 { util.Log(2, "DEBUG! Sending %v bytes reply to %v", reply.Len(), conn.RemoteAddr()) var deadline time.Time // zero value means "no deadline" if config.Timeout >= 0 { deadline = time.Now().Add(config.Timeout) } conn.SetWriteDeadline(deadline) _, err := util.WriteAll(conn, reply.Bytes()) if err != nil { util.Log(0, "ERROR! WriteAll: %v", err) } reply.Reset() util.WriteAll(conn, []byte{'\r', '\n'}) } if disconnect { util.Log(1, "INFO! Forcing disconnect of %v because of error", conn.RemoteAddr()) return } if Shutdown { util.Log(1, "INFO! Forcing disconnect of %v because of go-susi shutdown", conn.RemoteAddr()) return } } } } if buf.Len() != 0 { util.Log(0, "ERROR! Incomplete message from %v (i.e. not terminated by \"\\n\") of %v bytes: %v", conn.RemoteAddr(), buf.Len(), buf.String()) } }