1% This document is included for historical purposes only, and does not 2% apply to krb5 today. 3 4\documentstyle[12pt,fullpage]{article} 5 6%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 7%% Make _ actually generate an _, and allow line-breaking after it. 8\let\underscore=\_ 9\catcode`_=13 10\def_{\underscore\penalty75\relax} 11%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 12 13\setlength{\parskip}{.7\baselineskip} 14\setlength{\parindent}{0pt} 15 16\def\v#1{\verb+#1+} 17\def\k#1{K$_#1$} 18 19\title{KADM5 Library and Server \\ Implementation Design} 20\author{Barry Jaspan} 21 22\begin{document} 23 24\sloppy 25\maketitle 26 27{\setlength{\parskip}{0pt}\tableofcontents} 28 29\section{Overview} 30 31The KADM5 administration system is designed around the KADM5 API. The 32``server-side'' library libkadm5srv.a implements the KADM5 API by 33operating directly on the underlying KDC and admin databases. The 34``client-side'' library libkadm5clnt.a implements the KADM5 API via an 35RPC mechanism. The administration server kadmind accepts RPC requests 36from the client-side library and translates them into calls to the 37server-side library, performing authentication, authorization, and 38logging along the way. 39 40The two libraries, libkadm5clnt.a and libkadm5srv.a, export the 41identical kadm5 interface; for example, both contain definitions for 42kadm5_get_principal, and all other kadm5 functions. In most cases, 43the client library function just marshalls arguments and results into 44and out of an RPC call, whereas the server library function performs 45the actual operation on the database file. kadm5_init_*, however, are 46substantially different even though they export the same interface: on 47the client, they establish the RPC connection and GSS-API context, 48whereas on the server side the open the database files, read in the 49password dictionary, and the like. Also, the kadm5_free functions 50operate on local process memory in both libraries. 51 52The admin server is implemented as a nearly-stateless transaction 53server, where each admin API function represents a single transaction. 54No per-client or per-connection information is stored; only local 55database handles are maintained between requests. The RPC mechanism 56provides access to remote callers' authentication credentials for 57authorization purposes. 58 59The admin API is exported via an RPC interface that hides all details 60about network encoding, authentication, and encryption of data on the 61wire. The RPC mechanism does, however, allow the server to access the 62underlying authentication credentials for authorization purposes. 63 64The admin system maintains two databases: 65% 66\begin{itemize} 67\item The master Kerberos (KDC) database is used to store all the 68information that the Kerberos server understands, thus allowing the 69greatest functionality with no modifications to a standard KDC. 70 71\item The KDC database also stores kadm5-specific per-principal 72information in each principal's krb5_tl_data list. In a prior 73version, this data was stored in a separate admin principal database; 74thus, when this document refers to ``the admin principal database,'' 75it now refers to the appropriate krb5_tl_data entries in the KDC 76database. 77 78\item The policy database stores kadm5 policy information. 79\end{itemize} 80 81The per-principal information stored in the admin principal database 82consists of the principal's policy name and an array of the 83principal's previous keys. The old keys are stored encrypted in the 84key of the special principal ``kadmin/history'' that is created by the 85server library when it is first needed. Since a change in 86kadmin/history's key renders every principal's key history array 87useless, it can only be changed using the ovsec_adm_edit utility; that 88program will reencrypt every principal's key history in the new 89key.\footnote{ovsec_adm_edit has not yet been implemented, and there 90are currently no plans to implement it; thus, the history cannot 91currently be changed.} The server library refuses all requests to 92change kadmin/history's key. 93 94\section{API Handles} 95 96Each call to kadm5_init_* on the client or server creates a new API 97handle. The handles encapsulate the API and structure versions 98specified by kadm5_init_*'s caller and all other internal data needed 99by the library. A process can have multiple open API handles 100simultaneously by calling kadm5_init_* multiple times, and call can 101specify a different version, client or service principal, and so 102forth. 103 104Each kadm5 function verifies the handle it is given with the 105CHECK_HANDLE or _KADM5_CHECK_HANDLE macros. The CHECK_HANDLE macro 106differs for the client and server library because the handle types 107used by those libraries differ, so it is defined in both 108$<$client_internal.h$>$ and $<$server_internal.h$>$ in the library 109source directory. In each header file, CHECK_HANDLE first calls 110GENERIC_CHECK_HANDLE, defined in $<$admin_internal.h$>$, which 111verifies the magic number, API version, and structure version that is 112contained in both client and server handles. CHECK_HANDLE then calls 113either CLIENT_CHECK_HANDLE or SERVER_CHECK_HANDLE respectively to 114verify the client- or server-library specific handle fields. 115 116The CHECK_HANDLE macro is useful because it inlines the handle check 117instead of requiring a separate function call. However, using 118CHECK_HANDLE means that a source file cannot be compiled once and 119included into both the client and server library, because CHECK_HANDLE 120is always either specific to either the client or server library, not 121both. There are a number of functions that can be implemented with 122the same code in both the client and server libraries, however, 123including all of the kadm5_free functions and 124kadm5_chpass_principal_util. The _KADM5_CHECK_HANDLE macro solves 125this problem; instead of inlining the handle check, it calls the 126function _kadm5_check_handle which is defined separately in both the 127client and server library, in client_init.c and server_init.c. 128Since these two files are only compiled once and put in a single 129library, they simply verify the handle they are passed with 130CHECK_HANDLE and return the result. 131 132\section{API Versioning} 133 134The KADM5 system was designed by OpenVision to support multiple 135versions of the KADM5 API. MIT has not adopted this level of support, 136and considers the KADM5 C API to be unstable from release to release. 137This section describes the original design intent; bear in mind that 138only the most recent API is supported by current MIT krb5 releases, 139and that the API version does not necessarily change with API changes 140unless there is a need to do so for wire compatibility. 141 142Historically, three versions of the KADM5 API have existed: 143KADM5_API_VERSION_1 through KADM5_API_VERSION_3. The first version 144was equivalent to the initial OpenVision API, 145OVSEC_KADM_API_VERSION_1; the second was created during the initial 146integration of the OpenVision system into the MIT release; and the 147third was created for MIT krb5 1.8 to add lockout fields to policy 148entries. MIT dropped wire compatibility support for version 1 in MIT 149krb5 1.8 (as version 1 was never used in shipped MIT code), but 150retains wire compatibility support for version 2. 151 152Implementing a versioned API in C via with both local and RPC access 153presents a number of design issues, some of them quite subtle. The 154contexts in which versioning considerations must be made include: 155 156\begin{enumerate} 157\item Typedefs, function declarations, and defined constants depend on 158the API version a client is written to and must be correct at compile 159time. 160 161\item Each function in the server library must behave according to the 162API version specified by the caller at runtime to kadm5_init_*. 163 164\item The XDR functions used by the RPC layer to transmit function 165arguments and results must encode data structures correctly depending 166on the API version specified by the client at runtime. 167 168\item Each function in the client library must behave according to the 169API version specified by the caller at runtime to kadm5_init_*. 170 171\item The RPC server (kadmind) must accept calls from a client using 172any supported API version, and must then invoke the function in the 173server library corresponding to the RPC with the API version indicated 174by the client caller. 175 176\item When a first API function is invoked that needs to call a second 177function in the API on its own behalf, and that second API function's 178behavior depends on the API version specified, the first API function 179must either be prepared to call the second API function at whatever 180version its caller specifies or have a means of always calling the 181second API function at a pre-determined version. 182\end{enumerate} 183 184The following functions describe how each context is handled. 185 186\subsection{Designing for future compatibility} 187 188Any code whose behavior depends on the API version should be written 189so as to be compatible with future, currently unknown API versions on 190the grounds that any particular piece of API behavior will most 191likely not change between versions. For example, in the current 192system, the code is not written as ``if this is VERSION_1, do X, else 193if this is VERSION_2, do Y''; instead, it is written as ``if this is 194VERSION_1, do X; else, do Y.'' The former will require additional 195work when VERSION_3 is defined, even if ``do Y'' is still the correct 196action, whereas the latter will work without modification in that 197case. 198 199\subsection{Header file declarations} 200 201Typedefs, defined constants and macros, and function declarations may 202change between versions. A client is always written to a single, 203specific API version, and thus expects the header files to define 204everything according to that API. Failure of a header file to define 205values correctly will result in either compiler warnings (e.g. if the 206pointer type of a function argument changes) or fatal errors (e.g. if 207the number of arguments to a function changes, or the fields of a 208structure change). For example, in VERSION_1, kadm5_get_policy took a 209pointer to a pointer to a structure, and in VERSION_2 it takes a 210pointer to a structure; that would generate a warning if not correct. 211In VERSION_1, kadm5_randkey_principal accepted three arguments but in 212VERSION_2 accepts four; that would generate a fatal error. 213 214The header file defines everything correctly based on the value of the 215USE_KADM5_API_VERSION constant. The constant can be assigned to an 216integer corresponding to any supported API version, and defaults to 217the newest version. The header files then simply use an \#ifdef to 218include the right definitions: 219% 220\begin{verbatim} 221#if USE_KADM5_API_VERSION == 1 222kadm5_ret_t kadm5_get_principal(void *server_handle, 223 krb5_principal principal, 224 kadm5_principal_ent_t *ent); 225#else 226kadm5_ret_t kadm5_get_principal(void *server_handle, 227 krb5_principal principal, 228 kadm5_principal_ent_t ent, 229 long mask); 230#endif 231\end{verbatim} 232 233\subsection{Server library functions} 234 235Server library functions must know how many and what type of arguments 236to expect, and must operate on those arguments correctly, based on the 237API version with which they are invoked. The API version is contained 238in the handle that is always passed as their first argument, generated 239by kadm5_init_* (to which the client specified the API version to use 240at run-time). 241 242In general, it is probably unsafe for a compiled function in a library 243to re-interpret the number and type of defined arguments at run-time 244since the calling conventions may not allow it; for example, a 245function whose first argument was a short in one version and a pointer 246in the next might fail if it simply typed-casted the argument. In 247that case, the function would have to written to take variable 248arguments (i.e. use $<$stdarg.h$>$) and extract them from the stack 249based on the API version. Alternatively, a separate function for each 250API version could be defined, and $<$kadm5/admin.h$>$ could be written 251to \v{\#define} the exported function name based on the value of 252USE_KADM5_API_VERSION. 253 254In the current system, it turns out, that isn't necessary, and future 255implementors should take try to ensure that no version has semantics 256that will cause such problems in the future. All the functions in 257KADM5 that have different arguments or results between VERSION_1 and 258VERSION_2 do so simply by type-casting their arguments to the 259appropriate version and then have separate code paths to handle each 260one correctly. kadm5_get_principal, in svr_principal.c, is a good 261example. In VERSION_1, it took the address of a pointer to a 262kadm5_principal_ent_t to fill in with a pointer to allocated memory; 263in VERSION_2, it takes a pointer to a structure to fill in, and a mask 264of which fields in that structure should be filled in. Also, the 265contents of the kadm5_principal_ent_t changed slightly between the two 266versions. kadm5_get_principal handles versioning as follows 267(following along in the source code will be helpful): 268 269\begin{enumerate} 270\item If VERSION_1, it saves away its entry argument (address of a 271pointer to a structure) and resets its value to contain the address of 272a locally stack-allocated entry structure; this allows most of the 273function to written once, in terms of VERSION_2 semantics. If 274VERSION_1, it also resets its mask argument to be 275KADM5_PRINCIPAL_NORMAL_MASK, because that is the equivalent to 276VERSION_1 behavior, which was to return all the fields of the 277structure. 278 279\item The bulk of the function is implemented as expected for 280VERSION_2. 281 282\item The new fields in the VERSION_2 entry structure are assigned 283inside a block that is only execute if the caller specified 284VERSION_2. This saves a little time for a VERSION_1 caller. 285 286\item After the entry structure is filled, the function checks again 287if it was called as VERSION_1. If so, it allocates a new 288kadm5_principal_ent_t_v1 structure (which is conveniently defined in 289the header file) with malloc, copies the appropriate values from the 290entry structure into the VERSION_1 entry structure, and then writes 291the address of the newly allocated memory into address specified by 292the original entry argument which it had previously saved away. 293\end{enumerate} 294 295There is another complication involved in a function re-interpreting 296the number of arguments it receives at compile time---it cannot assign 297any value to an argument for which the client did not pass a value. 298For example, a VERSION_1 client only passes three arguments to 299kadm5_get_principal. If the implementation of kadm5_get_principal 300notices that the caller is VERSION_1 and therefore assigns its fourth 301argument, mask, to a value that mimics the VERSION_1 behavior, it may 302inadvertently overwrite data on its caller's stack. This problem can 303be avoided simply by using a true local variable in such cases, 304instead of treating an unpassed argument as a local variable. 305 306\subsection{XDR functions} 307 308The XDR functions used to encode function arguments and results must 309know how to encode the data for any API version. This is important 310both so that all the data gets correctly transmitted and so that 311protocol compatibility between clients or servers using the new 312library but an old API version is maintained; specific, new kadmind 313servers should support old kadm5 clients. 314 315The signature of all XDR functions is strictly defined: they take the 316address of an XDR function and the address of the data object to be 317encoded or decoded. It is thus impossible to provide the API version 318of the data object as an additional argument to an XDR function. 319There are two other means to convey the information, storing the API 320version to use as a field in the data object itself and creating 321separate XDR functions to handle each different version of the data 322object, and both of them are used in KADM5. 323 324In the client library, each kadm5 function collects its arguments into 325a single structure to be passed by the RPC; similarly, it expects all 326of the results to come back as a single structure from the RPC that it 327will then decode back into its constituent pieces (these are the 328standard ONC RPC semantics). In order to pass versioning information 329to the XDR functions, each function argument and result datatype has a 330filed to store the API version. For example, consider 331kadm5_get_principal's structures: 332% 333\begin{verbatim} 334struct gprinc_arg { 335 krb5_ui_4 api_version; 336 krb5_principal princ; 337 long mask; 338}; 339typedef struct gprinc_arg gprinc_arg; 340bool_t xdr_gprinc_arg(); 341 342struct gprinc_ret { 343 krb5_ui_4 api_version; 344 kadm5_ret_t code; 345 kadm5_principal_ent_rec rec; 346}; 347typedef struct gprinc_ret gprinc_ret; 348bool_t xdr_gprinc_ret(); 349\end{verbatim} 350% 351kadm5_get_principal (in client_principal.c) assigns the api_version 352field of the gprinc_arg to the version specified by its caller, 353assigns the princ field based on its arguments, and assigns the mask 354field from its argument if the caller specified VERSION_2. It then 355calls the RPC function clnt_call, specifying the XDR functions 356xdr_gprinc_arg and xdr_gprinc_ret to handle the arguments and results. 357 358xdr_gprinc_arg is invoked with a pointer to the gprinc_arg structure 359just described. It first encodes the api_version field; this allows 360the server to know what to expect. It then encodes the krb5_principal 361structure and, if api_version is VERSION_2, the mask. If api_version 362is not VERSION_2, it does not encode {\it anything} in place of the 363mask, because an old VERSION_1 server will not expect any other data 364to arrive on the wire there. 365 366The server performs the kadm5_get_principal call and returns its 367results in an XDR encoded gprinc_ret structure. clnt_call, which has 368been blocking until the results arrived, invokes xdr_gprinc_ret with a 369pointer to the encoded data for it to decode. xdr_gprinc_ret first 370decodes the api_version field, and then the code field since that is 371present in all versions to date. The kadm5_principal_ent_rec presents 372a problem, however. The structure does not itself contain an 373api_version field, but the structure is different between the two 374versions. Thus, a single XDR function cannot decode both versions of 375the structure because it will have no way to decide which version to 376expect. The solution is to have two functions, 377kadm5_principal_ent_rec_v1 and kadm5_principal_ent_rec, which always 378decode according to VERSION_1 or VERSION_2, respectively. gprinc_ret 379knows which one to invoke because it has the api_version field 380returned by the server (which is always the same as that specified by 381the client in the gpring_arg). 382 383In hindsight, it probably would have been better to encode the API 384version of all structures directly in a version field in the structure 385itself; then multiple XDR functions for a single data type wouldn't be 386necessary, and the data objects would stand complete on their own. 387This can be added in a future API version if desired. 388 389\subsection{Client library functions} 390 391Just as with server library functions, client library functions must 392be able to interpret their arguments and provide result according to 393the API version specified by the caller. Again, kadm5_get_principal 394(in client_principal.c) is a good example. The gprinc_ret structure 395that it gets back from clnt_call contains a kadm5_principal_ent_rec or 396a kadm5_principal_ent_rec_v1 (the logic is simplified somewhat because 397the VERSION_2 structure only has new fields added on the end). If 398kadm5_get_principal was invoked with VERSION_2, that structure should 399be copied into the pointer provided as the entry argument; if it was 400invoked with VERSION_1, however, the structure should be copied into 401allocated memory whose address is then written into the pointer 402provided by the entry argument. Client library functions make this 403determination based on the API version specified in the provided 404handle, just like server library functions do. 405 406\subsection{Admin server stubs} 407 408When an RPC call arrives at the server, the RPC layer authenticates 409the call using the GSS-API, decodes the arguments into their 410single-structure form (ie: a gprinc_arg) and dispatches the call to a 411stub function in the server (in server_stubs.c). The stub function 412first checks the caller's authorization to invoke the function and, if 413authorized, calls the kadm5 function corresponding to the RPC function 414with the arguments specified in the single-structure argument. 415 416Once again, kadm5_get_principal is a good example for the issues 417involved. The contents of the gprinc_arg given to the stub 418(get_principal_1) depends on the API version the caller on the client 419side specified; that version is available to the server in the 420api_version field of the gprinc_arg. When the server calls 421kadm5_get_principal in the server library, it must give that function 422an API handle that contains the API version requested by the client; 423otherwise the function semantics might not be correct. One 424possibility would be for the server to call kadm5_init for each client 425request, specifying the client's API version number and thus generating 426an API handle with the correct version, but that would be 427prohibitively inefficient. Instead, the server dips down in the 428server library's internal abstraction barrier, using the function 429new_server_handle to cons up a server handle based on the server's own 430global_server_handle but using the API version specified by the 431client. The server then passes the newly generated handle to 432kadm5_get_principal, ensuring the right behavior, and creates the 433gprinc_ret structure in a manner similar to that described above. 434 435Although new_server_handle solves the problem of providing the server 436with an API handle containing the right API version number, it does 437not solve another problem: that a single source file, server_stubs.c, 438needs to be able to invoke functions with arguments appropriate for 439multiple API versions. If the client specifies VERSION_1, for 440example, the server must invoke kadm5_get_principal with three 441arguments, but if the client specifies VERSION_2 the server must 442invoke kadm5_get_principal with four arguments. The compiler will not 443allow this inconsistency. The server defines wrapper functions in a 444separate source file that match the old version, and the separate 445source file is compiled with USE_KADM5_API_VERSION set to the old 446version; see kadm5_get_principal_v1 in server_glue_v1.c. The server 447then calls the correct variant of kadm5_get_principal_* based on the 448API version and puts the return values into the gprinc_ret in a manner 449similar to that described above. 450 451Neither of these solutions are necessarily correct. new_server_handle 452violates the server library's abstraction barrier and is at best a 453kludge; the server library should probably export a function to 454provide this behavior without violating the abstraction; 455alternatively, the librar should be modified so that having the server 456call kadm5_init for each client RPC request would not be too 457inefficient. The glue functions in server_glue_v1.c really are not 458necessary, because the server stubs could always just pass dummy 459arguments for the extra arguments; after all, the glue functions pass 460{\it nothing} for the extra arguments, so they just end up as stack 461garbage anyway. 462 463Another alternative to the new_server_handle problem is to have the 464server always invoke server library functions at a single API version, 465and then have the stubs take care of converting the function arguments 466and results back into the form expected by the caller. In general, 467however, this might require the stubs to duplicate substantial logic 468already present in the server library and further violate the server 469library's abstraction barrier. 470 471\subsection{KADM5 self-reference} 472 473Some kadm5 functions call other kadm5 functions ``on their own 474behalf'' to perform functionality that is necessary but that does not 475directly affect what the client sees. For example, 476kadm5_chpass_principal has to enforce password policies; thus, it 477needs to call kadm5_get_principal and, if the principal has a policy, 478kadm5_get_policy and kadm5_modify_principal in the process of changing 479a principal's password. This leads to a complication: what API handle 480should kadm5_chpass_principal pass to the other kadm5 functions it 481calls? 482 483The ``obvious,'' but wrong, answer is that it should pass the handle 484it was given by its caller. The caller may provide an API handle 485specifying any valid API version. Although the semantics of 486kadm5_chpass_principal did not change between VERSION_1 and VERSION_2, 487the declarations of both kadm5_get_principal and kadm5_get_policy 488did. Thus, to use the caller's API handle, kadm5_chpass_principal 489will have to have a separate code path for each API version, even 490though it itself did not change between versions, and duplicate a lot 491of logic found elsewhere in the library. 492 493Instead, each API handle contains a ``local-use handle,'' or lhandle, 494that kadm5 functions should use to call other kadm5 functions. For 495example, the client-side library's handle structure is: 496% 497\begin{verbatim} 498typedef struct _kadm5_server_handle_t { 499 krb5_ui_4 magic_number; 500 krb5_ui_4 struct_version; 501 krb5_ui_4 api_version; 502 char * cache_name; 503 int destroy_cache; 504 CLIENT * clnt; 505 krb5_context context; 506 kadm5_config_params params; 507 struct _kadm5_server_handle_t *lhandle; 508} kadm5_server_handle_rec, *kadm5_server_handle_t; 509\end{verbatim} 510% 511The lhandle field is allocated automatically when the handle is 512created. All of the fields of the API handle that are accessed 513outside kadm5_init are also duplicated in the lhandle; however, the 514api_version field of the lhandle is always set to a {\it constant} 515value, regardless of the API version specified by the caller to 516kadm5_init. In the current implementation, the lhandle's api_version 517is always VERSION_2. 518 519By passing the caller's handle's lhandle to recursively called kadm5 520functions, a kadm5 function is assured of invoking the second kadm5 521function with a known API version. Additionally, the lhandle's 522lhandle field points back to the lhandle, in case kadm5 functions call 523themselves more than one level deep; handle$->$lhandle always points 524to the same lhandle, no matter how many times the indirection is 525performed. 526 527This scheme might break down if a kadm5 function has to call another 528kadm5 function to perform operations that they client will see and for 529its own benefit, since the semantics of the recursively-called kadm5 530function may depend on the API version specified and the client may be 531depending on a particular version's behavior. Future implementors 532should avoid creating a situation in which this is possible. 533 534\section{Server Main} 535 536The admin server starts by trapping all fatal signals and directing 537them to a cleanup-and-exit function. It then creates and exports the 538RPC interface and enters its main loop. 539 540The main loop dispatches all incoming requests to the RPC mechanism. 541In a previous version, after 15 seconds of inactivity, the server 542closed all open databases; each database was be automatically reopened 543by the API function implementations as necessary. That behavior 544existed to protect against loss of written data before the process 545exited. The current database libraries write all changes out to disk 546immediately, however, so this behavior is no longer required or 547performed. 548 549\section{Remote Procedure Calls} 550 551The RPC for the Admin system will be based on ONC RPC. ONC RPC is 552used because it is a well-known, portable RPC mechanism. The 553underlying external data representation (xdr) mechanisms for wire 554encapsulation are well-known and extensible. Authentication to the 555admin server and encryption of all RPC functional arguments and 556results are be handled via the AUTH_GSSAPI authentication flavor of 557ONC RPC. 558 559\section{Database Record Types} 560\label{sec:db-types} 561 562\subsection{Admin Principal, osa_princ_ent_t} 563 564The admin principal database stores records of the type 565osa_princ_ent_t (declared in $<$kadm5/adb.h$>$), which is the 566subset of the kadm5_principal_ent_t structure that is not stored 567in the Kerberos database plus the necessary bookkeeping information. 568The records are keyed by the ASCII representation of the principal's 569name, including the trailing NULL. 570 571\begin{verbatim} 572typedef struct _osa_pw_hist_t { 573 int n_key_data; 574 krb5_key_data *key_data; 575} osa_pw_hist_ent, *osa_pw_hist_t; 576 577typedef struct _osa_princ_ent_t { 578 char * policy; 579 u_int32 aux_attributes; 580 581 unsigned int old_key_len; 582 unsigned int old_key_next; 583 krb5_kvno admin_history_kvno; 584 osa_pw_hist_ent *old_keys; 585 586 587 u_int32 num_old_keys; 588 u_int32 next_old_key; 589 krb5_kvno admin_history_kvno; 590 osa_pw_hist_ent *old_keys; 591} osa_princ_ent_rec, *osa_princ_ent_t; 592\end{verbatim} 593 594The fields that are different from kadm5_principal_ent_t are: 595 596\begin{description} 597\item[num_old_keys] The number of previous keys in the old_keys array. 598This value must be 0 $\le$ num_old_keys $<$ pw_history_num. 599 600\item[old_key_next] The index into old_keys where the next key should 601be inserted. This value must be 0 $\le$ old_key_next $\le$ 602num_old_keys. 603 604\item[admin_history_kvno] The key version number of the kadmin/history 605principal's key used to encrypt the values in old_keys. If the server 606library finds that kadmin/history's kvno is different from the value 607in this field, it returns KADM5_BAD_HIST_KEY. 608 609\item[old_keys] The array of the principal's previous passwords, each 610encrypted in the kadmin/history key. There are num_old_keys 611elements. Each ``password'' in the array is itself an array of 612n_key_data krb5_key_data structures, one for each keysalt type the 613password was encoded in. 614\end{description} 615 616\subsection{Policy, osa_policy_ent_t} 617 618The policy database stores records of the type osa_policy_ent_t 619(declared in $<$kadm5/adb.h$>$) , which is all of 620kadm5_policy_ent_t plus necessary bookkeeping information. The 621records are keyed by the policy name. 622 623\begin{verbatim} 624typedef struct _osa_policy_ent_t { 625 char *policy; 626 627 u_int32 pw_min_life; 628 u_int32 pw_max_life; 629 u_int32 pw_min_length; 630 u_int32 pw_min_classes; 631 u_int32 pw_history_num; 632 633 u_int32 refcnt; 634} osa_policy_ent_rec, *osa_policy_ent_t; 635\end{verbatim} 636 637\subsection{Kerberos, krb5_db_entry} 638 639The Kerberos database stores records of type krb5_db_entry, which is 640defined in the $<$k5-int.h$>$ header file. The semantics of each 641field are defined in the libkdb functional specification. 642 643\section{Database Access Methods} 644 645\subsection{Principal and Policy Databases} 646 647This section describes the database abstraction used for the admin 648policy database; the admin principal database used to be treated in 649the same manner but is now handled more directly as krb5_tl_data; 650thus, nothing in this section applies to it any more. Since both 651databases export equivalent functionality, the API is only described 652once. The character T is used to represent both ``princ'' and 653``policy''. The location of the principal database is defined by the 654configuration parameters given to any of the kadm5_init functions in 655the server library. 656 657Note that this is {\it only} a database abstraction. All functional 658intelligence, such as maintaining policy reference counts or sanity 659checking, must be implemented above this layer. 660 661Prototypes for the osa functions are supplied in 662$<$kadm5/adb.h$>$. The routines are defined in libkadm5srv.a. They 663require linking with the Berkely DB library. 664 665\subsubsection{Error codes} 666 667The database routines use com_err for error codes. The error code 668table name is ``adb'' and the offsets are the same as the order 669presented here. The error table header file is 670$<$kadm5/adb_err.h$>$. Callers of the OSA routines should first call 671init_adb_err_tbl() to initialize the database table. 672 673\begin{description} 674\item[OSA_ADB_OK] Operation successful. 675\item[OSA_ADB_FAILURE] General failure. 676\item[OSA_ADB_DUP] Operation would create a duplicate database entry. 677\item[OSA_ADB_NOENT] Named entry not in database. 678\item[OSA_ADB_BAD_PRINC] The krb5_principal structure is invalid. 679\item[OSA_ADB_BAD_POLICY] The specified policy name is invalid. 680\item[OSA_ADB_XDR_FAILURE] The principal or policy structure cannot be 681encoded for storage. 682\item[OSA_ADB_BADLOCKMODE] Bad lock mode specified. 683\item[OSA_ADB_CANTLOCK_DB] Cannot lock database, presumably because it 684is already locked. 685\item[OSA_ADB_NOTLOCKED] Internal error, database not locked when 686unlock is called. 687\item[OSA_ADB_NOLOCKFILE] KADM5 administration database lock file missing. 688\end{description} 689 690Database functions can also return system errors. Unless otherwise 691specified, database functions return OSA_ADB_OK. 692 693\subsubsection{Locking} 694 695All of the osa_adb functions except open and close lock and unlock the 696database to prevent concurrency collisions. The overall locking 697algorithm is as follows: 698 699\begin{enumerate} 700\item osa_adb_open_T calls osa_adb_init_db to allocate the osa_adb_T_t 701structure and open the locking file for further use. 702 703\item Each osa_adb functions locks the locking file and opens the 704appropriate database with osa_adb_open_and_lock, performs its action, 705and then closes the database and unlocks the locking file with 706osa_adb_close_and_unlock. 707 708\item osa_adb_close_T calls osa_adb_fini_db to close the locking file 709and deallocate the db structure. 710\end{enumerate} 711 712Functions which modify the database acquire an exclusive lock, others 713acquire a shared lock. osa_adb_iter_T acquires an exclusive lock for 714safety but as stated below consequences of modifying the database in 715the iteration function are undefined. 716 717\subsubsection{Function descriptions} 718 719\begin{verbatim} 720osa_adb_ret_t osa_adb_create_T_db(kadm5_config_params *params) 721\end{verbatim} 722% 723Create the database and lockfile specified in params. The database 724must not already exist, or EEXIST is returned. The lock file is only 725created after the database file has been created successfully. 726 727\begin{verbatim} 728osa_adb_ret_t osa_adb_rename_T_db(kadm5_config_params *fromparams, 729 kadm5_config_params *toparams) 730\end{verbatim} 731% 732Rename the database named by fromparams to that named by toparams. 733The fromparams database must already exist; the toparams database may 734exist or not. When the function returns, the database named by 735fromparams no longer exists, and toparams has been overwritten with 736fromparams. This function acquires a permanent lock on both databases 737for the duration of its operation, so a failure is likely to leave the 738databases unusable. 739 740\begin{verbatim} 741osa_adb_ret_t osa_adb_destroy_policy_db(kadm5_config_params *params) 742\end{verbatim} 743% 744Destroy the database named by params. The database file and lock file 745are deleted. 746 747\begin{verbatim} 748osa_adb_ret_t 749osa_adb_open_T(osa_adb_T_t *db, char *filename); 750\end{verbatim} 751% 752Open the database named filename. Returns OSA_ADB_NOLOCKFILE if the 753database does not exist or if the lock file is missing. The database 754is not actually opened in the operating-system file sense until a lock 755is acquire. 756 757\begin{verbatim} 758osa_adb_ret_t 759osa_adb_close_T(osa_adb_T_t db); 760\end{verbatim} 761% 762Release all shared or exclusive locks (on BOTH databases, since they 763use the same lock file) and close the database. 764 765It is an error to exit while a permanent lock is held; 766OSA_ADB_NOLOCKFILE is returned in this case. 767 768\begin{verbatim} 769osa_adb_ret_t osa_adb_get_lock(osa_adb_T_t db, int mode) 770\end{verbatim} 771 772Acquire a lock on the administration databases; note that both 773databases are locked simultaneously by a single call. The mode 774argument can be OSA_ADB_SHARED, OSA_ADB_EXCLUSIVE, or 775OSA_ADB_PERMANENT. The first two and the third are really disjoint 776locking semantics and should not be interleaved. 777 778Shared and exclusive locks have the usual semantics, and a program can 779upgrade a shared lock to an exclusive lock by calling the function 780again. A reference count of open locks is maintained by this function 781and osa_adb_release_lock so the functions can be called multiple 782times; the actual lock is not released until the final 783osa_adb_release_lock. Note, however, that once a lock is upgraded 784from shared to exclusive, or from exclusive to permanent, it is not 785downgraded again until released completely. In other words, 786get_lock(SHARED), get_lock(EXCLUSIVE), release_lock() leaves the 787process with an exclusive lock with a reference count of one. An 788attempt to get a shared or exclusive lock that conflicts with another 789process results in the OSA_ADB_CANLOCK_DB error code. 790 791This function and osa_adb_release_lock are called automatically as 792needed by all other osa_adb functions to acquire shared and exclusive 793locks and so are not normally needed. They can be used explicitly by 794a program that wants to perform multiple osa_adb functions within the 795context of a single lock. 796 797Acquiring an OSA_ADB_PERMANENT lock is different. A permanent lock 798consists of first acquiring an exclusive lock and then {\it deleting 799the lock file}. Any subsequent attempt to acquire a lock by a 800different process will fail with OSA_ADB_NOLOCKFILE instead of 801OSA_ADB_CANTLOCK_DB (attempts in the same process will ``succeed'' 802because only the reference count gets incremented). The lock file is 803recreated by osa_adb_release_lock when the last pending lock is released. 804 805The purpose of a permanent lock is to absolutely ensure that the 806database remain locked during non-atomic operations. If the locking 807process dies while holding a permanent lock, all subsequent osa_adb 808operations will fail, even through a system reboot. This is useful, 809for example, for ovsec_adm_import which creates both new database 810files in a temporary location and renames them into place. If both 811renames do not fully complete the database will probably be 812inconsistent and everything should stop working until an administrator 813can clean it up. 814 815\begin{verbatim} 816osa_adb_ret_t osa_adb_release_lock(osa_adb_T_t db) 817\end{verbatim} 818 819Releases a shared, exclusive, or permanent lock acquired with 820osa_adb_get_lock, or just decrements the reference count if multiple 821locks are held. When a permanent lock is released, the lock file is 822re-created. 823 824All of a process' shared or exclusive database locks are released when 825the process terminates. A permanent lock is {\it not} released when 826the process exits (although the exclusive lock it begins with 827obviously is). 828 829\begin{verbatim} 830osa_adb_ret_t 831osa_adb_create_T(osa_adb_T_t db, osa_T_ent_t entry); 832\end{verbatim} 833% 834Adds the entry to the database. All fields are defined. Returns 835OSA_ADB_DUP if it already exists. 836 837\begin{verbatim} 838osa_adb_ret_t 839osa_adb_destroy_T(osa_adb_T_t db, osa_T_t name); 840\end{verbatim} 841 842Removes the named entry from the database. Returns OSA_ADB_NOENT if 843it does not exist. 844 845\begin{verbatim} 846osa_adb_ret_t 847osa_adb_get_T(osa_adb_T_t db, osa_T_t name, 848 osa_princ_ent_t *entry); 849\end{verbatim} 850 851Looks up the named entry in the db, and returns it in *entry in 852allocated storage that must be freed with osa_adb_free_T. Returns 853OSA_ADB_NOENT if name does not exist, OSA_ADB_MEM if memory cannot be 854allocated. 855 856\begin{verbatim} 857osa_adb_ret_t 858osadb_adb_put_T(osa_adb_T_t db, osa_T_ent_t entry); 859\end{verbatim} 860 861Modifies the existing entry named in entry. All fields must be filled 862in. Returns OSA_DB_NOENT if the named entry does not exist. Note 863that this cannot be used to rename an entry; rename is implemented by 864deleting the old name and creating the new one (NOT ATOMIC!). 865 866\begin{verbatim} 867void osa_adb_free_T(osa_T_ent_t); 868\end{verbatim} 869 870Frees the memory associated with an osa_T_ent_t allocated by 871osa_adb_get_T. 872 873\begin{verbatim} 874typedef osa_adb_ret_t (*osa_adb_iter_T_func)(void *data, 875 osa_T_ent_t entry); 876 877osa_adb_ret_t osa_adb_iter_T(osa_adb_T_t db, osa_adb_iter_T_func func, 878 void *data); 879\end{verbatim} 880 881Iterates over every entry in the database. For each entry ent in the 882database db, the function (*func)(data, ent) is called. If func 883returns an error code, osa_adb_iter_T returns an error code. If all 884invocations of func return OSA_ADB_OK, osa_adb_iter_T returns 885OSA_ADB_OK. The function func is permitted to access the database, 886but the consequences of modifying the database during the iteration 887are undefined. 888 889\subsection{Kerberos Database} 890 891Kerberos uses the libkdb interface to store krb5_db_entry records. It 892can be accessed and modified in parallel with the Kerberos server, 893using functions that are defined inside the KDC and the libkdb.a. The 894libkdb interface is defined in the libkdb functional specifications. 895 896\subsubsection{Initialization and Key Access} 897 898Keys stored in the Kerberos database are encrypted in the Kerberos 899master key. The admin server will therefore have to acquire the key 900before it can perform any key-changing operations, and will have to 901decrypt and encrypt the keys retrieved from and placed into the 902database via krb5_db_get_principal and _put_principal. This section 903describes the internal admin server API that will be used to perform 904these functions. 905 906\begin{verbatim} 907krb5_principal master_princ; 908krb5_encrypt_block master_encblock; 909krb5_keyblock master_keyblock; 910 911void kdc_init_master() 912\end{verbatim} 913 914kdc_init_master opens the database and acquires the master key. It 915also sets the global variables master_princ, master_encblock, and 916master_keyblock: 917 918\begin{itemize} 919\item master_princ is set to the name of the Kerberos master principal 920(\v{K/M@REALM}). 921 922\item master_encblock is something I have no idea about. 923 924\item master_keyblock is the Kerberos master key 925\end{itemize} 926 927\begin{verbatim} 928krb5_error_code kdb_get_entry_and_key(krb5_principal principal, 929 krb5_db_entry *entry, 930 krb5_keyblock *key) 931\end{verbatim} 932 933kdb_get_entry_and_key retrieves the named principal's entry from the 934database in entry, and decrypts its key into key. The caller must 935free entry with krb5_dbm_db_free_principal and free key-$>$contents with 936free.\footnote{The caller should also \v{memset(key-$>$contents, 0, 937key-$>$length)}. There should be a function krb5_free_keyblock_contents 938for this, but there is not.} 939 940\begin{verbatim} 941krb5_error_code kdb_put_entry_pw(krb5_db_entry *entry, char *pw) 942\end{verbatim} 943 944kdb_put_entry_pw stores entry in the database. All the entry values 945must already be set; this function does not change any of them except 946the key. pw, the NULL-terminated password string, is converted to a 947key using string-to-key with the salt type specified in 948entry-$>$salt_type.\footnote{The salt_type should be set based on the 949command line arguments to the kadmin server (see the ``Command Line'' 950section of the functional specification).} 951 952\section{Admin Principal and Policy Database Implementation} 953 954The admin principal and policy databases will each be stored in a 955single hash table, implemented by the Berkeley 4.4BSD db library. 956Each record will consist of an entire osa_T_ent_t. The key into the 957hash table is the entry name (for principals, the ASCII representation 958of the name). The value is the T entry structure. Since the key and 959data must be self-contained, with no pointers, the Sun xdr mechanisms 960will be used to marshal and unmarshal data in the database. 961 962The server in the first release will be single-threaded in that a 963request will run to completion (or error) before the next will run, 964but multiple connections will be allowed simultaneously. 965 966\section{ACLs, acl_check} 967 968The ACL mechanism described in the ``Authorization ACLs'' section of 969the functional specifications will be implemented by the acl_check 970function. 971 972\begin{verbatim} 973enum access_t { 974 ACCESS_DENIED = 0, 975 ACCESS_OK = 1, 976}; 977 978enum access_t acl_check(krb5_principal princ, char *priv); 979\end{verbatim} 980 981The priv argument must be one of ``get'', ``add'', ``delete'', or 982``modify''. acl_check returns 1 if the principal princ has the named 983privilege, 0 if it does not. 984 985\section{Function Details} 986 987This section discusses specific design issues for Admin API functions 988that are not addressed by the functional specifications. 989 990\subsection{kadm5_create_principal} 991 992If the named principal exists in either the Kerberos or admin 993principal database, but not both, return KADM5_BAD_DB. 994 995The principal's initial key is not stored in the key history array at 996creation time. 997 998\subsection{kadm5_delete_principal} 999 1000If the named principal exists in either the Kerberos or admin 1001principal database, but not both, return KADM5_BAD_DB. 1002 1003\subsection{kadm5_modify_principal} 1004 1005If the named principal exists in either the Kerberos or admin 1006principal database, but not both, return KADM5_BAD_DB. 1007 1008If pw_history_num changes and the new value $n$ is smaller than the 1009current value of num_old_keys, old_keys should end up with the $n$ 1010most recent keys; these are found by counting backwards $n$ elements 1011in old_keys from old_key_next. old_key_nexts should then be reset to 10120, the oldest of the saved keys, and num_old_keys set to $n$, the 1013new actual number of old keys in the array. 1014 1015\subsection{kadm5_chpass_principal, randkey_principal} 1016 1017The algorithm for determining whether a password is in the principal's 1018key history is complicated by the use of the kadmin/history \k{h} 1019encrypting key. 1020 1021\begin{enumerate} 1022\item For kadm5_chpass_principal, convert the password to a key 1023using string-to-key and the salt method specified by the command line 1024arguments. 1025 1026\item If the POLICY bit is set and pw_history_num is not zero, check 1027if the new key is in the history. 1028\begin{enumerate} 1029\item Retrieve the principal's current key and decrypt it with \k{M}. 1030If it is the same as the new key, return KADM5_PASS_REUSE. 1031\item Retrieve the kadmin/history key \k{h} and decrypt it with \k{M}. 1032\item Encrypt the principal's new key in \k{h}. 1033\item If the principal's new key encrypted in \k{h} is in old_keys, 1034return KADM5_PASS_REUSE. 1035\item Encrypt the principal's current key in \k{h} and store it in 1036old_keys. 1037\item Erase the memory containing \k{h}. 1038\end{enumerate} 1039 1040\item Encrypt the principal's new key in \k{M} and store it in the 1041database. 1042\item Erase the memory containing \k{M}. 1043\end{enumerate} 1044 1045To store the an encrypted key in old_keys, insert it as the 1046old_key_next element of old_keys, and increment old_key_next by one 1047modulo pw_history_num. 1048 1049\subsection{kadm5_get_principal} 1050 1051If the named principal exists in either the Kerberos or admin 1052principal database, but not both, return KADM5_BAD_DB. 1053 1054\end{document} 1055