summaryrefslogtreecommitdiffstats
path: root/doc/kadm5/api-server-design.tex
diff options
context:
space:
mode:
authorBarry Jaspan <bjaspan@mit.edu>1993-10-07 15:23:51 +0000
committerBarry Jaspan <bjaspan@mit.edu>1993-10-07 15:23:51 +0000
commitba5c693c1fb575e6e35fd159659e0c5873af1cf2 (patch)
tree33a0f4e583b4ad68ef711b542875c8e4e921e770 /doc/kadm5/api-server-design.tex
parente426b8353e56d4e751b54a5ebeb4644eef112b8a (diff)
downloadkrb5-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.tex576
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}