in sddl/parseSddl.go [39:156]
func ParseSDDL(input string) (sddl SDDLString, err error) {
scope := 0 // if scope is 1, we're in an ACE string, if scope is 2, we're in a resource attribute.
inString := false // If a quotation mark was found, we've entered a string and should ignore all characters except another quotation mark.
elementStart := make([]int, 0) // This is the start of the element we're currently analyzing. If the array has more than one element, we're probably under a lower scope.
awaitingACLFlags := false // If this is true, a ACL section was just entered, and we're awaiting our first ACE string
var elementType rune // We need to keep track of which section of the SDDL string we're in.
for k, v := range input {
switch {
case inString: // ignore characters within a string-- except for the end of a string, and escaped quotes
if v == '"' && input[k-1] != '\\' {
inString = false
}
case v == '"':
inString = true
case v == '(': // this comes before scope == 1 because ACE strings can be multi-leveled. We only care about the bottom level.
scope++
if scope == 1 { // only do this if we're in the base of an ACE string-- We don't care about the metadata as much.
if awaitingACLFlags {
err := sddl.setACLFlags(input[elementStart[0]:k], elementType)
if err != nil {
return sddl, err
}
awaitingACLFlags = false
}
elementStart = append(elementStart, k+1) // raise the element start scope
err := sddl.startACL(elementType)
if err != nil {
return sddl, err
}
}
case v == ')':
// (...,...,...,(...))
scope--
if scope == 0 {
err := sddl.putACLElement(input[elementStart[1]:k], elementType)
if err != nil {
return sddl, err
}
elementStart = elementStart[:1] // lower the element start scope
}
case scope == 1: // We're at the top level of an ACE string
switch v {
case ';':
// moving to the next element
err := sddl.putACLElement(input[elementStart[1]:k], elementType)
if err != nil {
return sddl, err
}
elementStart[1] = k + 1 // move onto the next bit of the element scope
}
case scope == 0: // We're at the top level of a SDDL string
if k == len(input)-1 || v == ':' { // If we end the string OR start a new section
if elementType != 0x00 {
switch elementType {
case 'O':
// you are here:
// V
// O:...G:
// ^
// k-1
// string separations in go happen [x:y).
sddl.OwnerSID = strings.TrimSpace(input[elementStart[0]:IffInt(k == len(input)-1, len(input), k-1)])
case 'G':
sddl.GroupSID = strings.TrimSpace(input[elementStart[0]:IffInt(k == len(input)-1, len(input), k-1)])
case 'D', 'S': // These are both parsed WHILE they happen, UNLESS we're awaiting flags.
if awaitingACLFlags {
err := sddl.setACLFlags(strings.TrimSpace(input[elementStart[0]:IffInt(k == len(input)-1, len(input), k-1)]), elementType)
if err != nil {
return sddl, err
}
}
default:
return sddl, fmt.Errorf("%s is an invalid SDDL section", string(elementType))
}
}
if v == ':' {
// set element type to last character
elementType = rune(input[k-1])
// await ACL flags
if elementType == 'D' || elementType == 'S' {
awaitingACLFlags = true
}
// set element start to next character
if len(elementStart) == 0 { // start the list if it's empty
elementStart = append(elementStart, k+1)
} else if len(elementStart) > 1 {
return sddl, errors.New("elementStart too long for starting a new part of a SDDL")
} else { // assign the new element start
elementStart[0] = k + 1
}
}
}
}
}
if scope > 0 || inString {
return sddl, errors.New("string or scope not fully exited")
}
if err == nil {
if !sanityCheckSDDLParse(input, sddl) {
return sddl, errors.New("SDDL parsing sanity check failed")
}
}
return
}