Docker-DCO-1.1-Signed-off-by: Andrew Page <admwiggin@gmail.com> (github: tianon)
| ... | ... |
@@ -27,10 +27,10 @@ import ( |
| 27 | 27 |
"github.com/docker/docker/pkg/listenbuffer" |
| 28 | 28 |
"github.com/docker/docker/pkg/parsers" |
| 29 | 29 |
"github.com/docker/docker/pkg/systemd" |
| 30 |
- "github.com/docker/docker/pkg/user" |
|
| 31 | 30 |
"github.com/docker/docker/pkg/version" |
| 32 | 31 |
"github.com/docker/docker/registry" |
| 33 | 32 |
"github.com/docker/docker/utils" |
| 33 |
+ "github.com/docker/libcontainer/user" |
|
| 34 | 34 |
"github.com/gorilla/mux" |
| 35 | 35 |
) |
| 36 | 36 |
|
| 2 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,256 +0,0 @@ |
| 1 |
-package user |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "bufio" |
|
| 5 |
- "fmt" |
|
| 6 |
- "io" |
|
| 7 |
- "os" |
|
| 8 |
- "strconv" |
|
| 9 |
- "strings" |
|
| 10 |
-) |
|
| 11 |
- |
|
| 12 |
-const ( |
|
| 13 |
- minId = 0 |
|
| 14 |
- maxId = 1<<31 - 1 //for 32-bit systems compatibility |
|
| 15 |
-) |
|
| 16 |
- |
|
| 17 |
-var ( |
|
| 18 |
- ErrRange = fmt.Errorf("Uids and gids must be in range %d-%d", minId, maxId)
|
|
| 19 |
-) |
|
| 20 |
- |
|
| 21 |
-type User struct {
|
|
| 22 |
- Name string |
|
| 23 |
- Pass string |
|
| 24 |
- Uid int |
|
| 25 |
- Gid int |
|
| 26 |
- Gecos string |
|
| 27 |
- Home string |
|
| 28 |
- Shell string |
|
| 29 |
-} |
|
| 30 |
- |
|
| 31 |
-type Group struct {
|
|
| 32 |
- Name string |
|
| 33 |
- Pass string |
|
| 34 |
- Gid int |
|
| 35 |
- List []string |
|
| 36 |
-} |
|
| 37 |
- |
|
| 38 |
-func parseLine(line string, v ...interface{}) {
|
|
| 39 |
- if line == "" {
|
|
| 40 |
- return |
|
| 41 |
- } |
|
| 42 |
- |
|
| 43 |
- parts := strings.Split(line, ":") |
|
| 44 |
- for i, p := range parts {
|
|
| 45 |
- if len(v) <= i {
|
|
| 46 |
- // if we have more "parts" than we have places to put them, bail for great "tolerance" of naughty configuration files |
|
| 47 |
- break |
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- switch e := v[i].(type) {
|
|
| 51 |
- case *string: |
|
| 52 |
- // "root", "adm", "/bin/bash" |
|
| 53 |
- *e = p |
|
| 54 |
- case *int: |
|
| 55 |
- // "0", "4", "1000" |
|
| 56 |
- // ignore string to int conversion errors, for great "tolerance" of naughty configuration files |
|
| 57 |
- *e, _ = strconv.Atoi(p) |
|
| 58 |
- case *[]string: |
|
| 59 |
- // "", "root", "root,adm,daemon" |
|
| 60 |
- if p != "" {
|
|
| 61 |
- *e = strings.Split(p, ",") |
|
| 62 |
- } else {
|
|
| 63 |
- *e = []string{}
|
|
| 64 |
- } |
|
| 65 |
- default: |
|
| 66 |
- // panic, because this is a programming/logic error, not a runtime one |
|
| 67 |
- panic("parseLine expects only pointers! argument " + strconv.Itoa(i) + " is not a pointer!")
|
|
| 68 |
- } |
|
| 69 |
- } |
|
| 70 |
-} |
|
| 71 |
- |
|
| 72 |
-func ParsePasswd() ([]*User, error) {
|
|
| 73 |
- return ParsePasswdFilter(nil) |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-func ParsePasswdFilter(filter func(*User) bool) ([]*User, error) {
|
|
| 77 |
- f, err := os.Open("/etc/passwd")
|
|
| 78 |
- if err != nil {
|
|
| 79 |
- return nil, err |
|
| 80 |
- } |
|
| 81 |
- defer f.Close() |
|
| 82 |
- return parsePasswdFile(f, filter) |
|
| 83 |
-} |
|
| 84 |
- |
|
| 85 |
-func parsePasswdFile(r io.Reader, filter func(*User) bool) ([]*User, error) {
|
|
| 86 |
- var ( |
|
| 87 |
- s = bufio.NewScanner(r) |
|
| 88 |
- out = []*User{}
|
|
| 89 |
- ) |
|
| 90 |
- |
|
| 91 |
- for s.Scan() {
|
|
| 92 |
- if err := s.Err(); err != nil {
|
|
| 93 |
- return nil, err |
|
| 94 |
- } |
|
| 95 |
- |
|
| 96 |
- text := strings.TrimSpace(s.Text()) |
|
| 97 |
- if text == "" {
|
|
| 98 |
- continue |
|
| 99 |
- } |
|
| 100 |
- |
|
| 101 |
- // see: man 5 passwd |
|
| 102 |
- // name:password:UID:GID:GECOS:directory:shell |
|
| 103 |
- // Name:Pass:Uid:Gid:Gecos:Home:Shell |
|
| 104 |
- // root:x:0:0:root:/root:/bin/bash |
|
| 105 |
- // adm:x:3:4:adm:/var/adm:/bin/false |
|
| 106 |
- p := &User{}
|
|
| 107 |
- parseLine( |
|
| 108 |
- text, |
|
| 109 |
- &p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell, |
|
| 110 |
- ) |
|
| 111 |
- |
|
| 112 |
- if filter == nil || filter(p) {
|
|
| 113 |
- out = append(out, p) |
|
| 114 |
- } |
|
| 115 |
- } |
|
| 116 |
- |
|
| 117 |
- return out, nil |
|
| 118 |
-} |
|
| 119 |
- |
|
| 120 |
-func ParseGroup() ([]*Group, error) {
|
|
| 121 |
- return ParseGroupFilter(nil) |
|
| 122 |
-} |
|
| 123 |
- |
|
| 124 |
-func ParseGroupFilter(filter func(*Group) bool) ([]*Group, error) {
|
|
| 125 |
- f, err := os.Open("/etc/group")
|
|
| 126 |
- if err != nil {
|
|
| 127 |
- return nil, err |
|
| 128 |
- } |
|
| 129 |
- defer f.Close() |
|
| 130 |
- return parseGroupFile(f, filter) |
|
| 131 |
-} |
|
| 132 |
- |
|
| 133 |
-func parseGroupFile(r io.Reader, filter func(*Group) bool) ([]*Group, error) {
|
|
| 134 |
- var ( |
|
| 135 |
- s = bufio.NewScanner(r) |
|
| 136 |
- out = []*Group{}
|
|
| 137 |
- ) |
|
| 138 |
- |
|
| 139 |
- for s.Scan() {
|
|
| 140 |
- if err := s.Err(); err != nil {
|
|
| 141 |
- return nil, err |
|
| 142 |
- } |
|
| 143 |
- |
|
| 144 |
- text := s.Text() |
|
| 145 |
- if text == "" {
|
|
| 146 |
- continue |
|
| 147 |
- } |
|
| 148 |
- |
|
| 149 |
- // see: man 5 group |
|
| 150 |
- // group_name:password:GID:user_list |
|
| 151 |
- // Name:Pass:Gid:List |
|
| 152 |
- // root:x:0:root |
|
| 153 |
- // adm:x:4:root,adm,daemon |
|
| 154 |
- p := &Group{}
|
|
| 155 |
- parseLine( |
|
| 156 |
- text, |
|
| 157 |
- &p.Name, &p.Pass, &p.Gid, &p.List, |
|
| 158 |
- ) |
|
| 159 |
- |
|
| 160 |
- if filter == nil || filter(p) {
|
|
| 161 |
- out = append(out, p) |
|
| 162 |
- } |
|
| 163 |
- } |
|
| 164 |
- |
|
| 165 |
- return out, nil |
|
| 166 |
-} |
|
| 167 |
- |
|
| 168 |
-// Given a string like "user", "1000", "user:group", "1000:1000", returns the uid, gid, and list of supplementary group IDs, if possible. |
|
| 169 |
-func GetUserGroupSupplementary(userSpec string, defaultUid int, defaultGid int) (int, int, []int, error) {
|
|
| 170 |
- var ( |
|
| 171 |
- uid = defaultUid |
|
| 172 |
- gid = defaultGid |
|
| 173 |
- suppGids = []int{}
|
|
| 174 |
- |
|
| 175 |
- userArg, groupArg string |
|
| 176 |
- ) |
|
| 177 |
- |
|
| 178 |
- // allow for userArg to have either "user" syntax, or optionally "user:group" syntax |
|
| 179 |
- parseLine(userSpec, &userArg, &groupArg) |
|
| 180 |
- |
|
| 181 |
- users, err := ParsePasswdFilter(func(u *User) bool {
|
|
| 182 |
- if userArg == "" {
|
|
| 183 |
- return u.Uid == uid |
|
| 184 |
- } |
|
| 185 |
- return u.Name == userArg || strconv.Itoa(u.Uid) == userArg |
|
| 186 |
- }) |
|
| 187 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 188 |
- if userArg == "" {
|
|
| 189 |
- userArg = strconv.Itoa(uid) |
|
| 190 |
- } |
|
| 191 |
- return 0, 0, nil, fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
|
| 192 |
- } |
|
| 193 |
- |
|
| 194 |
- haveUser := users != nil && len(users) > 0 |
|
| 195 |
- if haveUser {
|
|
| 196 |
- // if we found any user entries that matched our filter, let's take the first one as "correct" |
|
| 197 |
- uid = users[0].Uid |
|
| 198 |
- gid = users[0].Gid |
|
| 199 |
- } else if userArg != "" {
|
|
| 200 |
- // we asked for a user but didn't find them... let's check to see if we wanted a numeric user |
|
| 201 |
- uid, err = strconv.Atoi(userArg) |
|
| 202 |
- if err != nil {
|
|
| 203 |
- // not numeric - we have to bail |
|
| 204 |
- return 0, 0, nil, fmt.Errorf("Unable to find user %v", userArg)
|
|
| 205 |
- } |
|
| 206 |
- if uid < minId || uid > maxId {
|
|
| 207 |
- return 0, 0, nil, ErrRange |
|
| 208 |
- } |
|
| 209 |
- |
|
| 210 |
- // if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit |
|
| 211 |
- } |
|
| 212 |
- |
|
| 213 |
- if groupArg != "" || (haveUser && users[0].Name != "") {
|
|
| 214 |
- groups, err := ParseGroupFilter(func(g *Group) bool {
|
|
| 215 |
- if groupArg != "" {
|
|
| 216 |
- return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg |
|
| 217 |
- } |
|
| 218 |
- for _, u := range g.List {
|
|
| 219 |
- if u == users[0].Name {
|
|
| 220 |
- return true |
|
| 221 |
- } |
|
| 222 |
- } |
|
| 223 |
- return false |
|
| 224 |
- }) |
|
| 225 |
- if err != nil && !os.IsNotExist(err) {
|
|
| 226 |
- return 0, 0, nil, fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
|
| 227 |
- } |
|
| 228 |
- |
|
| 229 |
- haveGroup := groups != nil && len(groups) > 0 |
|
| 230 |
- if groupArg != "" {
|
|
| 231 |
- if haveGroup {
|
|
| 232 |
- // if we found any group entries that matched our filter, let's take the first one as "correct" |
|
| 233 |
- gid = groups[0].Gid |
|
| 234 |
- } else {
|
|
| 235 |
- // we asked for a group but didn't find id... let's check to see if we wanted a numeric group |
|
| 236 |
- gid, err = strconv.Atoi(groupArg) |
|
| 237 |
- if err != nil {
|
|
| 238 |
- // not numeric - we have to bail |
|
| 239 |
- return 0, 0, nil, fmt.Errorf("Unable to find group %v", groupArg)
|
|
| 240 |
- } |
|
| 241 |
- if gid < minId || gid > maxId {
|
|
| 242 |
- return 0, 0, nil, ErrRange |
|
| 243 |
- } |
|
| 244 |
- |
|
| 245 |
- // if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit |
|
| 246 |
- } |
|
| 247 |
- } else if haveGroup {
|
|
| 248 |
- suppGids = make([]int, len(groups)) |
|
| 249 |
- for i, group := range groups {
|
|
| 250 |
- suppGids[i] = group.Gid |
|
| 251 |
- } |
|
| 252 |
- } |
|
| 253 |
- } |
|
| 254 |
- |
|
| 255 |
- return uid, gid, suppGids, nil |
|
| 256 |
-} |
| 257 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,94 +0,0 @@ |
| 1 |
-package user |
|
| 2 |
- |
|
| 3 |
-import ( |
|
| 4 |
- "strings" |
|
| 5 |
- "testing" |
|
| 6 |
-) |
|
| 7 |
- |
|
| 8 |
-func TestUserParseLine(t *testing.T) {
|
|
| 9 |
- var ( |
|
| 10 |
- a, b string |
|
| 11 |
- c []string |
|
| 12 |
- d int |
|
| 13 |
- ) |
|
| 14 |
- |
|
| 15 |
- parseLine("", &a, &b)
|
|
| 16 |
- if a != "" || b != "" {
|
|
| 17 |
- t.Fatalf("a and b should be empty ('%v', '%v')", a, b)
|
|
| 18 |
- } |
|
| 19 |
- |
|
| 20 |
- parseLine("a", &a, &b)
|
|
| 21 |
- if a != "a" || b != "" {
|
|
| 22 |
- t.Fatalf("a should be 'a' and b should be empty ('%v', '%v')", a, b)
|
|
| 23 |
- } |
|
| 24 |
- |
|
| 25 |
- parseLine("bad boys:corny cows", &a, &b)
|
|
| 26 |
- if a != "bad boys" || b != "corny cows" {
|
|
| 27 |
- t.Fatalf("a should be 'bad boys' and b should be 'corny cows' ('%v', '%v')", a, b)
|
|
| 28 |
- } |
|
| 29 |
- |
|
| 30 |
- parseLine("", &c)
|
|
| 31 |
- if len(c) != 0 {
|
|
| 32 |
- t.Fatalf("c should be empty (%#v)", c)
|
|
| 33 |
- } |
|
| 34 |
- |
|
| 35 |
- parseLine("d,e,f:g:h:i,j,k", &c, &a, &b, &c)
|
|
| 36 |
- if a != "g" || b != "h" || len(c) != 3 || c[0] != "i" || c[1] != "j" || c[2] != "k" {
|
|
| 37 |
- t.Fatalf("a should be 'g', b should be 'h', and c should be ['i','j','k'] ('%v', '%v', '%#v')", a, b, c)
|
|
| 38 |
- } |
|
| 39 |
- |
|
| 40 |
- parseLine("::::::::::", &a, &b, &c)
|
|
| 41 |
- if a != "" || b != "" || len(c) != 0 {
|
|
| 42 |
- t.Fatalf("a, b, and c should all be empty ('%v', '%v', '%#v')", a, b, c)
|
|
| 43 |
- } |
|
| 44 |
- |
|
| 45 |
- parseLine("not a number", &d)
|
|
| 46 |
- if d != 0 {
|
|
| 47 |
- t.Fatalf("d should be 0 (%v)", d)
|
|
| 48 |
- } |
|
| 49 |
- |
|
| 50 |
- parseLine("b:12:c", &a, &d, &b)
|
|
| 51 |
- if a != "b" || b != "c" || d != 12 {
|
|
| 52 |
- t.Fatalf("a should be 'b' and b should be 'c', and d should be 12 ('%v', '%v', %v)", a, b, d)
|
|
| 53 |
- } |
|
| 54 |
-} |
|
| 55 |
- |
|
| 56 |
-func TestUserParsePasswd(t *testing.T) {
|
|
| 57 |
- users, err := parsePasswdFile(strings.NewReader(` |
|
| 58 |
-root:x:0:0:root:/root:/bin/bash |
|
| 59 |
-adm:x:3:4:adm:/var/adm:/bin/false |
|
| 60 |
-this is just some garbage data |
|
| 61 |
-`), nil) |
|
| 62 |
- if err != nil {
|
|
| 63 |
- t.Fatalf("Unexpected error: %v", err)
|
|
| 64 |
- } |
|
| 65 |
- if len(users) != 3 {
|
|
| 66 |
- t.Fatalf("Expected 3 users, got %v", len(users))
|
|
| 67 |
- } |
|
| 68 |
- if users[0].Uid != 0 || users[0].Name != "root" {
|
|
| 69 |
- t.Fatalf("Expected users[0] to be 0 - root, got %v - %v", users[0].Uid, users[0].Name)
|
|
| 70 |
- } |
|
| 71 |
- if users[1].Uid != 3 || users[1].Name != "adm" {
|
|
| 72 |
- t.Fatalf("Expected users[1] to be 3 - adm, got %v - %v", users[1].Uid, users[1].Name)
|
|
| 73 |
- } |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-func TestUserParseGroup(t *testing.T) {
|
|
| 77 |
- groups, err := parseGroupFile(strings.NewReader(` |
|
| 78 |
-root:x:0:root |
|
| 79 |
-adm:x:4:root,adm,daemon |
|
| 80 |
-this is just some garbage data |
|
| 81 |
-`), nil) |
|
| 82 |
- if err != nil {
|
|
| 83 |
- t.Fatalf("Unexpected error: %v", err)
|
|
| 84 |
- } |
|
| 85 |
- if len(groups) != 3 {
|
|
| 86 |
- t.Fatalf("Expected 3 groups, got %v", len(groups))
|
|
| 87 |
- } |
|
| 88 |
- if groups[0].Gid != 0 || groups[0].Name != "root" || len(groups[0].List) != 1 {
|
|
| 89 |
- t.Fatalf("Expected groups[0] to be 0 - root - 1 member, got %v - %v - %v", groups[0].Gid, groups[0].Name, len(groups[0].List))
|
|
| 90 |
- } |
|
| 91 |
- if groups[1].Gid != 4 || groups[1].Name != "adm" || len(groups[1].List) != 3 {
|
|
| 92 |
- t.Fatalf("Expected groups[1] to be 4 - adm - 3 members, got %v - %v - %v", groups[1].Gid, groups[1].Name, len(groups[1].List))
|
|
| 93 |
- } |
|
| 94 |
-} |