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  }