package sslconf

  1. Overview
  2. Docs

an OCaml version of Openssl's NCONF library

Version 0.8.3homepage

Module Sslconf: a parser for Openssl config files.

Openssl config file documentation: config.html

Openssl config files are ubiquitous in books, in documentation, and in Stack Overflow answers.

Definition of X509 certificates in Openssl is usually done by Openssl config files.

This module reproduces the Openssl NCONF implementation. Openssl NCONF is part of the Openssl crypto library (-libcrypto).

This module attaches no semantics to the strings and stacks it returns. It is similar to Openssl NCONF in this way.

Openssl attaches semantics to the CONF data structure returned by Openssl NCONF, via commands such as openssl x509 or openssl ca.

This implementation is backed by standard OCaml hash tables and stacks. Some may view this as a weakness. Openssl's NCONF implementation draws on custom hash tables and stacks defined in Openssl's crypto library.

Openssl code: conf.h conf_def.c conf_api.c conf_lib.c bss_fd.c

Parser Overview

Lines recognized by the parser are:

  • comment lines, i.e.,

    • empty, or all whitespace;
    • (Win32 only) a semicolon (';') before non-white-space; or
    • whitespace, then a hash char ('#'), then arbitrary data;
  • section lines, i.e.,

    • a ("[<section>]") string inside square brackets; or
  • value lines, i.e.,

    • a ("<name> = <value>") assignment.

Openssl config file documentation: config.html

The Openssl config file documentation is enough for most users, but is not a complete specification.

What follows is detail for maintainers.

First Pass - Line Recognition

If the parser sees an escape char ('\\') at the end of a line, it combines the line with the next line.

The escape char may itself be escaped. If escaped, the escape char loses its special meaning as a line continuation mark.

Bounded Reads and Continuation Chars

The parser reads up to a newline, or a maximum length if no newline is found. If no newline is found, the parser does another read, and so on, until a newline is found or end of file is is detected.

The max number of characters the parser reads in one read is 510. This number is dictated by a need for C code to put null bytes in a 512 byte buffer.

This implementation follows the 510 char rule. A config file valid in Openssl will still be valid here.

If the parser sees an escape char ('\\') at the end of a read, i.e., as the 510th character, it will apply line continuation logic -- even if a newline is not seen at the end.

Both newlines and line continuation characters are removed from the strings passed to the parser in its second pass.

Second Pass - Configuration Recognition

Error reporting - line and column numbers

Error reporting reports line and column numbers. Line numbers are relative to the file being read.

The first line is line 1. The first column is column 1.

Comment lines

A comment line is either an empty line, a line of all whitespace, or whitespace followed by a hash char.

In Win32 only, a line starting with a semicolon is also a comment.

Comment lines are ignored and not saved.

The end of a value line may have a comment. Value line comments start with a hash char. Any text up to end of line can follow the hash char.

Section lines

A section line has a ("[<section>]") string surrounded by square brackets. The string names a section.

Whitespace is trimmed from both ends of the string.

A section may contain alphanumerics, underscores, punctuation, or whitespace.

Alphanumerics are any letter (from 'a' to z or 'A' to 'Z') or a number (from '0' to '9').

Underscores are one or more underscore ('_') characters.

Punctuation is one or more of the following characters: ('!', '.', '%', '&', '*', '+', ';', '?', '@', '^', '~', '|', or '-').

A section may also contain escaped characters. An escaped character immediately follows an escape ('\\') char. Any character may be an escaped character.

If an escaped character is ('r', 'n', 'b', or 't'), it is translated into a whitespace char ("\r", "\n", "\b", or "\t"). Otherwise, it is retained as is.

Escape chars are stripped out from a section. Escaped chars therefore appear in section names as if they were not escaped.

The section named in a section line becomes the default section for lines which follow. It remains the default until a new section line is read.

Value lines

A line with a ("<name> = <value>") assignment is a value line. A value line always has a name, an equal sign, and a value. Whitespace before or after the equal sign is optional.

A value line assigns a value to a name in a section.

Names

Lexical rules for a name (qualified or unqualified) are more restrictive than for a section in a section line.

Whitespace is trimmed from both ends of a name.

A name may contain alphanumerics, underscores, and punctuation. A name may not contain whitespace.

As with section names, escaped characters are allowed. An escaped character immediately follows an escape ('\\') char. Any character may be an escaped character.

Unlike with section names, escape chars are not stripped. An escape char goes in the hash table as part of a name.

A name can be qualified or unqualified.

A qualified name splits into two parts, separated by a two-colon string ("::"). The first part of a qualified name is the section. The second part is the name.

If the section of a qualified name is not in the hash table, a new entry for the section is added.

An unqualified name belongs to the default section. The default section is the section named by the most recent previous section line, or "default" if no section line has occured yet.

Values

A value is a sequence of parts, starting with the first non-whitespace character after the equal sign on a value line.

