From 7b822f41df9c0bf215917151df819475576e3d87 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Thu, 12 Aug 2021 16:07:19 +0200 Subject: [PATCH] lncli: add custom caveats to bakemacaroon With the new condition and checker in place, we can give the end user the ability to add such a custom caveat to a baked macaroon. There won't be an RPC counterpart for this operation since all first party caveats currently are only added on the client side. --- cmd/lncli/cmd_macaroon.go | 57 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/cmd/lncli/cmd_macaroon.go b/cmd/lncli/cmd_macaroon.go index 01fee4558..a9fa7aa83 100644 --- a/cmd/lncli/cmd_macaroon.go +++ b/cmd/lncli/cmd_macaroon.go @@ -8,6 +8,7 @@ import ( "net" "strconv" "strings" + "unicode" "github.com/golang/protobuf/proto" "github.com/lightningnetwork/lnd/lncfg" @@ -65,6 +66,16 @@ var bakeMacaroonCommand = cli.Command{ Name: "ip_address", Usage: "the IP address the macaroon will be bound to", }, + cli.StringFlag{ + Name: "custom_caveat_name", + Usage: "the name of the custom caveat to add", + }, + cli.StringFlag{ + Name: "custom_caveat_condition", + Usage: "the condition of the custom caveat to add, " + + "can be empty if custom caveat doesn't need " + + "a value", + }, cli.Uint64Flag{ Name: "root_key_id", Usage: "the numerical root key ID used to create the macaroon", @@ -92,6 +103,8 @@ func bakeMacaroon(ctx *cli.Context) error { savePath string timeout int64 ipAddress net.IP + customCaveatName string + customCaveatCond string rootKeyID uint64 parsedPermissions []*lnrpc.MacaroonPermission err error @@ -116,6 +129,32 @@ func bakeMacaroon(ctx *cli.Context) error { } } + if ctx.IsSet("custom_caveat_name") { + customCaveatName = ctx.String("custom_caveat_name") + if containsWhiteSpace(customCaveatName) { + return fmt.Errorf("unexpected white space found in " + + "custom caveat name") + } + if customCaveatName == "" { + return fmt.Errorf("invalid custom caveat name") + } + } + + if ctx.IsSet("custom_caveat_condition") { + customCaveatCond = ctx.String("custom_caveat_condition") + if containsWhiteSpace(customCaveatCond) { + return fmt.Errorf("unexpected white space found in " + + "custom caveat condition") + } + if customCaveatCond == "" { + return fmt.Errorf("invalid custom caveat condition") + } + if customCaveatCond != "" && customCaveatName == "" { + return fmt.Errorf("cannot set custom caveat " + + "condition without custom caveat name") + } + } + if ctx.IsSet("root_key_id") { rootKeyID = ctx.Uint64("root_key_id") } @@ -186,6 +225,17 @@ func bakeMacaroon(ctx *cli.Context) error { macaroons.IPLockConstraint(ipAddress.String()), ) } + + // The custom caveat condition is optional, it could just be a marker + // tag in the macaroon with just a name. The interceptor itself doesn't + // care about the value anyway. + if customCaveatName != "" { + macConstraints = append( + macConstraints, macaroons.CustomConstraint( + customCaveatName, customCaveatCond, + ), + ) + } constrainedMac, err := macaroons.AddConstraints( unmarshalMac, macConstraints..., ) @@ -419,3 +469,10 @@ func printMacaroon(ctx *cli.Context) error { return nil } + +// containsWhiteSpace returns true if the given string contains any character +// that is considered to be a white space or non-printable character such as +// space, tabulator, newline, carriage return and some more exotic ones. +func containsWhiteSpace(str string) bool { + return strings.IndexFunc(str, unicode.IsSpace) >= 0 +}