github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/protocol/v1/security_request.go (about) 1 // Copyright (c) 2017-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package v1 6 7 import ( 8 "encoding/base64" 9 "encoding/json" 10 "fmt" 11 "strings" 12 "sync" 13 14 "github.com/choria-io/go-choria/inter" 15 "github.com/choria-io/go-choria/protocol" 16 log "github.com/sirupsen/logrus" 17 ) 18 19 // SecureRequest contains 1 serialized Request signed and with the public cert attached 20 type SecureRequest struct { 21 Protocol protocol.ProtocolVersion `json:"protocol"` 22 MessageBody string `json:"message"` 23 Signature string `json:"signature"` 24 PublicCertificate string `json:"pubcert"` 25 26 security inter.SecurityProvider 27 mu sync.Mutex 28 } 29 30 // SetMessage sets the message contained in the Request and updates the signature 31 func (r *SecureRequest) SetMessage(request protocol.Request) (err error) { 32 r.mu.Lock() 33 defer r.mu.Unlock() 34 35 j, err := request.JSON() 36 if err != nil { 37 protocolErrorCtr.Inc() 38 return fmt.Errorf("could not JSON encode reply message to store it in the Secure Request: %s", err) 39 } 40 41 r.Signature = "insecure" 42 43 if protocol.IsSecure() && !protocol.IsRemoteSignerAgent(request.Agent()) { 44 var signature []byte 45 46 signature, err = r.security.SignBytes(j) 47 if err != nil { 48 // registration when doing anon tls might not have a certificate - so we allow that to go unsigned 49 if !protocol.IsRegistrationAgent(request.Agent()) { 50 return fmt.Errorf("could not sign message string: %s", err) 51 } 52 signature = []byte("insecure registration") 53 } 54 55 r.Signature = base64.StdEncoding.EncodeToString(signature) 56 } 57 58 r.MessageBody = string(j) 59 60 return nil 61 } 62 63 // Message retrieves the stored message. It will be a JSON encoded version of the request set via SetMessage 64 func (r *SecureRequest) Message() []byte { 65 r.mu.Lock() 66 defer r.mu.Unlock() 67 68 return []byte(r.MessageBody) 69 } 70 71 // Valid determines if the request is valid 72 func (r *SecureRequest) Valid() bool { 73 // should not be locked 74 75 if !protocol.IsSecure() { 76 log.Debug("Bypassing validation on secure request due to build time flags") 77 return true 78 } 79 80 req, err := NewRequestFromSecureRequest(r) 81 if err != nil { 82 log.Errorf("Could not create Request to validate Secure Request with: %s", err) 83 protocolErrorCtr.Inc() 84 return false 85 } 86 87 certname, err := r.security.CallerIdentity(req.CallerID()) 88 if err != nil { 89 log.Errorf("Could not extract certname from caller: %s", err) 90 protocolErrorCtr.Inc() 91 return false 92 } 93 94 _, err = r.security.ShouldAllowCaller(certname, []byte(r.PublicCertificate)) 95 if err != nil { 96 log.Errorf("Client Certificate verification failed: %s", err) 97 protocolErrorCtr.Inc() 98 return false 99 } 100 101 sig, err := base64.StdEncoding.DecodeString(r.Signature) 102 if err != nil { 103 log.Errorf("Could not bas64 decode signature: %s", err) 104 protocolErrorCtr.Inc() 105 return false 106 } 107 108 should, _ := r.security.VerifySignatureBytes([]byte(r.MessageBody), sig, []byte(r.PublicCertificate)) 109 if !should { 110 log.Errorf("Signature in request did not pass validation with embedded public certificate") 111 invalidCtr.Inc() 112 return false 113 } 114 115 validCtr.Inc() 116 117 return true 118 } 119 120 // JSON creates a JSON encoded request 121 func (r *SecureRequest) JSON() ([]byte, error) { 122 r.mu.Lock() 123 j, err := json.Marshal(r) 124 r.mu.Unlock() 125 if err != nil { 126 protocolErrorCtr.Inc() 127 return nil, fmt.Errorf("could not JSON Marshal: %s", err) 128 } 129 130 if err = r.IsValidJSON(j); err != nil { 131 return nil, fmt.Errorf("the JSON produced from the SecureRequest does not pass validation: %s", err) 132 } 133 134 return j, nil 135 } 136 137 // Version retrieves the protocol version for this message 138 func (r *SecureRequest) Version() protocol.ProtocolVersion { 139 r.mu.Lock() 140 defer r.mu.Unlock() 141 142 return r.Protocol 143 } 144 145 // IsValidJSON validates the given JSON data against the schema 146 func (r *SecureRequest) IsValidJSON(data []byte) (err error) { 147 _, errors, err := schemaValidate(protocol.SecureRequestV1, data) 148 if err != nil { 149 protocolErrorCtr.Inc() 150 return fmt.Errorf("could not validate SecureRequest JSON data: %s", err) 151 } 152 153 if len(errors) != 0 { 154 return fmt.Errorf("supplied JSON document is not a valid SecureRequest message: %s", strings.Join(errors, ", ")) 155 } 156 157 return nil 158 } 159 160 func (r *SecureRequest) SetSigner(_ []byte) error { 161 return nil 162 } 163 164 func (r *SecureRequest) CallerPublicData() string { 165 r.mu.Lock() 166 defer r.mu.Unlock() 167 168 return r.PublicCertificate 169 }