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 }