summaryrefslogtreecommitdiffstats
path: root/doc
diff options
context:
space:
mode:
authorBarry Jaspan <bjaspan@mit.edu>1993-10-08 22:07:20 +0000
committerBarry Jaspan <bjaspan@mit.edu>1993-10-08 22:07:20 +0000
commitdcaee6fde713ee9389b1fe82b8a75365a5b6a739 (patch)
tree94b6f343e48d4f30bb4f4aa33a12df24c4a01fed /doc
parentba5c693c1fb575e6e35fd159659e0c5873af1cf2 (diff)
downloadkrb5-dcaee6fde713ee9389b1fe82b8a75365a5b6a739.tar.gz
krb5-dcaee6fde713ee9389b1fe82b8a75365a5b6a739.tar.xz
krb5-dcaee6fde713ee9389b1fe82b8a75365a5b6a739.zip
*** empty log message ***
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@2651 dc483132-0cff-0310-8789-dd5450dbe970
Diffstat (limited to 'doc')
-rw-r--r--doc/kadm5/api-funcspec.tex360
-rw-r--r--doc/kadm5/api-server-design.tex262
2 files changed, 305 insertions, 317 deletions
diff --git a/doc/kadm5/api-funcspec.tex b/doc/kadm5/api-funcspec.tex
index 56602541b..f3605e5b7 100644
--- a/doc/kadm5/api-funcspec.tex
+++ b/doc/kadm5/api-funcspec.tex
@@ -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:
diff --git a/doc/kadm5/api-server-design.tex b/doc/kadm5/api-server-design.tex
index a955cf2d1..760fbc491 100644
--- a/doc/kadm5/api-server-design.tex
+++ b/doc/kadm5/api-server-design.tex
@@ -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}