A part is one of three kinds:

  • wrapped by double or single quotes;
  • a name substitution; or
  • regular text.

Whitespace is stripped from the start and end of a value. Whitespace following the equal sign, and whitespace, either to the end of a line, or up to a comment hash char if present, is ignored.

Quote wrapped parts

Unix and Cygwin use default quote wrapping.

Default quote wrapped parts accept any character as is, and also allow quotes if preceded immediately by escape chars.

Escape chars and the quotes which wrap surrounded parts are stripped.

Win32 uses double quote wrapping. Double quote wrapped parts accept any character as is, unless it is a double quote. There is one exception. A double quote may appear inside a double quote wrapped string, if a double quote immediately precedes it.

Wrapping double quotes, and double quotes which immediately precede double quotes, are stripped.

Name substitution

Name substitution starts with a ('$') character. The ('$') character should be followed by a name. The name may be optionally wrapped by parentheses ("()") or curly brackets ("{}"). The subst evaluator replaces the ('$'), the optional wrapping, and the name with the value corresponding to the name and its section.

If the section is not ("ENV"), a value is found from a hashtable with section+name keys. If the section is ("ENV"), a value is found from the process environment.

If substitution fails, the parser returns an error, reporting that no value could be found for the name.

A name given for name substitution may only contain alphanumeric or underscore characters. It may not contain punctuation or whitespace, unless these are escaped via an escape ('\\') character. If an escaped character is in a name, its escape char is retained, along with the escaped character in the name.

Regular value text, i.e., unwrapped parts

Regular text accepts any character other than single quotes, double quotes, or the ('$') substitution character.

It may still have

  • escaped characters; or
  • hash chars, for comments to end of line.
Escaped chars in regular text

Regular text can have escaped characters. An escaped character immediately follows an escape ('\\') char.

If an escaped character is ('r', 'n', 'b', or 't'), it is translated into a whitespace char ("\r", "\n", "\b", or "\t"). If it is not one of these characters, it is retained as is.

The special meanings of escaped characters are cancelled. For example, one can precede a double quote by an escape char, to cancel its usual meaning of starting a quote-wrapped part.

Escape chars are stripped. They don't appear in hash table values. Escaped characters remain.

Hash chars in regular text start comments to end of line

An unescaped hash ('#') char starts a comment to end of line. Any data following the hash char, up to the end of a line, is ignored.

Non-special chars in regular text

Characters not belonging to wrapped segments or name substitutions can be any character. For example, whitespace can occur in the middle of a value.

module Buf : sig ... end

Sslconf buffer.

module Bio : sig ... end

Basic I/O.

type t

Sslconf type

val create : unit -> t

Initialize an Sslconf instance

val nbits : int option ref

FOR TESTING ONLY Limits buffer allocated memory to a max bit length.

type error =
  1. | Open of string
  2. | Extend of string * int * int * int
  3. | Parse of string * int * int * string * string
    (*

    Open (msg):

    • open failure message

    Extend (msg nbits max_length max_alloc):

    • failure message;
    • number of bits allowed in buffer size;
    • requested new length of buffer;
    • allocation limit of buffer size.

    Parse (file lineno col proc msg):

    • file name;
    • line number where error was seen;
    • column position where error was seen;
    • procedure name in which error was seen;
    • error message.
    *)
val string_of_error : error -> string

string_of_error error converts an error to a user-readable string.

val conf_load_file : t -> string -> (unit, error) Rresult.result

conf_load_file conf filename loads a file into a t instance. The error instance describes only the first failure found.

val conf_get_value : ?conf:t -> ?section:string -> string -> string option

conf_get_value ?conf ?section name gets an optional value from a name. A string option is returned.

See Openssl NCONF_get_string() in conf_lib.c. If a config instance is provided, its hash table with section+name keys is searched. If no config instance is provided, the process environment is searched. If a section is provided, then:

  • if the section is "ENV", the process environment is searched.
  • else the section and name are made into a pair, and the pair is searched for in the hash table.

If no section is provided, or the search in a provided section fails, then section "default" is used with the name.

type stack = (string * string) Stack.t

A stack with (name, value) entries.

val conf_get_section : t -> string -> stack option

conf_get_section conf section gets an optional stack of name-value pairs.

See Openssl function NCONF_get_section() in conf_lib.c. A hash table with section names in the config instance is searched for a section. If a stack is returned, and <name> is in a name-value pair on it, then conf_get_value conf section name will get its most recent value.

val sexp_of_stack : stack -> Sexplib.Sexp.t

sexp_of_stack conf section converts a config instance section stack to an s-expression

val stack_of_sexp : Sexplib.Sexp.t -> stack

stack_of_sexp sexp converts an s-expression to a config instance section stack

val sexp_of_conf : t -> Sexplib.Sexp.t

sexp_of_conf conf converts a config instance to an s-expression

val conf_of_sexp : Sexplib.Sexp.t -> t

conf_of_sexp sexp converts an s-expression to a config instance