diff options
author | Barry Jaspan <bjaspan@mit.edu> | 1993-10-07 15:23:51 +0000 |
---|---|---|
committer | Barry Jaspan <bjaspan@mit.edu> | 1993-10-07 15:23:51 +0000 |
commit | ba5c693c1fb575e6e35fd159659e0c5873af1cf2 (patch) | |
tree | 33a0f4e583b4ad68ef711b542875c8e4e921e770 /doc/kadm5/api-server-design.tex | |
parent | e426b8353e56d4e751b54a5ebeb4644eef112b8a (diff) | |
download | krb5-ba5c693c1fb575e6e35fd159659e0c5873af1cf2.tar.gz krb5-ba5c693c1fb575e6e35fd159659e0c5873af1cf2.tar.xz krb5-ba5c693c1fb575e6e35fd159659e0c5873af1cf2.zip |
Initial revision
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2650 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'doc/kadm5/api-server-design.tex')
-rw-r--r-- | doc/kadm5/api-server-design.tex | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/doc/kadm5/api-server-design.tex b/doc/kadm5/api-server-design.tex new file mode 100644 index 0000000000..a955cf2d14 --- /dev/null +++ b/doc/kadm5/api-server-design.tex @@ -0,0 +1,576 @@ +\documentstyle[12pt,fullpage,changebar]{article} + +% $Id$ + +\setlength{\parskip}{.7\baselineskip} +\setlength{\parindent}{0pt} +\setcounter{secnumdepth}{4} +\setcounter{tocdepth}{4} + +\def\secure{OV*Secure} +\def\v#1{\verb+#1+} + +\title{OV*Secure Admin Server \\ Implementation Design} +\author{} +\date{\today} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Make _ actually generate an _, and allow line-breaking after it. +\let\underscore=\_ +\catcode`_=13 +\def_{\underscore\penalty75\relax} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\begin{document} + +\maketitle + +{\setlength{\parskip}{0pt}\tableofcontents} + +This section describes the implementation design for the admin server. + +\section{Overview} + +The admin server is implemented as a nearly-stateless transaction +server, where each admin API function represents a single transaction. +No per-client or per-connection information is stored; only local +database handles are maintained between requests. + +The admin API is exported via an RPC interface that hides all details +about network encoding, authentication, and encryption of data on the +wire. The RPC mechanism does, however, allow the server to access the +underlying authentication credentials for authorization purposes. + +The admin server accesses a total of three databases. + +\begin{itemize} +\item The master Kerberos database is used to store all the +information that the Kerberos server understands, thus allowing the +greatest functionality with no modifications to a standard KDC. + +\item The admin principal database stores \secure{}-specific per-principal +information. + +\item The policy database stores \secure{} policy information. +\end{itemize} + +\section{Main} + +The admin server starts by trapping all fatal signals and directing +them to a cleanup-and-exit function. It then creates and exports the +RPC interface and enters its main loop. + +The main loop dispatches all incoming requests to the RPC mechanism. +After 15 seconds of inactivity, the server closes all open databases; +each database will be automatically reopened by the API function +implementations as necessary. + +\section{Remote Procedure Calls} + +The RPC for the Admin system will be based on SUNRPC. SUNRPC is used +because it is a well-known, portable RPC mechanism. The underlying +external data representation (xdr) mechanisms for wire encapsulation +are well-known and extensible. + +%The RPC specifics can be found in \verb+~access/src/rpc/acl.x+. +%[This, and all the other rpcgen files referenced in this document +%should be in appendices at some point.] Supporting data types are in +%\verb+~access/src/common/api_types.x+. + +Authentication to the admin server will be handled by adding a GSS-API +authentication type within the existing SUNRPC structure. This will +require code modifications to SUNRPC, but the API and wire protocol do +not need to change. This may affect whether the RPC will use UDP or +TCP; although all the admin functions are stateless, the GSS-API +authentication binding will not be and it might be easier to use TCP +for this reason. + +\section{Database Record Types} +\label{sec:db-types} + +\subsection{Admin Principal, osa_princ_ent_t} + +The admin principal database stores records of the type +osa_princ_ent_t, which is the subset of the ovsec_kadm_principal_ent_t +structure that is not stored in the Kerberos database plus the +necessary bookkeeping information. The records are keyed by the ASCII +representation of the principal's name, including the trailing NULL. + +\begin{verbatim} +#define MAX_PW_HIST 10 + +typedef struct _osa_princ_ent_t { + krb5_principal name; + + char * policy; + u_int32 pw_min_life; + u_int32 pw_max_life; + u_int32 pw_min_length; + u_int32 pw_min_classes; + u_int32 pw_history_num; + u_int32 aux_attributes; + + krb5_keyblock old_keys[MAX_PW_HIST]; + u_int32 next_old_key; + + u_int32 expansion[8]; +} osa_princ_ent_rec, *osa_princ_ent_t; +\end{verbatim} + +The only fields that are different from ovsec_kadm_principal_ent_t are +old_keys and next_key. + +\begin{description} +\item[old_keys] The array of previous keys used for password history +checking. Only pw_history_num of these keys should be checked. + +\item[next_key] The index into old_keys where the next key should be +inserted. This value is always computed modulo pw_history_num. +\end{description} + +\subsection{Policy, osa_policy_ent_t} + +The policy database stores records of the type osa_policy_ent_t, which +is all of ovsec_kadm_policy_ent_t plus necessary bookkeeping +information. The records are keyed by the policy name. + +\begin{verbatim} +typedef struct _osa_policy_ent_t { + char *policy; + + u_int32 pw_min_life; + u_int32 pw_max_life; + u_int32 pw_min_length; + u_int32 pw_min_classes; + u_int32 pw_history_num; + + u_int32 refcnt; + u_int32 expansion[8]; +} osa_policy_ent_rec, *osa_policy_ent_t; +\end{verbatim} + +\subsection{Kerberos, krb5_db_entry} + +The Kerberos database stores records of type krb5_db_entry, which is +defined in the kdb.h header file. + +\begin{verbatim} +typedef struct _krb5_encrypted_keyblock { + krb5_keytype keytype; + int length; + krb5_octet *contents; +} krb5_encrypted_keyblock; + +typedef struct _krb5_db_entry { + krb5_principal principal; + krb5_encrypted_keyblock key; + krb5_kvno kvno; + krb5_deltat max_life; + krb5_deltat max_renewable_life; + krb5_kvno mkvno; + + krb5_timestamp expiration; + krb5_timestamp pw_expiration; + krb5_timestamp last_pwd_change; + krb5_timestamp last_success; + + krb5_timestamp last_failed; + krb5_kvno fail_auth_count; + + krb5_principal mod_name; + krb5_timestamp mod_date; + krb5_flags attributes; + krb5_int32 salt_type:8, + salt_length:24; + krb5_octet *salt; + krb5_encrypted_keyblock alt_key; + krb5_int32 alt_salt_type:8, + alt_salt_length:24; + krb5_octet *alt_salt; + + krb5_int32 expansion[8]; +} krb5_db_entry; +\end{verbatim} + +The interpretation of most of these fields is the same as given in the +``Principals, ovsec_kadm_principal_ent_t'' section of the functional +specification. The fields that are not defined there are not used by +\secure{}; however, the admin server preserves the value of any fields +it does not understand. + +\section{Database Access Methods} + +\subsection{Principal and Policy Databases} + +This section describes the database abstraction used for the admin +principal and policy databases. Since both databases export +equivalent functionality, the API is only described once. The +character T is used to represent both ``princ'' and ``policy''. + +Note that this is {\it only} a database abstraction. All functional +intelligence, such as maintaining policy reference counts or sanity +checking, must be implemented above this layer. + +The database routines use com_err for error codes. The error code +table name is ``kadb'' and the offsets are the same as the order +presented here. + +\begin{description} +\item[OSA_ADB_OK] Operation successful. +\item[OSA_ADB_DUP] Operation would create a duplicate database entry. +\item[OSA_ADB_NOENT] Named entry not in database. +\item[OSA_ADB_MEM] Out of memory performing operation. +\item[OSA_ADB_FAILURE] General failure. +\end{description} + +Unless otherwise specified, database functions return OSA_ADB_OK. + +\begin{verbatim} +osa_adb_ret_t +osa_adb_open_T(osa_adb_T_t *db); +\end{verbatim} +% +Open the database. Returns OSA_ADB_FAILURE if it cannot open the +database. + +\begin{verbatim} +osa_adb_ret_t +osa_adb_close_T(osa_adb_T_t db); +\end{verbatim} +% +Close an open database. + +\begin{verbatim} +osa_adb_ret_t +osa_adb_create_T(osa_adb_T_t db, ovsec_kadm_T_ent_t entry); +\end{verbatim} +% +Adds the entry to the database. All fields are defined. Returns +OSA_ADB_DUP if it already exists. + +\begin{verbatim} +osa_adb_ret_t +osa_adb_destroy_T(osa_adb_T_t db, ovsec_kadm_T_t name); +\end{verbatim} + +Removes the named entry from the database. Returns OSA_ADB_NOENT if +it does not exist. + +\begin{verbatim} +osa_adb_ret_t +osa_adb_get_T(osa_adb_T_t db, ovsec_kadm_T_t name, + ovsec_kadm_ent_T_t *entry); +\end{verbatim} + +Looks up the named entry in the db, and returns it in *entry in +allocated storage that must be freed with osa_adb_free_T. Returns +OSA_ADB_NOENT if name does not exist, OSA_ADB_MEM if memory cannot be +allocated. + +\begin{verbatim} +osa_adb_ret_t +osadb_adb_put_T(osa_adb_T_t db, ovsec_kadm_T_ent_t entry); +\end{verbatim} + +Modifies the existing entry named in entry. All fields must be filled +in. Returns OSA_DB_NOENT if the named entry does not exist. Note +that this cannot be used to rename an entry; rename is implemented by +deleting the old name and creating the new one (NOT ATOMIC!). + +\subsection{Kerberos Database} + +Kerberos uses dbm to store krb5_db_entry records. It can be accessed +and modified in parallel with the Kerberos server, using functions +that are defined inside the KDC and the libkdb.a. + +\subsubsection{Database Manipulation Functions} + +The following functions are declared in \v{lib/kdb/kdb_dbm.c} in the +Kerberos sources and are available in libkdb.a. They can return the +following error codes; error codes that can be returned by any +function are indicated with a ``*'' and are not listed specifically +for each function. + +\begin{description} +\item[* KRB5_KDB_NOTINITED] The database is not open; call +krb5_dbm_db_init. +\item[* KRB5_KDB_CANTLOCK_DB] The necessary lock cannot be acquired. Try +again later. +\item[* system errors] An error occurred accessing the database files. +\item[KRB5_KDB_DB_INUSE] The database was modified without the use +of proper locking.\footnote{This error occurs when the entire database +is swapped out from the under the process, say by a kdb5_edit restore. +It can only be returned by krb5_db_get_principal. It is not yet clear +what a program should do when it gets this error.} +\item[KRB5_KDB_NOENTRY] The principal to be deleted is not +in the database. +\end{description} + +\begin{verbatim} +krb5_dbm_db_init(void) +\end{verbatim} + +Opens the Kerberos database file (but does not actually call +dbm_open). This can be called even if the database is already open, +in which case it just returns success. + +\begin{verbatim} +krb5_dbm_db_fini(void) +\end{verbatim} + +Closes the database file; this MUST be called before the process +exits. Returns KRB5_KDB_DBNOTINITED if the database isn't open, but +that isn't really a fatal error. + +\begin{verbatim} +krb5_dbm_get_principal(krb5_principal searchfor, + krb5_db_entry *entries, int *nentries, krb5_boolean *more) +\end{verbatim} + +Search the database for the principal searchfor and write the results +into *entries. The interface is set up to handle wildcard gets, but +the code doesn't handle it: *nentries is assumed to be 1, and *more is +always returned as 0. + +This function does not retry if the database cannot be locked; that is +up to the caller. + +Returns KRB5_KDB_DB_INUSE. + +\begin{verbatim} +krb5_dbm_put_principal(krb5_db_entry *entries, int *nentries) +\end{verbatim} + +Stores *nentries elements from the entries array into the database. +On return *nentries is set to the number of entries actually written; +the first *nentries entries will have been written, even if an error +pis returned. + +This function does not retry if the database cannot be locked; that is +up to the caller. + +\begin{verbatim} +krb5_dbm_db_delete_principal(krb5_principal searchfor, int *nentries) +\end{verbatim} + +Removes the principal searchfor from the database. nentries will be +set to 0 or 1 on output, indicating the number of entries deleted (the +code does not currently support wildcards). + +Returns KRB5_KDB_NOENTRY. + +\begin{verbatim} +typedef krb5_error_code (*iter_func)(krb5_pointer, krb5_db_entry *); + +krb5_dbm_db_iterate(iter_func func, krb5_point func_arg) +\end{verbatim} + +Calls (*func)(func_arg, entry) for every entry in the database. If +func returns an error code, the iteration stops and that error code is +returned. + +Returns func error codes. + +\begin{verbatim} +void krb5_dbm_db_free_principal(krb5_db_entry *entries, int nentries) +\end{verbatim} + +Frees entries returned by krb5_dbm_db_get_principal. nentries entries +in the array entries will be freed. + +\subsubsection{Access Initialization} + +Before it is possible to access keys from the Kerberos database, the +Kerberos master key must be acquired so that it can be used to encrypt +and decrypt principal keys as necessary. + +Accessing principal keys from the Kerberos database is not as simple +as calling krb5_dbm_db_get_principal. The following function (from +the \v{krb524} server) performs the necessary initialization. + +\begin{verbatim} +#include <krb5/krb5.h> +#include <krb5/asn1.h> +#include <krb5/kdb.h> +#include <krb5/kdb_dbm.h> +#ifdef PROVIDE_DES_CBC_CRC +#include <krb5/mit-des.h> +#endif + +krb5_principal master_princ; +krb5_encrypt_block master_encblock; +krb5_keyblock master_keyblock; + +void init_master() +{ + int ret; + char *realm; + + if (ret = krb5_get_default_realm(&realm)) { + com_err(whoami, ret, "getting default realm"); + cleanup_and_exit(1); + } + if (ret = krb5_db_setup_mkey_name(NULL, realm, (char **) 0, + &master_princ)) { + com_err(whoami, ret, "while setting up master key name"); + cleanup_and_exit(1); + } + +#ifdef PROVIDE_DES_CBC_CRC + master_encblock.crypto_entry = &mit_des_cryptosystem_entry; +#else + error(You gotta figure out what cryptosystem to use in the KDC); +#endif + + master_keyblock.keytype = KEYTYPE_DES; + if (ret = krb5_db_fetch_mkey(master_princ, &master_encblock, + FALSE, /* non-manual type-in */ + FALSE, /* irrelevant, given prev. arg */ + 0, &master_keyblock)) { + com_err(whoami, ret, "while fetching master key"); + cleanup_and_exit(1); + } + + if (ret = krb5_db_init()) { + com_err(whoami, ret, "while initializing master database"); + cleanup_and_exit(1); + } + if (ret = krb5_process_key(&master_encblock, &master_keyblock)) { + krb5_db_fini(); + com_err(whoami, ret, "while processing master key"); + cleanup_and_exit(1); + } +} +\end{verbatim} + +\subsubsection{Reading Principals and Keys} + +The following function, taken from \v{krb524d} and modified somewhat, +shows how to read a principal from the database and decrypt its key. +server, key, and kvno must all point to allocated storage that will be +filled in. The returned values must be freed with +krb5_db_free_principal and krb5_free_keyblock_contents. + +\begin{verbatim} +krb5_error_code kdc_get_server(service, server, key, kvno) + krb5_principal service; + krb5_db_entry *server; + krb5_keyblock *key; + krb5_kvno *kvno; +{ + krb5_error_code ret; + int nprincs; + krb5_boolean more; + + nprincs = 1; + if (ret = krb5_db_get_principal(service, server, &nprincs, &more)) + return(ret); + + if (more) { + krb5_db_free_principal(server, nprincs); + return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE); + } else if (nprincs != 1) { + krb5_db_free_principal(server, nprincs); + return(KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN); + } + + /* + * convert server.key into a real key (it is encrypted in the + * database) + */ + ret = krb5_kdc_decrypt_key(&master_encblock, server->key, key); + if (kvno) + *kvno = server->kvno; + return ret; +} +\end{verbatim} + +\subsubsection{Writing Principals and Keys} + +I don't know how to do this yet, but the code is contained in +\v{src/admin/edit/kdb5_edit.c}. + +\section{Admin Principal and Policy Database Implementation} + +The admin principal and policy databases will each be stored in a +single hash table, implemented by the Berkeley 4.4BSD db library. +Each record will consist of an entire osa_T_ent_t. The key into the +hash table is the entry name (for principals, the ASCII representation +of the name). The value is the T entry structure. Since the key and +data must be self-contained, with no pointers, the Sun xdr mechanisms +will be used to marshal and unmarshal data in the database. + +The server in the first release will be single-threaded in that a +request will run to completion (or error) before the next will run, +but multiple connections will be allowed simultaneously. + +\section{ACLs, acl_check} + +The ACL mechanism described in the ``Authorization ACLs'' section of +the functional specifications will be implemented by the acl_check +function. + +\begin{verbatim} +enum access_t { + ACCESS_DENIED = 0, + ACCESS_OK = 1, +}; + +enum access_t acl_check(krb5_principal princ, char *priv); +\end{verbatim} + +The priv argument must be one of ``get'', ``add'', ``delete'', or +``modify''. acl_check returns 1 if the principal princ has the named +privilege, 0 if it does not. + +\section{Function Details} + +This section discusses specific design issues for Admin API functions +that are not addresed by the functional specifications. + +\subsection{ovsec_kadm_create_principal} + +If the named principal exists in either the Kerberos or admin +principal database, but not both, return OVSEC_KADM_BAD_DB. + +Initialize the elements of the key history array to contain DES keys +of all zero bits; such a key is guaranteed to be invalid (it does not +have proper parity) and so will never generate a false +OVSEC_KADM_PASS_REUSE error. + +\subsection{ovsec_kadm_delete_principal} + +If the named principal exists in either the Kerberos or admin +principal database, but not both, return OVSEC_KADM_BAD_DB. + +\subsection{ovsec_kadm_modify_principal} + +If the named principal exists in either the Kerberos or admin +principal database, but not both, return OVSEC_KADM_BAD_DB. + +If PW_HISTORY is specified and the new value $n$ is smaller than the +old value, copy the key values so that the $n$ most recent keys end up +in the part of the history array that will be checked by +ovsec_kadm_chpass_principal. If the new value $n$ is larger than the +old value, initialize the previously unused elements to all have DES +keys with all zero bits. + +\subsection{ovsec_kadm_chpass_principal} + +When comparing the new key to the elements of the key history array, +always compare it to the first pw_history_num values. Even if fewer +than pw_history_num keys have been placed into the array, the create +and modify functions guarantee that the unused elements will be +invalid DES keys and will therefore not result in a false +OVSEC_KADM_PASS_REUSE error. + +Insert the new key as the next_key element in the history array, and +increment next_key by one modulo pw_history_num. + +\subsection{ovsec_kadm_get_principal} + +If the named principal exists in either the Kerberos or admin +principal database, but not both, return OVSEC_KADM_BAD_DB. + + +\end{document} |