Some kadm5 functions call other kadm5 functions ``on their own behalf'' to perform functionality that is necessary but that does not directly affect what the client sees. For example, kadm5_chpass_principal has to enforce password policies; thus, it needs to call kadm5_get_principal and, if the principal has a policy, kadm5_get_policy and kadm5_modify_principal in the process of changing a principal's password. This leads to a complication: what API handle should kadm5_chpass_principal pass to the other kadm5 functions it calls?
The ``obvious,'' but wrong, answer is that it should pass the handle it was given by its caller. The caller may provide an API handle specifying any valid API version. Although the semantics of kadm5_chpass_principal did not change between VERSION_1 and VERSION_2, the declarations of both kadm5_get_principal and kadm5_get_policy did. Thus, to use the caller's API handle, kadm5_chpass_principal will have to have a separate code path for each API version, even though it itself did not change bewteen versions, and duplicate a lot of logic found elsewhere in the library.
Instead, each API handle contains a ``local-use handle,'' or lhandle, that kadm5 functions should use to call other kadm5 functions. For example, the client-side library's handle structure is:
typedef struct _kadm5_server_handle_t { krb5_ui_4 magic_number; krb5_ui_4 struct_version; krb5_ui_4 api_version; char * cache_name; int destroy_cache; CLIENT * clnt; krb5_context context; kadm5_config_params params; struct _kadm5_server_handle_t *lhandle; } kadm5_server_handle_rec, *kadm5_server_handle_t;The lhandle field is allocated automatically when the handle is created. All of the fields of the API handle that are accessed outside kadm5_init are also duplicated in the lhandle; however, the api_version field of the lhandle is always set to a constant value, regardless of the API version specified by the caller to kadm5_init. In the current implementation, the lhandle's api_version is always VERSION_2.
By passing the caller's handle's lhandle to recursively called kadm5
functions, a kadm5 function is assured of invoking the second kadm5
function with a known API version. Additionally, the lhandle's
lhandle field points back to the lhandle, in case kadm5 functions call
themselves more than one level deep; handlelhandle always points
to the same lhandle, no matter how many times the indirection is
performed.
This scheme might break down if a kadm5 function has to call another kadm5 function to perform operations that they client will see and for its own benefit, since the semantics of the recursively-called kadm5 function may depend on the API version specified and the client may be depending on a particular version's behavior. Future implementators should avoid creating a situation in which this is possible.