Configuration Files Advanced Programming Topics

242 The functions shown in Example 10-1 , like many members of the OpenSSL API, are implemented via macros. Thus, it should be obvious that function pointer manipulation is not possible. In general, applications should use the API directly since the underlying implementation is subject to change in future versions. The operations on the stack should be self-explanatory; well cover them briefly. The sk_TYPE_new_null function simply creates an empty stack, while the sk_TYPE_free function frees a stack; the latter frees only the stack, not the objects contained in the stack. To free a stack and all its members, the sk_TYPE_pop_free function should be used; we must pass in a function pointer to the free method for this to work correctly. The last of the general manipulation functions are sk_TYPE_zero to empty a stack and sk_TYPE_dup to copy a stack object. There are also the general functions sk_TYPE_push and sk_TYPE_pop that we would expect in a stack implementation. Two that are less likely are sk_TYPE_unshift and sk_TYPE_shift ; the former pushes an element to the bottom of the stack, and the latter pops the bottom item off the stack. The functions that add elements sk_TYPE_push and sk_TYPE_unshift return the total number of elements in the stack, and the other two functions return the item that was removed from the stack. The last group of macros is a set of nontypical functions for a stack implementation; they add functionality that is more commonly found in lists. The first, sk_TYPE_num , returns the total number of items in the stack. To retrieve an item by index number if the bottom item is zero without changing the stack, we should use sk_TYPE_value . In order to set an item in the stack explicitly, the function sk_TYPE_set will replace the i th item with the item passed to the call. The delete functions delete a single item from the stack. The item to delete can be selected by index or pointer value; the stack is shifted to fill the space left by the removed item. Finally, sk_TYPE_insert adds the indicated item to the i th position in the stack and moves all the items above it, including the i th item before the call, up one position. As well see when we move into further topics of this chapter, proper stack handling is critical for setting up some data structures. While well probably put only a subset of this simple interface to use, we now have the tools to do more complex tasks.

10.2 Configuration Files

