*** empty log message ***
authorBarry Jaspan <bjaspan@mit.edu>
Fri, 8 Oct 1993 22:07:20 +0000 (22:07 +0000)
committerBarry Jaspan <bjaspan@mit.edu>
Fri, 8 Oct 1993 22:07:20 +0000 (22:07 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2651 dc483132-0cff-0310-8789-dd5450dbe970

doc/kadm5/api-funcspec.tex
doc/kadm5/api-server-design.tex

index 56602541b81f7793ae225cfe60d4cac70674cfa6..f3605e5b7d4a1756e886a2181242fab6957bd108 100644 (file)
@@ -10,7 +10,7 @@
 
 \title{OV*Secure Admin \\ Functional Specifications}
 \author{}
-\date{\today}
+\date{DRAFT --- \today}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %% Make _ actually generate an _, and allow line-breaking after it.
@@ -68,34 +68,26 @@ in the dictionary will not be accepted.
 component and the realm of the principal's name will not be accepted.
 \end{itemize}
 
-\subsection{Principals, ovsec_kadm_principal_ent_t}
+\subsection{Data Structures}
+
+This section describes the data structures used by the Admin API that
+are unique to \secure{}.
+
+\subsubsection{Principals, ovsec_kadm_principal_ent_t}
 \label{sec:principal-structure}
 
 A Kerberos principal entry is represented by a
 ovsec_kadm_principal_ent_t.  It contains a subset of the information
 stored in the master Kerberos database as well as the additional
-information maintained by \secure{}.  The new principal information
-consists of a named password policy (\v{policy}) and optional
-overrides for each field of the password policy (\v{pw_*}).  
-
-The policy name and override fields may or may not be enabled
-depending on the value of the aux_attributes bitmask (see section
-\ref{sec:masks}).  Specfically:
-
-\begin{itemize}
-\item For each PW_* bit set in aux_attributes, the corresponding pw_*
-field is enforced on the principal.
-
-\item If the POLICY bit is set in aux_attributes, then for each PW_*
-bit that is {\it not} set in aux_attributes, the corresponding pw_*
-field from the named policy is enforced on the principal.
+information maintained by \secure{}.  In the current version, the only
+additional information is the principal's policy and the
+aux_attributes flags.
 
-\item If the POLICY bit is not set, then for each PW_* bit that is
-{\it not} set in aux_attributes, no corresponding policy is enforced.
-\end{itemize}
-
-For a retrieved principal, the value of the policy or pw_* fields when
-the corresponding bits in aux_attributes are not set is undefined.
+The principal may or may not have a policy enforced on it.  If the
+POLICY bit (see section \ref{sec:masks}) is set in aux_attributes, the
+policy field names the principal's policy.  If the POLICY bit is not
+set in aux_attributes, no policy is enforced on the principal and the
+value of the policy field is undefined.
 
 \begin{figure}[htbp]
 \begin{verbatim}
@@ -113,11 +105,6 @@ typedef struct _ovsec_kadm_principal_ent_t {
         krb5_mkvno mkvno;
 
         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;
 } ovsec_kadm_principal_ent_rec, *ovsec_kadm_principal_ent_t;
 \end{verbatim}
@@ -126,8 +113,7 @@ typedef struct _ovsec_kadm_principal_ent_t {
 \end{figure}
 
 The fields of an ovsec_kadm_principal_ent_t are interpreted as
-follows.  For the semantics of the policy overriding fields, see
-\ref{sec:policy-fields}.
+follows.
 
 \begin{description}
 \item[principal] The name of the principal; must conform to Kerberos
@@ -148,24 +134,25 @@ user can still obtain ticket-granting tickets.
 \item[max_life] The maximum lifetime of any Kerberos ticket issued to
 this principal.
 
-\item[attributes] A bitfield of attributes for use by the KDC.  The
-bits are as follows.  Note that only DISALLOW_ALL_TIX and
-REQUIRES_PWCHANGE are explicitly supported by \secure{}.
-
-\begin{verbatim}
-KRB5_KDB_DISALLOW_POSTDATED     0x00000001
-KRB5_KDB_DISALLOW_FORWARDABLE   0x00000002
-KRB5_KDB_DISALLOW_TGT_BASED     0x00000004
-KRB5_KDB_DISALLOW_RENEWABLE     0x00000008
-KRB5_KDB_DISALLOW_PROXIABLE     0x00000010
-KRB5_KDB_DISALLOW_DUP_SKEY      0x00000020
-KRB5_KDB_DISALLOW_ALL_TIX       0x00000040
-KRB5_KDB_REQUIRES_PRE_AUTH      0x00000080
-KRB5_KDB_REQUIRES_HW_AUTH       0x00000100
-KRB5_KDB_REQUIRES_PWCHANGE      0x00000200
-KRB5_KDB_DISALLOW_SVR           0x00001000
-KRB5_KDB_PWCHANGE_SERVICE       0x00002000
-\end{verbatim}
+\item[attributes] A bitfield of attributes for use by the KDC.  
+Note that only some are explicitly supported by \secure{}.  XXX Note:
+We really should check what all of these mean.
+
+\begin{tabular}{clr}
+{\bf Supported} & {\bf Name} & {\bf Value} \\
+  & KRB5_KDB_DISALLOW_POSTDATED     & 0x00000001 \\
+  & KRB5_KDB_DISALLOW_FORWARDABLE   & 0x00000002 \\
+X & KRB5_KDB_DISALLOW_TGT_BASED     & 0x00000004 \\
+  & KRB5_KDB_DISALLOW_RENEWABLE     & 0x00000008 \\
+  & KRB5_KDB_DISALLOW_PROXIABLE     & 0x00000010 \\
+  & KRB5_KDB_DISALLOW_DUP_SKEY      & 0x00000020 \\
+X & KRB5_KDB_DISALLOW_ALL_TIX       & 0x00000040 \\
+  & KRB5_KDB_REQUIRES_PRE_AUTH      & 0x00000080 \\
+  & KRB5_KDB_REQUIRES_HW_AUTH       & 0x00000100 \\
+X & KRB5_KDB_REQUIRES_PWCHANGE      & 0x00000200 \\
+  & KRB5_KDB_DISALLOW_SVR           & 0x00001000 \\
+  & KRB5_KDB_PWCHANGE_SERVICE       & 0x00002000
+\end{tabular}
 
 \item[mod_name] The name of the Kerberos principal that most recently
 modified this principal.
@@ -181,28 +168,12 @@ this principal's key was last changed.
 \item[policy] If the POLICY bit is set in aux_attributes, the name
 of the policy controlling this principal.
 
-\item[pw_min_life] If the PW_MIN_LIFE bit is set in aux_attributes,
-the pw_min_life for this principal.
-
-\item[pw_max_life] If the PW_MAX_LIFE bit is set in aux_attributes,
-the pw_max_life for this principal.
-
-\item[pw_min_length] If the PW_MIN_LENGTH bit is set in
-aux_attributes, the pw_min_length for this principal.
-
-\item[pw_min_classes] If the PW_MIN_CLASSES bit is set in the
-aux_attributes, the pw_min_classes for this principal.
-
-\item[pw_history_num] If the PW_HISTORY_NUM bit is set in
-aux_attributes, the pw_history_num for this principal.
-
 \item[aux_attributes]  A bitfield of flags for use by the
-administration system.  Currently, the only valid flags specify which
-policy fields are overridden for this principal.  See section
-\ref{sec:masks}.
+administration system.  Currently, the only valid flag is POLICY, and
+it indicates whether or not the principal has a policy enforced on it.
 \end{description}
 
-\subsection{Policies, ovsec_kadm_policy_ent_t}
+\subsubsection{Policies, ovsec_kadm_policy_ent_t}
 \label{sec:policy-fields}
 
 If the POLICY bit is set in aux_attributes, the \v{policy} name field
@@ -224,11 +195,10 @@ typedef struct _ovsec_kadm_policy_ent_t {
 
 The fields of an ovsec_kadm_policy_ent_t are interpreted as follows.
 Note that a policy's values only apply to a principal using that
-policy, and only when the corresponding override bit is not set in the
-principal's aux_attributes field.
+policy.
 
 \begin{description}
-\item[name] The name of this policy, as a NULL-terminated string.
+\item[policy] The name of this policy, as a NULL-terminated string.
 The ASCII characters between 32 (space) and 126 (tilde), inclusive,
 are legal.
 
@@ -256,19 +226,21 @@ set its password to any of its previous pw_history_num passwords.
 A policy cannot be deleted unless this number is zero.
 \end{description}
 
-\subsection{Create/Modify Masks}
+\subsubsection{Create/Modify Masks}
 \label{sec:masks}
 
 The API functions for creating and modifying principals and policies
-allow for a relevant subset of the fields to be specified or changed.
-The chosen fields are determined by a bitmask that is passed to the
-relevant function.  Each API function has different rules for which
-mask values can be specified, and can specify whether a given mask
-value is mandatory, optional, or forbidden.  Mandatory fields must be
-present and forbidden fields must not be present or an error is
-generated.  When creating a principal or policy, optional fields have
-a default value if they are not specified; when modifying a principal
-or policy, optional fields are unchanged if they are not specified.
+allow for a relevant subset of the fields of the
+ovsec_kadm_principal_ent_t and ovsec_kadm_policy_ent_t to be specified
+or changed.  The chosen fields are determined by a bitmask that is
+passed to the relevant function.  Each API function has different
+rules for which mask values can be specified, and can specify whether
+a given mask value is mandatory, optional, or forbidden.  Mandatory
+fields must be present and forbidden fields must not be present or an
+error is generated.  When creating a principal or policy, optional
+fields have a default value if they are not specified; when modifying
+a principal or policy, optional fields are unchanged if they are not
+specified.
 
 The masks for principals are in table \ref{tab:princ-bits} and the
 masks for policies are in table \ref{tab:policy-bits}.  The
@@ -277,19 +249,13 @@ Create and Modify fields, M means mandatory, F means forbidden, and O
 means optional.  Create fields that are optional specify the default
 value.
 
-Note that the POLICY, POLICY_CLR, PW_*, and PW_*_CLR masks are
-special.  When POLICY is set, the policy is assigned to the principal.
-When POLICY_CLR is specified, the policy is unassigned to the
-principal and as a result no policy controls the principal.  When a
-PW_* bit is set, the new value is stored and registered as a policy
-override.  When a PW_*_CLR bit is set, the policy override value is
-removed, thus putting that field back under the principal's policy's
-control.  It is an error to specify both a PW_* bit and its PW_*_CLR
-bit or both the POLICY and POLICY_CLR bits.
+Note that the POLICY and POLICY_CLR bits are special.  When POLICY is
+set, the policy is assigned to the principal.  When POLICY_CLR is
+specified, the policy is unassigned to the principal and as a result
+no policy controls the principal.
 
 If the principal has a policy assigned, the POLICY bit is set in
-aux_attributes.  Each enabled override is indicated by having the
-corresponding PW_* bit set in aux_attributes.
+aux_attributes.
 
 \begin{table}[htbp]
 \begin{tabular}{@{}lclll}
@@ -305,19 +271,9 @@ MOD_TIME                & 0x000040 & mod_date & F & F \\
 MOD_NAME                & 0x000080 & mod_name & F & F \\
 KVNO                    & 0x000100 & kvno & O, 1 & O \\
 MKVNO                   & 0x000200 & mkvno & F & F \\
-POLICY                  & 0x000400 & policy & O, none & O \\
-POLICY_CLR              & 0x000800 & policy & F & O \\
-PW_MAX_LIFE             & 0x001000 & pw_max_life & O, policy & O \\
-PW_MAX_LIFE_CLR         & 0x002000 & pw_max_life & F & O \\
-PW_MIN_LIFE             & 0x004000 & pw_min_life & O, policy & O \\
-PW_MIN_LIFE_CLR         & 0x008000 & pw_min_life & F & O \\
-PW_MIN_LENGTH           & 0x010000 & pw_min_length & O, policy & O \\
-PW_MIN_LENGTH_CLR       & 0x020000 & pw_min_length & F & O \\
-PW_MIN_CLASSES          & 0x040000 & pw_min_classes & O, policy & O \\
-PW_MIN_CLASSES_CLR      & 0x080000 & pw_min_classes & F & O \\
-PW_HISTORY_NUM          & 0x100000 & pw_history_num & O, policy & O \\
-PW_HISTORY_NUM_CLR      & 0x200000 & pw_history_num & F & O \\
-AUX_ATTRIBUTES          & 0x400000 & aux_attributes & O, 0 & O \\
+AUX_ATTRIBUTES          & 0x000400 & aux_attributes & O, 0 & O \\
+POLICY                  & 0x000800 & policy & O, none & O \\
+POLICY_CLR              & 0x001000 & policy & F & O
 \end{tabular}
 \caption{Mask bits for creating/modifying principals.}
 \label{tab:princ-bits}
@@ -326,13 +282,13 @@ AUX_ATTRIBUTES          & 0x400000 & aux_attributes & O, 0 & O \\
 \begin{table}[htbp]
 \begin{tabular}{@{}lclll}
 Name & Value & Field Affected & Create & Modify \\
-POLICY & same & policy & M & F \\
-PW_MAX_LIFE & same & pw_max_life & O, infinite & O \\
-PW_MIN_LIFE & same & pw_min_life & O, 0 & O \\
-PW_MIN_LENGTH & same & pw_min_length & O, 0 & O \\
-PW_MIN_CLASSES & same & pw_min_classes & O, 1 & O \\
-PW_HISTORY_NUM & same & pw_history_num & O, 0 & O \\
-REF_COUNT & 0x01 & pw_refcnt & O, 0 & O 
+POLICY                  & 0x002000 & policy & M & F \\
+PW_MAX_LIFE             & 0x004000 & pw_max_life & O, infinite & O \\
+PW_MIN_LIFE             & 0x008000 & pw_min_life & O, 0 & O \\
+PW_MIN_LENGTH           & 0x010000 & pw_min_length & O, 0 & O \\
+PW_MIN_CLASSES          & 0x020000 & pw_min_classes & O, 1 & O \\
+PW_HISTORY_NUM          & 0x040000 & pw_history_num & O, 0 & O \\
+REF_COUNT               & 0x080000 & pw_refcnt & O, 0 & O 
 \end{tabular}
 \caption{Mask bits for creating/modifying policies.}
 \label{tab:policy-bits}
@@ -373,6 +329,8 @@ policy.
 \item[OVSEC_KADM_UNK_POLICY] The named policy does not exist.
 \item[OVSEC_KADM_BAD_MASK] The principal or policy field mask is invalid
 for the current operation.
+\item[OVSEC_KADM_BAD_CLASS] The number of character classes specified
+is invalid.
 \item[OVSEC_KADM_PASS_Q_TOOSHORT] The password does not contain enough
 characters.
 \item[OVSEC_KADM_PASS_Q_CLASS] The password must contain characters
@@ -426,12 +384,10 @@ ever returned to an unauthorized user.
 obviously invalid values, and returns OVSEC_KADM_BAD_ARG if any are
 detected.
 
-\item Any function that uses a policy value uses the principal's
-policy override value if the appropriate aux_attributes bit is set;
-otherwise, if the POLICY bit is set in aux_attributes, it uses the
-value from the principal's policy.  If a function attempts to check a
-policy value and neither the principal's corresponding PW_* nor POLICY
-bits are set in aux_attributes, the policy check is not performed.
+\item Any function that performs a policy check uses the policy named
+in the principal's policy field.  If the POLICY bit is not set in the
+principal's aux_attributes field, however, the principal has no
+policy, so the policy check is not performed.
 
 \item Unless otherwise specified, all functions return OVSEC_KADM_OK.
 \end{itemize}
@@ -447,7 +403,8 @@ rename_principal & add and delete & Rename a principal. \\
 get_principal & get\footnotemark & Retrieve a principal. \\
 chpass_principal & modify\footnotemark[\thefootnote] &
          Change a principal's password. \\
-randkey_principal & modify & Randomize a principal's key. \\
+randkey_principal & modify\footnotemark[\thefootnote] &
+        Randomize a principal's key. \\
 create_policy & add & Create a new policy. \\
 delete_policy & delete & Delete a policy. \\
 modify_policy & modify & Modify the attributes of a policy. \\
@@ -466,8 +423,7 @@ details.}
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_create_principal(ovsec_kadm_princ_ent_t, 
-                ovsec_kadm_princ_mask_t, char *);
+ovsec_kadm_create_principal(ovsec_kadm_princ_ent_t, u_int32, char *);
 \end{verbatim}
 
 AUTHORIZATION REQUIRED: add
@@ -485,10 +441,15 @@ count by one.
 
 \item Set the pw_expiration field.
 \begin{enumerate}
-\item If the POLICY bit is not set, set pw_expiration to never.
-\item Otherwise, if the PW_EXPIRATION bit is set, set
-pw_expiration to the given value.
-\item Otherwise, set pw_expiration time to now + pw_max_life.
+\item If the POLICY bit is not set, then
+\begin{enumerate}
+\item if the PW_EXPIRATION bit is set, set pw_expiration to the given
+value, else
+\item set pw_expiration to never.
+\end{enumerate}
+\item Otherwise, if the PW_EXPIRATION bit is set, set pw_expiration to
+the maximum of the given value and now + pw_max_life.
+\item Otherwise, set pw_expiration to now + pw_max_life.
 \end{enumerate}
 
 \item Set last_pwd_change and mod_date to now and set mod_name to caller.
@@ -529,8 +490,7 @@ RETURN CODES:
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_modify_principal(ovsec_kadm_prin_ent_t,
-                        ovsec_kadm_princ_mask);
+ovsec_kadm_modify_principal(ovsec_kadm_prin_ent_t, u_int32);
 \end{verbatim}
 
 Modify the attributes of the principal named in
@@ -544,8 +504,8 @@ AUTHORIZATION REQUIRED: modify
 \item Return OVSEC_KADM_BAD_MASK if the mask is invalid.
 \item If POLICY bit is set but the new policy does not exist, return
 OVSEC_KADM_UNK_POLICY.
-\item If any of the POLICY, POLICY_CLR, PW_*, or PW_*_CLR bits are
-set, update the corresponding bits in aux_attributes.
+\item If either the POLICY or POLICY_CLR bits are set, update the
+corresponding bits in aux_attributes.
 
 \item Update policy reference counts.
 \begin{enumerate}
@@ -557,14 +517,15 @@ aux_attributes is set, decrement policy count on old policy.
 
 \item Set pw_expiration according to the new policy.
 \begin{enumerate}
-\item If the POLICY bit is not set in aux_attributes, set
-pw_expiration to never.  
-\item Otherwise, if PW_EXPIRATION is specified, then set
-pw_expiration to the new value.
-\item Otherwise, if PW_MAX_LIFE changes (either because it or POLICY
-is specified in the mask), set pw_expiration to last_pwd_change +
-pw_max_life.  Note that this means an explicit PW_EXPIRATION overrides
-pw_max_life.
+\item If the POLICY bit is not set in aux_attributes, then
+\begin{enumerate}
+\item if the PW_EXPIRATION bit is set, set pw_expiration to the given
+value, else
+\item set pw_expiration to never.
+\end{enumerate}
+\item Otherwise, if the PW_EXPIRATION bit is set, set pw_expiration to
+the maximum of the given value and last_pwd_change + pw_max_life.
+\item Otherwise, set pw_expiration to last_pwd_change + pw_max_life.
 \end{enumerate}
 
 \item Update the fields specified in the mask.
@@ -585,8 +546,7 @@ policy does not exist.
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_rename_principal(krb5_principal source,
-                         krb5_principal target);
+ovsec_kadm_rename_principal(krb5_principal source, krb5_principal target);
 \end{verbatim}
 
 AUTHORIZATION REQUIRED: add and delete
@@ -598,6 +558,19 @@ OVSEC_KADM_UNK_PRINC error.
 \item Rename principal.
 \end{enumerate}
 
+Note that since the principal name may have been used as the salt for
+the principal's key, renaming the principal may render the principal's
+current password useless; with the new salt, the key generated by
+string-to-key on the password will suddenly be different.  Therefore,
+an application that renames a principal must also require the user to
+specify a new password for the principal (and administrators should
+notify the affected party).
+
+Note also that, by the same argument, renaming a principal will
+invalidate that principal's password history information; since the
+salt will be different, a user will be able to select a previous
+password without error.
+
 RETURN CODES: 
 
 \begin{description}
@@ -609,37 +582,51 @@ RETURN CODES:
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_chpass_principal(krb5_principal princ, char *pw);
+ovsec_kadm_chpass_principal(krb5_principal princ, char *pw, 
+                int override_qual);
 \end{verbatim}
 
 AUTHORIZATION REQUIRED: modify, or the calling principal being the
 same as the princ argument.
 
-Change a principal's password.  In the description below, all the
-checks that can result in password-related errors do not apply to
-callers that have the modify privilege but are {\it not} the same as
-the principal being affected.  Thus, an administrator can change a
-principal's password to one that violates the principal's policy, but
-cannot change its own password to one that violates its own policy.
+Change a principal's password.  
 
-Also note that the password quality or history steps should only be
-performed if the POLICY bit is set in the principal's aux_attributes
-field.
+In the description below, all the checks that can result in
+policy-related errors do not apply to callers that have the modify
+privilege but are {\it not} the same as the principal being affected.
+Thus, an administrator can change a principal's password in violation
+of that principal's policy, but cannot change its own password in
+violation of its own policy.
 
+Note that the policy checks are only be performed if the POLICY bit is
+set in the principal's aux_attributes field.
+
+\begin{enumerate}
+\item Determine whether password quality checks should be overridden.
 \begin{enumerate}
+\item If the POLICY bit is not set in aux_attributes, set
+override_qual to true.
+\item Otherwise, if the caller does not have the modify priviledge,
+set override_qual to false.
+\item Otherwise, if the caller has the modify privilege, but princ is the
+same as the caller, set override_qual to false.
+\item Otherwise, if the caller has the modify privilege and princ is
+not the same as the caller, leave override_qual as it is.
+\end{enumerate}
 \item Make sure principal exists, if not return OVSEC_KADM_UNK_PRINC error.
-\item See if current password is older than password minimum life,
-if not return OVSEC_KADM_PASS_TOOSOON error.
-\item If the password does not meet quality standards, return the
-appropriate OVSEC_KADM_PASS_Q_* error code.
-\item Convert password to key.
-\item Check to see if new key is in history, if so return
-OVSEC_KADM_PASS_REUSE error.
+\item If override_qual is false and (now - last_pwd_change) $<$
+pw_min_life, return OVSEC_KADM_PASS_TOOSOON.
+\item If override_qual is false and the password does not meet the quality
+standards, return the appropriate OVSEC_KADM_PASS_Q_* error code.
+\item Convert password to key.  The key is generated with
+Kerberos' string-to-key function, using the salt method specified on
+the admin server's command line; see section \ref{sec:commandline}.
+\item If override_qual is false and the new key is in the principal's
+password history, return OVSEC_KADM_PASS_REUSE.
 \item Store old key in history.
 \item Update principal to have new key.
 \item Increment principal's key version number by one.
-\item If the POLICY bit in aux_attributes if set, set pw_expiration to
-now + max_pw_life.
+\item If the POLICY bit is set, set pw_expiration to now + max_pw_life.
 \item Update last_pwd_change and mod_date to now, update mod_name to
 caller.
 \end{enumerate}
@@ -660,21 +647,46 @@ life.
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_randkey_principal(krb5_principal, krb5_keyblock **);
+ovsec_kadm_randkey_principal(krb5_principal, krb5_keyblock **,
+               int override_qual);
 \end{verbatim}
 
-AUTHORIZATION REQUIRED: modify
-
 Generate and assign a new random key to the named principal, and
 return the generated key in allocated storage.  The caller must free
 the returned krb5_keyblock * with krb5_free_keyblock.
 
+AUTHORIZATION REQUIRED: modify, or the calling principal being the
+same as the princ argument.
+
+In the description below, all the checks that can result in
+key-related errors do not apply to callers that have the modify
+privilege but are {\it not} the same as the principal being affected.
+Thus, an administrator can randomize a principal's password in
+violation of the principal's policy, but cannot randomize its own
+password in violation of its own policy.
+
+Note that the policy checks are only be performed if the POLICY bit is
+set in the principal's aux_attributes field.
+
+\begin{enumerate}
+\item Determine whether policy checks should be overriden.
 \begin{enumerate}
+\item If the POLICY bit is not set in aux_attributes, set
+override_qual to true.
+\item Otherwise, if the caller does not have the modify priviledge,
+set override_qual to false.
+\item Otherwise, if the caller has the modify privilege, but princ is the
+same as the caller, set override_qual to false.
+\item Otherwise, if the caller has the modify privilege and princ is
+not the same as the caller, leave override_qual as it is.
+\end{enumerate}
 \item If the principal does not exist, return OVSEC_KADM_UNK_PRINC.
+\item If override_qual is false and (now - last_pwd_change) $<$
+pw_min_life, return OVSEC_KADM_PASS_TOOSOON.
 \item Store old key in history.
 \item Update principal to have new key.
 \item Increment principal's key version number by one.
-\item If the POLICY bit in aux_attributes if set, set pw_expiration to
+\item If the POLICY bit in aux_attributes is set, set pw_expiration to
 now + max_pw_life.
 \item Update last_pwd_change and mod_date to now, update mod_name to
 caller.
@@ -684,6 +696,8 @@ RETURN CODES:
 
 \begin{description}
 \item[OVSEC_KADM_UNK_PRINC] Principal does not exist.
+\item[OVSEC_KADM_PASS_TOOSOON] The minimum lifetime for the current
+key has not expired.
 \end{description}
 
 This function can also be used as part of a sequence to create a new
@@ -705,8 +719,7 @@ KRB5_KDB_DISALLOW_ALL_TIX bit in the attributes field.
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_get_principal(krb5_principal princ,,
-                        ovsec_kadm_princ_ent_t *ent);  
+ovsec_kadm_get_principal(krb5_principal princ, ovsec_kadm_princ_ent_t *ent);  
 \end{verbatim}
 
 Return the principal's attributes in allocated memory.  The caller
@@ -725,8 +738,7 @@ RETURN CODES:
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_create_policy(ovsec_kadm_policy_ent_t,
-                        ovsec_kadm_policy_mask); 
+ovsec_kadm_create_policy(ovsec_kadm_policy_ent_t, u_int32); 
 \end{verbatim}
 
 Create a new policy.
@@ -737,6 +749,8 @@ AUTHORIZATION REQUIRED: add
 \item Check to see if mask is valid, if not return OVSEC_KADM_BAD_MASK error.
 \item Check to see if the policy already exists, if so return
 OVSEC_KADM_DUP error. 
+\item If the PW_MIN_CLASSES bit is set and pw_min_classes is not 1, 2,
+3, or 4, return OVSEC_KADM_BAD_CLASS.
 \item Create a new policy setting the appropriate fields determined
 by the mask.
 \end{enumerate}
@@ -747,6 +761,8 @@ RETURN CODES:
 \item[OVSEC_KADM_DUP] Policy already exists
 \item[OVSEC_KADM_BAD_MASK] The mask is not valid for a create
 operation.
+\item[OVSEC_KADM_BAD_CLASS] The specified number of character classes
+is invalid.
 \end{description}
 
 \subsection{ovsec_kadm_delete_policy}
@@ -777,8 +793,7 @@ RETURN CODES:
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_modify_policy(ovsec_kadm_policy_ent_t,
-                        ovsec_kadm_policy_mask);
+ovsec_kadm_modify_policy(ovsec_kadm_policy_ent_t, u_int32);
 \end{verbatim}
 
 Modify an existing policy.  Note that modifying a policy has no affect
@@ -791,6 +806,8 @@ AUTHORIZATION REQUIRED: modify
 \item Check to see if mask is legal, if not return OVSEC_KADM_BAD_MASK error.
 \item Check to see if policy exists, if not return
 OVSEC_KADM_UNK_POLICY error.
+\item If the PW_MIN_CLASSES bit is set and pw_min_classes is not 1, 2,
+3, or 4, return OVSEC_KADM_BAD_CLASS.
 \item Update the fields specified in the mask.
 \end{enumerate}
 
@@ -800,14 +817,15 @@ RETURN CODES:
 \item[OVSEC_KADM_UNK_POLICY] Policy not found.
 \item[OVSEC_KADM_BAD_MASK] The mask is not valid for a modify
 operation.
+\item[OVSEC_KADM_BAD_CLASS] The specified number of character classes
+is invalid.
 \end{description}
 
 \subsection{ovsec_kadm_get_policy}
 
 \begin{verbatim}
 ovsec_kadm_ret_t
-ovsec_kadm_get_policy(char *,
-                        ovsec_kadm_policy_ent_t *); 
+ovsec_kadm_get_policy(char *, ovsec_kadm_policy_ent_t *); 
 \end{verbatim}
 
 AUTHORIZATION REQUIRED: get
@@ -1015,6 +1033,10 @@ admin principal database.
 \item  If a policy's reference count does not equal the
 number of principals that use the policy, the reference count is
 corrected.
+
+\item  If a principal has the POLICY bit set in aux_attributes, and
+its (pw_expiration - last_pwd_change) $>$ pw_max_life, the pw_expiration
+field is set to last_pwd_change + pw_max_life.
 \end{itemize}
 
 The operations that are only performed if -p is not specified are:
index a955cf2d14a3a0be285dbea014f5a626d2236d62..760fbc4910d0a5933901c59c73d26400e3a444cb 100644 (file)
@@ -4,15 +4,14 @@
 
 \setlength{\parskip}{.7\baselineskip}
 \setlength{\parindent}{0pt}
-\setcounter{secnumdepth}{4}
-\setcounter{tocdepth}{4}
 
 \def\secure{OV*Secure}
 \def\v#1{\verb+#1+}
+\def\k#1{K$_#1$}
 
 \title{OV*Secure Admin Server \\ Implementation Design}
-\author{}
-\date{\today}
+\author{Barry Jaspan}
+\date{DRAFT --- \today}
 
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %% Make _ actually generate an _, and allow line-breaking after it.
@@ -27,8 +26,6 @@
 
 {\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
@@ -54,6 +51,16 @@ information.
 \item The policy database stores \secure{} policy information.
 \end{itemize}
 
+The per-principal information stored in the admin principal database
+consists of the principal's policy name and an array of the
+principal's previous keys.  The old keys are stored encrypted in the
+key of the special principal ``kadmin/history'' that is created by
+ovsec_kadm_create.  Since a change in kadmin/history's key renders
+every principal's key history array useless, it can only be changed
+using the ovsec_kadm_edit utility; that program will reencrypt every
+principal's key history in the new key.  The admin server refuses all
+requests to change kdamin/history's key.
+
 \section{Main}
 
 The admin server starts by trapping all fatal signals and directing
@@ -72,11 +79,6 @@ 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
@@ -97,35 +99,36 @@ 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 num_old_keys;
         u_int32 next_old_key;
-
-        u_int32 expansion[8];
+        krb5_kvno admin_history_kvno;
+        krb5_encrypted_keyblock *old_keys;
 } 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.
+The fields that are different from ovsec_kadm_principal_ent_t are:
 
 \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[num_old_keys] The number of previous keys in the old_keys array.
+This value must be 0 $\le$ num_old_keys $<$ pw_history_num.
 
-\item[next_key] The index into old_keys where the next key should be
-inserted.  This value is always computed modulo pw_history_num.
+\item[next_old_key] The index into old_keys where the next key should
+be inserted.  This value must be 0 $\le$ next_old_key $\le$
+num_old_keys.
+
+\item[admin_history_kvno] The key version number of the admin/history
+principal's key used to encrypt the values in old_keys.  If the admin
+server finds that admin/history's kvno is different from the value in
+this field, an error message is logged.  (XXX where?)
+
+\item[old_keys] The array of the principal's previous keys, each
+encrypted in the admin/history key.  There are num_old_keys elements.
 \end{description}
 
 \subsection{Policy, osa_policy_ent_t}
@@ -145,7 +148,6 @@ typedef struct _osa_policy_ent_t {
         u_int32 pw_history_num;
 
         u_int32 refcnt;
-        u_int32 expansion[8];
 } osa_policy_ent_rec, *osa_policy_ent_t;
 \end{verbatim}
 
@@ -223,7 +225,8 @@ presented here.
 \item[OSA_ADB_FAILURE] General failure.
 \end{description}
 
-Unless otherwise specified, database functions return OSA_ADB_OK.
+Database functions can also return system errors.  Unless otherwise
+specified, database functions return OSA_ADB_OK.
 
 \begin{verbatim}
 osa_adb_ret_t
@@ -378,117 +381,61 @@ void krb5_dbm_db_free_principal(krb5_db_entry *entries, int nentries)
 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.
+\subsubsection{Initialization and Key Access}
 
-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.
+Keys stored in the Kerberos database are encrypted in the Kerberos
+master key.  The admin server will therefore have to acquire the key
+before it can perform any key-changing operations, and will have to
+decrypt and encrypt the keys retrieved from and placed into the
+database via krb5_db_get_principal and _put_principal.  This section
+describes the internal admin server API that will be used to perform
+these functions.
 
 \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);
-     }
-}
+void kdc_init_master()
 \end{verbatim}
 
