// Runs this Hash's text child through a base64 encoder and replaces it with // the result. func (self *Hash) EncodeBase64() { text := self.TextFragments() self.SetText() carry := make([]byte, 0, 2) for i := range text { l := len(text[i]) + len(carry) var rem int var buf []byte if i < len(text)-1 { // if we have a next string that we can carry over into rem = l % 3 // round down l to multiple of 3 and carry over the remainder l -= rem if l == 0 { // nothing remaining? carry over everything carry = append(carry, text[i]...) continue } buf = make([]byte, (l/3)<<2) } else { // last string, can't carry over buf = make([]byte, ((l+2)/3)<<2) } idx := len(buf) - l copy(buf[idx:], carry) copy(buf[idx+len(carry):], text[i]) carry = append(carry[0:0], text[i][len(text[i])-rem:]...) self.AppendString(string(util.Base64EncodeInPlace(buf, idx))) } }
// Replaces the contents of buf with the base64 representation of the data // after encryption with the given key. // The key is a word as used in gosa-si.conf whose md5sum will be used as // the actual AES key. buf is empty, it won't be changed. func GosaEncryptBuffer(buf *bytes.Buffer, key string) { datalen := buf.Len() if datalen == 0 { return } ciph, _ := aes.NewCipher([]byte(util.Md5sum(key))) crypter := cipher.NewCBCEncrypter(ciph, config.InitializationVector) cryptpad := (aes.BlockSize - datalen%aes.BlockSize) &^ aes.BlockSize cryptlen := cryptpad + datalen b64len := ((cryptlen + 2) / 3) << 2 for i := datalen; i < b64len; i++ { buf.WriteByte(0) } data := buf.Bytes() copy(data[b64len-datalen:], data) // move data back idx := b64len - cryptlen copy(data[idx:], make([]byte, cryptpad)) // insert 0s in front crypter.CryptBlocks(data[idx:], data[idx:]) util.Base64EncodeInPlace(data, idx) }
// Handles the message "gosa_get_log_file_by_date_and_mac". // xmlmsg: the decrypted and parsed message // Returns: // unencrypted reply func gosa_get_log_file_by_date_and_mac(xmlmsg *xml.Hash) *xml.Hash { macaddress := xmlmsg.Text("mac") lmac := strings.ToLower(macaddress) subdir := xmlmsg.Text("date") log_file := xmlmsg.Text("log_file") header := "get_log_file_by_date_and_mac" x := xml.NewHash("xml", "header", header) x.Add(header) x.Add("source", config.ServerSourceAddress) x.Add("target", "GOSA") x.Add("session_id", "1") if !macAddressRegexp.MatchString(macaddress) { emsg := fmt.Sprintf("Illegal or missing <mac> element in message: %v", xmlmsg) util.Log(0, "ERROR! %v", emsg) return ErrorReplyXML(emsg) } // As a precaution, make sure subdir and log_file contain no slashes. subdir = strings.Replace(subdir, "/", "_", -1) log_file = strings.Replace(log_file, "/", "_", -1) if subdir == "" { // When you open the installation logs in GOsa for the first time, GOsa sends // a broken request that is characterized by an empty <date> and log_file==0. // If we return an error, GOsa presents it to the user which // gives a bad experience. So we instead return an empty reply in this special case. if log_file == "0" { return x } emsg := fmt.Sprintf("Missing or empty <date> element in message: %v", xmlmsg) util.Log(0, "ERROR! %v", emsg) return ErrorReplyXML(emsg) } if log_file == "" { emsg := fmt.Sprintf("Missing or empty <log_file> element in message: %v", xmlmsg) util.Log(0, "ERROR! %v", emsg) return ErrorReplyXML(emsg) } f, err := os.Open(path.Join(config.FAILogPath, lmac, subdir, log_file)) if err != nil { emsg := fmt.Sprintf("gosa_get_log_file_by_date_and_mac: %v", err) util.Log(0, "ERROR! %v", emsg) return ErrorReplyXML(emsg) } defer f.Close() var b bytes.Buffer defer b.Reset() buffy := make([]byte, 65536) for { n, err := f.Read(buffy) b.Write(buffy[0:n]) if err == io.EOF { break } if err != nil { emsg := fmt.Sprintf("gosa_get_log_file_by_date_and_mac: %v", err) util.Log(0, "ERROR! %v", emsg) return ErrorReplyXML(emsg) } if n == 0 { util.Log(0, "WARNING! Read returned 0 bytes but no error. Assuming EOF") break } } idx := (((b.Len() + 2) / 3) << 2) - b.Len() b.Write0(idx) data := b.Bytes() copy(data[idx:], data) data = util.Base64EncodeInPlace(data, idx) data_element := x.Add(log_file) // To reduce memory leak potential, we append in pieces rather than as one large string end := xml.MaxFragmentLength for ; end < len(data); end += xml.MaxFragmentLength { data_element.AppendString(string(data[end-xml.MaxFragmentLength : end])) } data_element.AppendString(string(data[end-xml.MaxFragmentLength:])) return x }
func testBase64() { check(util.Base64DecodeString("", nil), []byte{}) check(util.Base64DecodeInPlace([]byte{}), []byte{}) check(util.Base64DecodeString("=", nil), []byte{}) check(util.Base64DecodeInPlace([]byte("=")), []byte{}) check(util.Base64DecodeString(" = ", nil), []byte{}) check(util.Base64DecodeInPlace([]byte(" = ")), []byte{}) check(util.Base64DecodeString("/+/+", nil), []byte{0xff, 0xef, 0xfe}) check(util.Base64DecodeInPlace([]byte("/+/+")), []byte{0xff, 0xef, 0xfe}) check(util.Base64DecodeString("_-_-", nil), []byte{0xff, 0xef, 0xfe}) check(util.Base64DecodeInPlace([]byte("_-_-")), []byte{0xff, 0xef, 0xfe}) var devnull int check(string(util.Base64DecodeString("SGFsbG8=", nil)), "Hallo") check(string(util.Base64DecodeInPlace([]byte("SGFsbG8="))), "Hallo") check(string(util.Base64DecodeString("SGFsbA==", nil)), "Hall") check(string(util.Base64DecodeInPlace([]byte("SGFsbA=="))), "Hall") check(string(util.Base64DecodeString("SGFsbG8", nil)), "Hallo") check(string(util.Base64DecodeInPlace([]byte("SGFsbG8"))), "Hallo") check(string(util.Base64DecodeString("SGFsbA=", nil)), "Hall") check(string(util.Base64DecodeInPlace([]byte("SGFsbA="))), "Hall") check(string(util.Base64DecodeString("SGFsbG8===", nil)), "Hallo") check(string(util.Base64DecodeInPlace([]byte("SGFsbG8==="))), "Hallo") check(string(util.Base64DecodeString("SGFsbA", nil)), "Hall") check(string(util.Base64DecodeInPlace([]byte("SGFsbA"))), "Hall") check(string(util.Base64DecodeString("SGFsbG8=", &devnull)), "Hallo") check(devnull, 0) check(string(util.Base64DecodeString("SGFsbA==", &devnull)), "Hall") check(devnull, 0) check(string(util.Base64DecodeString("SGFsbA=", &devnull)), "Hall") check(devnull, 0) check(string(util.Base64DecodeString("SGFsbG8===", &devnull)), "Hallo") check(devnull, 0) check(string(util.Base64DecodeString("AA", nil)), "\000") check(string(util.Base64DecodeInPlace([]byte("AA"))), "\000") check(string(util.Base64DecodeString("AAA", nil)), "\000\000") check(string(util.Base64DecodeInPlace([]byte("AAA"))), "\000\000") var zerocarry int check(string(util.Base64DecodeString("AA", &zerocarry)), "") check(zerocarry != 0, true) check(string(util.Base64DecodeString("=", &zerocarry)), "\000") check(zerocarry, 0) check(string(util.Base64DecodeString("AAA", &zerocarry)), "") check(zerocarry != 0, true) check(string(util.Base64DecodeString("=", &zerocarry)), "\000\000") check(zerocarry, 0) testbuf := make([]byte, 1024) for i := range testbuf { testbuf[i] = byte(i) } error_list := "" for length := 0; length <= 12; length++ { for eq := 0; eq <= 4; eq++ { for err := 0; err <= 12; err++ { b64_1 := base64.StdEncoding.EncodeToString(testbuf[0 : 512-length]) testslice := b64_1[0:] errors := []int{0} for e := 0; e < err; e++ { errors = append(errors, errors[e]+rand.Intn(len(testslice)-errors[e])) } errors = append(errors, len(testslice)) teststr := "" for i := 0; i < len(errors)-1; i++ { if i != 0 { teststr = teststr + "\000\n" } teststr = teststr + testslice[errors[i]:errors[i+1]] } for i := 0; i < eq; i++ { teststr += "=" } // because we're concatenating we need at least 1 "=" if the // first string ends in an incomplete block if eq == 0 && (length&3) != 0 { teststr += "=" } b64_2 := base64.URLEncoding.EncodeToString(testbuf[512-length:]) testslice = b64_2[0:] errors = []int{0} for e := 0; e < err; e++ { errors = append(errors, errors[e]+rand.Intn(len(testslice)-errors[e])) } errors = append(errors, len(testslice)) for i := 0; i < len(errors)-1; i++ { if i != 0 { teststr = teststr + " " } teststr = teststr + testslice[errors[i]:errors[i+1]] } for i := 0; i < eq; i++ { teststr += "=" } for parts := 0; parts < 5; parts++ { stops := []int{0} for e := 0; e < parts; e++ { stops = append(stops, stops[e]+rand.Intn(len(teststr)-stops[e])) } stops = append(stops, len(teststr)) decoded := "" carry := 0 for i := 0; i < len(stops)-1; i++ { decoded += string(util.Base64DecodeString(teststr[stops[i]:stops[i+1]], &carry)) } if decoded != string(testbuf) { error_list += fmt.Sprintf("(util.Base64DecodeString() failed for length=%v eq=%v err=%v parts=%v)\n", length, eq, err, parts) } buffy := []byte(string(teststr)) decbuffy := util.Base64DecodeInPlace(buffy) if &(decbuffy[0]) != &(buffy[0]) || // verify the in-place property string(decbuffy) != string(testbuf) { error_list += fmt.Sprintf("(util.Base64DecodeInPlace() failed for length=%v eq=%v err=%v parts=%v)\n", length, eq, err, parts) } } } } } check(error_list, "") buffy := make([]byte, 1024) st := "Nehmt, esst. Dies ist mein Leib - sprach der Schokohase" copy(buffy[32:], st) result := util.Base64EncodeInPlace(buffy[:32+len(st)], 32) check(&(buffy[0]) == &(result[0]), true) check(cap(result), cap(buffy)) check(string(result), "TmVobXQsIGVzc3QuIERpZXMgaXN0IG1laW4gTGVpYiAtIHNwcmFjaCBkZXIgU2Nob2tvaGFzZQ==") st = "Nehmt, esst. Dies ist mein Leib - sprach der Schokohase\n" copy(buffy[256:], st) result = util.Base64EncodeInPlace(buffy[:256+len(st)], 256) check(&(buffy[0]) == &(result[0]), true) check(cap(result), cap(buffy)) check(string(result), "TmVobXQsIGVzc3QuIERpZXMgaXN0IG1laW4gTGVpYiAtIHNwcmFjaCBkZXIgU2Nob2tvaGFzZQo=") for n := 0; n <= 12; n++ { buffy = make([]byte, n) for i := range buffy { buffy[i] = byte(i) } check(string(util.Base64EncodeString(string(buffy))), base64.StdEncoding.EncodeToString(buffy)) } }