@@ -2,17 +2,26 @@ package securitypolicy
22
33import (
44 "context"
5+ "crypto/sha256"
56 "encoding/base64"
67 "encoding/json"
78 "fmt"
9+ "os"
10+ "path/filepath"
811 "regexp"
912 "strconv"
1013 "sync"
1114 "syscall"
15+ "time"
1216
17+ "github.com/Microsoft/cosesign1go/pkg/cosesign1"
18+ didx509resolver "github.com/Microsoft/didx509go/pkg/did-x509-resolver"
19+ "github.com/Microsoft/hcsshim/internal/log"
1320 "github.com/Microsoft/hcsshim/internal/protocol/guestrequest"
21+ "github.com/Microsoft/hcsshim/internal/protocol/guestresource"
1422 oci "github.com/opencontainers/runtime-spec/specs-go"
1523 "github.com/pkg/errors"
24+ "github.com/sirupsen/logrus"
1625)
1726
1827type createEnforcerFunc func (base64EncodedPolicy string , criMounts , criPrivilegedMounts []oci.Mount , maxErrorMessageLength int ) (SecurityPolicyEnforcer , error )
@@ -124,7 +133,7 @@ type SecurityPolicyEnforcer interface {
124133 EnforceGetPropertiesPolicy (ctx context.Context ) error
125134 EnforceDumpStacksPolicy (ctx context.Context ) error
126135 EnforceRuntimeLoggingPolicy (ctx context.Context ) (err error )
127- LoadFragment (ctx context.Context , issuer string , feed string , code string ) error
136+ LoadFragment (ctx context.Context , issuer string , feed string , rego string ) error
128137 EnforceScratchMountPolicy (ctx context.Context , scratchPath string , encrypted bool ) (err error )
129138 EnforceScratchUnmountPolicy (ctx context.Context , scratchPath string ) (err error )
130139 GetUserInfo (spec * oci.Process , rootPath string ) (IDName , []IDName , string , error )
@@ -245,6 +254,69 @@ func createStandardEnforcer(
245254 return enforcer , nil
246255}
247256
257+ // Fragment extends current security policy with additional constraints
258+ // from the incoming fragment. Note that it is base64 encoded over the bridge/
259+ //
260+ // There are three checking steps:
261+ // 1 - Unpack the cose document and check it was actually signed with the cert
262+ // chain inside its header
263+ // 2 - Check that the issuer field did:x509 identifier is for that cert chain
264+ // (ie fingerprint of a non leaf cert and the subject matches the leaf cert)
265+ // 3 - Check that this issuer/feed match the requirement of the user provided
266+ // security policy (done in the regoby LoadFragment)
267+ func ExtractAndVerifyFragment (ctx context.Context , fragment * guestresource.LCOWSecurityPolicyFragment ) (issuer string , feed string , payloadString string , err error ) {
268+ log .G (ctx ).WithField ("fragment" , fmt .Sprintf ("%+v" , fragment )).Debug ("VerifyAndExtractFragment" )
269+
270+ raw , err := base64 .StdEncoding .DecodeString (fragment .Fragment )
271+ if err != nil {
272+ return "" , "" , "" , fmt .Errorf ("failed to decode fragment: %w" , err )
273+ }
274+ blob := []byte (fragment .Fragment )
275+ // keep a copy of the fragment, so we can manually figure out what went wrong
276+ // will be removed eventually. Give it a unique name to avoid any potential
277+ // race conditions.
278+ sha := sha256 .New ()
279+ sha .Write (blob )
280+ timestamp := time .Now ()
281+ fragmentPath := fmt .Sprintf ("fragment-%x-%d.blob" , sha .Sum (nil ), timestamp .UnixMilli ())
282+ _ = os .WriteFile (filepath .Join (os .TempDir (), fragmentPath ), blob , 0644 )
283+
284+ unpacked , err := cosesign1 .UnpackAndValidateCOSE1CertChain (raw )
285+ if err != nil {
286+ return "" , "" , "" , fmt .Errorf ("InjectFragment failed COSE validation: %w" , err )
287+ }
288+
289+ payloadString = string (unpacked .Payload [:])
290+ issuer = unpacked .Issuer
291+ feed = unpacked .Feed
292+ chainPem := unpacked .ChainPem
293+
294+ log .G (ctx ).WithFields (logrus.Fields {
295+ "issuer" : issuer , // eg the DID:x509:blah....
296+ "feed" : feed ,
297+ "cty" : unpacked .ContentType ,
298+ "chainPem" : chainPem ,
299+ }).Debugf ("unpacked COSE1 cert chain" )
300+
301+ log .G (ctx ).WithFields (logrus.Fields {
302+ "payload" : payloadString ,
303+ }).Tracef ("unpacked COSE1 payload" )
304+
305+ if len (issuer ) == 0 || len (feed ) == 0 { // must both be present
306+ return "" , "" , "" , fmt .Errorf ("either issuer and feed must both be provided in the COSE_Sign1 protected header" )
307+ }
308+
309+ // Resolve returns a did doc that we don't need
310+ // we only care if there was an error or not
311+ _ , err = didx509resolver .Resolve (unpacked .ChainPem , issuer , true )
312+ if err != nil {
313+ log .G (ctx ).Printf ("Badly formed fragment - did resolver failed to match fragment did:x509 from chain with purported issuer %s, feed %s - err %s" , issuer , feed , err .Error ())
314+ return "" , "" , "" , err
315+ }
316+
317+ return issuer , feed , payloadString , nil
318+ }
319+
248320// CreateSecurityPolicyEnforcer returns an appropriate enforcer for input parameters.
249321// When `enforcer` isn't return either an AllowAll or default enforcer.
250322// Returns an error if the requested `enforcer` implementation isn't registered.
@@ -649,7 +721,7 @@ func (*StandardSecurityPolicyEnforcer) EnforceRuntimeLoggingPolicy(context.Conte
649721
650722// Stub. We are deprecating the standard enforcer. Newly added enforcement
651723// points are simply allowed.
652- func (* StandardSecurityPolicyEnforcer ) LoadFragment (context.Context , string , string , string ) error {
724+ func (* StandardSecurityPolicyEnforcer ) LoadFragment (ctx context.Context , issuer string , feed string , rego string ) error {
653725 return nil
654726}
655727
@@ -1039,7 +1111,7 @@ func (OpenDoorSecurityPolicyEnforcer) EnforceDumpStacksPolicy(context.Context) e
10391111 return nil
10401112}
10411113
1042- func (OpenDoorSecurityPolicyEnforcer ) LoadFragment (context.Context , string , string , string ) error {
1114+ func (OpenDoorSecurityPolicyEnforcer ) LoadFragment (ctx context.Context , issuer string , feed string , rego string ) error {
10431115 return nil
10441116}
10451117
@@ -1158,7 +1230,7 @@ func (ClosedDoorSecurityPolicyEnforcer) EnforceDumpStacksPolicy(context.Context)
11581230 return errors .New ("getting stack dumps is denied by policy" )
11591231}
11601232
1161- func (ClosedDoorSecurityPolicyEnforcer ) LoadFragment (context.Context , string , string , string ) error {
1233+ func (ClosedDoorSecurityPolicyEnforcer ) LoadFragment (ctx context.Context , issuer string , feed string , rego string ) error {
11621234 return errors .New ("loading fragments is denied by policy" )
11631235}
11641236
0 commit comments