We learned how to create a CA by first creating a configuration file to hold our parameters in Chapter 3 . The command-line tool used this file to perform as we had configured, such as obeying choices for algorithms, providing default values for fields in the subject name, etc. The public API has a suite of functions for processing and accessing values of configuration files. The files themselves simply organize and map keys to values. In general, the keys are strings, and the values can be either integers or strings, although all values are stored internally as strings. The goal of the configuration file interface is to make the format of the file opaque to the code that processes it. This is done through NCONF objects. When such objects are created, a CONF_METHOD structure is specified that aggregates the routines to perform the low-level file parsing operations. OpenSSL most commonly uses the function NCONF_default to get the CONF_METHOD object. This method reads files of the format we described in Chapter 2 . Because of the flexibility afforded by specifying the underlying CONF_METHOD , the NCONF interface may be extended in future versions of OpenSSL to include support for reading configuration files of new formats, such as XML. 243 There are only a few functions to this simple interface, and well explore them by looking at an example. Example 10-2 presents a small sample configuration file. Example 10-2. A sample configuration file testconf.cnf The config file GlobalVar = foo GlobalNum = 12 [Params] SectionName = mySection [mySection] myVar = bar myNum = 7 Example 10-3 provides a test program to read the sample configuration file. Example 10-3. Code to interact with the configuration file include stdio.h include stdlib.h include opensslconf.h void handle_errorconst char file, int lineno, const char msg { fprintfstderr, s:i s\n, file, lineno, msg; ERR_print_errors_fpstderr; exit-1; } define int_errormsg handle_error__FILE__, __LINE_ _, msg define GLOB_VAR GlobalVar define GLOB_NUM GlobalNum define PARAMS Params define SEC_NAME SectionName define CONFFILE testconf.cnf int mainint argc, char argv[] { int i; long i_val, err = 0; char key, s_val; STACK_OFCONF_VALUE sec; CONF_VALUE item; CONF conf; conf = NCONF_newNCONF_default; if NCONF_loadconf, CONFFILE, err { if err == 0 int_errorError opening configuration file; else { fprintfstderr, Error in s on line li\n, CONFFILE, err; int_errorErrors parsing configuration file; } } 244 if s_val = NCONF_get_stringconf, NULL, GLOB_VAR { fprintfstderr, Error finding \s\ in [s]\n, GLOB_VAR, NULL; int_errorError finding string; } printfSec: s, Key: s, Val: s\n, NULL, GLOB_VAR, s_val; if OPENSSL_VERSION_NUMBER 0x00907000L if err = NCONF_get_number_econf, NULL, GLOB_NUM, i_val { fprintfstderr, Error finding \s\ in [s]\n, GLOB_NUM, NULL; int_errorError finding number; } else if s_val = NCONF_get_stringconf, NULL, GLOB_NUM { fprintfstderr, Error finding \s\ in [s]\n, GLOB_VAR, NULL; int_errorError finding number; } i_val = atois_val; endif printfSec: s, Key: s, Val: i\n, NULL, GLOB_VAR, i_val; if key = NCONF_get_stringconf, PARAMS, SEC_NAME { fprintfstderr, Error finding \s\ in [s]\n, SEC_NAME, PARAMS; int_errorError finding string; } printfSec: s, Key: s, Val: s\n, PARAMS, SEC_NAME, key; if sec = NCONF_get_sectionconf, key { fprintfstderr, Error finding [s]\n, key; int_errorError finding string; } for i = 0; i sk_CONF_VALUE_numsec; i++ { item = sk_CONF_VALUE_valuesec, i; printfSec: s, Key: s, Val: s\n, item-section, item-name, item-value; } NCONF_freeconf; return 0; } In the example program, a new CONF object is created using the default method, and the file is loaded by the call to NCONF_load . There are also other functions for loading the file from open FILE or BIO objects: NCONF_load_fp and NCONF_load_bio . Three different functions are used to probe the configuration file for values. The first one that we use is NCONF_get_string , which returns the string of the value corresponding to the section and key values passed to it. If either the section or the key is undefined, it returns NULL . Our sample program uses a few preprocessor defines as shortcuts to the section and key strings: char NCONF_get_stringconst CONF conf, const char section, const char key; int NCONF_get_number_econst CONF conf, const char section, const char key, long result; STACK_OFCONF_VALUE NCONF_get_sectionconst CONF conf, 245 const char section; One interesting point made in the example is the preprocessor conditionals around usage of the function NCONF_get_number_e . Versions of OpenSSL prior to 0.9.7 have a function, NCONF_get_number , that takes three arguments, the same as NCONF_get_string , except that NCONF_get_number returns an integer value instead of a string. This function should be avoided since it does not allow for error checking. The better way to read integer values from a configuration file is to get the value as a string, check for an error condition by checking for a NULL return value, and then convert the string to an integer ourselves. Of course, if the NCONF_get_number_e function is available, it can be used safely. This function writes the retrieved value to the last argument and returns nonzero on success. The function NCONF_get_number may be reimplemented in 0.9.7 simply as a macro for NCONF_get_number_e , requiring calling applications to be changed to account for the modified interface. Because of possible changes, it is safest to use NCONF_get_number_e . The last NCONF function used in the example is NCONF_get_section . This function returns a stack of all the configuration parameters in the specified section. Our sample program iterates over the stack and prints all the pairs that it contains. This code actually manually accesses members of the CONF_VALUE structure; the declaration is provided in Example 10-4 . Example 10-4. The declaration of CONF_VALUE typedef struct { char section; char name; char value; } CONF_VALUE; In general, the NCONF interface can provide a simple, readily available infrastructure for customized, application-specific configuration files. This interface is useful for other reasons, as well. For instance, we can build a custom CA application that shares configuration files with the command-line tool.

10.3 X.509