Skip to content

Instantly share code, notes, and snippets.

@dpb587-pivotal
Last active Sep 20, 2019
Embed
What would you like to do?
#!/bin/bash
func build() {
build_dir=/tmp/padding-bug/build
openssl_version=$1
install_dir=$2
lzo_version=2.10
openvpn_version=2.4.7
mkdir -p ${build_dir}
pushd ${build_dir}
wget -qO https://www.openssl.org/source/openssl-${openssl_version}.tar.gz | tar -xzf-
pushd openssl-*
./config \
-DSSL_ALLOW_ADH \
--shared \
--prefix=${install_dir}/external/openssl \
--openssldir=${install_dir}/external/openssldir
make
make install
popd
wget -qO https://www.oberhumer.com/opensource/lzo/download/lzo-${lzo_version}.tar.gz | tar -xzf-
pushd lzo-*
./configure \
--prefix=${install_dir}/external/lzo
make
make install
popd
wget -qO https://swupdate.openvpn.org/community/releases/openvpn-${openvpn_version}.tar.gz | tar -xzf-
pushd openvpn-*
export CFLAGS="-I${install_dir}/external/openssl/include -Wl,-rpath ${install_dir}/external/openssl/lib -L${install_dir}/external/openssl/lib"
export LZO_CFLAGS="-I${install_dir}/external/lzo/include"
export LZO_LIBS="-L${install_dir}/external/lzo/lib -llzo2"
./configure \
--prefix=${install_dir} \
--disable-plugin-auth-pam
make
make install
popd
popd
rm -fr ${build_dir}
}
build 1.1.0l ~/tmp/padding-bug/with-openssl-1.1.0
~/tmp/padding-bug/with-openssl-1.1.0/sbin/openvpn --version
# OpenVPN 2.4.7 x86_64-apple-darwin18.7.0 [SSL (OpenSSL)] [LZO] [LZ4] [MH/RECVDA] [AEAD] built on Sep 20 2019
# library versions: OpenSSL 1.1.0l 10 Sep 2019, LZO 2.10
# Originally developed by James Yonan
# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
# Compile time defines: enable_async_push=no enable_comp_stub=no enable_crypto=yes enable_crypto_ofb_cfb=yes enable_debug=yes enable_def_auth=yes enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_fast_install=needless enable_fragment=yes enable_iproute2=no enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes enable_management=yes enable_multihome=yes enable_pam_dlopen=no enable_pedantic=no enable_pf=yes enable_pkcs11=no enable_plugin_auth_pam=no enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no enable_server=yes enable_shared=yes enable_shared_with_static_runtimes=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no enable_systemd=no enable_werror=no enable_win32_dll=yes enable_x509_alt_username=no with_aix_soname=aix with_crypto_library=openssl with_gnu_ld=no with_mem_check=no with_sysroot=no
build 1.1.1d ~/tmp/padding-bug/with-openssl-1.1.1
~/tmp/padding-bug/with-openssl-1.1.1/sbin/openvpn --version
# OpenVPN 2.4.7 x86_64-apple-darwin18.7.0 [SSL (OpenSSL)] [LZO] [LZ4] [MH/RECVDA] [AEAD] built on Sep 20 2019
# library versions: OpenSSL 1.1.1d 10 Sep 2019, LZO 2.10
# Originally developed by James Yonan
# Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
# Compile time defines: enable_async_push=no enable_comp_stub=no enable_crypto=yes enable_crypto_ofb_cfb=yes enable_debug=yes enable_def_auth=yes enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown enable_fast_install=needless enable_fragment=yes enable_iproute2=no enable_libtool_lock=yes enable_lz4=yes enable_lzo=yes enable_management=yes enable_multihome=yes enable_pam_dlopen=no enable_pedantic=no enable_pf=yes enable_pkcs11=no enable_plugin_auth_pam=no enable_plugin_down_root=yes enable_plugins=yes enable_port_share=yes enable_selinux=no enable_server=yes enable_shared=yes enable_shared_with_static_runtimes=no enable_small=no enable_static=yes enable_strict=no enable_strict_options=no enable_systemd=no enable_werror=no enable_win32_dll=yes enable_x509_alt_username=no with_aix_soname=aix with_crypto_library=openssl with_gnu_ld=no with_mem_check=no with_sysroot=no
package main
import (
"bufio"
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"net"
"os"
"strings"
"github.com/dpb587/go-openvpn/ovpn"
"github.com/pkg/errors"
)
var managementAddress = "127.0.0.1:12913"
func main() {
data, err := ioutil.ReadFile(os.Args[1])
if err != nil {
panic(errors.Wrap(err, "reading profile"))
}
profile, err := ovpn.Parse(data)
if err != nil {
panic(errors.Wrap(err, "parsing profile"))
}
switch os.Args[2] {
case "profile":
fmt.Printf("%s", createManagedProfile(profile, managementAddress))
case "management":
err = startManagementServer(managementAddress, profile)
if err != nil {
panic(err)
}
default:
panic(fmt.Errorf("unexpected command: %s", os.Args[2]))
}
}
func createManagedProfile(profile *ovpn.Profile, managementAddress string) []byte {
w := &bytes.Buffer{}
for _, e := range profile.Elements {
switch pe := e.(type) {
case ovpn.DirectiveProfileElement:
fmt.Fprintf(w, "%s %s\n", pe.Directive(), strings.Join(pe.Args(), " "))
case ovpn.EmbeddedProfileElement:
switch pe.Embed() {
case "cert", "key":
// ignore; handled via management
default:
fmt.Fprintf(w, "<%s>\n%s</%s>\n", pe.Embed(), pe.Data(), pe.Embed())
}
}
}
fmt.Fprintf(w, "management %s\n", strings.Join(strings.Split(managementAddress, ":"), " "))
fmt.Fprintf(w, "management-client\n")
fmt.Fprintf(w, "management-external-cert ssoca\n")
fmt.Fprintf(w, "management-external-key\n")
return w.Bytes()
}
func startManagementServer(managementAddress string, profile *ovpn.Profile) error {
listener, err := net.Listen("tcp", managementAddress)
if err != nil {
return errors.Wrap(err, "binding")
}
conn, err := listener.Accept()
defer listener.Close()
if err != nil {
if opErr, ok := err.(*net.OpError); ok && opErr.Timeout() {
return nil
}
return err
}
log.Printf("new openvpn management connection (%s)", conn.RemoteAddr().String())
err = handleManagementConnection(conn, profile)
if err != nil {
panic(err)
}
return nil
}
func handleManagementConnection(conn net.Conn, profile *ovpn.Profile) error {
defer conn.Close()
certPEM := profile.GetEmbedded("cert")[0].(ovpn.EmbeddedProfileElement).Data()
block, _ := pem.Decode([]byte(profile.GetEmbedded("key")[0].(ovpn.EmbeddedProfileElement).Data()))
if block == nil {
return errors.New("no private key found")
}
privRSA, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return errors.Wrap(err, "parsing key from profile")
}
reader := bufio.NewReader(conn)
for {
log.Print("openvpn management waiting for data")
message, err := reader.ReadString('\n')
if err != nil {
return err
}
log.Printf("openvpn management data recv: %s", message)
var keepGoing bool
if strings.HasPrefix(message, ">") {
keepGoing = func(message string) bool {
split := strings.SplitN(message, ":", 2)
if len(split) != 2 {
fmt.Println(fmt.Errorf("unexpected realtime message: %s", message))
return true
}
var err error
messageSource, messageText := split[0], split[1][0:len(split[1])-1]
switch messageSource {
case "INFO":
log.Print(messageText)
case "NEED-CERTIFICATE":
conn.Write([]byte("certificate\n"))
conn.Write([]byte(certPEM))
conn.Write([]byte("\n"))
conn.Write([]byte("END\n"))
case "RSA_SIGN":
data64, err := base64.StdEncoding.DecodeString(messageText)
if err != nil {
log.Fatal(errors.Wrap(err, "decoding signing token"))
}
signature, err := rsa.SignPKCS1v15(rand.Reader, privRSA, 0, data64)
if err != nil {
log.Fatal(errors.Wrap(err, "signing token"))
}
signature64 := base64.StdEncoding.EncodeToString(signature)
conn.Write([]byte("rsa-sig"))
conn.Write([]byte("\n"))
conn.Write([]byte(signature64))
conn.Write([]byte("\n"))
conn.Write([]byte("END\n"))
default:
log.Printf("unexpected realtime message: %s", messageSource)
}
if err != nil {
log.Printf("unexpected error: %s", err)
conn.Write([]byte("signal SIGTERM\n"))
}
return true
}(message[1:])
} else {
keepGoing = true
log.Print(message)
}
if !keepGoing {
log.Print("openvpn management exiting loop")
break
}
}
return nil
}
#!/bin/bash
go get gist.github.com/dpb587-pivotal/d0d132c586ed4053323b4d4246664a07.git
go build -o ~/tmp/padding-bug/management-tool gist.github.com/dpb587-pivotal/d0d132c586ed4053323b4d4246664a07.git
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment