github.com/rkt/rkt@v1.30.1-0.20200224141603-171c416fac02/pkg/passwd/passwd.go (about) 1 // Copyright 2016 The rkt Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package passwd 16 17 import ( 18 "bufio" 19 "errors" 20 "fmt" 21 "io" 22 "os" 23 "strconv" 24 "strings" 25 26 "github.com/hashicorp/errwrap" 27 ) 28 29 const ( 30 passwdFilePath = "/etc/passwd" 31 ) 32 33 // User represents an entry in the passwd file. 34 type User struct { 35 Name string 36 Pass string 37 Uid int 38 Gid int 39 Comment string 40 Home string 41 Interpreter string 42 } 43 44 // LookupUidFromFile reads the passwd file specified by passwdFile, and returns the 45 // uid of the user specified by userName. 46 func LookupUidFromFile(userName, passwdFile string) (uid int, err error) { 47 users, err := parsePasswdFile(passwdFile) 48 if err != nil { 49 return -1, errwrap.Wrap(fmt.Errorf("error parsing %q file", passwdFile), err) 50 } 51 52 user, ok := users[userName] 53 if !ok { 54 return -1, fmt.Errorf("%q user not found", userName) 55 } 56 57 return user.Uid, nil 58 } 59 60 // LookupUid reads the passwd file and returns the uid of the user 61 // specified by userName. 62 func LookupUid(userName string) (uid int, err error) { 63 return LookupUidFromFile(userName, passwdFilePath) 64 } 65 66 func parsePasswdFile(path string) (user map[string]*User, err error) { 67 passwdFile, err := os.Open(path) 68 if err != nil { 69 return nil, err 70 } 71 defer passwdFile.Close() 72 73 return parseUsers(passwdFile) 74 } 75 76 func parseUsers(r io.Reader) (user map[string]*User, err error) { 77 s := bufio.NewScanner(r) 78 out := make(map[string]*User) 79 80 for s.Scan() { 81 if err := s.Err(); err != nil { 82 return nil, err 83 } 84 85 text := s.Text() 86 if text == "" { 87 continue 88 } 89 90 p, err := parsePasswdLine(text) 91 if err != nil { 92 return nil, errwrap.Wrap(errors.New("error parsing line"), err) 93 } 94 95 out[p.Name] = p 96 } 97 98 return out, nil 99 } 100 101 func parsePasswdLine(line string) (*User, error) { 102 const ( 103 NameIdx = iota 104 PassIdx 105 UidIdx 106 GidIdx 107 CommentIdx 108 HomeIdx 109 InterpreterIdx 110 ) 111 var err error 112 113 if line == "" { 114 return nil, errors.New("cannot parse empty line") 115 } 116 117 splits := strings.Split(line, ":") 118 if len(splits) < 7 { 119 return nil, fmt.Errorf("expected at least 7 fields, got %d", len(splits)) 120 } 121 122 user := &User{ 123 Name: splits[NameIdx], 124 Pass: splits[PassIdx], 125 Comment: splits[CommentIdx], 126 Home: splits[HomeIdx], 127 Interpreter: splits[InterpreterIdx], 128 } 129 130 user.Uid, err = strconv.Atoi(splits[UidIdx]) 131 if err != nil { 132 return nil, errwrap.Wrap(errors.New("unable to parse uid"), err) 133 } 134 user.Gid, err = strconv.Atoi(splits[GidIdx]) 135 if err != nil { 136 return nil, errwrap.Wrap(errors.New("unable to parse gid"), err) 137 } 138 139 return user, nil 140 }