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

     1  // Copyright (c) Mondoo, Inc.
     2  // SPDX-License-Identifier: BUSL-1.1
     3  
     4  package sshd
     5  
     6  import (
     7  	"errors"
     8  	"fmt"
     9  	"strings"
    10  	"time"
    11  )
    12  
    13  type SshdLine struct {
    14  	key  string
    15  	args string
    16  }
    17  
    18  var keysWithTimeValue = map[string]int{
    19  	"ClientAliveInterval": 1,
    20  	"LoginGraceTime":      1,
    21  }
    22  
    23  // ParseLine parses a single line of the sshd_config file
    24  // A valid line contains a key. If the entire line is a
    25  // comment, key will be empty. If the line could not be
    26  // parsed, an error is returned.
    27  //
    28  // Note: The caller must provide only a single line.
    29  func ParseLine(s []rune) (SshdLine, error) {
    30  	l := SshdLine{}
    31  	if len(s) == 0 {
    32  		return l, nil
    33  	}
    34  
    35  	l.key, s = parseKeyword(s)
    36  
    37  	var err error
    38  	l.args, s, err = parseArgs(s)
    39  	if err != nil {
    40  		return l, err
    41  	}
    42  
    43  	if _, ok := keysWithTimeValue[l.key]; ok {
    44  		duration, err := time.ParseDuration(l.args)
    45  		if err == nil {
    46  			l.args = fmt.Sprintf("%.0f", duration.Seconds())
    47  		}
    48  	}
    49  
    50  	return l, nil
    51  }
    52  
    53  func parseKeyword(s []rune) (string, []rune) {
    54  	s = consumeWhitespace(s)
    55  
    56  	i := 0
    57  LOOP:
    58  	for {
    59  		if i >= len(s) {
    60  			break
    61  		}
    62  
    63  		switch s[i] {
    64  		case '#':
    65  			break LOOP
    66  		case ' ', '\t', '\r', '\n', '=':
    67  			break LOOP
    68  		}
    69  		i++
    70  	}
    71  
    72  	return string(s[0:i]), consumeEqualOrWhitespace(s[i:])
    73  }
    74  
    75  func parseArgs(s []rune) (string, []rune, error) {
    76  	args := []string{}
    77  LOOP:
    78  	for len(s) > 0 {
    79  		var arg string
    80  
    81  		switch s[0] {
    82  		case '#':
    83  			break LOOP
    84  		case '"':
    85  			var err error
    86  			arg, s, err = parseQuotedArg(s)
    87  			if err != nil {
    88  				return "", nil, err
    89  			}
    90  			args = append(args, arg)
    91  		default:
    92  			arg, s = parseArg(s)
    93  			args = append(args, arg)
    94  		}
    95  	}
    96  
    97  	return strings.Join(args, " "), s, nil
    98  }
    99  
   100  func parseArg(s []rune) (string, []rune) {
   101  	i := 0
   102  LOOP:
   103  	for i < len(s) {
   104  		switch s[i] {
   105  		case ' ', '\t', '\r', '\n':
   106  			break LOOP
   107  		}
   108  		i++
   109  	}
   110  	return string(s[0:i]), consumeWhitespace(s[i:])
   111  }
   112  
   113  var errUnexpectedEOF = errors.New("Unexpected EOF")
   114  
   115  func parseQuotedArg(s []rune) (string, []rune, error) {
   116  	i := 1
   117  LOOP:
   118  	for {
   119  		if i >= len(s) {
   120  			return "", nil, errUnexpectedEOF
   121  		}
   122  
   123  		switch s[i] {
   124  		case '\\':
   125  			i++
   126  			if i < len(s) && s[i] == '"' {
   127  				i++
   128  			}
   129  		case '\r', '\n':
   130  			return "", nil, errUnexpectedEOF
   131  		case '"':
   132  			i++
   133  			break LOOP
   134  		}
   135  		i++
   136  	}
   137  	return string(s[0:i]), consumeWhitespace(s[i:]), nil
   138  }
   139  
   140  func consumeWhitespace(s []rune) []rune {
   141  	for i := 0; i < len(s); i++ {
   142  		switch s[i] {
   143  		case ' ', '\t', '\r', '\n':
   144  		default:
   145  			return s[i:]
   146  		}
   147  	}
   148  	return []rune{}
   149  }
   150  
   151  func consumeEqualOrWhitespace(s []rune) []rune {
   152  	s = consumeWhitespace(s)
   153  	if len(s) > 0 && s[0] == '=' {
   154  		s = consumeWhitespace(s[1:])
   155  	}
   156  	return s
   157  }