1/*2Maddy Mail Server - Composable all-in-one email server.3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors45This program is free software: you can redistribute it and/or modify6it under the terms of the GNU General Public License as published by7the Free Software Foundation, either version 3 of the License, or8(at your option) any later version.910This program is distributed in the hope that it will be useful,11but WITHOUT ANY WARRANTY; without even the implied warranty of12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13GNU General Public License for more details.1415You should have received a copy of the GNU General Public License16along with this program. If not, see <https://www.gnu.org/licenses/>.17*/1819package shadow2021import (22 "bufio"23 "errors"24 "fmt"25 "os"26 "strconv"27 "strings"28)2930var (31 ErrNoSuchUser = errors.New("shadow: user entry is not present in database")32 ErrWrongPassword = errors.New("shadow: wrong password")33)3435// Read reads system shadow passwords database and returns all entires in it.36func Read() ([]Entry, error) {37 f, err := os.Open("/etc/shadow")38 if err != nil {39 return nil, err40 }41 scnr := bufio.NewScanner(f)4243 var res []Entry44 for scnr.Scan() {45 ent, err := parseEntry(scnr.Text())46 if err != nil {47 return res, err48 }4950 res = append(res, *ent)51 }52 if err := scnr.Err(); err != nil {53 return res, err54 }55 return res, nil56}5758func parseEntry(line string) (*Entry, error) {59 parts := strings.Split(line, ":")60 if len(parts) != 9 {61 return nil, errors.New("read: malformed entry")62 }6364 res := &Entry{65 Name: parts[0],66 Pass: parts[1],67 }6869 for i, value := range [...]*int{70 &res.LastChange, &res.MinPassAge, &res.MaxPassAge,71 &res.WarnPeriod, &res.InactivityPeriod, &res.AcctExpiry, &res.Flags,72 } {73 if parts[2+i] == "" {74 *value = -175 } else {76 var err error77 *value, err = strconv.Atoi(parts[2+i])78 if err != nil {79 return nil, fmt.Errorf("read: invalid value for field %d", 2+i)80 }81 }82 }8384 return res, nil85}8687func Lookup(name string) (*Entry, error) {88 entries, err := Read()89 if err != nil {90 return nil, err91 }9293 for _, entry := range entries {94 if entry.Name == name {95 return &entry, nil96 }97 }9899 return nil, ErrNoSuchUser100}