maddy

Fork https://github.com/foxcpp/maddy

git clone git://git.lin.moe/go/maddy.git

  1/*
  2Maddy Mail Server - Composable all-in-one email server.
  3Copyright © 2019-2020 Max Mazurov <fox.cpp@disroot.org>, Maddy Mail Server contributors
  4
  5This program is free software: you can redistribute it and/or modify
  6it under the terms of the GNU General Public License as published by
  7the Free Software Foundation, either version 3 of the License, or
  8(at your option) any later version.
  9
 10This program is distributed in the hope that it will be useful,
 11but WITHOUT ANY WARRANTY; without even the implied warranty of
 12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13GNU General Public License for more details.
 14
 15You should have received a copy of the GNU General Public License
 16along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17*/
 18
 19package shadow
 20
 21import (
 22	"bufio"
 23	"errors"
 24	"fmt"
 25	"os"
 26	"strconv"
 27	"strings"
 28)
 29
 30var (
 31	ErrNoSuchUser    = errors.New("shadow: user entry is not present in database")
 32	ErrWrongPassword = errors.New("shadow: wrong password")
 33)
 34
 35// 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, err
 40	}
 41	scnr := bufio.NewScanner(f)
 42
 43	var res []Entry
 44	for scnr.Scan() {
 45		ent, err := parseEntry(scnr.Text())
 46		if err != nil {
 47			return res, err
 48		}
 49
 50		res = append(res, *ent)
 51	}
 52	if err := scnr.Err(); err != nil {
 53		return res, err
 54	}
 55	return res, nil
 56}
 57
 58func parseEntry(line string) (*Entry, error) {
 59	parts := strings.Split(line, ":")
 60	if len(parts) != 9 {
 61		return nil, errors.New("read: malformed entry")
 62	}
 63
 64	res := &Entry{
 65		Name: parts[0],
 66		Pass: parts[1],
 67	}
 68
 69	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 = -1
 75		} else {
 76			var err error
 77			*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	}
 83
 84	return res, nil
 85}
 86
 87func Lookup(name string) (*Entry, error) {
 88	entries, err := Read()
 89	if err != nil {
 90		return nil, err
 91	}
 92
 93	for _, entry := range entries {
 94		if entry.Name == name {
 95			return &entry, nil
 96		}
 97	}
 98
 99	return nil, ErrNoSuchUser
100}