-\subsubsection{Reading Principals and Keys}
+kdc_init_master opens the database and acquires the master key.  It
+also sets the global variables master_princ, master_encblock, and
+master_keyblock:
+
+\begin{itemize}
+\item master_princ is set to the name of the Kerberos master principal
+(\v{K/M@REALM}).
+
+\item master_encblock is something I have no idea about.
 
-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.
+\item master_keyblock is the Kerberos master key
+\end{itemize}
 
 \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;
-}
+krb5_error_code kdb_get_entry_and_key(krb5_principal principal,
+                                      krb5_db_entry *entry,
+                                      krb5_keyblock *key)
 \end{verbatim}
 
-\subsubsection{Writing Principals and Keys}
+kdb_get_entry_and_key retrieves the named principal's entry from the
+database in entry, and decrypts its key into key.  The caller must
+free entry with krb5_dbm_db_free_principal and free key-$>$contents with
+free.\footnote{The caller should also \v{memset(key-$>$contents, 0,
+key-$>$length)}.  There should be a function krb5_free_keyblock_contents
+for this, but there is not.}
+
+\begin{verbatim}
+krb5_error_code kdb_put_entry_pw(krb5_db_entry *entry, char *pw)
+\end{verbatim}
 
-I don't know how to do this yet, but the code is contained in
-\v{src/admin/edit/kdb5_edit.c}.
+kdb_put_entry_pw stores entry in the database.  All the entry values
+must already be set; this function does not change any of them except
+the key.  pw, the NULL-terminated password string, is converted to a
+key using string-to-key with the salt type specified in
+entry-$>$salt_type.\footnote{The salt_type should be set based on the
+command line arguments to the kadmin server (see the ``Command Line''
+section of the functional specification).}
 
 \section{Admin Principal and Policy Database Implementation}
 
