| 1 | package packagegrant |
| 2 | |
| 3 | import ( |
| 4 | "encoding/binary" |
| 5 | "errors" |
| 6 | ) |
| 7 | |
| 8 | // Report is what one pass changed, what it found and could not change, and |
| 9 | // what it could not read. An unread object is not known to carry a grant. |
| 10 | type Report struct { |
| 11 | Stripped []string `json:"stripped"` |
| 12 | Refused []Failure `json:"refused"` |
| 13 | Unread []Failure `json:"unread"` |
| 14 | } |
| 15 | |
| 16 | // Failure is one object the pass could not finish, and why. |
| 17 | type Failure struct { |
| 18 | Path string `json:"path"` |
| 19 | Err error `json:"-"` |
| 20 | } |
| 21 | |
| 22 | // ErrMalformed marks an ACL whose sizes do not account for its bytes. Nothing is |
| 23 | // written back from one. |
| 24 | var ErrMalformed = errors.New("packagegrant: malformed ACL") |
| 25 | |
| 26 | const ( |
| 27 | aclHeaderSize = 8 |
| 28 | aceHeaderSize = 4 |
| 29 | aceMaskSize = 4 |
| 30 | |
| 31 | aceTypeAccessAllowed = 0x0 |
| 32 | aceTypeAccessAllowedCallback = 0x9 |
| 33 | aceFlagInherited = 0x10 |
| 34 | |
| 35 | sidMinSize = 8 |
| 36 | appPackageAuthority = 15 |
| 37 | appPackageBaseRID = 2 |
| 38 | // SECURITY_BUILTIN_APP_PACKAGE_RID_COUNT: the groups every package belongs to. |
| 39 | builtinPackageRIDCount = 2 |
| 40 | ) |
| 41 | |
| 42 | // rewrite returns acl without the allow entries that name a specific package. |
| 43 | // changed is false when there were none. protect says the result must be |
| 44 | // written as a protected DACL: a removed entry was inherited, so the object |
| 45 | // stops inheriting and keeps the rest of what it had, now as its own entries. |
| 46 | func rewrite(acl []byte, wasProtected bool) (out []byte, protect, changed bool, err error) { |
| 47 | if len(acl) < aclHeaderSize { |
| 48 | return nil, false, false, ErrMalformed |
| 49 | } |
| 50 | size := int(binary.LittleEndian.Uint16(acl[2:4])) |
| 51 | count := int(binary.LittleEndian.Uint16(acl[4:6])) |
| 52 | if size < aclHeaderSize || size > len(acl) { |
| 53 | return nil, false, false, ErrMalformed |
| 54 | } |
| 55 | var kept [][]byte |
| 56 | inheritedRemoved := false |
| 57 | offset := aclHeaderSize |
| 58 | for range count { |
| 59 | if offset+aceHeaderSize > size { |
| 60 | return nil, false, false, ErrMalformed |
| 61 | } |
| 62 | aceSize := int(binary.LittleEndian.Uint16(acl[offset+2 : offset+4])) |
| 63 | if aceSize < aceHeaderSize || offset+aceSize > size { |
| 64 | return nil, false, false, ErrMalformed |
| 65 | } |
| 66 | ace := acl[offset : offset+aceSize] |
| 67 | offset += aceSize |
| 68 | if grantsSpecificPackage(ace) { |
| 69 | changed = true |
| 70 | inheritedRemoved = inheritedRemoved || ace[1]&aceFlagInherited != 0 |
| 71 | continue |
| 72 | } |
| 73 | kept = append(kept, ace) |
| 74 | } |
| 75 | if !changed { |
| 76 | return nil, false, false, nil |
| 77 | } |
| 78 | total := aclHeaderSize |
| 79 | for _, ace := range kept { |
| 80 | total += len(ace) |
| 81 | } |
| 82 | out = make([]byte, aclHeaderSize, total) |
| 83 | out[0] = acl[0] |
| 84 | binary.LittleEndian.PutUint16(out[2:4], uint16(total)) |
| 85 | binary.LittleEndian.PutUint16(out[4:6], uint16(len(kept))) |
| 86 | for _, ace := range kept { |
| 87 | start := len(out) |
| 88 | out = append(out, ace...) |
| 89 | if inheritedRemoved { |
| 90 | out[start+1] &^= aceFlagInherited |
| 91 | } |
| 92 | } |
| 93 | return out, wasProtected || inheritedRemoved, true, nil |
| 94 | } |
| 95 | |
| 96 | // grantsSpecificPackage reports whether ace allows access to one app package |
| 97 | // rather than to a built-in package group. Entry types whose SID does not sit |
| 98 | // right after the mask are never matched, so they are always kept. |
| 99 | func grantsSpecificPackage(ace []byte) bool { |
| 100 | if ace[0] != aceTypeAccessAllowed && ace[0] != aceTypeAccessAllowedCallback { |
| 101 | return false |
| 102 | } |
| 103 | sid := ace[aceHeaderSize+aceMaskSize:] |
| 104 | if len(sid) < sidMinSize { |
| 105 | return false |
| 106 | } |
| 107 | subCount := int(sid[1]) |
| 108 | if len(sid) < sidMinSize+4*subCount || subCount <= builtinPackageRIDCount { |
| 109 | return false |
| 110 | } |
| 111 | authority := sid[2:8] |
| 112 | for _, b := range authority[:5] { |
| 113 | if b != 0 { |
| 114 | return false |
| 115 | } |
| 116 | } |
| 117 | return authority[5] == appPackageAuthority && binary.LittleEndian.Uint32(sid[8:12]) == appPackageBaseRID |
| 118 | } |
| 119 |