258 lines
5.3 KiB
Go
258 lines
5.3 KiB
Go
|
package UNIXAccounts
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"fmt"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
)
|
||
|
|
||
|
// Base accounts structure and configuration.
|
||
|
type UNIXAccounts struct {
|
||
|
Groups []*UNIXGroup
|
||
|
Users []*UNIXUser
|
||
|
PasswdPath string
|
||
|
GroupPath string
|
||
|
}
|
||
|
|
||
|
// Read the /etc/group and /etc/passwd files to parse information.
|
||
|
func NewUNIXAccounts() (*UNIXAccounts, error) {
|
||
|
u := &UNIXAccounts{
|
||
|
PasswdPath: "/etc/passwd",
|
||
|
GroupPath: "/etc/group",
|
||
|
}
|
||
|
err := u.Parse()
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return u, nil
|
||
|
}
|
||
|
|
||
|
// Group data structure.
|
||
|
type UNIXGroup struct {
|
||
|
Name string
|
||
|
ID int
|
||
|
Users []string
|
||
|
}
|
||
|
|
||
|
// User data structure.
|
||
|
type UNIXUser struct {
|
||
|
Name string
|
||
|
ID int
|
||
|
GID int
|
||
|
FullName string
|
||
|
HomeDir string
|
||
|
Shell string
|
||
|
Disabled bool
|
||
|
}
|
||
|
|
||
|
// Parse: Parse unix accounts and groups.
|
||
|
func (u *UNIXAccounts) Parse() error {
|
||
|
// Remove any previously parsed users to re-parse.
|
||
|
u.Groups = nil
|
||
|
u.Users = nil
|
||
|
|
||
|
// Open the group file.
|
||
|
groupFile, err := os.Open(u.GroupPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer groupFile.Close()
|
||
|
|
||
|
scanner := bufio.NewScanner(groupFile)
|
||
|
scanner.Split(bufio.ScanLines)
|
||
|
lineCount := 0
|
||
|
for scanner.Scan() {
|
||
|
// Read a line.
|
||
|
line := scanner.Text()
|
||
|
|
||
|
// Ignore comments.
|
||
|
if line[0] == '#' {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Fields are separated with a :.
|
||
|
fields := strings.Split(line, ":")
|
||
|
|
||
|
// Groups should have 4 fields. Nothing more, nothing less.
|
||
|
if len(fields) != 4 {
|
||
|
return fmt.Errorf("unexpected field count in group file on line %d", lineCount)
|
||
|
}
|
||
|
|
||
|
// Parse information.
|
||
|
group := new(UNIXGroup)
|
||
|
group.Name = fields[0]
|
||
|
group.ID, _ = strconv.Atoi(fields[2])
|
||
|
group.Users = strings.Split(fields[3], ",")
|
||
|
|
||
|
// Add group to array.
|
||
|
u.Groups = append(u.Groups, group)
|
||
|
|
||
|
// Increment line count.
|
||
|
lineCount++
|
||
|
}
|
||
|
|
||
|
// Open the user file.
|
||
|
passwdFile, err := os.Open(u.PasswdPath)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer passwdFile.Close()
|
||
|
|
||
|
scanner = bufio.NewScanner(passwdFile)
|
||
|
scanner.Split(bufio.ScanLines)
|
||
|
lineCount = 0
|
||
|
for scanner.Scan() {
|
||
|
// Read a line.
|
||
|
line := scanner.Text()
|
||
|
|
||
|
// Ignore comments.
|
||
|
if line[0] == '#' {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Fields are separated with a :.
|
||
|
fields := strings.Split(line, ":")
|
||
|
|
||
|
// Users have 7 fields. No more or less.
|
||
|
if len(fields) != 7 {
|
||
|
return fmt.Errorf("unexpected field count in passwd file on line %d", lineCount)
|
||
|
}
|
||
|
|
||
|
// Prase information.
|
||
|
user := new(UNIXUser)
|
||
|
user.Name = fields[0]
|
||
|
user.ID, _ = strconv.Atoi(fields[2])
|
||
|
user.GID, _ = strconv.Atoi(fields[3])
|
||
|
user.FullName = fields[4]
|
||
|
user.HomeDir = filepath.Clean(fields[5])
|
||
|
user.Shell = fields[6]
|
||
|
|
||
|
// A user is disabled if their shell is set to nologin or false. Users with no shell should also be disabled.
|
||
|
user.Disabled = false
|
||
|
if strings.Contains(user.Shell, "nologin") {
|
||
|
user.Disabled = true
|
||
|
}
|
||
|
if strings.Contains(user.Shell, "false") {
|
||
|
user.Disabled = true
|
||
|
}
|
||
|
if user.Shell == "" {
|
||
|
user.Disabled = true
|
||
|
}
|
||
|
|
||
|
// Add user to array.
|
||
|
u.Users = append(u.Users, user)
|
||
|
|
||
|
// Increment line count.
|
||
|
lineCount++
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Find user info for ID.
|
||
|
func (u *UNIXAccounts) UserWithID(id int) *UNIXUser {
|
||
|
for _, user := range u.Users {
|
||
|
if user.ID == id {
|
||
|
return user
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Find user info for name.
|
||
|
func (u *UNIXAccounts) UserWithName(name string) *UNIXUser {
|
||
|
for _, user := range u.Users {
|
||
|
if user.Name == name {
|
||
|
return user
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Find group info for ID.
|
||
|
func (u *UNIXAccounts) GroupWithID(id int) *UNIXGroup {
|
||
|
for _, group := range u.Groups {
|
||
|
if group.ID == id {
|
||
|
return group
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Find group info for name.
|
||
|
func (u *UNIXAccounts) GroupWithName(name string) *UNIXGroup {
|
||
|
for _, group := range u.Groups {
|
||
|
if group.Name == name {
|
||
|
return group
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Get all user accounts which are members of a group.
|
||
|
func (u *UNIXAccounts) UsersInGroup(group *UNIXGroup) []*UNIXUser {
|
||
|
var users []*UNIXUser
|
||
|
// Users with the Group ID set to the group's ID are a member.
|
||
|
for _, user := range u.Users {
|
||
|
if user.GID == group.ID {
|
||
|
users = append(users, user)
|
||
|
}
|
||
|
}
|
||
|
// Find user info for each member.
|
||
|
for _, name := range group.Users {
|
||
|
user := u.UserWithName(name)
|
||
|
if user == nil {
|
||
|
continue
|
||
|
}
|
||
|
// If the member was added previously, we do not want duplicates.
|
||
|
alreadyExists := false
|
||
|
for _, usr := range users {
|
||
|
if usr == user {
|
||
|
alreadyExists = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !alreadyExists {
|
||
|
// The member is not a duplicate, so we add it to the array.
|
||
|
users = append(users, user)
|
||
|
}
|
||
|
}
|
||
|
return users
|
||
|
}
|
||
|
|
||
|
// List of groups a user is a member of.
|
||
|
func (u *UNIXAccounts) UserMemberOf(user *UNIXUser) []*UNIXGroup {
|
||
|
var groups []*UNIXGroup
|
||
|
|
||
|
// Look at each group and check if this user is a member.
|
||
|
for _, group := range u.Groups {
|
||
|
// If the GID of the user is this group, add it.
|
||
|
if group.ID == user.GID {
|
||
|
groups = append(groups, group)
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
// Check each user assigned to this group and add the group if the user matches.
|
||
|
for _, thisUser := range group.Users {
|
||
|
if thisUser == user.Name {
|
||
|
// If the group was added previously, we do not want duplicates.
|
||
|
alreadyExists := false
|
||
|
for _, grp := range groups {
|
||
|
if grp == group {
|
||
|
alreadyExists = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !alreadyExists {
|
||
|
// The group is not a duplicate, so we add it to the array.
|
||
|
groups = append(groups, group)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return groups
|
||
|
}
|