@@ -533,10 +480,8 @@ that are not addresed by the functional specifications.
 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.
+The principal's initial key is not stored in the key history array at
+creation time.
 
 \subsection{ovsec_kadm_delete_principal}
 
@@ -548,29 +493,50 @@ principal database, but not both, return OVSEC_KADM_BAD_DB.
 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.
+If pw_history_num changes and the new value $n$ is smaller than the
+current value of num_old_keys, old_keys should end up with the $n$
+most recent keys; these are found by counting backwards $n$ elements
+in old_keys from next_old_key.  next_old_keys should then be reset to
+0, the oldest of the saved keys, and num_old_keys set to $n$, the
+new actual number of old keys in the array.  
+
+\subsection{ovsec_kadm_chpass_principal, randkey_principal}
+
+The algorithm for determining whether a password is in the principal's
+key history is complicated by the use of the kadmin/history \k{h}
+encrypting key.  
+
+\begin{enumerate}
+\item For ovsec_kadm_chpass_principal, convert the password to a key
+using string-to-key and the salt method specified by the command line
+arguments.
+
+\item If the POLICY bit is set and pw_history_num is not zero, check
+if the new key is in the history.
+\begin{enumerate}
+\item Retrieve the principal's current key and decrypt it with \k{M}.
+If it is the same as the new key, return OVSEC_KADM_PASS_REUSE.
+\item Retrieve the kadmin/history key \k{h} and decrypt it with \k{M}.
+\item Encrypt the principal's new key in \k{h}.
+\item If the principal's new key encrypted in \k{h} is in old_keys,
+return OVSEC_KADM_PASS_REUSE.
+\item Encrypt the principal's current key in \k{h} and store it in
+old_keys.
+\item Erase the memory containing \k{h}.
+\end{enumerate}
+
+\item Encrypt the principal's new key in \k{M} and store it in the
+database.
+\item Erase the memory containing \k{M}.
+\end{enumerate}
+
+To store the an encrypted key in old_keys, insert it as the
+next_old_key element of old_keys, and increment next_old_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}