1 \documentstyle[12pt,fullpage,changebar,rcsid]{article}
3 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4 %% Make _ actually generate an _, and allow line-breaking after it.
7 \def_{\underscore\penalty75\relax}
8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
12 \setlength{\parskip}{.7\baselineskip}
13 \setlength{\parindent}{0pt}
15 \def\secure{OV*Secure}
19 \title{OV*Secure Admin Server \\ Implementation Design\thanks{\rcsId}}
27 {\setlength{\parskip}{0pt}\tableofcontents}
31 The admin server is implemented as a nearly-stateless transaction
32 server, where each admin API function represents a single transaction.
33 No per-client or per-connection information is stored; only local
34 database handles are maintained between requests.
36 The admin API is exported via an RPC interface that hides all details
37 about network encoding, authentication, and encryption of data on the
38 wire. The RPC mechanism does, however, allow the server to access the
39 underlying authentication credentials for authorization purposes.
41 The admin server accesses a total of three databases.
43 \item The master Kerberos database is used to store all the
44 information that the Kerberos server understands, thus allowing the
45 greatest functionality with no modifications to a standard KDC.
47 \item The admin principal database stores \secure{}-specific per-principal
50 \item The policy database stores \secure{} policy information.
53 The per-principal information stored in the admin principal database
54 consists of the principal's policy name and an array of the
55 principal's previous keys. The old keys are stored encrypted in the
56 key of the special principal ``ovsec_adm/history'' that is created by
57 ovsec_adm_create. Since a change in ovsec_adm/history's key renders
58 every principal's key history array useless, it can only be changed
59 using the ovsec_adm_edit utility; that program will reencrypt every
60 principal's key history in the new key.\footnote{ovsec_adm_edit has
61 not yet been implemented, and there are currently no plans to
62 implement it.} The admin server refuses all requests to change
63 ovsec_adm/history's key.
67 The admin server starts by trapping all fatal signals and directing
68 them to a cleanup-and-exit function. It then creates and exports the
69 RPC interface and enters its main loop.
71 The main loop dispatches all incoming requests to the RPC mechanism.
72 After 15 seconds of inactivity, the server closes all open databases;
73 each database will be automatically reopened by the API function
74 implementations as necessary.
76 \section{Remote Procedure Calls}
78 The RPC for the Admin system will be based on SUNRPC. SUNRPC is used
79 because it is a well-known, portable RPC mechanism. The underlying
80 external data representation (xdr) mechanisms for wire encapsulation
81 are well-known and extensible.
83 Authentication to the admin server will be handled by adding a GSS-API
84 authentication type within the existing SUNRPC structure. This will
85 require code modifications to SUNRPC, but the API and wire protocol do
86 not need to change. This may affect whether the RPC will use UDP or
87 TCP; although all the admin functions are stateless, the GSS-API
88 authentication binding will not be and it might be easier to use TCP
91 \section{Database Record Types}
94 \subsection{Admin Principal, osa_princ_ent_t}
96 The admin principal database stores records of the type
97 osa_princ_ent_t (declared in $<$ovsec_admin/adb.h$>$), which is the
98 subset of the ovsec_kadm_principal_ent_t structure that is not stored
99 in the Kerberos database plus the necessary bookkeeping information.
100 The records are keyed by the ASCII representation of the principal's
101 name, including the trailing NULL.
104 typedef struct _osa_princ_ent_t {
108 u_int32 aux_attributes;
110 u_int32 num_old_keys;
111 u_int32 next_old_key;
112 krb5_kvno admin_history_kvno;
113 krb5_encrypted_keyblock *old_keys;
114 } osa_princ_ent_rec, *osa_princ_ent_t;
117 The fields that are different from ovsec_kadm_principal_ent_t are:
120 \item[num_old_keys] The number of previous keys in the old_keys array.
121 This value must be 0 $\le$ num_old_keys $<$ pw_history_num.
123 \item[next_old_key] The index into old_keys where the next key should
124 be inserted. This value must be 0 $\le$ next_old_key $\le$
127 \item[admin_history_kvno] The key version number of the admin/history
128 principal's key used to encrypt the values in old_keys. If the admin
129 server finds that admin/history's kvno is different from the value in
130 this field, an error message is logged. (XXX where?)
132 \item[old_keys] The array of the principal's previous keys, each
133 encrypted in the admin/history key. There are num_old_keys elements.
136 \subsection{Policy, osa_policy_ent_t}
138 The policy database stores records of the type osa_policy_ent_t
139 (declared in $<$ovsec_admin/adb.h$>$) , which is all of
140 ovsec_kadm_policy_ent_t plus necessary bookkeeping information. The
141 records are keyed by the policy name.
144 typedef struct _osa_policy_ent_t {
149 u_int32 pw_min_length;
150 u_int32 pw_min_classes;
151 u_int32 pw_history_num;
154 } osa_policy_ent_rec, *osa_policy_ent_t;
157 \subsection{Kerberos, krb5_db_entry}
159 The Kerberos database stores records of type krb5_db_entry, which is
160 defined in the $<$krb5/kdb.h$>$ header file.
163 typedef struct _krb5_encrypted_keyblock {
164 krb5_keytype keytype;
166 krb5_octet *contents;
167 } krb5_encrypted_keyblock;
169 typedef struct _krb5_db_entry {
170 krb5_principal principal;
171 krb5_encrypted_keyblock key;
173 krb5_deltat max_life;
174 krb5_deltat max_renewable_life;
177 krb5_timestamp expiration;
178 krb5_timestamp pw_expiration;
179 krb5_timestamp last_pwd_change;
180 krb5_timestamp last_success;
182 krb5_timestamp last_failed;
183 krb5_kvno fail_auth_count;
185 krb5_principal mod_name;
186 krb5_timestamp mod_date;
187 krb5_flags attributes;
188 krb5_int32 salt_type:8,
191 krb5_encrypted_keyblock alt_key;
192 krb5_int32 alt_salt_type:8,
194 krb5_octet *alt_salt;
196 krb5_int32 expansion[8];
200 The interpretation of most of these fields is the same as given in the
201 ``Principals, ovsec_kadm_principal_ent_t'' section of the functional
202 specification. The fields that are not defined there are not used by
203 \secure{}; however, the admin server preserves the value of any fields
204 it does not understand.
206 \section{Database Access Methods}
208 \subsection{Principal and Policy Databases}
210 This section describes the database abstraction used for the admin
211 principal and policy databases. Since both databases export
212 equivalent functionality, the API is only described once. The
213 character T is used to represent both ``princ'' and ``policy''. The
214 location of the principal database is defined by the \#define
215 PRINCIPAL_DB (``/krb5/ovsec_principal.db'') in $<$ovsec_admin/adb.h$>$. The
216 location of the policy database is defined by the \#define POLICY_DB
217 (``/krb5/ovsec_policy.db'') in $<$ovsec_admin/adb.h$>$.
219 Note that this is {\it only} a database abstraction. All functional
220 intelligence, such as maintaining policy reference counts or sanity
221 checking, must be implemented above this layer.
223 Prototypes for the osa functions are supplied in
224 $<$ovsec_admin/adb.h$>$. The routines can be found (in the first
225 relase) in ``stage/lib/libadb.a''. They require linking with the
226 Berkely DB library (``stage/lib/libdb.a''). [Note: We needed to remove
227 the dbm compatibility routines from libdb.a because we want to leave
228 KDB library alone in case somebody wants to run a stock MIT KDC with
231 The database routines use com_err for error codes. The error code
232 table name is ``adb'' and the offsets are the same as the order
233 presented here. The error table header file is
234 $<$ovsec_admin/adb_err.h$>$. Callers of the OSA routines should first call
235 init_adb_err_tbl() to initialize the database table.
238 \item[OSA_ADB_OK] Operation successful.
239 \item[OSA_ADB_FAILURE] General failure.
240 \item[OSA_ADB_DUP] Operation would create a duplicate database entry.
241 \item[OSA_ADB_NOENT] Named entry not in database.
242 \item[OSA_ADB_BAD_PRINC] The krb5_principal structure is invalid.
243 \item[OSA_ADB_BAD_POLICY] The specified policy name is invalid.
244 \item[OSA_ADB_XDR_FAILURE] The principal or policy structure cannot be
248 Database functions can also return system errors. Unless otherwise
249 specified, database functions return OSA_ADB_OK.
253 osa_adb_open_T(osa_adb_T_t *db, char *filename);
256 Open the database named filename. Returns OSA_ADB_FAILURE if it
257 cannot open the database.
261 osa_adb_close_T(osa_adb_T_t db);
264 Close an open database.
268 osa_adb_create_T(osa_adb_T_t db, osa_T_ent_t entry);
271 Adds the entry to the database. All fields are defined. Returns
272 OSA_ADB_DUP if it already exists.
276 osa_adb_destroy_T(osa_adb_T_t db, osa_T_t name);
279 Removes the named entry from the database. Returns OSA_ADB_NOENT if
284 osa_adb_get_T(osa_adb_T_t db, osa_T_t name,
285 osa_princ_ent_t *entry);
288 Looks up the named entry in the db, and returns it in *entry in
289 allocated storage that must be freed with osa_adb_free_T. Returns
290 OSA_ADB_NOENT if name does not exist, OSA_ADB_MEM if memory cannot be
295 osadb_adb_put_T(osa_adb_T_t db, osa_T_ent_t entry);
298 Modifies the existing entry named in entry. All fields must be filled
299 in. Returns OSA_DB_NOENT if the named entry does not exist. Note
300 that this cannot be used to rename an entry; rename is implemented by
301 deleting the old name and creating the new one (NOT ATOMIC!).
304 void osa_adb_free_T(osa_T_ent_t);
307 Frees the memory associated with an osa_T_ent_t allocated by
311 typedef osa_adb_ret_t (*osa_adb_iter_T_func)(void *data,
314 osa_adb_ret_t osa_adb_iter_T(osa_adb_T_t db, osa_adb_iter_T_func func,
318 Iterates over every entry in the database. For each entry ent in the
319 database db, the function (*func)(data, ent) is called. If func
320 returns an error code, osa_adb_iter_T returns an error code. If all
321 invokations of func return OSA_ADB_OK, osa_adb_iter_T returns
322 OSA_ADB_OK. The function func is permitted to access the database,
323 but the consequences of modifying the database during the iteration
326 \subsection{Kerberos Database}
328 Kerberos uses dbm to store krb5_db_entry records. It can be accessed
329 and modified in parallel with the Kerberos server, using functions
330 that are defined inside the KDC and the libkdb.a.
332 \subsubsection{Database Manipulation Functions}
334 The following functions are declared in \v{lib/kdb/kdb_dbm.c} in the
335 Kerberos sources and are available in libkdb.a. They can return the
336 following error codes; error codes that can be returned by any
337 function are indicated with a ``*'' and are not listed specifically
341 \item[* KRB5_KDB_NOTINITED] The database is not open; call
343 \item[* KRB5_KDB_CANTLOCK_DB] The necessary lock cannot be acquired. Try
345 \item[* system errors] An error occurred accessing the database files.
346 \item[KRB5_KDB_DB_INUSE] The database was modified without the use
347 of proper locking.\footnote{This error occurs when the entire database
348 is swapped out from the under the process, say by a kdb5_edit restore.
349 It can only be returned by krb5_db_get_principal. It is not yet clear
350 what a program should do when it gets this error.}
351 \item[KRB5_KDB_NOENTRY] The principal to be deleted is not
356 krb5_dbm_db_init(void)
359 Opens the Kerberos database file (but does not actually call
360 dbm_open). This can be called even if the database is already open,
361 in which case it just returns success.
364 krb5_dbm_db_fini(void)
367 Closes the database file; this MUST be called before the process
368 exits. Returns KRB5_KDB_DBNOTINITED if the database isn't open, but
369 that isn't really a fatal error.
372 krb5_dbm_get_principal(krb5_principal searchfor,
373 krb5_db_entry *entries, int *nentries, krb5_boolean *more)
376 Search the database for the principal searchfor and write the results
377 into *entries. The interface is set up to handle wildcard gets, but
378 the code doesn't handle it: *nentries is assumed to be 1, and *more is
379 always returned as 0.
381 This function does not retry if the database cannot be locked; that is
384 Returns KRB5_KDB_DB_INUSE.
387 krb5_dbm_put_principal(krb5_db_entry *entries, int *nentries)
390 Stores *nentries elements from the entries array into the database.
391 On return *nentries is set to the number of entries actually written;
392 the first *nentries entries will have been written, even if an error
395 This function does not retry if the database cannot be locked; that is
399 krb5_dbm_db_delete_principal(krb5_principal searchfor, int *nentries)
402 Removes the principal searchfor from the database. nentries will be
403 set to 0 or 1 on output, indicating the number of entries deleted (the
404 code does not currently support wildcards).
406 Returns KRB5_KDB_NOENTRY.
409 typedef krb5_error_code (*iter_func)(krb5_pointer, krb5_db_entry *);
411 krb5_dbm_db_iterate(iter_func func, krb5_point func_arg)
414 Calls (*func)(func_arg, entry) for every entry in the database. If
415 func returns an error code, the iteration stops and that error code is
418 Returns func error codes.
421 void krb5_dbm_db_free_principal(krb5_db_entry *entries, int nentries)
424 Frees entries returned by krb5_dbm_db_get_principal. nentries entries
425 in the array entries will be freed.
427 \subsubsection{Initialization and Key Access}
429 Keys stored in the Kerberos database are encrypted in the Kerberos
430 master key. The admin server will therefore have to acquire the key
431 before it can perform any key-changing operations, and will have to
432 decrypt and encrypt the keys retrieved from and placed into the
433 database via krb5_db_get_principal and _put_principal. This section
434 describes the internal admin server API that will be used to perform
438 krb5_principal master_princ;
439 krb5_encrypt_block master_encblock;
440 krb5_keyblock master_keyblock;
442 void kdc_init_master()
445 kdc_init_master opens the database and acquires the master key. It
446 also sets the global variables master_princ, master_encblock, and
450 \item master_princ is set to the name of the Kerberos master principal
453 \item master_encblock is something I have no idea about.
455 \item master_keyblock is the Kerberos master key
459 krb5_error_code kdb_get_entry_and_key(krb5_principal principal,
460 krb5_db_entry *entry,
464 kdb_get_entry_and_key retrieves the named principal's entry from the
465 database in entry, and decrypts its key into key. The caller must
466 free entry with krb5_dbm_db_free_principal and free key-$>$contents with
467 free.\footnote{The caller should also \v{memset(key-$>$contents, 0,
468 key-$>$length)}. There should be a function krb5_free_keyblock_contents
469 for this, but there is not.}
472 krb5_error_code kdb_put_entry_pw(krb5_db_entry *entry, char *pw)
475 kdb_put_entry_pw stores entry in the database. All the entry values
476 must already be set; this function does not change any of them except
477 the key. pw, the NULL-terminated password string, is converted to a
478 key using string-to-key with the salt type specified in
479 entry-$>$salt_type.\footnote{The salt_type should be set based on the
480 command line arguments to the kadmin server (see the ``Command Line''
481 section of the functional specification).}
483 \section{Admin Principal and Policy Database Implementation}
485 The admin principal and policy databases will each be stored in a
486 single hash table, implemented by the Berkeley 4.4BSD db library.
487 Each record will consist of an entire osa_T_ent_t. The key into the
488 hash table is the entry name (for principals, the ASCII representation
489 of the name). The value is the T entry structure. Since the key and
490 data must be self-contained, with no pointers, the Sun xdr mechanisms
491 will be used to marshal and unmarshal data in the database.
493 The server in the first release will be single-threaded in that a
494 request will run to completion (or error) before the next will run,
495 but multiple connections will be allowed simultaneously.
497 \section{ACLs, acl_check}
499 The ACL mechanism described in the ``Authorization ACLs'' section of
500 the functional specifications will be implemented by the acl_check
509 enum access_t acl_check(krb5_principal princ, char *priv);
512 The priv argument must be one of ``get'', ``add'', ``delete'', or
513 ``modify''. acl_check returns 1 if the principal princ has the named
514 privilege, 0 if it does not.
516 \section{Function Details}
518 This section discusses specific design issues for Admin API functions
519 that are not addresed by the functional specifications.
521 \subsection{ovsec_kadm_create_principal}
523 If the named principal exists in either the Kerberos or admin
524 principal database, but not both, return OVSEC_KADM_BAD_DB.
526 The principal's initial key is not stored in the key history array at
529 \subsection{ovsec_kadm_delete_principal}
531 If the named principal exists in either the Kerberos or admin
532 principal database, but not both, return OVSEC_KADM_BAD_DB.
534 \subsection{ovsec_kadm_modify_principal}
536 If the named principal exists in either the Kerberos or admin
537 principal database, but not both, return OVSEC_KADM_BAD_DB.
539 If pw_history_num changes and the new value $n$ is smaller than the
540 current value of num_old_keys, old_keys should end up with the $n$
541 most recent keys; these are found by counting backwards $n$ elements
542 in old_keys from next_old_key. next_old_keys should then be reset to
543 0, the oldest of the saved keys, and num_old_keys set to $n$, the
544 new actual number of old keys in the array.
546 \subsection{ovsec_kadm_chpass_principal, randkey_principal}
548 The algorithm for determining whether a password is in the principal's
549 key history is complicated by the use of the kadmin/history \k{h}
553 \item For ovsec_kadm_chpass_principal, convert the password to a key
554 using string-to-key and the salt method specified by the command line
557 \item If the POLICY bit is set and pw_history_num is not zero, check
558 if the new key is in the history.
560 \item Retrieve the principal's current key and decrypt it with \k{M}.
561 If it is the same as the new key, return OVSEC_KADM_PASS_REUSE.
562 \item Retrieve the kadmin/history key \k{h} and decrypt it with \k{M}.
563 \item Encrypt the principal's new key in \k{h}.
564 \item If the principal's new key encrypted in \k{h} is in old_keys,
565 return OVSEC_KADM_PASS_REUSE.
566 \item Encrypt the principal's current key in \k{h} and store it in
568 \item Erase the memory containing \k{h}.
571 \item Encrypt the principal's new key in \k{M} and store it in the
573 \item Erase the memory containing \k{M}.
576 To store the an encrypted key in old_keys, insert it as the
577 next_old_key element of old_keys, and increment next_old_key by one
578 modulo pw_history_num.
580 \subsection{ovsec_kadm_get_principal}
582 If the named principal exists in either the Kerberos or admin
583 principal database, but not both, return OVSEC_KADM_BAD_DB.