// 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 }
// Handles the message "CLMSG_save_fai_log". // buf: the decrypted message func clmsg_save_fai_log(buf *bytes.Buffer) { macaddress := "" action := "" start := 0 end := 0 data := buf.Bytes() for i := 0; i < len(data)-19; i++ { if data[i] == '<' { if i+12+17 <= len(data) && match(data, i, "<macaddress>") { macaddress = string(data[i+12 : i+12+17]) } else if match(data, i, "<fai_action>") { for k := i + 12; k < len(data); k++ { if data[k] == '<' { action = string(data[i+12 : k]) i = k break } } } else if match(data, i, "<CLMSG_save_fai_log>") { start = i + 20 } else if match(data, i, "</CLMSG_save_fai_log>") { end = i } } } if !macAddressRegexp.MatchString(macaddress) { util.Log(0, "ERROR! CLMSG_save_fai_log with illegal <macaddress> \"%v\"", macaddress) return } if !actionRegexp.MatchString(action) { util.Log(0, "ERROR! CLMSG_save_fai_log with illegal <fai_action> \"%v\"", action) return } util.Log(1, "INFO! Received log files from client %v. Assuming CLMSG_PROGRESS 100", macaddress) progress_msg := xml.NewHash("xml", "CLMSG_PROGRESS", "100") progress_msg.Add("macaddress", macaddress) clmsg_progress(progress_msg) timestamp := util.MakeTimestamp(time.Now()) logname := action + "_" + timestamp[0:8] + "_" + timestamp[8:] logdir := path.Join(config.FAILogPath, strings.ToLower(macaddress), logname) // NOTE: 1kB = 1000B, 1kiB = 1024B util.Log(1, "INFO! Storing %vkB of %v log files from %v in %v", len(data)/1000, action, macaddress, logdir) err := os.MkdirAll(logdir, 0755) if err != nil { util.Log(0, "ERROR! Error creating log directory \"%v\": %v", logdir, err) return } // Create convenience symlink with the system's name as alias for MAC address. go util.WithPanicHandler(func() { if plainname := db.SystemPlainnameForMAC(macaddress); plainname != "none" { linkpath := path.Join(config.FAILogPath, strings.ToLower(plainname)) link_target, err := os.Readlink(linkpath) if err != nil && !os.IsNotExist(err.(*os.PathError).Err) { util.Log(0, "ERROR! %v exists but is not a symlink: %v", linkpath, err) return } if err == nil { if link_target == strings.ToLower(macaddress) { return // symlink is already correct => nothing to do } util.Log(0, "WARNING! Machine %v has a new MAC %v . Removing old symlink %v => %v", plainname, macaddress, linkpath, link_target) err = os.Remove(linkpath) if err != nil { util.Log(0, "ERROR! Removing %v failed: %v", linkpath, err) // Don't bail out. Maybe we can create the new symlink anyway. } } err = os.Symlink(strings.ToLower(macaddress), linkpath) if err != nil && !os.IsExist(err.(*os.LinkError).Err) { util.Log(0, "ERROR! Could not create symlink %v => %v: %v", linkpath, strings.ToLower(macaddress), err) } } }) files := []int{} for i := start; i < end; i++ { if data[i] == ':' && match(data, i-8, "log_file") { k := i i++ for i < end { if data[i] == ':' { if k+1 < i { files = append(files, k+1, i) } break } i++ } } } files = append(files, end+8) for i := 0; i < len(files)-1; i += 2 { fname := string(data[files[i]:files[i+1]]) logdata := data[files[i+1]+1 : files[i+2]-8] util.Log(1, "INFO! Processing \"%v\" (%vkB)", fname, len(logdata)/1000) logdata = util.Base64DecodeInPlace(logdata) // As a precaution, make sure fname contains no slashes. fname = strings.Replace(fname, "/", "_", -1) err = ioutil.WriteFile(path.Join(logdir, fname), logdata, 0644) if err != nil { util.Log(0, "ERROR! Could not store \"%v\": %v", path.Join(logdir, fname), err) continue } } }
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)) } }