\title{OV*Secure Admin \\ Functional Specifications}
\author{}
-\date{\today}
+\date{DRAFT --- \today}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Make _ actually generate an _, and allow line-breaking after it.
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}
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}
\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
\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.
\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
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.
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
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}
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}
\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}
\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
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}
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. \\
\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
\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.
\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
\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}
\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.
\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
\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}
\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}
\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.
\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
\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
\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.
\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}
\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}
\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
\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}
\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
\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:
\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.
{\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
\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
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
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}
u_int32 pw_history_num;
u_int32 refcnt;
- u_int32 expansion[8];
} osa_policy_ent_rec, *osa_policy_ent_t;
\end{verbatim}
\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
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}
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}
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}