func ParseSDDL()

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
}