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  }