You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

218 lines
4.2 KiB
Go

package service
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
)
import "golang.org/x/crypto/blowfish"
type Status int
const (
OK Status = iota
WAITING
ERROR
)
type BaseMessage struct {
Signature string
}
type httpServer struct {
manager *Manager
status Status
}
type CreateCSRRequest struct {
*BaseMessage
Hostname string
}
type CreateCSRResponse struct {
*BaseMessage
CSR string `json:"csr"`
}
type UpdateOpenVPNConfigRequest struct {
*BaseMessage
Config string
}
type DeliverCertificateRequest struct {
*BaseMessage
Certificate string
}
func NewHttpServer(manager *Manager) *httpServer {
return &httpServer{manager, WAITING}
}
func (it *httpServer) Start() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(200)
writer.Write([]byte("Don't"))
})
http.HandleFunc("/create-csr", func(writer http.ResponseWriter, request *http.Request) {
req := &CreateCSRRequest{}
err := it.verifyRequest(request, req)
if err != nil {
writer.WriteHeader(400)
return
}
csr, err := it.manager.CreateCSR(req.Hostname)
if err != nil {
writer.WriteHeader(500)
return
}
it.writeResponse(writer, CreateCSRResponse{
CSR: string(csr),
})
})
http.HandleFunc("/deliver-crt", func(writer http.ResponseWriter, request *http.Request) {
req := &DeliverCertificateRequest{}
err := it.verifyRequest(request, req)
if err != nil {
writer.WriteHeader(400)
return
}
err = it.manager.UpdateCertificate(req.Certificate)
if err != nil {
writer.WriteHeader(500)
return
}
it.manager.openVPN.Start()
})
http.HandleFunc("/update-openvpn-config", func(writer http.ResponseWriter, request *http.Request) {
req := &UpdateOpenVPNConfigRequest{}
err := it.verifyRequest(request, req)
if err != nil {
writer.WriteHeader(400)
return
}
err = it.manager.openVPN.UpdateConfig(req.Config)
if err != nil {
writer.WriteHeader(500)
return
}
it.manager.openVPN.Restart()
})
http.ListenAndServe(":7864", nil)
}
func (it *httpServer) writeResponse(writer http.ResponseWriter, v interface{}) error {
passwordAndIV := make([]byte, 48)
_, err := rand.Read(passwordAndIV)
if err != nil {
return err
}
encPasswordAndIV, err := rsa.EncryptPKCS1v15(rand.Reader, it.manager.CAPublicKey(), passwordAndIV)
if err != nil {
return err
}
_, err = writer.Write([]byte(hex.EncodeToString(encPasswordAndIV)))
if err != nil {
return err
}
password := passwordAndIV[:32]
iv := passwordAndIV[32:]
cipher, err := blowfish.NewSaltedCipher(password, iv)
if err != nil {
return err
}
hashedFingerpint := sha256.Sum256(it.manager.GetServerFingerprint())
plainSignature, err := rsa.SignPKCS1v15(rand.Reader, it.manager.privateKey, 0, hashedFingerpint[:])
if err != nil {
return err
}
sign, ok := v.(*BaseMessage)
if !ok {
return errors.New("Given message can't be signed")
}
sign.Signature = hex.EncodeToString(plainSignature)
body, err := json.Marshal(v)
if err != nil {
return err
}
encBody := make([]byte, len(body))
cipher.Encrypt(encBody, body)
writer.Write([]byte(hex.EncodeToString(encBody)))
return nil
}
func (it *httpServer) verifyRequest(r *http.Request, v interface{}) (error) {
all, err := ioutil.ReadAll(hex.NewDecoder(r.Body))
if err != nil {
return err
}
encPasswordAndIV := all[:256]
encBody := all[256:]
passwordAndIV, err := rsa.DecryptPKCS1v15(rand.Reader, it.manager.privateKey, encPasswordAndIV)
if err != nil {
return err
}
password := passwordAndIV[:32]
iv := passwordAndIV[32:]
cipher, err := blowfish.NewSaltedCipher(password, iv)
if err != nil {
return err
}
body := make([]byte, len(encBody))
cipher.Decrypt(body, encBody)
err = json.Unmarshal(body, v)
if err != nil {
return err
}
baseReq, ok := v.(*BaseMessage)
if !ok {
return errors.New("non-base request")
}
signature, err := hex.DecodeString(baseReq.Signature)
if err != nil {
return err
}
fingerprint := it.manager.GetServerFingerprint()
fingerprintHashed := sha256.Sum256(fingerprint)
err = rsa.VerifyPKCS1v15(it.manager.CAPublicKey(), 0, fingerprintHashed[:], signature)
return err
}