github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/qln/remotecontrol.go (about) 1 package qln 2 3 import ( 4 "bytes" 5 "encoding/binary" 6 "encoding/json" 7 "fmt" 8 "reflect" 9 "strings" 10 11 "github.com/boltdb/bolt" 12 "github.com/mit-dci/lit/btcutil" 13 "github.com/mit-dci/lit/crypto/fastsha256" 14 "github.com/mit-dci/lit/crypto/koblitz" 15 "github.com/mit-dci/lit/lnutil" 16 "github.com/mit-dci/lit/logging" 17 "github.com/mit-dci/lit/sig64" 18 ) 19 20 // RCRequestAuthArgs contains the required parameters 21 // to request authorization for remote control. Only 22 // the pub key is necessary 23 type RCRequestAuthArgs struct { 24 // Public Key to authorize 25 PubKey [33]byte 26 } 27 28 // RemoteControlRequestHandler handles an incoming remote control request 29 func (nd *LitNode) RemoteControlRequestHandler(msg lnutil.RemoteControlRpcRequestMsg, peer *RemotePeer) error { 30 var pubKey [33]byte 31 // transportAuthenticated will store whether the request authorization 32 // should be checked against the transport's public key, or the one 33 // that's included in the message. 34 transportAuthenticated := true 35 copy(pubKey[:], peer.Con.RemotePub().SerializeCompressed()) 36 37 if msg.PubKey != [33]byte{} { 38 // The message contains a pubkey. So use that to authorize. We also 39 // need to verify the signature inside the message then. If the 40 // transport authorization is used, we can skip that because the 41 // transport is already secured using signatures. 42 pubKey = msg.PubKey 43 transportAuthenticated = false 44 } 45 logging.Infof("Received remote control request [%s] from [%x]\nArguments Passed: %s", msg.Method, pubKey, string(msg.Args)) 46 47 // Fetch the remote control authorization based on the used public key 48 auth, err := nd.GetRemoteControlAuthorization(pubKey) 49 if err != nil { 50 logging.Errorf("Error while checking authorization for remote control: %s", err.Error()) 51 return err 52 } 53 54 // Whitelisted method(s) - Methods that don't require authorization. 55 whitelisted := false 56 if msg.Method == "LitRPC.RequestRemoteControlAuthorization" { 57 // Request for authorization. You do not need to be authorized for this 58 // All this method does is include the remote caller's public key into 59 // the remote control authorization database as "requested authorization" 60 // It will remain unauthorized until approved. 61 whitelisted = true 62 args := new(RCRequestAuthArgs) 63 args.PubKey = pubKey 64 msg.Args, err = json.Marshal(args) 65 if err != nil { 66 logging.Errorf("Error while updating RequestRemoteControlAuthorization arguments: %s", err.Error()) 67 return err 68 } 69 } 70 71 // Method to check if the remote caller is authorized. This is not actually 72 // part of the RPC surface, but only for the remote control. Hence it being 73 // in a different namespace and returning a result from this method directly 74 // without calling RPC methods. 75 if msg.Method == "RemoteControl.CheckAuthorizationStatus" { 76 resp, err := json.Marshal(map[string]interface{}{"Authorized": auth.Allowed}) 77 if err != nil { 78 return err 79 } 80 outMsg := lnutil.NewRemoteControlRpcResponseMsg(peer.Idx, msg.Idx, false, resp) 81 nd.tmpSendLitMsg(outMsg) 82 return nil 83 } 84 85 // If i'm not authorized, and it's not a whitelisted method then we fail the 86 // request with an 'unauthorized' error 87 if !auth.Allowed && !whitelisted { 88 err = fmt.Errorf("Received remote control request from unauthorized peer: %x", pubKey) 89 logging.Errorf(err.Error()) 90 91 outMsg := lnutil.NewRemoteControlRpcResponseMsg(peer.Idx, msg.Idx, true, []byte("Unauthorized")) 92 nd.tmpSendLitMsg(outMsg) 93 94 return err 95 } 96 97 // If the transport is not authenticated, and we're using the key provided 98 // in the message, then a signature is required to verify the caller actually 99 // controls that given key. 100 if !transportAuthenticated { 101 msgSig := msg.Sig 102 103 // Make the signature empty. The caller signed the message byte slice 104 // with an empty signature. 105 msg.Sig = [64]byte{} 106 107 var digest []byte 108 if msg.DigestType == lnutil.DIGEST_TYPE_SHA256 { 109 hash := fastsha256.Sum256(msg.Bytes()) 110 digest = make([]byte, len(hash)) 111 copy(digest[:], hash[:]) 112 } else if msg.DigestType == lnutil.DIGEST_TYPE_RIPEMD160 { 113 hash := btcutil.Hash160(msg.Bytes()) 114 digest = make([]byte, len(hash)) 115 copy(digest[:], hash[:]) 116 } 117 118 pub, err := koblitz.ParsePubKey(msg.PubKey[:], koblitz.S256()) 119 if err != nil { 120 logging.Errorf("Error parsing public key for remote control: %s", err.Error()) 121 return err 122 } 123 sig := sig64.SigDecompress(msgSig) 124 signature, err := koblitz.ParseDERSignature(sig, koblitz.S256()) 125 if err != nil { 126 logging.Errorf("Error parsing signature for remote control: %s", err.Error()) 127 return err 128 } 129 130 if !signature.Verify(digest, pub) { 131 err = fmt.Errorf("Signature verification failed in remote control request") 132 logging.Errorf(err.Error()) 133 return err 134 } 135 } 136 137 // Check if the arguments property is valid JSON by deserializing it 138 obj := map[string]interface{}{} 139 err = json.Unmarshal(msg.Args, &obj) 140 if err != nil { 141 logging.Errorf("Could not parse JSON: %s", err.Error()) 142 return err 143 } 144 145 // Handle the request in a goroutine 146 go func() { 147 // Sanity check for the method 148 if !strings.HasPrefix(msg.Method, "LitRPC.") { 149 logging.Warnf("Remote control method does not start with `LitRPC.`. We don't know any better. Yet.") 150 return 151 } 152 153 // Use reflection to call the method on the RPC object 154 methodName := strings.TrimPrefix(msg.Method, "LitRPC.") 155 rpcType := reflect.ValueOf(nd.RPC) 156 if rpcType.IsValid() { 157 method := rpcType.MethodByName(methodName) 158 if method.IsValid() { 159 160 // Our RPC calls always have params as (args, reply) 161 argsType := method.Type().In(0) 162 argsPointer := false 163 164 // Unfortunately, sometimes the arguments to the function need 165 // a pointer, and sometimes a value. So we check and adjust. 166 if argsType.Kind() == reflect.Ptr { 167 argsPointer = true 168 argsType = argsType.Elem() 169 } 170 171 argsPayload := reflect.New(argsType) 172 173 // Unmarshal the JSON into the args object 174 err = json.Unmarshal(msg.Args, argsPayload.Interface()) 175 if err != nil { 176 logging.Errorf("Error parsing json argument: %s", err.Error()) 177 return 178 } 179 180 replyType := method.Type().In(1).Elem() 181 replyPayload := reflect.New(replyType) 182 183 if !argsPointer { 184 argsPayload = argsPayload.Elem() 185 } 186 result := method.Call([]reflect.Value{argsPayload, replyPayload}) 187 188 var reply []byte 189 replyIsError := false 190 if !result[0].IsNil() { 191 replyIsError = true 192 err = result[0].Interface().(error) 193 reply = []byte(err.Error()) 194 } else { 195 reply, err = json.Marshal(replyPayload.Interface()) 196 if err != nil { 197 replyIsError = true 198 reply = []byte(err.Error()) 199 } 200 } 201 202 outMsg := lnutil.NewRemoteControlRpcResponseMsg(peer.Idx, msg.Idx, replyIsError, reply) 203 nd.tmpSendLitMsg(outMsg) 204 } 205 } 206 }() 207 return nil 208 } 209 210 // RemoteControlResponseHandler handles the remote control response messages. 211 // At this time, this is not used in lit itself. Remote control messages are 212 // sent from the LndcRpcClient and responses are handled there. In normal operation 213 // two regular lit nodes do not talk to each other using remote control. But 214 // just in case someone sends us one, we print it out here. 215 func (nd *LitNode) RemoteControlResponseHandler(msg lnutil.RemoteControlRpcResponseMsg, peer *RemotePeer) error { 216 logging.Debugf("Received remote control reply from peer %d:\n%s", peer.Idx, string(msg.Result)) 217 return nil 218 } 219 220 // For now, this is simple: Allowed yes or no 221 // In the future allow more finegrained control 222 // over which RPCs are allowed and which are not, 223 // and perhaps authorize up to a certain amount for 224 // commands like send / push 225 type RemoteControlAuthorization struct { 226 PubKey [33]byte 227 Allowed bool 228 UnansweredRequest bool 229 } 230 231 // Bytes serializes a remotecontrol authorization into a byteslice 232 func (r *RemoteControlAuthorization) Bytes() []byte { 233 var buf bytes.Buffer 234 235 binary.Write(&buf, binary.BigEndian, r.Allowed) 236 binary.Write(&buf, binary.BigEndian, r.UnansweredRequest) 237 return buf.Bytes() 238 } 239 240 // RemoteControlAuthorizationFromBytes parses a byteslice into a 241 // RemoteControlAuthorization object 242 func RemoteControlAuthorizationFromBytes(b []byte, pubKey [33]byte) *RemoteControlAuthorization { 243 r := new(RemoteControlAuthorization) 244 buf := bytes.NewBuffer(b) 245 binary.Read(buf, binary.BigEndian, &r.Allowed) 246 binary.Read(buf, binary.BigEndian, &r.UnansweredRequest) 247 r.PubKey = pubKey 248 return r 249 } 250 251 // SaveRemoteControlAuthorization saves the authorization for a specific 252 // pubkey into the database. 253 func (nd *LitNode) SaveRemoteControlAuthorization(pub [33]byte, auth *RemoteControlAuthorization) error { 254 return nd.LitDB.Update(func(btx *bolt.Tx) error { 255 cbk := btx.Bucket(BKTRCAuth) 256 // serialize state 257 b := auth.Bytes() 258 return cbk.Put(pub[:], b) 259 }) 260 } 261 262 // GetRemoteControlAuthorization retrieves the remote controlauthorizzation for 263 // a specific pubkey from the database. 264 func (nd *LitNode) GetRemoteControlAuthorization(pub [33]byte) (*RemoteControlAuthorization, error) { 265 r := new(RemoteControlAuthorization) 266 267 // If the client uses our default remote control key (derived from our root priv) 268 // then it has access to our private key (file) and is most likely running from our 269 // localhost. So we always accept this. 270 if bytes.Equal(pub[:], nd.DefaultRemoteControlKey.SerializeCompressed()) { 271 r.Allowed = true 272 return r, nil 273 } 274 275 // Fetch the authorization from the database and return it. 276 err := nd.LitDB.View(func(btx *bolt.Tx) error { 277 cbk := btx.Bucket(BKTRCAuth) 278 // serialize state 279 b := cbk.Get(pub[:]) 280 r = RemoteControlAuthorizationFromBytes(b, pub) 281 return nil 282 }) 283 return r, err 284 } 285 286 // GetPendingRemoteControlRequests retrieves all pending remote control 287 // authorization requests, so that a GUI can print them out for the user to 288 // authorize or not. 289 func (nd *LitNode) GetPendingRemoteControlRequests() ([]*RemoteControlAuthorization, error) { 290 r := make([]*RemoteControlAuthorization, 0) 291 err := nd.LitDB.View(func(btx *bolt.Tx) error { 292 cbk := btx.Bucket(BKTRCAuth) 293 // serialize state 294 err := cbk.ForEach(func(k, v []byte) error { 295 logging.Debugf("%x : %s\n", k, v) 296 if len(v) >= 2 { 297 if v[1] != 0x00 { 298 var pubKey [33]byte 299 copy(pubKey[:], k) 300 r = append(r, RemoteControlAuthorizationFromBytes(v, pubKey)) 301 } 302 } 303 return nil 304 }) 305 return err 306 }) 307 return r, err 308 }