go.mondoo.com/cnquery@v0.0.0-20231005093811-59568235f6ea/providers/os/resources/groups/dscache.go (about)

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package groups
     5  
     6  import (
     7  	"bufio"
     8  	"io"
     9  	"regexp"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/rs/zerolog/log"
    14  	"go.mondoo.com/cnquery/providers/os/connection/shared"
    15  )
    16  
    17  var GROUP_OSX_DSCACHEUTIL_REGEX = regexp.MustCompile(`^(\S+):\s(.*?)$`)
    18  
    19  func ParseDscacheutilResult(input io.Reader) ([]*Group, error) {
    20  	groups := map[string]*Group{}
    21  
    22  	add := func(group Group) {
    23  		// a group must have a username, otherwise it is not valid
    24  		// this will happen at least for the last item, where we got an empty row
    25  		// we also need to eliminate duplicates, it happens on macos with dscacheutil -q group
    26  		if len(group.ID) != 0 {
    27  			groups[group.ID] = &group
    28  		}
    29  	}
    30  
    31  	scanner := bufio.NewScanner(input)
    32  	group := Group{Members: []string{}}
    33  
    34  	var key string
    35  	for scanner.Scan() {
    36  		line := scanner.Text()
    37  
    38  		// reset group definition once we reach an empty line
    39  		if len(line) == 0 {
    40  			add(group)
    41  			group = Group{Members: []string{}}
    42  		}
    43  
    44  		m := GROUP_OSX_DSCACHEUTIL_REGEX.FindStringSubmatch(line)
    45  		key = ""
    46  		if m != nil {
    47  			key = m[1]
    48  		}
    49  
    50  		// Parse the group content
    51  		switch key {
    52  		case "name":
    53  			group.Name = strings.TrimSpace(m[2])
    54  		case "password":
    55  			// we ignore the password for now
    56  		case "gid":
    57  			gid, err := strconv.ParseInt(m[2], 10, 0)
    58  			if err != nil {
    59  				log.Error().Err(err).Str("group", m[0]).Msg("could not parse gid")
    60  			}
    61  			group.ID = m[2]
    62  			group.Gid = gid
    63  		case "users":
    64  			content := strings.TrimSpace(m[2])
    65  			if len(content) > 0 {
    66  				group.Members = strings.Split(content, " ")
    67  			}
    68  		}
    69  	}
    70  
    71  	// if the last line is not an empty line we have things in flight, lets check it
    72  	add(group)
    73  
    74  	res := []*Group{}
    75  	for k := range groups {
    76  		res = append(res, groups[k])
    77  	}
    78  
    79  	return res, nil
    80  }
    81  
    82  type OSXGroupManager struct {
    83  	conn shared.Connection
    84  }
    85  
    86  func (s *OSXGroupManager) Name() string {
    87  	return "macOS Group Manager"
    88  }
    89  
    90  func (s *OSXGroupManager) Group(id string) (*Group, error) {
    91  	groups, err := s.List()
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	return findGroup(groups, id)
    97  }
    98  
    99  func (s *OSXGroupManager) List() ([]*Group, error) {
   100  	c, err := s.conn.RunCommand("dscacheutil -q group")
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	return ParseDscacheutilResult(c.Stdout)
   105  }