Merge from branch sun-iprop
authorKen Raeburn <raeburn@mit.edu>
Tue, 24 Jun 2008 05:04:29 +0000 (05:04 +0000)
committerKen Raeburn <raeburn@mit.edu>
Tue, 24 Jun 2008 05:04:29 +0000 (05:04 +0000)
git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20465 dc483132-0cff-0310-8789-dd5450dbe970

52 files changed:
README
doc/copyright.texinfo
doc/install.texinfo
doc/iprop-notes.txt [new file with mode: 0644]
src/config-files/kdc.conf.M
src/include/iprop.h [new file with mode: 0644]
src/include/iprop_hdr.h [new file with mode: 0644]
src/include/k5-int.h
src/include/k5-platform.h
src/include/kdb_log.h [new file with mode: 0644]
src/include/osconf.hin
src/kadmin/cli/kadmin.M
src/kadmin/cli/kadmin.c
src/kadmin/dbutil/Makefile.in
src/kadmin/dbutil/dump.c
src/kadmin/dbutil/kadm5_create.c
src/kadmin/dbutil/kdb5_create.c
src/kadmin/dbutil/kdb5_destroy.c
src/kadmin/dbutil/kdb5_util.c
src/kadmin/dbutil/kdb5_util.h
src/kadmin/server/Makefile.in
src/kadmin/server/ipropd_svc.c [new file with mode: 0644]
src/kadmin/server/kadm_rpc_svc.c
src/kadmin/server/kadmind.M
src/kadmin/server/misc.h
src/kadmin/server/ovsec_kadmd.c
src/kadmin/server/server_stubs.c
src/lib/kadm5/Makefile.in
src/lib/kadm5/admin.h
src/lib/kadm5/alt_prof.c
src/lib/kadm5/clnt/Makefile.in
src/lib/kadm5/clnt/client_init.c
src/lib/kadm5/clnt/libkadm5clnt.exports
src/lib/kadm5/srv/Makefile.in
src/lib/kadm5/srv/libkadm5srv.exports
src/lib/kadm5/srv/server_acl.c
src/lib/kadm5/srv/server_acl.h
src/lib/kadm5/srv/server_init.c
src/lib/kdb/Makefile.in
src/lib/kdb/iprop.x [new file with mode: 0644]
src/lib/kdb/iprop_xdr.c [new file with mode: 0644]
src/lib/kdb/kdb5.c
src/lib/kdb/kdb_convert.c [new file with mode: 0644]
src/lib/kdb/kdb_log.c [new file with mode: 0644]
src/lib/kdb/libkdb5.exports
src/lib/krb5/error_tables/kdb5_err.et
src/slave/Makefile.in
src/slave/kpropd.M
src/slave/kpropd.c
src/slave/kpropd_rpc.c [new file with mode: 0644]
src/slave/kproplog.M [new file with mode: 0644]
src/slave/kproplog.c [new file with mode: 0644]

diff --git a/README b/README
index 7dcc5b23b81a9e406f64709e7020c01a685e8b83..6955f1482e68447146ca59507ca8246aef2a6311 100644 (file)
--- a/README
+++ b/README
@@ -412,6 +412,19 @@ src/lib/gssapi, including the following files:
   lib/gssapi/spnego/gssapiP_spnego.h
   lib/gssapi/spnego/spnego_mech.c
 
+and the initial implementation of incremental propagation, including
+the following new or changed files:
+
+  include/iprop_hdr.h
+  kadmin/server/ipropd_svc.c
+  lib/kdb/iprop.x
+  lib/kdb/kdb_convert.c
+  lib/kdb/kdb_log.c
+  lib/kdb/kdb_log.h
+  lib/krb5/error_tables/kdb5_err.et
+  slave/kpropd_rpc.c
+  slave/kproplog.c
+
 are subject to the following license:
 
   Copyright (c) 2004 Sun Microsystems, Inc.
@@ -565,7 +578,7 @@ Thanks to Novell for donating the KDB abstraction layer and the LDAP
 database plug-in.
 
 Thanks to Sun Microsystems for donating their implementations of
-mechglue and SPNEGO.
+mechglue, SPNEGO, and incremental propagation.
 
 Thanks to the members of the Kerberos V5 development team at MIT, both
 past and present: Danilo Almeida, Jeffrey Altman, Justin Anderson,
index 5d239b561148e418ab926e552071d500d1ee674a..cc6b577fc264f8273c20eb2fab7c8f13b146199b 100644 (file)
@@ -1,4 +1,4 @@
-Copyright @copyright{} 1985-2007 by the Massachusetts Institute of Technology.
+Copyright @copyright{} 1985-2008 by the Massachusetts Institute of Technology.
 
 @quotation
 Export of software employing encryption from the United States of
@@ -309,6 +309,21 @@ lib/gssapi/spnego/gssapiP_spnego.h
 lib/gssapi/spnego/spnego_mech.c
 @end smallexample
 
+and the initial implementation of incremental propagation, including
+the following new or changed files:
+
+@smallexample
+  include/iprop_hdr.h
+  kadmin/server/ipropd_svc.c
+  lib/kdb/iprop.x
+  lib/kdb/kdb_convert.c
+  lib/kdb/kdb_log.c
+  lib/kdb/kdb_log.h
+  lib/krb5/error_tables/kdb5_err.et
+  slave/kpropd_rpc.c
+  slave/kproplog.c
+@end smallexample
+
 are subject to the following license:
 
 @quotation
index 64cdac70e31ccdcc1c2e82da44d17bf713af0618..54f183d43523f0631fa94a0e3db9496c836675d7 100644 (file)
@@ -363,6 +363,7 @@ procedure is based on that recommendation.
 * Add Kerberos Principals to the Database::  
 * Limit Access to the KDCs::    
 * Switching Master and Slave KDCs::  
+* Incremental Database Propagation::  
 @end menu
 
 @node Install the Master KDC, Install the Slave KDCs, Installing KDCs, Installing KDCs
@@ -950,7 +951,7 @@ eklogin   stream tcp nowait root @value{ROOTDIR}/sbin/klogind
 @end group
 @end smallexample
 
-@node Switching Master and Slave KDCs,  , Limit Access to the KDCs, Installing KDCs
+@node Switching Master and Slave KDCs, Incremental Database Propagation, Limit Access to the KDCs, Installing KDCs
 @subsection Switching Master and Slave KDCs
 
 You may occasionally want to use one of your slave KDCs as the master.
@@ -1001,6 +1002,133 @@ machine in your Kerberos realm.)
 
 @end enumerate
 
+@node Incremental Database Propagation,  , Switching Master and Slave KDCs, Installing KDCs
+@subsection Incremental Database Propagation
+
+At some very large sites, dumping and transmitting the database can
+take more time than is desirable for changes to propagate from the
+master KDC to the slave KDCs.  The incremental propagation support
+added in the 1.7 release is intended to address this.
+
+With incremental propagation enabled, all programs on the master KDC
+that change the database also write information about the changes to
+an ``update log'' file, maintained as a circular buffer of a certain
+size.  A process on each slave KDC connects to a service on the master
+KDC (currently implmented in the @code{kadmind} server) and
+periodically requests the changes that have been made since the last
+check.  By default, this check is done every two minutes.  If the
+database has just been modified in the previous several seconds
+(currently the threshold is hard-coded at 10 seconds), the slave will
+not retrieve updates, but instead will pause and try again soon after.
+This reduces the likelihood that incremental update queries will cause
+delays for an administrator trying to make a bunch of changes to the
+database at the same time.
+
+Incremental propagation uses the following entries in the per-realm
+data in the KDC config file:
+
+@table @asis
+@item @code{iprop_enable} (boolean)
+If this is set to @code{true}, then incremental propagation is
+enabled, and (as noted below) normal @code{kprop} propagation is
+disabled.  The default is @code{false}.
+
+@item @code{iprop_master_ulogsize} (integer)
+This indicates the number of entries that should be retained in the
+update log.  The default is 1000; the maximum number is 2500.
+
+@item @code{iprop_slave_poll} (time interval)
+This indicates how often the slave should poll the master KDC for
+changes to the database.  The default is two minutes.
+
+@item @code{iprop_port} (integer)
+This specifies the port number to be used for incremental
+propagation.  This is required in both master and slave configuration
+files.
+
+@item @code{iprop_logfile} (file name)
+This specifies where the update log file for the realm database is to
+be stored.  The default is to use the @code{database_name} entry from
+the @code{realms} section of the config file, with @file{.ulog} appended.
+(NOTE: If @code{database_name} isn't specified in the @code{realms}
+section, perhaps because the LDAP database back end is being used, or
+the file name is specified in the @code{dbmodules} section, then the
+hard-coded default for @code{database_name} is used.  Determination of
+the @code{iprop_logfile} default value will not use values from the
+@code{dbmodules} section.)
+@end table
+
+Both master and slave sides must have principals named
+@code{kiprop/@var{hostname}} (where @var{hostname} is, as usual, the
+lower-case, fully-qualified, canonical name for the host) registered
+and keys stored in the default keytab file (@file{/etc/krb5.keytab}).
+@c XXX: I think the master side, at least, might be able to read the
+@c key out of the database.  Test and document this.
+
+On the master KDC side, the @code{kiprop/@var{hostname}} principal
+must be listed in the @code{kadmind} ACL file @code{kadm5.acl}, and
+given the @code{p} privilege.
+
+On the slave KDC side, @code{kpropd} should be run.  When incremental
+propagation is enabled, it will connect to the @code{kadmind} on the
+master KDC and start requesting updates.
+
+The normal @code{kprop} mechanism is disabled by the incremental
+propagation support.  However, if the slave has been unable to fetch
+changes from the master KDC for too long (network problems, perhaps),
+the log on the master may wrap around and overwrite some of the
+updates that the slave has not yet retrieved.  In this case, the slave
+will instruct the master KDC to dump the current database out to a
+file and invoke a one-time @code{kprop} propagation, with special
+options to also convey the point in the update log at which the slave
+should resume fetching incremental updates.  Thus, all the keytab and
+ACL setup previously described for @code{kprop} propagation is still
+needed.
+
+There are several known bugs and restrictions in the current
+implementation:
+@itemize
+@item
+The ``call out to @code{kprop}'' mechanism is a bit fragile; if the
+@code{kprop} propagation fails to connect for some reason, the process
+on the slave may hang waiting for it, and will need to be restarted.
+@item
+The master and slave must be able to initiate TCP connections in
+both directions, without an intervening NAT.  They must also be able
+to communicate over IPv4, since MIT's RPC code does not currently
+support IPv6.
+@end itemize
+
+@menu
+* Sun/MIT Incremental Propagation Differences::  
+@end menu
+
+@node Sun/MIT Incremental Propagation Differences,  , Incremental Database Propagation, Incremental Database Propagation
+@subsubsection Sun/MIT Incremental Propagation Differences
+
+Sun donated the original code for supporting incremental database
+propagation to MIT.  Some changes have been made in the MIT source
+tree that will be visible to administrators.  (These notes are based
+on Sun's patches.  Changes to Sun's implementation since then may not
+be reflected here.)
+
+The Sun config file support looks for @code{sunw_dbprop_enable},
+@code{sunw_dbprop_master_ulogsize}, and @code{sunw_dbprop_slave_poll}.
+
+The incremental propagation service is implemented as an ONC RPC
+service.  In the Sun implementation, the service is registered with
+@code{rpcbind} (also known as @code{portmapper}) and the client looks
+up the port number to contact.  In the MIT implementation, where
+interaction with some modern versions of @code{rpcbind} doesn't always
+work well, the port number must be specified in the config file on
+both the master and slave sides.
+
+The Sun implementation hard-codes pathnames in @file{/var/krb5} for
+the update log and the per-slave @code{kprop} dump files.  In the MIT
+implementation, the pathname for the update log is specified in the
+config file, and the per-slave dump files are stored in
+@code{@value{ROOTDIR}/var/krb5kdc/slave_datatrans_@var{hostname}}.
+
 @node Installing and Configuring UNIX Client Machines, UNIX Application Servers, Installing KDCs, Installing Kerberos V5
 @section Installing and Configuring UNIX Client Machines
 
diff --git a/doc/iprop-notes.txt b/doc/iprop-notes.txt
new file mode 100644 (file)
index 0000000..2b1ee43
--- /dev/null
@@ -0,0 +1,120 @@
+Some (intentional) changes from Sun's submission are noted in the
+install guide.
+
+Bugs or issues:
+
+The "full resync" part of the protocol involves the master side firing
+off a normal kprop (and going back to servicing requests), and the
+slave side stopping all the incremental propagation stuff and waiting
+for the kprop.  If the connection from the master never comes in for
+some reason, the slave side just blocks forever, and never resumes
+incremental propagation.
+
+Shawn had a good suggestion after I started the integration work, and
+which I haven't had a chance to implement: Make the update-log code
+fit in as a sort of pseudo-database layer via the DAL, being called
+through the standard DAL methods, and doing its work around calls
+through to the real database back end again through the DAL methods.
+So for example, creating a "iprop+db2" database would create an update
+log and the real db2 database; storing a principal entry would update
+the update log as well; etc.  At least initially, we wouldn't treat it
+as a differently-named database; the installation of the hooks would
+be done by explicitly checking if iprop is enabled, etc.
+
+The "iprop role" is assumed to be either master or slave.  The master
+writes a log, and the slave fetches it.  But what about a cascade
+propagation model where A sends to B which sends to C, perhaps because
+A's bandwidth is highly limited, or B and C are co-located?  In such a
+case, B would want to operate in both modes.  Granted, with iprop the
+bandwidth issues should be less important, but there may still be
+reasons one may wish to run in such a configuration.
+
+The propagation of changes does not happen in real time.  It's not a
+"push" protocol; the slaves poll periodically for changes.  Perhaps a
+future revision of the protocol could address that.
+
+kadmin/cli/kadmin.c call to kadm5_init_iprop - is this needed in
+client-side program? Should it be done in libkadm5srv instead as part
+of the existing kadm5_init* so that database-accessing applications
+that don't get updated at the source level will automatically start
+changing the update log as needed?
+
+Locking: Currently DAL exports the DB locking interface to the caller;
+we want to slip the iprop code in between -- run it plus the DB update
+operation with the DB lock held, whether or not the caller grabbed the
+lock.  (Does the caller always grab the lock before making changes?)
+Currently we're using a file lock on the update log itself; this will
+be independent of whether the DB back end implements locking (which
+may be a good thing or a bad thing, depending).
+
+Various logging calls with odd format strings like "<null>" should be
+fixed.
+
+Why are different principal names used, when incremental propagation
+requires that normal kprop (which uses host principals) be possible
+anyways?
+
+Why is this tied to kadmind, aside from (a) wanting to prevent other
+db changes, which locking protocols should deal with anyways, (b)
+existing acl code, (c) existing server process?
+
+The incremental propagation protocol requires an ACL entry on the
+master, listing the slave.  Since the full-resync part uses normal
+kprop, the slave also has to have an ACL entry for the master.  If
+this is missing, I suspect the behavior will be that every two
+minutes, the master side will (at the prompting of the slave) dump out
+the database and attempt a full propagation.
+
+Possible optimizations: If an existing dump file has a recent enough
+serial number, just send it, without dumping again?  Use just one dump
+file instead of one per slave?
+
+Requiring normal kprop means the slave still can't be behind a NAT or
+firewall without special configuration.  The incremental parts can
+work in such a configuration, so long as outgoing TCP connections are
+allowed.
+
+Still limited to IPv4 because of limitations in MIT's version of the
+RPC code.  (This could be fixed for kprop, if IPv6 sites want to do
+full propagation only.  Doing incremental propagation over IPv6 will
+take work on the RPC library, and probably introduce
+backwards-incompatible ABI changes.)
+
+Overflow checks for ulogentries times block size?
+
+If file can't be made the size indicated by ulogentries, shoud we
+truncate or error out?  If we error out, this could blow out when
+resizing the log because of a too-large log entry.
+
+The kprop invocation doesn't specify a realm name, so it'll only work
+for the default realm.  No clean way to specify a port number, either.
+Would it be overkill to come up with a way to configure host+port for
+kpropd on the master?  Preferably in a way that'd support cascading
+propagations.
+
+The kadmind process, when it needs to run kprop, extracts the slave
+host name from the client principal name.  It assumes that the
+principal name will be of the form foo/hostname@REALM, and looks
+specifically for the "/" and "@" to chop up the string form of the
+name.  If looking up that name won't give a working IPv4 address for
+the slave, kprop will fail (and kpropd will keep waiting, incremental
+updates will stop, etc).
+
+Mapping between file offsets and structure addresses, we should be
+careful about alignment.  We're probably okay on current platforms,
+but if we break log-format compatibility with Sun at some point, use
+the chance to make the kdb_ent_header_t offsets be more strictly
+aligned in the file.  (16 or 32 bytes?)
+
+Not thread safe!  The kdb5.c code will get a lock on the update log
+file while making changes, but the lock is per-process.  Currently
+there are no processes I know of that use multiple threads and change
+the database.  (There's the Novell patch to make the KDC
+multithreaded, but the kdc-kdb-update option doesn't currently
+compile.)
+
+Logging in kpropd is poor to useless.  If there are any problems, run
+it in debug mode ("-d").  You'll still lose all output from the
+invocation of kdb5_util dump and kprop run out of kadmind.
+
+Other man page updates needed: Anything with new -x options.
index f19c6e00836a7f31ebe919e67843865c57f35b34..21f173bbe26fcc5706c4280b97b64032c6fb7acf 100644 (file)
@@ -1,4 +1,4 @@
-.\" Copyright 1995 by the Massachusetts Institute of Technology.
+.\" Copyright 1995, 2008 by the Massachusetts Institute of Technology.
 .\"
 .\" Export of this software from the United States of America may
 .\"   require a specific license from the United States Government.
@@ -224,6 +224,24 @@ This
 specifies the maximum time period that a ticket may be renewed for in
 this realm. 
 
+.IP iprop_enable
+This
+.B boolean
+("true" or "false") specifies whether incremental database propagation
+is enabled.  The default is "false".
+
+.IP iprop_master_ulogsize
+This
+.B numeric value
+specifies the maximum number of log entries to be retained for
+incremental propagation.  The maximum value is 2500; default is 1000.
+
+.IP iprop_slave_poll
+This
+.B delta time string
+specfies how often the slave KDC polls for new updates from the
+master.  Default is "2m" (that is, two minutes).
+
 .IP supported_enctypes
 list of key:salt strings that specifies the default key/salt
 combinations of principals for this realm
diff --git a/src/include/iprop.h b/src/include/iprop.h
new file mode 100644 (file)
index 0000000..208f4dc
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#ifndef _IPROP_H_RPCGEN
+#define _IPROP_H_RPCGEN
+
+#include <gssrpc/rpc.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+typedef struct {
+       u_int utf8str_t_len;
+       char *utf8str_t_val;
+} utf8str_t;
+
+typedef uint32_t kdb_sno_t;
+
+struct kdbe_time_t {
+       uint32_t seconds;
+       uint32_t useconds;
+};
+typedef struct kdbe_time_t kdbe_time_t;
+
+struct kdbe_key_t {
+       int32_t k_ver;
+       int32_t k_kvno;
+       struct {
+               u_int k_enctype_len;
+               int32_t *k_enctype_val;
+       } k_enctype;
+       struct {
+               u_int k_contents_len;
+               utf8str_t *k_contents_val;
+       } k_contents;
+};
+typedef struct kdbe_key_t kdbe_key_t;
+
+struct kdbe_data_t {
+       int32_t k_magic;
+       utf8str_t k_data;
+};
+typedef struct kdbe_data_t kdbe_data_t;
+
+struct kdbe_princ_t {
+       utf8str_t k_realm;
+       struct {
+               u_int k_components_len;
+               kdbe_data_t *k_components_val;
+       } k_components;
+       int32_t k_nametype;
+};
+typedef struct kdbe_princ_t kdbe_princ_t;
+
+struct kdbe_tl_t {
+       int16_t tl_type;
+       struct {
+               u_int tl_data_len;
+               char *tl_data_val;
+       } tl_data;
+};
+typedef struct kdbe_tl_t kdbe_tl_t;
+
+typedef struct {
+       u_int kdbe_pw_hist_t_len;
+       kdbe_key_t *kdbe_pw_hist_t_val;
+} kdbe_pw_hist_t;
+
+enum kdbe_attr_type_t {
+       AT_ATTRFLAGS = 0,
+       AT_MAX_LIFE = 1,
+       AT_MAX_RENEW_LIFE = 2,
+       AT_EXP = 3,
+       AT_PW_EXP = 4,
+       AT_LAST_SUCCESS = 5,
+       AT_LAST_FAILED = 6,
+       AT_FAIL_AUTH_COUNT = 7,
+       AT_PRINC = 8,
+       AT_KEYDATA = 9,
+       AT_TL_DATA = 10,
+       AT_LEN = 11,
+       AT_MOD_PRINC = 12,
+       AT_MOD_TIME = 13,
+       AT_MOD_WHERE = 14,
+       AT_PW_LAST_CHANGE = 15,
+       AT_PW_POLICY = 16,
+       AT_PW_POLICY_SWITCH = 17,
+       AT_PW_HIST_KVNO = 18,
+       AT_PW_HIST = 19,
+};
+typedef enum kdbe_attr_type_t kdbe_attr_type_t;
+
+struct kdbe_val_t {
+       kdbe_attr_type_t av_type;
+       union {
+               uint32_t av_attrflags;
+               uint32_t av_max_life;
+               uint32_t av_max_renew_life;
+               uint32_t av_exp;
+               uint32_t av_pw_exp;
+               uint32_t av_last_success;
+               uint32_t av_last_failed;
+               uint32_t av_fail_auth_count;
+               kdbe_princ_t av_princ;
+               struct {
+                       u_int av_keydata_len;
+                       kdbe_key_t *av_keydata_val;
+               } av_keydata;
+               struct {
+                       u_int av_tldata_len;
+                       kdbe_tl_t *av_tldata_val;
+               } av_tldata;
+               int16_t av_len;
+               uint32_t av_pw_last_change;
+               kdbe_princ_t av_mod_princ;
+               uint32_t av_mod_time;
+               utf8str_t av_mod_where;
+               utf8str_t av_pw_policy;
+               bool_t av_pw_policy_switch;
+               uint32_t av_pw_hist_kvno;
+               struct {
+                       u_int av_pw_hist_len;
+                       kdbe_pw_hist_t *av_pw_hist_val;
+               } av_pw_hist;
+               struct {
+                       u_int av_extension_len;
+                       char *av_extension_val;
+               } av_extension;
+       } kdbe_val_t_u;
+};
+typedef struct kdbe_val_t kdbe_val_t;
+
+typedef struct {
+       u_int kdbe_t_len;
+       kdbe_val_t *kdbe_t_val;
+} kdbe_t;
+
+struct kdb_incr_update_t {
+       utf8str_t kdb_princ_name;
+       kdb_sno_t kdb_entry_sno;
+       kdbe_time_t kdb_time;
+       kdbe_t kdb_update;
+       bool_t kdb_deleted;
+       bool_t kdb_commit;
+       struct {
+               u_int kdb_kdcs_seen_by_len;
+               utf8str_t *kdb_kdcs_seen_by_val;
+       } kdb_kdcs_seen_by;
+       struct {
+               u_int kdb_futures_len;
+               char *kdb_futures_val;
+       } kdb_futures;
+};
+typedef struct kdb_incr_update_t kdb_incr_update_t;
+
+typedef struct {
+       u_int kdb_ulog_t_len;
+       kdb_incr_update_t *kdb_ulog_t_val;
+} kdb_ulog_t;
+
+enum update_status_t {
+       UPDATE_OK = 0,
+       UPDATE_ERROR = 1,
+       UPDATE_FULL_RESYNC_NEEDED = 2,
+       UPDATE_BUSY = 3,
+       UPDATE_NIL = 4,
+       UPDATE_PERM_DENIED = 5,
+};
+typedef enum update_status_t update_status_t;
+
+struct kdb_last_t {
+       kdb_sno_t last_sno;
+       kdbe_time_t last_time;
+};
+typedef struct kdb_last_t kdb_last_t;
+
+struct kdb_incr_result_t {
+       kdb_last_t lastentry;
+       kdb_ulog_t updates;
+       update_status_t ret;
+};
+typedef struct kdb_incr_result_t kdb_incr_result_t;
+
+struct kdb_fullresync_result_t {
+       kdb_last_t lastentry;
+       update_status_t ret;
+};
+typedef struct kdb_fullresync_result_t kdb_fullresync_result_t;
+
+#define KRB5_IPROP_PROG 100423
+#define KRB5_IPROP_VERS 1
+
+#if defined(__STDC__) || defined(__cplusplus)
+#define IPROP_NULL 0
+extern  void * iprop_null_1(void *, CLIENT *);
+extern  void * iprop_null_1_svc(void *, struct svc_req *);
+#define IPROP_GET_UPDATES 1
+extern  kdb_incr_result_t * iprop_get_updates_1(kdb_last_t *, CLIENT *);
+extern  kdb_incr_result_t * iprop_get_updates_1_svc(kdb_last_t *, struct svc_req *);
+#define IPROP_FULL_RESYNC 2
+extern  kdb_fullresync_result_t * iprop_full_resync_1(void *, CLIENT *);
+extern  kdb_fullresync_result_t * iprop_full_resync_1_svc(void *, struct svc_req *);
+extern int krb5_iprop_prog_1_freeresult (SVCXPRT *, xdrproc_t, caddr_t);
+
+#else /* K&R C */
+#define IPROP_NULL 0
+extern  void * iprop_null_1();
+extern  void * iprop_null_1_svc();
+#define IPROP_GET_UPDATES 1
+extern  kdb_incr_result_t * iprop_get_updates_1();
+extern  kdb_incr_result_t * iprop_get_updates_1_svc();
+#define IPROP_FULL_RESYNC 2
+extern  kdb_fullresync_result_t * iprop_full_resync_1();
+extern  kdb_fullresync_result_t * iprop_full_resync_1_svc();
+extern int krb5_iprop_prog_1_freeresult ();
+#endif /* K&R C */
+
+/* the xdr functions */
+
+#if defined(__STDC__) || defined(__cplusplus)
+extern  bool_t xdr_utf8str_t (XDR *, utf8str_t*);
+extern  bool_t xdr_kdb_sno_t (XDR *, kdb_sno_t*);
+extern  bool_t xdr_kdbe_time_t (XDR *, kdbe_time_t*);
+extern  bool_t xdr_kdbe_key_t (XDR *, kdbe_key_t*);
+extern  bool_t xdr_kdbe_data_t (XDR *, kdbe_data_t*);
+extern  bool_t xdr_kdbe_princ_t (XDR *, kdbe_princ_t*);
+extern  bool_t xdr_kdbe_tl_t (XDR *, kdbe_tl_t*);
+extern  bool_t xdr_kdbe_pw_hist_t (XDR *, kdbe_pw_hist_t*);
+extern  bool_t xdr_kdbe_attr_type_t (XDR *, kdbe_attr_type_t*);
+extern  bool_t xdr_kdbe_val_t (XDR *, kdbe_val_t*);
+extern  bool_t xdr_kdbe_t (XDR *, kdbe_t*);
+extern  bool_t xdr_kdb_incr_update_t (XDR *, kdb_incr_update_t*);
+extern  bool_t xdr_kdb_ulog_t (XDR *, kdb_ulog_t*);
+extern  bool_t xdr_update_status_t (XDR *, update_status_t*);
+extern  bool_t xdr_kdb_last_t (XDR *, kdb_last_t*);
+extern  bool_t xdr_kdb_incr_result_t (XDR *, kdb_incr_result_t*);
+extern  bool_t xdr_kdb_fullresync_result_t (XDR *, kdb_fullresync_result_t*);
+
+#else /* K&R C */
+extern bool_t xdr_utf8str_t ();
+extern bool_t xdr_kdb_sno_t ();
+extern bool_t xdr_kdbe_time_t ();
+extern bool_t xdr_kdbe_key_t ();
+extern bool_t xdr_kdbe_data_t ();
+extern bool_t xdr_kdbe_princ_t ();
+extern bool_t xdr_kdbe_tl_t ();
+extern bool_t xdr_kdbe_pw_hist_t ();
+extern bool_t xdr_kdbe_attr_type_t ();
+extern bool_t xdr_kdbe_val_t ();
+extern bool_t xdr_kdbe_t ();
+extern bool_t xdr_kdb_incr_update_t ();
+extern bool_t xdr_kdb_ulog_t ();
+extern bool_t xdr_update_status_t ();
+extern bool_t xdr_kdb_last_t ();
+extern bool_t xdr_kdb_incr_result_t ();
+extern bool_t xdr_kdb_fullresync_result_t ();
+
+#endif /* K&R C */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_IPROP_H_RPCGEN */
diff --git a/src/include/iprop_hdr.h b/src/include/iprop_hdr.h
new file mode 100644 (file)
index 0000000..d629542
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+
+#ifndef        _IPROP_HDR_H
+#define        _IPROP_HDR_H
+
+/* #pragma ident       "@(#)iprop_hdr.h        1.1     04/02/20 SMI" */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This file has some defines common to the iprop client and
+ * server routines.
+ */
+
+/*
+ * Maximum size for each ulog entry is 2KB and maximum
+ * possible attribute-value pairs for each ulog entry is 20
+ */
+#define        MAXENTRY_SIZE   2048
+#define        MAXATTRS_SIZE   20
+
+#define        KIPROP_SVC_NAME "kiprop"
+#define        MAX_BACKOFF     300     /* Backoff for a maximum for 5 mts */
+
+enum iprop_role {
+       IPROP_NULL = 0,
+       IPROP_MASTER = 1,
+       IPROP_SLAVE = 2
+};
+typedef enum iprop_role iprop_role;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_IPROP_HDR_H */
index 5e1fd113a3f1f13e024a355d43d3d3ca1eb4633c..3763399d9716fa607aec529cbc2a8b836a0bd515 100644 (file)
@@ -1127,6 +1127,7 @@ void KRB5_CALLCONV krb5_free_pa_enc_ts
 
 struct _kdb5_dal_handle;       /* private, in kdb5.h */
 typedef struct _kdb5_dal_handle kdb5_dal_handle;
+struct _kdb_log_context;
 struct _krb5_context {
        krb5_magic      magic;
        krb5_enctype    *in_tkt_ktypes;
@@ -1172,6 +1173,9 @@ struct _krb5_context {
 
     /* error detail info */
     struct errinfo err;
+
+    /* For Sun iprop code; does this really have to be here?  */
+    struct _kdb_log_context *kdblog_context;
 };
 
 /* could be used in a table to find an etype and initialize a block */
index c9bcb8a52cf03d20e20f01417e8ac4295b17a69e..2a4463a2044b6272abbd2d4e82b15c4ef081c41c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * k5-platform.h
  *
- * Copyright 2003, 2004, 2005, 2007 Massachusetts Institute of Technology.
+ * Copyright 2003, 2004, 2005, 2007, 2008 Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -980,6 +980,9 @@ extern int krb5int_mkstemp(char *);
 #define mkstemp krb5int_mkstemp
 #endif
 
-
+/* Fudge for future adoption of gettext or the like.  */
+#ifndef _
+#define _(X) (X)
+#endif
 
 #endif /* K5_PLATFORM_H */
diff --git a/src/include/kdb_log.h b/src/include/kdb_log.h
new file mode 100644 (file)
index 0000000..804b732
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef        _KDB_LOG_H
+#define        _KDB_LOG_H
+
+/* #pragma ident       "@(#)kdb_log.h  1.3     04/02/23 SMI" */
+
+#include <iprop_hdr.h>
+#include <iprop.h>
+#include <limits.h>
+#include "kdb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * DB macros
+ */
+#define        INDEX(ulogaddr, i) ((unsigned long) ulogaddr + sizeof (kdb_hlog_t) + \
+       (i*ulog->kdb_block))
+
+/*
+ * Current DB version #
+ */
+#define        KDB_VERSION     1
+
+/*
+ * DB log states
+ */
+#define        KDB_STABLE      1
+#define        KDB_UNSTABLE    2
+#define        KDB_CORRUPT     3
+
+/*
+ * DB log constants
+ */
+#define        KDB_ULOG_MAGIC          0x6661212
+#define        KDB_ULOG_HDR_MAGIC      0x6662323
+
+/*
+ * DB Flags
+ */
+#define        FKADMIND        1
+#define        FKPROPLOG       2
+#define        FKPROPD         3
+#define        FKCOMMAND       4       /* Includes kadmin.local and kdb5_util */
+
+/*
+ * Default ulog file attributes
+ */
+#define        MAX_ULOGENTRIES 2500
+#define        DEF_ULOGENTRIES 1000
+#define        ULOG_IDLE_TIME  10              /* in seconds */
+/*
+ * Max size of update entry + update header
+ * We make this large since resizing can be costly.
+ */
+#define        ULOG_BLOCK      2048            /* Default size of principal record */
+
+#define        MAXLOGLEN       0x10000000      /* 256 MB log file */
+
+/*
+ * Prototype declarations
+ */
+extern krb5_error_code ulog_map(krb5_context context,
+                               const char *logname, uint32_t entries,
+                               int caller,
+                               char **db_args);
+extern krb5_error_code ulog_add_update(krb5_context context,
+       kdb_incr_update_t *upd);
+extern krb5_error_code ulog_delete_update(krb5_context context,
+       kdb_incr_update_t *upd);
+extern krb5_error_code ulog_finish_update(krb5_context context,
+       kdb_incr_update_t *upd);
+extern krb5_error_code ulog_get_entries(krb5_context context, kdb_last_t last,
+       kdb_incr_result_t *ulog_handle);
+extern krb5_error_code ulog_replay(krb5_context context,
+                                  kdb_incr_result_t *incr_ret, char **db_args);
+extern krb5_error_code ulog_conv_2logentry(krb5_context context,
+       krb5_db_entry *entries, kdb_incr_update_t *updates, int nentries);
+extern krb5_error_code ulog_conv_2dbentry(krb5_context context,
+       krb5_db_entry *entries, kdb_incr_update_t *updates, int nentries);
+extern void ulog_free_entries(kdb_incr_update_t *updates, int no_of_updates);
+extern krb5_error_code ulog_set_role(krb5_context ctx, iprop_role role);
+
+extern krb5_error_code ulog_lock(krb5_context ctx, int mode);
+
+typedef struct kdb_hlog {
+       uint32_t        kdb_hmagic;     /* Log header magic # */
+       uint16_t        db_version_num; /* Kerberos database version no. */
+       uint32_t        kdb_num;        /* # of updates in log */
+       kdbe_time_t     kdb_first_time; /* Timestamp of first update */
+       kdbe_time_t     kdb_last_time;  /* Timestamp of last update */
+       kdb_sno_t       kdb_first_sno;  /* First serial # in the update log */
+       kdb_sno_t       kdb_last_sno;   /* Last serial # in the update log */
+       uint16_t        kdb_state;      /* State of update log */
+       uint16_t        kdb_block;      /* Block size of each element */
+} kdb_hlog_t;
+
+typedef struct kdb_ent_header {
+       uint32_t        kdb_umagic;     /* Update entry magic # */
+       kdb_sno_t       kdb_entry_sno;  /* Serial # of entry */
+       kdbe_time_t     kdb_time;       /* Timestamp of update */
+       bool_t          kdb_commit;     /* Is the entry committed or not */
+       uint32_t        kdb_entry_size; /* Size of update entry */
+       uint8_t         entry_data[4];  /* Address of kdb_incr_update_t */
+} kdb_ent_header_t;
+
+typedef struct _kdb_log_context {
+       iprop_role      iproprole;
+       kdb_hlog_t      *ulog;
+       uint32_t        ulogentries;
+       int             ulogfd;
+} kdb_log_context;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_KDB_LOG_H */
index 48bf8b1f97e15cc2f962b9b1aa1c21bb05a6aa51..11e721b3714e5a1ae42e313bf76cfea94ea25ad3 100644 (file)
@@ -1,7 +1,7 @@
 /* -*- c -*-
  * include/krb5/stock/osconf.h
  *
- * Copyright 1990,1991 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
 #define KPROPD_DEFAULT_FILE "@LOCALSTATEDIR/krb5kdc/from_master"
 #define KPROPD_DEFAULT_KDB5_UTIL "@SBINDIR/kdb5_util"
 #define KPROPD_DEFAULT_KDB5_EDIT "@SBINDIR/kdb5_edit"
+#define KPROPD_DEFAULT_KPROP "@SBINDIR/kprop"
 #define KPROPD_DEFAULT_KRB_DB DEFAULT_KDB_FILE
 #define KPROPD_ACL_FILE "@LOCALSTATEDIR/krb5kdc/kpropd.acl"
 
index 76dee5c48aea8e5881b9d30087d0fd47e4d6ceba..165bf0c73611e4b849ed78309b5672469f63d130 100644 (file)
@@ -71,6 +71,20 @@ except for database dump and load, which is now provided by the
 utility.
 .PP
 If the database is LDAP, kadmin.local need not be run on the KDC. 
+.PP
+kadmin.local can be configured to log updates for incremental database
+propagation.  Incremental propagation allows slave KDC servers to
+receive principal and policy updates incrementally instead of
+receiving full dumps of the database.  This facility can be enabled in
+the
+.I kdc.conf
+file with the
+.I iprop_enable
+option.  See the
+.I kdc.conf
+documentation for other options for tuning incremental propagation
+parameters.
+
 .SH OPTIONS
 .TP
 \fB\-r\fP \fIrealm\fP
index 98ff995008161218a30faf5cdcabff1c0be8460d..02394e7f02495bcb3dea98f49d3cc1179c2ffb75 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1994 by the Massachusetts Institute of Technology.
+ * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * kadmin.c: base functions for a kadmin command line interface using
  * the OVSecure library
  */
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
 
+/* for "_" macro */
+#include "k5-platform.h"
 #include <krb5.h>
 #include <kadm5/admin.h>
 #include <adm_proto.h>
@@ -551,6 +557,11 @@ char *kadmin_startup(argc, argv)
        krb5_defkeyname = DEFAULT_KEYTAB;
     }
 
+    if ((retval = kadm5_init_iprop(handle)) != 0) {
+       com_err(whoami, retval, _("while mapping update log"));
+       exit(1);
+    }
+
     return query;
 }
 
index a7fa1778e9affa4373bed0e2007cdd690473bbff..5d480df4c3842f10007102b9ff0db0b9832257ae 100644 (file)
@@ -4,7 +4,7 @@ mydir=kadmin/dbutil
 BUILDTOP=$(REL)..$(S)..
 DEFINES = -DKDB4_DISABLE
 DEFS=
-LOCALINCLUDES = -I. @KRB4_INCLUDES@ 
+LOCALINCLUDES = -I. @KRB4_INCLUDES@
 PROG_LIBPATH=-L$(TOPLIBD) $(KRB4_LIBPATH)
 PROG_RPATH=$(KRB5_LIBDIR)
 KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
@@ -50,13 +50,15 @@ $(OUTPRE)kdb5_util.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
   $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
   $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
-  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/k5-err.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-err.h \
   $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
   $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
-  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
-  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
-  $(SRCTOP)/include/socket-utils.h kdb5_util.c kdb5_util.h
+  $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  kdb5_util.c kdb5_util.h
 $(OUTPRE)kdb5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
   $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
@@ -68,13 +70,15 @@ $(OUTPRE)kdb5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
   $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
   $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
-  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/k5-err.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-err.h \
   $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
   $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
-  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
-  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
-  $(SRCTOP)/include/socket-utils.h kdb5_create.c kdb5_util.h
+  $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  kdb5_create.c kdb5_util.h
 $(OUTPRE)kadm5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
   $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
@@ -86,10 +90,12 @@ $(OUTPRE)kadm5_create.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h kadm5_create.c kdb5_util.h \
   string_table.h
@@ -104,10 +110,12 @@ $(OUTPRE)kdb5_destroy.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h kdb5_destroy.c kdb5_util.h
 $(OUTPRE)kdb5_stash.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
@@ -120,10 +128,12 @@ $(OUTPRE)kdb5_stash.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h kdb5_stash.c kdb5_util.h
 $(OUTPRE)import_err.$(OBJEXT): $(COM_ERR_DEPS) import_err.c
@@ -139,10 +149,12 @@ $(OUTPRE)dump.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h dump.c kdb5_util.h
 $(OUTPRE)ovload.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
@@ -156,10 +168,12 @@ $(OUTPRE)ovload.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h import_err.h kdb5_util.h \
   nstrtok.h ovload.c
index 44675a6b2cac6712ab9a4e83a635df3649c2aa3e..8fcb56e5acc1e76af09c00ebeeaa01bd60f2e5ed 100644 (file)
  *
  * Dump a KDC database
  */
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
 
 #include <stdio.h>
 #include <k5-int.h>
@@ -77,6 +81,8 @@ static krb5_error_code dump_k5beta6_iterator (krb5_pointer,
 static krb5_error_code dump_k5beta6_iterator_ext (krb5_pointer,
                                                  krb5_db_entry *,
                                                  int);
+static krb5_error_code dump_iprop_iterator (krb5_pointer,
+                                           krb5_db_entry *);
 static krb5_error_code dump_k5beta7_princ (krb5_pointer,
                                           krb5_db_entry *);
 static krb5_error_code dump_k5beta7_princ_ext (krb5_pointer,
@@ -84,6 +90,8 @@ static krb5_error_code dump_k5beta7_princ_ext (krb5_pointer,
                                               int);
 static krb5_error_code dump_k5beta7_princ_withpolicy
                        (krb5_pointer, krb5_db_entry *);
+static krb5_error_code dump_iprop_princ (krb5_pointer,
+                                        krb5_db_entry *);
 static krb5_error_code dump_ov_princ (krb5_pointer,
                                      krb5_db_entry *);
 static void dump_k5beta7_policy (void *, osa_policy_ent_t);
@@ -139,6 +147,15 @@ dump_version beta7_version = {
      dump_k5beta7_policy,
      process_k5beta7_record,
 };
+dump_version iprop_version = {
+     "Kerberos iprop version",
+     "iprop",
+     0,
+     0,
+     dump_iprop_princ,
+     dump_k5beta7_policy,
+     process_k5beta7_record,
+};
 dump_version ov_version = {
      "OpenV*Secure V1.0",
      "OpenV*Secure V1.0\t",
@@ -237,6 +254,7 @@ static const char null_mprinc_name[] = "kdb5_dump@MISSING";
 static const char oldoption[] = "-old";
 static const char b6option[] = "-b6";
 static const char b7option[] = "-b7";
+static const char ipropoption[] = "-i";
 static const char verboseoption[] = "-verbose";
 static const char updateoption[] = "-update";
 static const char hashoption[] = "-hash";
@@ -825,6 +843,17 @@ dump_k5beta6_iterator_ext(ptr, entry, kadm)
     return(retval);
 }
 
+/*
+ * dump_iprop_iterator()       - Output a dump record in iprop format.
+ */
+static krb5_error_code
+dump_iprop_iterator(ptr, entry)
+    krb5_pointer       ptr;
+    krb5_db_entry      *entry;
+{
+    return dump_k5beta6_iterator_ext(ptr, entry, 1);
+}
+
 /*
  * dump_k5beta7_iterator()     - Output a dump record in krb5b7 format.
  */
@@ -887,6 +916,51 @@ dump_k5beta7_princ_withpolicy(ptr, entry)
     return dump_k5beta7_princ_ext(ptr, entry, 1);
 }
 
+/*
+ * dump_iprop_princ()  - Output a dump record in iprop format.
+ * This was created in order to dump more data, such as kadm5 tl
+ */
+static krb5_error_code
+dump_iprop_princ(ptr, entry)
+    krb5_pointer       ptr;
+    krb5_db_entry      *entry;
+{
+     krb5_error_code retval;
+     struct dump_args *arg;
+     char *name;
+     int tmp_nnames;
+
+     /* Initialize */
+     arg = (struct dump_args *) ptr;
+     name = (char *) NULL;
+
+     /*
+      * Flatten the principal name.
+      */
+     if ((retval = krb5_unparse_name(arg->kcontext,
+                                    entry->princ,
+                                    &name))) {
+               fprintf(stderr, _(pname_unp_err),
+                 arg->programname, error_message(retval));
+         return(retval);
+     }
+     /*
+      * If we don't have any match strings, or if our name matches, then
+      * proceed with the dump, otherwise, just forget about it.
+      */
+     if (!arg->nnames || name_matches(name, arg)) {
+         fprintf(arg->ofile, "princ\t");
+         
+         /* save the callee from matching the name again */
+         tmp_nnames = arg->nnames;
+         arg->nnames = 0;
+         retval = dump_iprop_iterator(ptr, entry);
+         arg->nnames = tmp_nnames;
+     }
+     free(name);
+       return (retval);
+}
+
 void dump_k5beta7_policy(void *data, osa_policy_ent_t entry)
 {
      struct dump_args *arg;
@@ -1024,7 +1098,10 @@ dump_db(argc, argv)
     int                        aindex;
     krb5_boolean       locked;
     char               *new_mkey_file = 0;
-       
+    bool_t             dump_sno = FALSE;
+    kdb_log_context    *log_ctx;
+    char               **db_args = 0; /* XXX */
+
     /*
      * Parse the arguments.
      */
@@ -1038,6 +1115,7 @@ dump_db(argc, argv)
     mkey_convert = 0;
     backwards = 0;
     recursive = 0;
+    log_ctx = util_context->kdblog_context;
 
     /*
      * Parse the qualifiers.
@@ -1051,7 +1129,22 @@ dump_db(argc, argv)
             dump = &beta7_version;
        else if (!strcmp(argv[aindex], ovoption))
             dump = &ov_version;
-       else if (!strcmp(argv[aindex], verboseoption))
+       else if (!strcmp(argv[aindex], ipropoption)) {
+           if (log_ctx && log_ctx->iproprole) {
+               dump = &iprop_version;
+               /*
+                * dump_sno is used to indicate if the serial
+                * # should be populated in the output
+                * file to be used later by iprop for updating
+                * the slave's update log when loading
+                */
+               dump_sno = TRUE;
+           } else {
+               fprintf(stderr, _("Iprop not enabled\n"));
+               exit_status++;
+               return;
+           }
+       } else if (!strcmp(argv[aindex], verboseoption))
            arglist.verbose++;
        else if (!strcmp(argv[aindex], "-mkey_convert"))
            mkey_convert = 1;
@@ -1172,6 +1265,32 @@ dump_db(argc, argv)
        arglist.ofile = f;
        arglist.kcontext = util_context;
        fprintf(arglist.ofile, "%s", dump->header);
+
+       if (dump_sno) {
+           if (ulog_map(util_context, global_params.iprop_logfile,
+                        global_params.iprop_ulogsize, FKCOMMAND, db_args)) {
+               fprintf(stderr,
+                       _("%s: Could not map log\n"), programname);
+               exit_status++;
+               goto unlock_and_return;
+           }
+
+           /*
+            * We grab the lock twice (once again in the iterator call),
+            * but that's ok since the lock func handles incr locks held.
+            */
+           if (krb5_db_lock(util_context, KRB5_LOCKMODE_SHARED)) {
+               fprintf(stderr,
+                       _("%s: Couldn't grab lock\n"), programname);
+               exit_status++;
+               goto unlock_and_return;
+           }
+
+           fprintf(f, " %u", log_ctx->ulog->kdb_last_sno);
+           fprintf(f, " %u", log_ctx->ulog->kdb_last_time.seconds);
+           fprintf(f, " %u", log_ctx->ulog->kdb_last_time.useconds);
+       }
+
        if (dump->header[strlen(dump->header)-1] != '\n')
             fputc('\n', arglist.ofile);
        
@@ -1182,6 +1301,8 @@ dump_db(argc, argv)
             fprintf(stderr, dumprec_err,
                     programname, dump->name, error_message(kret));
             exit_status++;
+            if (dump_sno)
+                (void) krb5_db_unlock(util_context);
        }
        if (dump->dump_policy &&
            (kret = krb5_db_iter_policy( util_context, "*", dump->dump_policy,
@@ -1199,6 +1320,7 @@ dump_db(argc, argv)
            update_ok_file(ofile);
        }
     }
+unlock_and_return:
     if (locked)
        (void) krb5_lock_file(util_context, fileno(f), KRB5_LOCKMODE_UNLOCK);
 }
@@ -2137,6 +2259,10 @@ load_db(argc, argv)
     krb5_int32         crflags;
     int                        aindex;
     int                        db_locked = 0;
+    char               iheader[MAX_HEADER];
+    kdb_log_context    *log_ctx;
+    int                        add_update = 1;
+    uint32_t           caller, last_sno, last_seconds, last_useconds;
 
     /*
      * Parse the arguments.
@@ -2152,6 +2278,8 @@ load_db(argc, argv)
     crflags = KRB5_KDB_CREATE_BTREE;
     exit_status = 0;
     dbname_tmp = (char *) NULL;
+    log_ctx = util_context->kdblog_context;
+
     for (aindex = 1; aindex < argc; aindex++) {
        if (!strcmp(argv[aindex], oldoption))
             load = &old_version;
@@ -2161,7 +2289,16 @@ load_db(argc, argv)
             load = &beta7_version;
        else if (!strcmp(argv[aindex], ovoption))
             load = &ov_version;
-       else if (!strcmp(argv[aindex], verboseoption))
+       else if (!strcmp(argv[aindex], ipropoption)) {
+           if (log_ctx && log_ctx->iproprole) {
+               load = &iprop_version;
+               add_update = FALSE;
+           } else {
+               fprintf(stderr, _("Iprop not enabled\n"));
+               exit_status++;
+               return;
+           }
+       } else if (!strcmp(argv[aindex], verboseoption))
            verbose = 1;
        else if (!strcmp(argv[aindex], updateoption))
            update = 1;
@@ -2206,6 +2343,9 @@ load_db(argc, argv)
        return;
     }
 
+    if (log_ctx && log_ctx->iproprole)
+       kcontext->kdblog_context = log_ctx;
+
     /*
      * Open the dumpfile
      */
@@ -2358,6 +2498,53 @@ load_db(argc, argv)
     else
        db_locked = 1;
     
+    if (log_ctx && log_ctx->iproprole) {
+       if (add_update)
+           caller = FKCOMMAND;
+       else
+           caller = FKPROPD;
+
+       if (ulog_map(kcontext, global_params.iprop_logfile,
+                    global_params.iprop_ulogsize, caller, db5util_db_args)) {
+           fprintf(stderr, _("%s: Could not map log\n"),
+                   programname);
+           exit_status++;
+           goto error;
+       }
+
+       /*
+        * We don't want to take out the ulog out from underneath
+        * kadmind so we reinit the header log.
+        *
+        * We also don't want to add to the update log since we
+        * are doing a whole sale replace of the db, because:
+        *      we could easily exceed # of update entries
+        *      we could implicity delete db entries during a replace
+        *      no advantage in incr updates when entire db is replaced
+        */
+       if (!update) {
+           memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t));
+
+           log_ctx->ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
+           log_ctx->ulog->db_version_num = KDB_VERSION;
+           log_ctx->ulog->kdb_state = KDB_STABLE;
+           log_ctx->ulog->kdb_block = ULOG_BLOCK;
+
+           log_ctx->iproprole = IPROP_NULL;
+
+           if (!add_update) {
+               sscanf(buf, "%s %u %u %u", iheader, &last_sno,
+                      &last_seconds, &last_useconds);
+
+               log_ctx->ulog->kdb_last_sno = last_sno;
+               log_ctx->ulog->kdb_last_time.seconds =
+                   last_seconds;
+               log_ctx->ulog->kdb_last_time.useconds =
+                   last_useconds;
+           }
+       }
+    }
+
     if (restore_dump(programname, kcontext, (dumpfile) ? dumpfile : stdin_name,
                     f, verbose, load)) {
         fprintf(stderr, restfail_fmt,
index 3f6f6557f64fa0ae4be73403ef555d486fc4a847..c02b40287668a52e7d608efe76aa2998cfbe690f 100644 (file)
@@ -242,6 +242,7 @@ static int add_admin_princs(void *handle, krb5_context context, char *realm)
        goto clean_and_exit;
   
 clean_and_exit:
+  free(service_name);
 
   return ret;
 }
index da192256ea612e5eb62bd2b93ab5787dada6c175..b8959e102a27d89fde59357a4dc2366c936afebb 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * kadmin/dbutil/kdb5_create.c
  *
- * Copyright 1990,1991,2001, 2002 by the Massachusetts Institute of Technology.
+ * Copyright 1990,1991,2001, 2002, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  */
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
 
 #include <stdio.h>
 #include <k5-int.h>
@@ -161,6 +165,7 @@ void kdb5_create(argc, argv)
     unsigned int pw_size = 0;
     int do_stash = 0;
     krb5_data pwd, seed;
+    kdb_log_context *log_ctx;
           
     if (strrchr(argv[0], '/'))
        argv[0] = strrchr(argv[0], '/')+1;
@@ -190,6 +195,8 @@ void kdb5_create(argc, argv)
     rblock.nkslist = global_params.num_keysalts;
     rblock.kslist = global_params.keysalts;
 
+    log_ctx = util_context->kdblog_context;
+
     printf ("Loading random data\n");
     retval = krb5_c_random_os_entropy (util_context, 1, NULL);
     if (retval) {
@@ -274,6 +281,36 @@ master key name '%s'\n",
 /*             global_params.dbname); */
 /*     exit_status++; return; */
 /*     } */
+
+    if (log_ctx && log_ctx->iproprole) {
+       if (retval = ulog_map(util_context, global_params.iprop_logfile,
+                             global_params.iprop_ulogsize, FKCOMMAND,
+                             db5util_db_args)) {
+           com_err(argv[0], retval,
+                   _("while creating update log"));
+           exit_status++;
+           return;
+       }
+
+       /*
+        * We're reinitializing the update log in case one already
+        * existed, but this should never happen.
+        */
+       (void) memset(log_ctx->ulog, 0, sizeof (kdb_hlog_t));
+
+       log_ctx->ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
+       log_ctx->ulog->db_version_num = KDB_VERSION;
+       log_ctx->ulog->kdb_state = KDB_STABLE;
+       log_ctx->ulog->kdb_block = ULOG_BLOCK;
+
+       /*
+        * Since we're creating a new db we shouldn't worry about
+        * adding the initial principals since any slave might as well
+        * do full resyncs from this newly created db.
+        */
+       log_ctx->iproprole = IPROP_NULL;
+    }
+
     if ((retval = add_principal(util_context, master_princ, MASTER_KEY, &rblock)) ||
        (retval = add_principal(util_context, &tgt_princ, TGT_KEY, &rblock))) {
        (void) krb5_db_fini(util_context);
index 22b75eef68dd1a6a87c00462da0be86ef4398e23..160268f24ec9f12db2347ff0deabb79b93a0c229 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * admin/destroy/kdb5_destroy.c
  *
- * Copyright 1990 by the Massachusetts Institute of Technology.
+ * Copyright 1990, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -106,6 +106,10 @@ kdb5_destroy(argc, argv)
        exit_status++; return;
     }
 
+    if (global_params.iprop_enabled) {
+       (void) unlink(global_params.iprop_logfile);
+    }
+
     dbactive = FALSE;
     printf("** Database '%s' destroyed.\n", dbname);
     return;
index 1807d1ad00fcfe164e659b9ad9697f557673b23d..1b2aa60c57fcef0c9e209fe7c9a9944fbf9a400f 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * admin/edit/kdb5_edit.c
  *
- * (C) Copyright 1990,1991, 1996 by the Massachusetts Institute of Technology.
+ * (C) Copyright 1990,1991, 1996, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -308,6 +308,11 @@ int main(argc, argv)
     if (cmd->opendb && open_db_and_mkey())
         return exit_status;
 
+    if (global_params.iprop_enabled == TRUE)
+       ulog_set_role(util_context, IPROP_MASTER);
+    else
+       ulog_set_role(util_context, IPROP_NULL);
+
     (*cmd->func)(cmd_argc, cmd_argv);
 
     if( db_name_tmp )
index 90b7b43ef35c364d2b77e09f2aeab09f63874735..69dfba9862f6d5f353d242ff8b304ec966597b5c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * admin/edit/kdb5_edit.h
  *
- * Copyright 1992 by the Massachusetts Institute of Technology.
+ * Copyright 1992, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -25,6 +25,9 @@
  * 
  */
 
+#include <kdb_log.h>
+
+#define MAX_HEADER     1024
 #define REALM_SEP      '@'
 #define REALM_SEP_STR  "@"
 
index bc3052b25c423a6134e89e60aa05747b0f10f4d8..63b4941b4de929e215b4ed6e18bd284579a0d2b6 100644 (file)
@@ -6,14 +6,15 @@ KDB_DEP_LIB=$(DL_LIB) $(THREAD_LINKOPTS)
 DEFS=
 
 LOCALINCLUDES = -I$(SRCTOP)/lib/gssapi/generic -I$(SRCTOP)/lib/gssapi/krb5 \
-       -I$(BUILDTOP)/lib/gssapi/generic -I$(BUILDTOP)/lib/gssapi/krb5
+       -I$(BUILDTOP)/lib/gssapi/generic -I$(BUILDTOP)/lib/gssapi/krb5 \
+       -I$(SRCTOP)/lib/kadm5/srv
 
 PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
 
 PROG = kadmind
-OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o server_glue_v1.o
-SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c server_glue_v1.c
+OBJS = kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o server_glue_v1.o ipropd_svc.o
+SRCS = kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c misc.c server_glue_v1.c ipropd_svc.c
 
 all:: $(PROG)
 
@@ -75,15 +76,16 @@ $(OUTPRE)ovsec_kadmd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
   $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_kt.h \
-  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
-  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
-  $(SRCTOP)/include/socket-utils.h $(SRCTOP)/lib/gssapi/generic/gssapiP_generic.h \
-  $(SRCTOP)/lib/gssapi/generic/gssapi_generic.h $(SRCTOP)/lib/gssapi/krb5/gssapiP_krb5.h \
-  misc.h ovsec_kadmd.c
+  $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  $(SRCTOP)/lib/gssapi/generic/gssapiP_generic.h $(SRCTOP)/lib/gssapi/generic/gssapi_generic.h \
+  $(SRCTOP)/lib/gssapi/krb5/gssapiP_krb5.h misc.h ovsec_kadmd.c
 $(OUTPRE)schpw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
   $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
@@ -130,3 +132,20 @@ $(OUTPRE)server_glue_v1.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-thread.h \
   $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h misc.h \
   server_glue_v1.c
+$(OUTPRE)ipropd_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+  $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
+  $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+  $(BUILDTOP)/include/kadm5/kadm_rpc.h $(BUILDTOP)/include/kadm5/server_internal.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/lib/gssapi/krb5/gssapi_krb5.h $(COM_ERR_DEPS) \
+  $(SRCTOP)/include/adm_proto.h $(SRCTOP)/include/gssrpc/auth.h \
+  $(SRCTOP)/include/gssrpc/auth_gss.h $(SRCTOP)/include/gssrpc/auth_unix.h \
+  $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
+  $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
+  $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
+  $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/lib/kadm5/srv/server_acl.h ipropd_svc.c misc.h
diff --git a/src/kadmin/server/ipropd_svc.c b/src/kadmin/server/ipropd_svc.c
new file mode 100644 (file)
index 0000000..673d2a9
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)ipropd_svc.c       1.2     04/02/20 SMI" */
+
+
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/resource.h> /* rlimit */
+#include <syslog.h>
+
+#include "k5-platform.h"
+#include <kadm5/admin.h>
+#include <kadm5/kadm_rpc.h>
+#include <kadm5/server_internal.h>
+#include <server_acl.h>
+#include <adm_proto.h>
+#include <string.h>
+#include <gssapi_krb5.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <kdb_log.h>
+#include "misc.h"
+#include "osconf.h"
+
+extern gss_name_t rqst2name(struct svc_req *rqstp);
+
+extern int setup_gss_names(struct svc_req *, gss_buffer_desc *,
+                          gss_buffer_desc *);
+extern char *client_addr(struct svc_req *, char *);
+extern void *global_server_handle;
+extern int nofork;
+extern short l_port;
+static char abuf[33];
+
+char *client_addr(struct svc_req *svc, char *buf) {
+    return strcpy(buf, inet_ntoa(svc->rq_xprt->xp_raddr.sin_addr));
+}
+
+static char *reply_ok_str      = "UPDATE_OK";
+static char *reply_err_str     = "UPDATE_ERROR";
+static char *reply_fr_str      = "UPDATE_FULL_RESYNC_NEEDED";
+static char *reply_busy_str    = "UPDATE_BUSY";
+static char *reply_nil_str     = "UPDATE_NIL";
+static char *reply_perm_str    = "UPDATE_PERM_DENIED";
+static char *reply_unknown_str = "<UNKNOWN_CODE>";
+
+#define        LOG_UNAUTH  _("Unauthorized request: %s, %s, "  \
+                     "client=%s, service=%s, addr=%s")
+#define        LOG_DONE    _("Request: %s, %s, %s, client=%s, "        \
+                     "service=%s, addr=%s")
+
+#ifdef DPRINT
+#undef DPRINT
+#endif
+#define        DPRINT(i) if (nofork) printf i
+
+
+static void
+debprret(char *w, update_status_t ret, kdb_sno_t sno)
+{
+    switch (ret) {
+    case UPDATE_OK:
+       printf("%s: end (OK, sno=%u)\n",
+              w, sno);
+       break;
+    case UPDATE_ERROR:
+       printf("%s: end (ERROR)\n", w);
+       break;
+    case UPDATE_FULL_RESYNC_NEEDED:
+       printf("%s: end (FR NEEDED)\n", w);
+       break;
+    case UPDATE_BUSY:
+       printf("%s: end (BUSY)\n", w);
+       break;
+    case UPDATE_NIL:
+       printf("%s: end (NIL)\n", w);
+       break;
+    case UPDATE_PERM_DENIED:
+       printf("%s: end (PERM)\n", w);
+       break;
+    default:
+       printf("%s: end (UNKNOWN return code (%d))\n", w, ret);
+    }
+}
+
+static char *
+replystr(update_status_t ret)
+{
+    switch (ret) {
+    case UPDATE_OK:
+       return (reply_ok_str);
+    case UPDATE_ERROR:
+       return (reply_err_str);
+    case UPDATE_FULL_RESYNC_NEEDED:
+       return (reply_fr_str);
+    case UPDATE_BUSY:
+       return (reply_busy_str);
+    case UPDATE_NIL:
+       return (reply_nil_str);
+    case UPDATE_PERM_DENIED:
+       return (reply_perm_str);
+    default:
+       return (reply_unknown_str);
+    }
+}
+
+/* Returns null on allocation failure.
+   Regardless of success or failure, frees the input buffer.  */
+static char *
+buf_to_string(gss_buffer_desc *b)
+{
+    OM_uint32 min_stat;
+    char *s = malloc(b->length+1);
+
+    if (s) {
+       memcpy(s, b->value, b->length);
+       s[b->length] = 0;
+    }
+    (void) gss_release_buffer(&min_stat, b);
+    return s;
+}
+
+kdb_incr_result_t *
+iprop_get_updates_1_svc(kdb_last_t *arg, struct svc_req *rqstp)
+{
+    static kdb_incr_result_t ret;
+    char *whoami = "iprop_get_updates_1";
+    int kret;
+    kadm5_server_handle_t handle = global_server_handle;
+    char *client_name = 0, *service_name = 0;
+    char obuf[256] = {0};
+
+    /* default return code */
+    ret.ret = UPDATE_ERROR;
+
+    DPRINT(("%s: start, last_sno=%lu\n", whoami,
+           (unsigned long) arg->last_sno));
+
+    if (!handle) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("%s: server handle is NULL"),
+                        whoami);
+       goto out;
+    }
+
+    {
+       gss_buffer_desc client_desc, service_desc;
+
+       if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
+           krb5_klog_syslog(LOG_ERR,
+                            _("%s: setup_gss_names failed"),
+                            whoami);
+           goto out;
+       }
+       client_name = buf_to_string(&client_desc);
+       service_name = buf_to_string(&service_desc);
+       if (client_name == NULL || service_name == NULL) {
+           free(client_name);
+           free(service_name);
+           krb5_klog_syslog(LOG_ERR,
+                            "%s: out of memory recording principal names",
+                            whoami);
+           goto out;
+       }
+    }
+
+    DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
+           whoami, client_name, service_name));
+
+    if (!kadm5int_acl_check(handle->context,
+                           rqst2name(rqstp),
+                           ACL_IPROP,
+                           NULL,
+                           NULL)) {
+       ret.ret = UPDATE_PERM_DENIED;
+
+       krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
+                        "<null>", client_name, service_name,
+                        client_addr(rqstp, abuf));
+       goto out;
+    }
+
+    kret = ulog_get_entries(handle->context, *arg, &ret);
+
+    if (ret.ret == UPDATE_OK) {
+       (void) snprintf(obuf, sizeof (obuf),
+                       _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=%lu"),
+                       replystr(ret.ret),
+                       (unsigned long)arg->last_sno,
+                       (unsigned long)ret.lastentry.last_sno);
+    } else {
+       (void) snprintf(obuf, sizeof (obuf),
+                       _("%s; Incoming SerialNo=%lu; Outgoing SerialNo=N/A"),
+                       replystr(ret.ret),
+                       (unsigned long)arg->last_sno);
+    }
+
+    krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami,
+                    obuf,
+                    ((kret == 0) ? "success" : error_message(kret)),
+                    client_name, service_name,
+                    client_addr(rqstp, abuf));
+
+out:
+    if (nofork)
+       debprret(whoami, ret.ret, ret.lastentry.last_sno);
+    free(client_name);
+    free(service_name);
+    return (&ret);
+}
+
+
+/*
+ * Given a client princ (foo/fqdn@R), copy (in arg cl) the fqdn substring.
+ * Return arg cl str ptr on success, else NULL.
+ */
+static char *
+getclhoststr(char *clprinc, char *cl, int len)
+{
+    char *s;
+    if ((s = strchr(clprinc, '/')) != NULL) {
+       /* XXX "!++s"?  */
+       if (!++s)
+           return NULL;
+       if (strlen(s) >= len)
+           return NULL;
+       strcpy(cl, s);
+       /* XXX Copy with @REALM first, with bounds check, then
+          chop off the realm??  */
+       if ((s = strchr(cl, '@')) != NULL) {
+           *s = '\0';
+           return (cl); /* success */
+       }
+    }
+
+    return (NULL);
+}
+
+kdb_fullresync_result_t *
+iprop_full_resync_1_svc(/* LINTED */ void *argp, struct svc_req *rqstp)
+{
+    static kdb_fullresync_result_t ret;
+    char *tmpf = 0;
+    char *ubuf = 0;
+    char clhost[MAXHOSTNAMELEN] = {0};
+    int pret, fret;
+    kadm5_server_handle_t handle = global_server_handle;
+    OM_uint32 min_stat;
+    gss_name_t name = NULL;
+    char *client_name = NULL, *service_name = NULL;
+    char *whoami = "iprop_full_resync_1";
+
+    /* default return code */
+    ret.ret = UPDATE_ERROR;
+
+    if (!handle) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("%s: server handle is NULL"),
+                        whoami);
+       goto out;
+    }
+
+    DPRINT(("%s: start\n", whoami));
+
+    {
+       gss_buffer_desc client_desc, service_desc;
+
+       if (setup_gss_names(rqstp, &client_desc, &service_desc) < 0) {
+           krb5_klog_syslog(LOG_ERR,
+                            _("%s: setup_gss_names failed"),
+                            whoami);
+           goto out;
+       }
+       client_name = buf_to_string(&client_desc);
+       service_name = buf_to_string(&service_desc);
+       if (client_name == NULL || service_name == NULL) {
+           free(client_name);
+           free(service_name);
+           krb5_klog_syslog(LOG_ERR,
+                            "%s: out of memory recording principal names",
+                            whoami);
+           goto out;
+       }
+    }
+
+    DPRINT(("%s: clprinc=`%s'\n\tsvcprinc=`%s'\n",
+           whoami, client_name, service_name));
+
+    if (!kadm5int_acl_check(handle->context,
+                           rqst2name(rqstp),
+                           ACL_IPROP,
+                           NULL,
+                           NULL)) {
+       ret.ret = UPDATE_PERM_DENIED;
+
+       krb5_klog_syslog(LOG_NOTICE, LOG_UNAUTH, whoami,
+                        "<null>", client_name, service_name,
+                        client_addr(rqstp, abuf));
+       goto out;
+    }
+
+    if (!getclhoststr(client_name, clhost, sizeof (clhost))) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("%s: getclhoststr failed"),
+                        whoami);
+       goto out;
+    }
+
+    /*
+     * construct db dump file name; kprop style name + clnt fqdn
+     */
+    if (asprintf(&tmpf, "%s_%s", KPROP_DEFAULT_FILE, clhost) < 0) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("%s: unable to construct db dump file name; out of memory"),
+                        whoami);
+       goto out;
+    }
+
+    /*
+     * note the -i; modified version of kdb5_util dump format
+     * to include sno (serial number)
+     */
+    if (asprintf(&ubuf, "%s dump -i %s", KPROPD_DEFAULT_KDB5_UTIL,
+                tmpf) < 0) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("%s: cannot construct kdb5 util dump string too long; out of memory"),
+                        whoami);
+       goto out;
+    }
+
+    /*
+     * Fork to dump the db and xfer it to the slave.
+     * (the fork allows parent to return quickly and the child
+     * acts like a callback to the slave).
+     */
+    fret = fork();
+    DPRINT(("%s: fork=%d (%d)\n", whoami, fret, getpid()));
+
+    switch (fret) {
+    case -1: /* error */
+       if (nofork) {
+           perror(whoami);
+       }
+       krb5_klog_syslog(LOG_ERR,
+                        _("%s: fork failed: %s"),
+                        whoami,
+                        error_message(errno));
+       goto out;
+
+    case 0: /* child */
+       DPRINT(("%s: run `%s' ...\n", whoami, ubuf));
+       (void) signal(SIGCHLD, SIG_DFL);
+       /* run kdb5_util(1M) dump for IProp */
+       /* XXX popen can return NULL; is pclose(NULL) okay?  */
+       pret = pclose(popen(ubuf, "w"));
+       DPRINT(("%s: pclose=%d\n", whoami, pret));
+       if (pret != 0) {
+           /* XXX popen/pclose may not set errno
+              properly, and the error could be from the
+              subprocess anyways.  */
+           if (nofork) {
+               perror(whoami);
+           }
+           krb5_klog_syslog(LOG_ERR,
+                            _("%s: pclose(popen) failed: %s"),
+                            whoami,
+                            error_message(errno));
+           goto out;
+       }
+
+       DPRINT(("%s: exec `kprop -f %s %s' ...\n",
+               whoami, tmpf, clhost));
+       /* XXX Yuck!  */
+       if (getenv("KPROP_PORT"))
+           pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf,
+                        "-P", getenv("KPROP_PORT"),
+                        clhost, NULL);
+       else
+           pret = execl(KPROPD_DEFAULT_KPROP, "kprop", "-f", tmpf,
+                        clhost, NULL);
+       if (pret == -1) {
+           if (nofork) {
+               perror(whoami);
+           }
+           krb5_klog_syslog(LOG_ERR,
+                            _("%s: exec failed: %s"),
+                            whoami,
+                            error_message(errno));
+           goto out;
+       }
+
+    default: /* parent */
+       ret.ret = UPDATE_OK;
+       /* not used by slave (sno is retrieved from kdb5_util dump) */
+       ret.lastentry.last_sno = 0;
+       ret.lastentry.last_time.seconds = 0;
+       ret.lastentry.last_time.useconds = 0;
+
+       krb5_klog_syslog(LOG_NOTICE, LOG_DONE, whoami,
+                        "<null>",
+                        "success",
+                        client_name, service_name,
+                        client_addr(rqstp, abuf));
+
+       goto out;
+    }
+
+out:
+    if (nofork)
+       debprret(whoami, ret.ret, 0);
+    free(client_name);
+    free(service_name);
+    if (name)
+       gss_release_name(&min_stat, &name);
+    free(tmpf);
+    free(ubuf);
+    return (&ret);
+}
+
+static int
+check_iprop_rpcsec_auth(struct svc_req *rqstp)
+{
+    /* XXX Since the client can authenticate against any principal in
+       the database, we need to do a sanity check.  Only checking for
+       "kiprop" now, but that means theoretically the client could be
+       authenticating to kiprop on some other machine.  */
+    /* Code taken from kadm_rpc_svc.c, tweaked.  */
+
+     gss_ctx_id_t ctx;
+     krb5_context kctx;
+     OM_uint32 maj_stat, min_stat;
+     gss_name_t name;
+     krb5_principal princ;
+     int ret, success;
+     krb5_data *c1, *c2, *realm;
+     gss_buffer_desc gss_str;
+     kadm5_server_handle_t handle;
+     size_t slen;
+     char *sdots;
+
+     success = 0;
+     handle = (kadm5_server_handle_t)global_server_handle;
+
+     if (rqstp->rq_cred.oa_flavor != RPCSEC_GSS)
+         return 0;
+
+     ctx = rqstp->rq_svccred;
+
+     maj_stat = gss_inquire_context(&min_stat, ctx, NULL, &name,
+                                   NULL, NULL, NULL, NULL, NULL);
+     if (maj_stat != GSS_S_COMPLETE) {
+         krb5_klog_syslog(LOG_ERR, "check_rpcsec_auth: "
+                          "failed inquire_context, stat=%u", maj_stat);
+         log_badauth(maj_stat, min_stat,
+                     &rqstp->rq_xprt->xp_raddr, NULL);
+         goto fail_name;
+     }
+
+     kctx = handle->context;
+     ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
+     if (ret == 0)
+         goto fail_name;
+
+     slen = gss_str.length;
+     trunc_name(&slen, &sdots);
+     /*
+      * Since we accept with GSS_C_NO_NAME, the client can authenticate
+      * against the entire kdb.  Therefore, ensure that the service
+      * name is something reasonable.
+      */
+     if (krb5_princ_size(kctx, princ) != 2)
+         goto fail_princ;
+
+     c1 = krb5_princ_component(kctx, princ, 0);
+     c2 = krb5_princ_component(kctx, princ, 1);
+     realm = krb5_princ_realm(kctx, princ);
+     if (strncmp(handle->params.realm, realm->data, realm->length) == 0
+        && strncmp("kiprop", c1->data, c1->length) == 0) {
+        success = 1;
+     }
+
+fail_princ:
+     if (!success) {
+        krb5_klog_syslog(LOG_ERR, "bad service principal %.*s%s",
+                         (int) slen, (char *) gss_str.value, sdots);
+     }
+     gss_release_buffer(&min_stat, &gss_str);
+     krb5_free_principal(kctx, princ);
+fail_name:
+     gss_release_name(&min_stat, &name);
+     return success;
+}
+
+void
+krb5_iprop_prog_1(struct svc_req *rqstp,
+                 register SVCXPRT *transp)
+{
+    union {
+       kdb_last_t iprop_get_updates_1_arg;
+    } argument;
+    char *result;
+    bool_t (*_xdr_argument)(), (*_xdr_result)();
+    char *(*local)(/* union XXX *, struct svc_req * */);
+    char *whoami = "krb5_iprop_prog_1";
+
+    if (!check_iprop_rpcsec_auth(rqstp)) {
+       krb5_klog_syslog(LOG_ERR,
+                        "authentication attempt failed: %s, RPC authentication flavor %d",
+                        inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
+                        rqstp->rq_cred.oa_flavor);
+       svcerr_weakauth(transp);
+       return;
+    }
+
+    switch (rqstp->rq_proc) {
+    case NULLPROC:
+       (void) svc_sendreply(transp, xdr_void,
+                            (char *)NULL);
+       return;
+
+    case IPROP_GET_UPDATES:
+       _xdr_argument = xdr_kdb_last_t;
+       _xdr_result = xdr_kdb_incr_result_t;
+       local = (char *(*)()) iprop_get_updates_1_svc;
+       break;
+
+    case IPROP_FULL_RESYNC:
+       _xdr_argument = xdr_void;
+       _xdr_result = xdr_kdb_fullresync_result_t;
+       local = (char *(*)()) iprop_full_resync_1_svc;
+       break;
+
+    default:
+       krb5_klog_syslog(LOG_ERR,
+                        _("RPC unknown request: %d (%s)"),
+                        rqstp->rq_proc, whoami);
+       svcerr_noproc(transp);
+       return;
+    }
+    (void) memset((char *)&argument, 0, sizeof (argument));
+    if (!svc_getargs(transp, _xdr_argument, (caddr_t)&argument)) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("RPC svc_getargs failed (%s)"),
+                        whoami);
+       svcerr_decode(transp);
+       return;
+    }
+    result = (*local)(&argument, rqstp);
+
+    if (_xdr_result && result != NULL &&
+       !svc_sendreply(transp, _xdr_result, result)) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("RPC svc_sendreply failed (%s)"),
+                        whoami);
+       svcerr_systemerr(transp);
+    }
+    if (!svc_freeargs(transp, _xdr_argument, (caddr_t)&argument)) {
+       krb5_klog_syslog(LOG_ERR,
+                        _("RPC svc_freeargs failed (%s)"),
+                        whoami);
+
+       exit(1);
+    }
+
+    if (rqstp->rq_proc == IPROP_GET_UPDATES) {
+       /* LINTED */
+       kdb_incr_result_t *r = (kdb_incr_result_t *)result;
+
+       if (r->ret == UPDATE_OK) {
+           ulog_free_entries(r->updates.kdb_ulog_t_val,
+                             r->updates.kdb_ulog_t_len);
+           r->updates.kdb_ulog_t_val = NULL;
+           r->updates.kdb_ulog_t_len = 0;
+       }
+    }
+
+}
+
+#if 0
+/*
+ * Get the host base service name for the kiprop principal. Returns
+ * KADM5_OK on success. Caller must free the storage allocated for
+ * host_service_name.
+ */
+kadm5_ret_t
+kiprop_get_adm_host_srv_name(krb5_context context,
+                            const char *realm,
+                            char **host_service_name)
+{
+    kadm5_ret_t ret;
+    char *name;
+    char *host;
+
+    if (ret = kadm5_get_master(context, realm, &host))
+       return (ret);
+
+    name = malloc(strlen(KIPROP_SVC_NAME)+ strlen(host) + 2);
+    if (name == NULL) {
+       free(host);
+       return (ENOMEM);
+    }
+    (void) sprintf(name, "%s@%s", KIPROP_SVC_NAME, host);
+    free(host);
+    *host_service_name = name;
+
+    return (KADM5_OK);
+}
+#endif
index 733602052a95c460db00a66e4cc91c4ba04b9183..c56eedd0b80bef54ddda4d838da60344dd355943 100644 (file)
@@ -24,7 +24,6 @@
 extern void *global_server_handle;
 
 static int check_rpcsec_auth(struct svc_req *);
-static int gss_to_krb5_name(struct svc_req *, krb5_context, gss_name_t, krb5_principal *, gss_buffer_t);
 
 void log_badauth(OM_uint32 major, OM_uint32 minor,
                 struct sockaddr_in *addr, char *data);
@@ -272,7 +271,7 @@ check_rpcsec_auth(struct svc_req *rqstp)
      }
 
      kctx = handle->context;
-     ret = gss_to_krb5_name(rqstp, kctx, name, &princ, &gss_str);
+     ret = gss_to_krb5_name_1(rqstp, kctx, name, &princ, &gss_str);
      if (ret == 0)
          goto fail_name;
 
@@ -310,9 +309,9 @@ fail_name:
      return success;
 }
 
-static int
-gss_to_krb5_name(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
-                krb5_principal *princ, gss_buffer_t gss_str)
+int
+gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
+                  krb5_principal *princ, gss_buffer_t gss_str)
 {
      OM_uint32 status, minor_stat;
      gss_OID gss_type;
index ad810e6f2da46faf6a51e172631113f1df14aafe..2a227fb4e473f851c49225f540bcd1eca15fec34 100644 (file)
@@ -54,6 +54,21 @@ section below.
 .PP
 After the server begins running, it puts itself in the background and
 disassociates itself from its controlling terminal.
+.PP
+kadmind can be configured for incremental database propagation.
+Incremental propagation allows slave KDC servers to receive principal
+and policy updates incrementally instead of receiving full dumps of
+the database.  This facility can be enabled in the
+.I kdc.conf
+file with the
+.I iprop_enable
+option.  See the
+.I kdc.conf
+documentation for other options for tuning incremental propagation
+parameters.  Incremental propagation requires the principal
+"kiprop/MASTER@REALM" (where MASTER is the master KDC's canonical host
+name, and REALM the realm name) to be registered in the database.
+
 .SH OPTIONS
 .TP 
 \fB\-x\fP \fIdb_args\fP
@@ -199,6 +214,10 @@ character is lower-case, then the operation is permitted.
 [Dis]allows the listing of principals or policies in the database.
 .sp -1v
 .TP
+.B p
+[Dis]allows the propagation of the principal database.
+.sp -1v
+.TP
 .B x
 Short for
 .IR admcil .
index a020874fda5b98976545e78dd00b5204b9e786c1..74d703c4adf050980fc621aeb953a325e1d051a1 100644 (file)
@@ -47,3 +47,7 @@ void  kadm_1(struct svc_req *, SVCXPRT *);
 #endif
 
 void trunc_name(size_t *len, char **dots);
+
+int
+gss_to_krb5_name_1(struct svc_req *rqstp, krb5_context ctx, gss_name_t gss_name,
+                  krb5_principal *princ, gss_buffer_t gss_str);
index 659ebfba3877d40ed32286266613aae8ef8d5ffe..88a60e026d1649de1e72a60d77855dbc6ea1fada 100644 (file)
@@ -53,6 +53,7 @@
 #include    "kdb_kt.h" /* for krb5_ktkdb_set_context */
 #include    <string.h>
 #include    "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */
+#include    <kdb_log.h>
 
 #include    "misc.h"
 
@@ -71,7 +72,7 @@ extern int daemon(int, int);
 
 volatile int   signal_request_exit = 0;
 volatile int   signal_request_hup = 0;
-void    setup_signal_handlers(void);
+void    setup_signal_handlers(iprop_role iproprole);
 void   request_exit(int);
 void   request_hup(int);
 void   reset_db(void);
@@ -114,10 +115,18 @@ void log_badauth_display_status_1(char *m, OM_uint32 code, int type,
 int schpw;
 void do_schpw(int s, kadm5_config_params *params);
 
+int ipropfd;
+
 #ifdef USE_PASSWORD_SERVER
 void kadm5_set_use_password_server (void);
 #endif
 
+extern void krb5_iprop_prog_1();
+extern kadm5_ret_t kiprop_get_adm_host_srv_name(
+       krb5_context,
+       const char *,
+       char **);
+
 /*
  * Function: usage
  * 
@@ -199,12 +208,14 @@ static krb5_context context;
 
 static krb5_context hctx;
 
+int nofork = 0;
+
 int main(int argc, char *argv[])
 {
-     register  SVCXPRT *transp;
+    register   SVCXPRT *transp, *iproptransp;
      extern    char *optarg;
      extern    int optind, opterr;
-     int ret, nofork, oldnames = 0;
+     int ret, oldnames = 0;
      OM_uint32 OMret, major_status, minor_status;
      char *whoami;
      gss_buffer_desc in_buf;
@@ -218,6 +229,9 @@ int main(int argc, char *argv[])
      int    db_args_size = 0;
      char *errmsg;
 
+     char *kiprop_name = NULL; /* iprop svc name */
+     kdb_log_context *log_ctx;
+
      setvbuf(stderr, NULL, _IONBF, 0);
 
      /* This is OID value the Krb5_Name NameType */
@@ -316,11 +330,6 @@ int main(int argc, char *argv[])
          exit(1);
      }
 
-     if( db_args )
-     {
-        free(db_args), db_args=NULL;
-     }
-     
      if ((ret = kadm5_get_config_params(context, 1, &params,
                                        &params))) {
          const char *e_txt = krb5_get_error_message (context, ret);
@@ -353,19 +362,23 @@ int main(int argc, char *argv[])
      addr.sin_port = htons(params.kadmind_port);
 
      if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
-         const char *e_txt = krb5_get_error_message (context, ret);
-         krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s",
-                          e_txt);
-         fprintf(stderr, "Cannot create TCP socket: %s",
-                 e_txt);
-         kadm5_destroy(global_server_handle);
-         krb5_klog_close(context);       
-         exit(1);
+        const char *e_txt;
+        ret = SOCKET_ERRNO;
+        e_txt = krb5_get_error_message (context, ret);
+        krb5_klog_syslog(LOG_ERR, "Cannot create TCP socket: %s",
+                         e_txt);
+        fprintf(stderr, "Cannot create TCP socket: %s",
+                e_txt);
+        kadm5_destroy(global_server_handle);
+        krb5_klog_close(context);        
+        exit(1);
      }
      set_cloexec_fd(s);
 
      if ((schpw = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-        const char *e_txt = krb5_get_error_message (context, ret);
+        const char *e_txt;
+        ret = SOCKET_ERRNO;
+        e_txt = krb5_get_error_message (context, ret);
         krb5_klog_syslog(LOG_ERR,
                          "cannot create simple chpw socket: %s",
                          e_txt);
@@ -377,6 +390,21 @@ int main(int argc, char *argv[])
      }
      set_cloexec_fd(schpw);
 
+     if ((ipropfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+        const char *e_txt;
+        ret = SOCKET_ERRNO;
+        e_txt = krb5_get_error_message (context, ret);
+        krb5_klog_syslog(LOG_ERR,
+                         "cannot create iprop listening socket: %s",
+                         e_txt);
+        fprintf(stderr, "cannot create iprop listening socket: %s",
+                e_txt);
+        kadm5_destroy(global_server_handle);
+        krb5_klog_close(context);
+        exit(1);
+     }
+     set_cloexec_fd(ipropfd);
+
 #ifdef SO_REUSEADDR
      /* the old admin server turned on SO_REUSEADDR for non-default
        port numbers.  this was necessary, on solaris, for the tests
@@ -390,12 +418,16 @@ int main(int argc, char *argv[])
         int    allowed;
 
         allowed = 1;
-        if (setsockopt(s,
-                       SOL_SOCKET,
-                       SO_REUSEADDR,
-                       (char *) &allowed,
-                       sizeof(allowed)) < 0) {
-            const char *e_txt = krb5_get_error_message (context, ret);
+        if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                       (char *) &allowed, sizeof(allowed)) < 0 ||
+            setsockopt(schpw, SOL_SOCKET, SO_REUSEADDR,
+                       (char *) &allowed, sizeof(allowed)) < 0 ||
+            setsockopt(ipropfd, SOL_SOCKET, SO_REUSEADDR,
+                       (char *) &allowed, sizeof(allowed)) < 0
+            ) {
+            const char *e_txt;
+            ret = SOCKET_ERRNO;
+            e_txt = krb5_get_error_message (context, ret);
             krb5_klog_syslog(LOG_ERR, "Cannot set SO_REUSEADDR: %s",
                              e_txt);
             fprintf(stderr, "Cannot set SO_REUSEADDR: %s", e_txt);
@@ -403,19 +435,6 @@ int main(int argc, char *argv[])
             krb5_klog_close(context);    
             exit(1);
         }
-        if (setsockopt(schpw, SOL_SOCKET, SO_REUSEADDR,
-                       (char *) &allowed, sizeof(allowed)) < 0) {
-            const char *e_txt = krb5_get_error_message (context, ret);
-            krb5_klog_syslog(LOG_ERR,
-                             "cannot set SO_REUSEADDR on simple chpw socket: %s", 
-                             e_txt);
-            fprintf(stderr,
-                    "Cannot set SO_REUSEADDR on simple chpw socket: %s",
-                    e_txt);
-            kadm5_destroy(global_server_handle);
-            krb5_klog_close(context);
-        }
-
      }
 #endif /* SO_REUSEADDR */
      memset(&addr, 0, sizeof(addr));
@@ -456,6 +475,7 @@ int main(int argc, char *argv[])
          krb5_klog_close(context);
          exit(1);
      }
+
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = AF_INET;
      addr.sin_addr.s_addr = INADDR_ANY;
@@ -491,7 +511,41 @@ int main(int argc, char *argv[])
          krb5_klog_close(context);
          exit(1);
      }
-     
+
+     memset(&addr, 0, sizeof(addr));
+     addr.sin_family = AF_INET;
+     addr.sin_addr.s_addr = INADDR_ANY;
+     addr.sin_port = htons(params.iprop_port);
+     if (bind(ipropfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+         char portbuf[32];
+         int oerrno = errno;
+         const char *e_txt = krb5_get_error_message (context, errno);
+         fprintf(stderr, "%s: Cannot bind socket.\n", whoami);
+         fprintf(stderr, "bind: %s\n", e_txt);
+         errno = oerrno;
+         snprintf(portbuf, sizeof(portbuf), "%d", ntohs(addr.sin_port));
+         krb5_klog_syslog(LOG_ERR, "cannot bind iprop socket: %s",
+                          e_txt);
+         if(oerrno == EADDRINUSE) {
+              char *w = strrchr(whoami, '/');
+              if (w) {
+                   w++;
+              }
+              else {
+                   w = whoami;
+              }
+              fprintf(stderr,
+"This probably means that another %s process is already\n"
+"running, or that another program is using the server port (number %d).\n"
+"If another %s is already running, you should kill it before\n"
+"restarting the server.\n",
+                      w, ntohs(addr.sin_port), w);
+         }
+         kadm5_destroy(global_server_handle);
+         krb5_klog_close(context);
+         exit(1);
+     }
+
      transp = svctcp_create(s, 0, 0);
      if(transp == NULL) {
          fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
@@ -508,6 +562,25 @@ int main(int argc, char *argv[])
          exit(1);
      }
 
+     iproptransp = svctcp_create(ipropfd, 0, 0);
+     if (iproptransp == NULL) {
+         fprintf(stderr, "%s: Cannot create RPC service.\n", whoami);
+         krb5_klog_syslog(LOG_ERR, "Cannot create RPC service: %m");
+         kadm5_destroy(global_server_handle);
+         krb5_klog_close(context);       
+         exit(1);
+     }
+     if (!svc_register(iproptransp, KRB5_IPROP_PROG, KRB5_IPROP_VERS, krb5_iprop_prog_1, IPPROTO_TCP)) {
+         fprintf(stderr, "%s: Cannot register RPC service.\n", whoami);
+         krb5_klog_syslog(LOG_ERR, "Cannot register RPC service, continuing.");
+#if 0
+         kadm5_destroy(global_server_handle);
+         krb5_klog_close(context);       
+         exit(1);
+#endif
+     }
+
+
      names[0].name = build_princ_name(KADM5_ADMIN_SERVICE, params.realm);
      names[1].name = build_princ_name(KADM5_CHANGEPW_SERVICE, params.realm);
      names[2].name = build_princ_name(OVSEC_KADM_ADMIN_SERVICE, params.realm);
@@ -643,8 +716,108 @@ kterr:
          exit(1);
      }
          
-     setup_signal_handlers();
-     krb5_klog_syslog(LOG_INFO, "starting");
+    if (params.iprop_enabled == TRUE)
+       ulog_set_role(hctx, IPROP_MASTER);
+    else
+       ulog_set_role(hctx, IPROP_NULL);
+
+    log_ctx = hctx->kdblog_context;
+
+    if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
+       /*
+        * IProp is enabled, so let's map in the update log
+        * and setup the service.
+        */
+       if ((ret = ulog_map(hctx, params.iprop_logfile,
+                           params.iprop_ulogsize, FKADMIND, db_args)) != 0) {
+           fprintf(stderr,
+                   _("%s: %s while mapping update log (`%s.ulog')\n"),
+                   whoami, error_message(ret), params.dbname);
+           krb5_klog_syslog(LOG_ERR,
+                            _("%s while mapping update log (`%s.ulog')"),
+                            error_message(ret), params.dbname);
+           krb5_klog_close(context);
+           exit(1);
+       }
+
+       if (nofork)
+           fprintf(stderr,
+                   "%s: create IPROP svc (PROG=%d, VERS=%d)\n",
+                   whoami, KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+
+#if 0
+       if (!svc_create(krb5_iprop_prog_1,
+                       KRB5_IPROP_PROG, KRB5_IPROP_VERS,
+                       "circuit_v")) {
+           fprintf(stderr,
+                   _("%s: Cannot create IProp RPC service (PROG=%d, VERS=%d)\n"),
+                   whoami,
+                   KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+           krb5_klog_syslog(LOG_ERR,
+                            _("Cannot create IProp RPC service (PROG=%d, VERS=%d), failing."),
+                            KRB5_IPROP_PROG, KRB5_IPROP_VERS);
+           krb5_klog_close(context);
+           exit(1);
+       }
+#endif
+
+#if 0 /* authgss only? */
+       if ((ret = kiprop_get_adm_host_srv_name(context,
+                                               params.realm,
+                                               &kiprop_name)) != 0) {
+           krb5_klog_syslog(LOG_ERR,
+                            _("%s while getting IProp svc name, failing"),
+                            error_message(ret));
+           fprintf(stderr,
+                   _("%s: %s while getting IProp svc name, failing\n"),
+                   whoami, error_message(ret));
+           krb5_klog_close(context);
+           exit(1);
+       }
+
+       auth_gssapi_name iprop_name;
+       iprop_name.name = build_princ_name(foo, bar);
+       if (iprop_name.name == NULL) {
+           foo error;
+       }
+       iprop_name.type = nt_krb5_name_oid;
+       if (svcauth_gssapi_set_names(&iprop_name, 1) == FALSE) {
+           foo error;
+       }
+       if (!rpc_gss_set_svc_name(kiprop_name, "kerberos_v5", 0,
+                                 KRB5_IPROP_PROG, KRB5_IPROP_VERS)) {
+           rpc_gss_error_t err;
+           (void) rpc_gss_get_error(&err);
+
+           krb5_klog_syslog(LOG_ERR,
+                            _("Unable to set RPCSEC_GSS service name (`%s'), failing."),
+                            kiprop_name ? kiprop_name : "<null>");
+
+           fprintf(stderr,
+                   _("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"),
+                   whoami,
+                   kiprop_name ? kiprop_name : "<null>");
+
+           if (nofork) {
+               fprintf(stderr,
+                       "%s: set svc name (rpcsec err=%d, sys err=%d)\n",
+                       whoami,
+                       err.rpc_gss_error,
+                       err.system_error);
+           }
+
+           exit(1);
+       }
+       free(kiprop_name);
+#endif
+    }
+
+    setup_signal_handlers(log_ctx->iproprole);
+    krb5_klog_syslog(LOG_INFO, _("starting"));
+    if (nofork)
+       fprintf(stderr, "%s: starting...\n", whoami);
+
      kadm_svc_run(&params);
      krb5_klog_syslog(LOG_INFO, "finished, exiting");
 
@@ -677,7 +850,7 @@ kterr:
  * if possible, otherwise with System V's signal().
  */
 
-void setup_signal_handlers(void) {
+void setup_signal_handlers(iprop_role iproprole) {
 #ifdef POSIX_SIGNALS
      (void) sigemptyset(&s_action.sa_mask);
      s_action.sa_handler = request_exit;
@@ -694,6 +867,15 @@ void setup_signal_handlers(void) {
      s_action.sa_handler = request_pure_clear;
      (void) sigaction(SIGUSR2, &s_action, (struct sigaction *) NULL);
 #endif /* PURIFY */
+
+     /*
+      * IProp will fork for a full-resync, we don't want to
+      * wait on it and we don't want the living dead procs either.
+      */
+     if (iproprole == IPROP_MASTER) {
+        s_action.sa_handler = SIG_IGN;
+        (void) sigaction(SIGCHLD, &s_action, (struct sigaction *) NULL);
+     }
 #else /* POSIX_SIGNALS */
      signal(SIGINT, request_exit);
      signal(SIGTERM, request_exit);
@@ -704,6 +886,13 @@ void setup_signal_handlers(void) {
      signal(SIGUSR1, request_pure_report);
      signal(SIGUSR2, request_pure_clear);
 #endif /* PURIFY */
+
+     /*
+      * IProp will fork for a full-resync, we don't want to
+      * wait on it and we don't want the living dead procs either.
+      */
+     if (iproprole == IPROP_MASTER)
+        (void) signal(SIGCHLD, SIG_IGN);
 #endif /* POSIX_SIGNALS */
 }
 
index 5e50971f1e387338aecced309b701604b6ca0269..cf93e86676855019efd35434973429ee8a8d2cc4 100644 (file)
@@ -37,7 +37,7 @@ static int gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str);
 
 static gss_name_t acceptor_name(gss_ctx_id_t context);
 
-static gss_name_t rqst2name(struct svc_req *rqstp);
+gss_name_t rqst2name(struct svc_req *rqstp);
 
 static int cmp_gss_names(gss_name_t n1, gss_name_t n2)
 {
@@ -158,7 +158,6 @@ static void free_server_handle(kadm5_server_handle_t handle)
  * server_name, both of which must be freed by the caller.  Returns 0
  * on success and -1 on failure.
  */
-static 
 int setup_gss_names(struct svc_req *rqstp,
                    gss_buffer_desc *client_name,
                    gss_buffer_desc *server_name)
@@ -1657,7 +1656,7 @@ exit_func:
      return(&ret);
 }
 
-static gss_name_t
+gss_name_t
 rqst2name(struct svc_req *rqstp)
 {
 
index bcb46cede32bf24f0f27a5ad90d322d7e3340a25..8cc931acaccdfcdf6ff4f346b08bc8425a49adea 100644 (file)
@@ -171,10 +171,12 @@ alt_prof.so alt_prof.po $(OUTPRE)alt_prof.$(OBJEXT): \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h alt_prof.c
 str_conv.so str_conv.po $(OUTPRE)str_conv.$(OBJEXT): \
index 06a77dce64987ac66080d371b4572e07502aba71..9013305b6934d15c9d32320ce5164a4cf7d500b6 100644 (file)
@@ -48,6 +48,7 @@
 #define KADM5_ADMIN_SERVICE    "kadmin/admin"
 #define KADM5_CHANGEPW_SERVICE "kadmin/changepw"
 #define KADM5_HIST_PRINCIPAL   "kadmin/history"
+#define KADM5_KIPROP_HOST_SERVICE "kiprop"
 
 typedef krb5_principal kadm5_princ_t;
 typedef        char            *kadm5_policy_t;
@@ -107,32 +108,37 @@ typedef long              kadm5_ret_t;
 #define KADM5_REF_COUNT                0x080000
 
 /* kadm5_config_params */
-#define KADM5_CONFIG_REALM             0x000001
-#define KADM5_CONFIG_DBNAME            0x000002
-#define KADM5_CONFIG_MKEY_NAME         0x000004
-#define KADM5_CONFIG_MAX_LIFE          0x000008
-#define KADM5_CONFIG_MAX_RLIFE         0x000010
-#define KADM5_CONFIG_EXPIRATION                0x000020
-#define KADM5_CONFIG_FLAGS             0x000040
-#define KADM5_CONFIG_ADMIN_KEYTAB      0x000080
-#define KADM5_CONFIG_STASH_FILE                0x000100
-#define KADM5_CONFIG_ENCTYPE           0x000200
-#define KADM5_CONFIG_ADBNAME           0x000400
-#define KADM5_CONFIG_ADB_LOCKFILE      0x000800
-/*#define KADM5_CONFIG_PROFILE         0x001000*/
-#define KADM5_CONFIG_ACL_FILE          0x002000
-#define KADM5_CONFIG_KADMIND_PORT      0x004000
-#define KADM5_CONFIG_ENCTYPES          0x008000
-#define KADM5_CONFIG_ADMIN_SERVER      0x010000
-#define KADM5_CONFIG_DICT_FILE         0x020000
-#define KADM5_CONFIG_MKEY_FROM_KBD     0x040000
-#define KADM5_CONFIG_KPASSWD_PORT      0x080000
-#define KADM5_CONFIG_OLD_AUTH_GSSAPI   0x100000
-#define KADM5_CONFIG_NO_AUTH           0x200000
-#define KADM5_CONFIG_AUTH_NOFALLBACK   0x400000
+#define KADM5_CONFIG_REALM             0x00000001
+#define KADM5_CONFIG_DBNAME            0x00000002
+#define KADM5_CONFIG_MKEY_NAME         0x00000004
+#define KADM5_CONFIG_MAX_LIFE          0x00000008
+#define KADM5_CONFIG_MAX_RLIFE         0x00000010
+#define KADM5_CONFIG_EXPIRATION                0x00000020
+#define KADM5_CONFIG_FLAGS             0x00000040
+#define KADM5_CONFIG_ADMIN_KEYTAB      0x00000080
+#define KADM5_CONFIG_STASH_FILE                0x00000100
+#define KADM5_CONFIG_ENCTYPE           0x00000200
+#define KADM5_CONFIG_ADBNAME           0x00000400
+#define KADM5_CONFIG_ADB_LOCKFILE      0x00000800
+/*#define KADM5_CONFIG_PROFILE         0x00001000*/
+#define KADM5_CONFIG_ACL_FILE          0x00002000
+#define KADM5_CONFIG_KADMIND_PORT      0x00004000
+#define KADM5_CONFIG_ENCTYPES          0x00008000
+#define KADM5_CONFIG_ADMIN_SERVER      0x00010000
+#define KADM5_CONFIG_DICT_FILE         0x00020000
+#define KADM5_CONFIG_MKEY_FROM_KBD     0x00040000
+#define KADM5_CONFIG_KPASSWD_PORT      0x00080000
+#define KADM5_CONFIG_OLD_AUTH_GSSAPI   0x00100000
+#define KADM5_CONFIG_NO_AUTH           0x00200000
+#define KADM5_CONFIG_AUTH_NOFALLBACK   0x00400000
 #ifdef notyet /* Novell */
-#define KADM5_CONFIG_KPASSWD_SERVER     0x800000
+#define KADM5_CONFIG_KPASSWD_SERVER     0x00800000
 #endif
+#define KADM5_CONFIG_IPROP_ENABLED     0x01000000
+#define KADM5_CONFIG_ULOG_SIZE         0x02000000
+#define KADM5_CONFIG_POLL_TIME         0x04000000
+#define KADM5_CONFIG_IPROP_LOGFILE     0x08000000
+#define KADM5_CONFIG_IPROP_PORT                0x10000000
 /*
  * permission bits
  */
@@ -250,6 +256,13 @@ typedef struct _kadm5_config_params {
      krb5_flags                flags;
      krb5_key_salt_tuple *keysalts;
      krb5_int32                num_keysalts;
+
+    bool_t             iprop_enabled;
+    uint32_t           iprop_ulogsize;
+    krb5_deltat                iprop_poll_time;
+    char *             iprop_logfile;
+/*    char *           iprop_server;*/
+    int                        iprop_port;
 } kadm5_config_params;
 
 /***********************************************************************
index c56ceac2ff69f709e8447557df20e76b3e18ec28..c8bc6b052d565ca7f2d78eccd962741681574b46 100644 (file)
  * or implied warranty.
  *
  */
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
 
 /*
  * alt_prof.c - Implement alternate profile file handling.
@@ -33,6 +37,7 @@
 #include "adm_proto.h"
 #include <stdio.h>
 #include <ctype.h>
+#include <kdb_log.h>
 
 static krb5_key_salt_tuple *copy_key_salt_tuple(ksalt, len)
 krb5_key_salt_tuple *ksalt;
@@ -381,7 +386,7 @@ get_string_param(char **param_out, char *param_in,
 }
 /*
  * Similar, for (host-order) port number, if not already set in the
- * output field; default is required.
+ * output field; default_value==0 means no default.
  */
 static void
 get_port_param(int *param_out, int param_in,
@@ -402,7 +407,7 @@ get_port_param(int *param_out, int param_in,
                   !krb5_aprof_get_int32(aprofile, hierarchy, TRUE, &ivalue)) {
            *param_out = ivalue;
            *mask_out |= mask_bit;
-       } else {
+       } else if (default_value) {
            *param_out = default_value;
            *mask_out |= mask_bit;
        }
@@ -475,6 +480,7 @@ krb5_error_code kadm5_get_config_params(context, use_kdc_config,
     krb5_pointer       aprofile = 0;
     const char         *hierarchy[4];
     char               *svalue;
+    krb5_int32         ivalue;
     kadm5_config_params params, empty_params;
 
     krb5_error_code    kret = 0;
@@ -714,6 +720,59 @@ krb5_error_code kadm5_get_config_params(context, use_kdc_config,
         krb5_xfree(svalue);
     }
     
+       hierarchy[2] = "iprop_enable";
+
+       params.iprop_enabled = FALSE;
+       params.mask |= KADM5_CONFIG_IPROP_ENABLED;
+
+       if (params_in->mask & KADM5_CONFIG_IPROP_ENABLED) {
+               params.mask |= KADM5_CONFIG_IPROP_ENABLED;
+               params.iprop_enabled = params_in->iprop_enabled;
+       } else {
+               krb5_boolean bvalue;
+               if (aprofile &&
+                   !krb5_aprof_get_boolean(aprofile, hierarchy, TRUE, &bvalue)) {
+                   params.iprop_enabled = bvalue;
+                   params.mask |= KADM5_CONFIG_IPROP_ENABLED;
+               }
+       }
+
+       if (!GET_STRING_PARAM(iprop_logfile, KADM5_CONFIG_IPROP_LOGFILE,
+                             "iprop_logfile", NULL)) {
+           if (params.mask & KADM5_CONFIG_DBNAME) {
+               if (asprintf(&params.iprop_logfile, "%s.ulog", params.dbname) >= 0) {
+                   params.mask |= KADM5_CONFIG_IPROP_LOGFILE;
+               }
+           }
+       }
+
+       GET_PORT_PARAM(iprop_port, KADM5_CONFIG_IPROP_PORT,
+                      "iprop_port", 0);
+
+       hierarchy[2] = "iprop_master_ulogsize";
+
+       params.iprop_ulogsize = DEF_ULOGENTRIES;
+       params.mask |= KADM5_CONFIG_ULOG_SIZE;
+
+       if (params_in->mask & KADM5_CONFIG_ULOG_SIZE) {
+               params.mask |= KADM5_CONFIG_ULOG_SIZE;
+               params.iprop_ulogsize = params_in->iprop_ulogsize;
+       } else {
+               if (aprofile && !krb5_aprof_get_int32(aprofile, hierarchy,
+                   TRUE, &ivalue)) {
+                       if (ivalue > MAX_ULOGENTRIES)
+                               params.iprop_ulogsize = MAX_ULOGENTRIES;
+                       else if (ivalue <= 0)
+                               params.iprop_ulogsize = DEF_ULOGENTRIES;
+                       else
+                               params.iprop_ulogsize = ivalue;
+                       params.mask |= KADM5_CONFIG_ULOG_SIZE;
+               }
+       }
+
+       GET_DELTAT_PARAM(iprop_poll_time, KADM5_CONFIG_POLL_TIME,
+                        "iprop_slave_poll", 2 * 60); /* 2m */
+
     *params_out = params;
     
 cleanup:
index 8c0bce10567aef84907ccf0ff0554f83605e1df2..69679a9c89542d525e6b99efbcabddaa2a0c2218 100644 (file)
@@ -6,8 +6,8 @@ LOCALINCLUDES = -I$(BUILDTOP)/include/kadm5
 DEFS=
 
 LIBBASE=kadm5clnt
-LIBMAJOR=5
-LIBMINOR=1
+LIBMAJOR=6
+LIBMINOR=0
 STOBJLISTS=../OBJS.ST OBJS.ST
 SHLIB_EXPDEPS=\
        $(TOPLIBD)/libgssrpc$(SHLIBEXT) \
@@ -130,7 +130,8 @@ client_init.so client_init.po $(OUTPRE)client_init.$(OBJEXT): \
   $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
   $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
   $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
-  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/k5-err.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-err.h \
   $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
   $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
index 8fc3dc1e2893d68a6458457feb5fe8d338e26015..d5131cfdad960ed907fb00aa0fb7135c89a24815 100644 (file)
@@ -48,6 +48,8 @@
 #include <kadm5/admin.h>
 #include <kadm5/kadm_rpc.h>
 #include "client_internal.h"
+#include <iprop_hdr.h>
+#include "iprop.h"
 
 #include <gssrpc/rpc.h>
 #include <gssapi/gssapi.h>
@@ -165,6 +167,8 @@ static kadm5_ret_t _kadm5_init_any(char *client_name,
      struct hostent *hp;
      int fd;
 
+     char *iprop_svc;
+     int iprop_enable = 0;
      char full_svcname[BUFSIZ];
      char *realm;
      
@@ -296,15 +300,37 @@ static kadm5_ret_t _kadm5_init_any(char *client_name,
          goto cleanup;
      }
 
+     /*
+      * If the service_name and client_name are iprop-centric,
+      * we need to clnttcp_create to the appropriate RPC prog.
+      */
+     iprop_svc = strdup(KIPROP_SVC_NAME);
+     if (iprop_svc == NULL)
+        return ENOMEM;
+
+     if (service_name != NULL &&
+        (strstr(service_name, iprop_svc) != NULL) &&
+        (strstr(client_name, iprop_svc) != NULL))
+        iprop_enable = 1;
+     else
+        iprop_enable = 0;
+
      memset(&addr, 0, sizeof(addr));
      addr.sin_family = hp->h_addrtype;
      (void) memcpy((char *) &addr.sin_addr, (char *) hp->h_addr,
                   sizeof(addr.sin_addr));
-     addr.sin_port = htons((u_short) handle->params.kadmind_port);
+     if (iprop_enable)
+        addr.sin_port = htons((u_short) handle->params.iprop_port);
+     else
+        addr.sin_port = htons((u_short) handle->params.kadmind_port);
      
      fd = RPC_ANYSOCK;
-     
-     handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0);
+
+     if (iprop_enable) {
+        handle->clnt = clnttcp_create(&addr, KRB5_IPROP_PROG, KRB5_IPROP_VERS,
+                                      &fd, 0, 0);
+     } else
+        handle->clnt = clnttcp_create(&addr, KADM, KADMVERS, &fd, 0, 0);
      if (handle->clnt == NULL) {
          code = KADM5_RPC_ERROR;
 #ifdef DEBUG
@@ -326,6 +352,16 @@ static kadm5_ret_t _kadm5_init_any(char *client_name,
      if (code)
          goto error;
 
+     /*
+      * Bypass the remainder of the code and return straightaway
+      * if the gss service requested is kiprop
+      */
+     if (iprop_enable == 1) {
+        code = 0;
+        *server_handle = (void *) handle;
+        goto cleanup;
+     }
+
      r = init_2(&handle->api_version, handle->clnt);
      if (r == NULL) {
          code = KADM5_RPC_ERROR;
@@ -794,3 +830,13 @@ krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
 {
     return krb5_init_context(ctx);
 }
+
+/*
+ * Stub function for kadmin.  It was created to eliminate the dependency on
+ * libkdb's ulog functions.  The srv equivalent makes the actual calls.
+ */
+krb5_error_code
+kadm5_init_iprop(void *handle)
+{
+       return (0);
+}
index f7f873e29273d2958ae246d1bf7744deece9e0d7..7f11f320acfc7df24ff0d5161992e6ddfc8f76e4 100644 (file)
@@ -129,3 +129,4 @@ xdr_setkey3_arg
 xdr_setkey_arg
 xdr_setv4key_arg
 xdr_ui_4
+kadm5_init_iprop
index 21628bd7e5b773bf971ea63ecfad24862bfb9676..9889c1e2226a9445ed555fe8d17b0ae9a183bbc0 100644 (file)
@@ -12,8 +12,8 @@ DEFS=
 ##DOSLIBNAME = libkadm5srv.lib
 
 LIBBASE=kadm5srv
-LIBMAJOR=5
-LIBMINOR=1
+LIBMAJOR=6
+LIBMINOR=0
 STOBJLISTS=../OBJS.ST OBJS.ST
 
 SHLIB_EXPDEPS=\
@@ -191,10 +191,12 @@ server_init.so server_init.po $(OUTPRE)server_init.$(OBJEXT): \
   $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
   $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
   $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
   $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
   $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
   $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
-  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
   $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
   $(SRCTOP)/include/socket-utils.h $(srcdir)/../../gssapi/generic/gssapiP_generic.h \
   $(srcdir)/../../gssapi/generic/gssapi_generic.h $(srcdir)/../../gssapi/krb5/gssapiP_krb5.h \
index 23a4ee1e560873d392bd6c52e1b41e1fb12ac89d..a4d2156f771e0fc68023404e0be03aa33bf2d18c 100644 (file)
@@ -161,3 +161,4 @@ xdr_setkey3_arg
 xdr_setkey_arg
 xdr_setv4key_arg
 xdr_ui_4
+kadm5_init_iprop
index bcfe35f84856f34a4710c3df7750ba7e1627493c..6cc9492c372109976cda1503cd88cd5d233b4362 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lib/kadm5/srv/server_acl.c
  *
- * Copyright 1995-2004, 2007 by the Massachusetts Institute of Technology.
+ * Copyright 1995-2004, 2007, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -66,6 +66,7 @@ static const aop_t acl_op_table[] = {
     { 'c',     ACL_CHANGEPW },
     { 'i',     ACL_INQUIRE },
     { 'l',     ACL_LIST },
+    { 'p',     ACL_IPROP },
     { 's',     ACL_SETKEY },
     { 'x',     ACL_ALL_MASK },
     { '*',     ACL_ALL_MASK },
index 5024730cf685255c53d07f3f0a229443bdacdae5..b0ed0bf3dd5717f562286ebfd5ad761a6aea994e 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * lib/kadm5/srv/server_acl.h
  *
- * Copyright 1995-2004, 2007 by the Massachusetts Institute of Technology.
+ * Copyright 1995-2004, 2007, 2008 by the Massachusetts Institute of Technology.
  * All Rights Reserved.
  *
  * Export of this software from the United States of America may
@@ -58,6 +58,7 @@
 /* #define ACL_EXTRACT         64 */
 #define        ACL_LIST                128
 #define ACL_SETKEY             256
+#define ACL_IPROP              512
 #define        ACL_RENAME              (ACL_ADD+ACL_DELETE)
 
 #define        ACL_ALL_MASK            (ACL_ADD        | \
@@ -66,6 +67,7 @@
                                 ACL_CHANGEPW   | \
                                 ACL_INQUIRE    | \
                                 ACL_LIST       | \
+                                ACL_IPROP      | \
                                 ACL_SETKEY)
 
 typedef struct _restriction {
index dbb7ff78e635c145b98aec025b10a5ce48519af5..febf79bdf8af984490ff37ebc6a4f3f187d99f80 100644 (file)
@@ -4,6 +4,10 @@
  * $Id$
  * $Source$
  */
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
 
 #if !defined(lint) && !defined(__CODECENTER__)
 static char *rcsid = "$Header$";
@@ -16,8 +20,10 @@ static char *rcsid = "$Header$";
 #include "k5-int.h"            /* needed for gssapiP_krb5.h */
 #include <kadm5/admin.h>
 #include <krb5.h>
+#include <kdb_log.h>
 #include "server_internal.h"
 #include "osconf.h"
+#include "iprop_hdr.h"
 
 /*
  * Function check_handle
@@ -238,12 +244,26 @@ kadm5_ret_t kadm5_init(char *client_name, char *pass,
                         KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \
                         KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES)
 
+#define IPROP_REQUIRED_PARAMS \
+                       (KADM5_CONFIG_IPROP_ENABLED | \
+                        KADM5_CONFIG_IPROP_LOGFILE | \
+                        KADM5_CONFIG_IPROP_PORT)
+
      if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) {
          krb5_free_context(handle->context);
          free_db_args(handle);
          free(handle);
          return KADM5_MISSING_CONF_PARAMS;
      }
+     if ((handle->params.mask & KADM5_CONFIG_IPROP_ENABLED) == KADM5_CONFIG_IPROP_ENABLED
+        && handle->params.iprop_enabled) {
+        if ((handle->params.mask & IPROP_REQUIRED_PARAMS) != IPROP_REQUIRED_PARAMS) {
+            krb5_free_context(handle->context);
+            free_db_args(handle);
+            free(handle);
+            return KADM5_MISSING_CONF_PARAMS;
+        }
+     }
 
      ret = krb5_set_default_realm(handle->context, handle->params.realm);
      if (ret) {
@@ -430,3 +450,21 @@ krb5_error_code kadm5_init_krb5_context (krb5_context *ctx)
     }
     return krb5int_init_context_kdc(ctx);
 }
+
+krb5_error_code
+kadm5_init_iprop(void *handle, char **db_args)
+{
+       kadm5_server_handle_t iprop_h;
+       krb5_error_code retval;
+
+       iprop_h = handle;
+       if (iprop_h->params.iprop_enabled) {
+               ulog_set_role(iprop_h->context, IPROP_MASTER);
+               if ((retval = ulog_map(iprop_h->context,
+                                      iprop_h->params.iprop_logfile,
+                                      iprop_h->params.iprop_ulogsize,
+                                      FKCOMMAND, db_args)) != 0)
+                       return (retval);
+       }
+       return (0);
+}
index 65ee8e7704f4881569f7feab234f8c7ebc0bd23f..9935a450a03124f2868d6f0c28784233f4c76a4a 100644 (file)
@@ -20,8 +20,9 @@ RELDIR=kdb
 
 SHLIB_EXPDEPS = \
        $(TOPLIBD)/libk5crypto$(SHLIBEXT) \
+       $(TOPLIBD)/libgssrpc$(SHLIBEXT) \
        $(TOPLIBD)/libkrb5$(SHLIBEXT)
-SHLIB_EXPLIBS=-lkrb5 -lcom_err -lk5crypto $(SUPPORT_LIB) $(DL_LIB) $(LIBS)
+SHLIB_EXPLIBS=-lgssrpc -lkrb5 -lk5crypto -lcom_err $(SUPPORT_LIB) $(DL_LIB) $(LIBS)
 SHLIB_DIRS=-L$(TOPLIBD)
 SHLIB_RDIRS=$(KRB5_LIBDIR)
 
@@ -35,6 +36,9 @@ SRCS= \
        $(srcdir)/kdb_default.c \
        $(srcdir)/kdb_cpw.c \
        adb_err.c \
+       $(srcdir)/iprop_xdr.c \
+       $(srcdir)/kdb_convert.c \
+       $(srcdir)/kdb_log.c \
        $(srcdir)/keytab.c
 
 STOBJLISTS=OBJS.ST
@@ -45,6 +49,9 @@ STLIBOBJS= \
        kdb_default.o \
        kdb_cpw.o \
        adb_err.o \
+       iprop_xdr.o \
+       kdb_convert.o \
+       kdb_log.o \
        keytab.o
 
 all-unix:: all-liblinks
@@ -61,14 +68,22 @@ clean-unix:: clean-liblinks clean-libs clean-libobjs
 # the Makefile.in file
 #
 kdb5.so kdb5.po $(OUTPRE)kdb5.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h \
+  $(SRCTOP)/include/gssrpc/auth_gss.h $(SRCTOP)/include/gssrpc/auth_unix.h \
+  $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
+  $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
+  $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-err.h \
   $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
   $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
   $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
-  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
-  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
-  $(SRCTOP)/include/socket-utils.h adb_err.h kdb5.c kdb5.h
+  $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  adb_err.h kdb5.c kdb5.h
 encrypt_key.so encrypt_key.po $(OUTPRE)encrypt_key.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -107,6 +122,47 @@ kdb_cpw.so kdb_cpw.po $(OUTPRE)kdb_cpw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h
   $(SRCTOP)/include/socket-utils.h kdb_cpw.c
 adb_err.so adb_err.po $(OUTPRE)adb_err.$(OBJEXT): $(COM_ERR_DEPS) \
   adb_err.c
+iprop_xdr.so iprop_xdr.po $(OUTPRE)iprop_xdr.$(OBJEXT): \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+  $(SRCTOP)/include/gssrpc/auth.h $(SRCTOP)/include/gssrpc/auth_gss.h \
+  $(SRCTOP)/include/gssrpc/auth_unix.h $(SRCTOP)/include/gssrpc/clnt.h \
+  $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
+  $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
+  $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h iprop_xdr.c
+kdb_convert.so kdb_convert.po $(OUTPRE)kdb_convert.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/krb5/krb5.h \
+  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h $(SRCTOP)/include/gssrpc/auth_gss.h \
+  $(SRCTOP)/include/gssrpc/auth_unix.h $(SRCTOP)/include/gssrpc/clnt.h \
+  $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
+  $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
+  $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+  $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+  $(SRCTOP)/include/socket-utils.h kdb_convert.c
+kdb_log.so kdb_log.po $(OUTPRE)kdb_log.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h \
+  $(SRCTOP)/include/gssrpc/auth_gss.h $(SRCTOP)/include/gssrpc/auth_unix.h \
+  $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
+  $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
+  $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  $(SRCTOP)/include/iprop_hdr.h $(SRCTOP)/include/k5-err.h \
+  $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
+  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
+  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/kdb.h \
+  $(SRCTOP)/include/kdb_log.h $(SRCTOP)/include/krb5.h \
+  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
+  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
+  kdb5.h kdb_log.c
 keytab.so keytab.po $(OUTPRE)keytab.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
   $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h \
diff --git a/src/lib/kdb/iprop.x b/src/lib/kdb/iprop.x
new file mode 100644 (file)
index 0000000..840e7a2
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* %#pragma ident      "@(#)iprop.x    1.2     04/02/20 SMI" */
+
+/*
+ * Main source:
+ * lib/kdb/iprop.x
+ *
+ * Generated files:
+ * lib/kdb/iprop_xdr.c
+ * include/iprop.h
+ * slave/kpropd_rpc.c (clnt)
+ *
+ * Derived files:
+ * kadmin/server/ipropd_svc.c
+ */
+
+#ifdef RPC_XDR
+%#include "iprop.h"
+#endif /* RPC_XDR */
+
+/*
+ * Initial declarations
+ */
+
+#ifndef RPC_HDR
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned int uint32_t;
+/*typedef hyper int64_t;*/
+/*typedef unsigned hyper uint64_t;*/
+#endif  /* !RPC_HDR */
+
+typedef opaque  utf8str_t<>;
+
+/*
+ * Transaction log serial no.
+ */
+typedef uint32_t       kdb_sno_t;
+
+/* Timestamp */
+struct kdbe_time_t {
+       uint32_t        seconds;
+       uint32_t        useconds;
+};
+
+/* Key Data */
+struct kdbe_key_t {
+       int32_t         k_ver;  /* Version */
+       int32_t         k_kvno; /* Key version no. */
+       int32_t         k_enctype<>;
+       utf8str_t       k_contents<>;
+};
+
+/* Content data */
+struct kdbe_data_t {
+       int32_t         k_magic;
+       utf8str_t       k_data;
+};
+
+/* Principal Data */
+struct kdbe_princ_t {
+       utf8str_t       k_realm;
+       kdbe_data_t     k_components<>;
+       int32_t         k_nametype;
+};
+
+/* TL data (pre-auth specific data) */
+struct kdbe_tl_t {
+       int16_t         tl_type;
+       opaque          tl_data<>;
+};
+
+/* Structure to store pwd history */
+typedef kdbe_key_t kdbe_pw_hist_t<>;
+
+/* Basic KDB entry attributes */
+enum kdbe_attr_type_t {
+       AT_ATTRFLAGS = 0,
+       AT_MAX_LIFE = 1,
+       AT_MAX_RENEW_LIFE = 2,
+       AT_EXP = 3,
+       AT_PW_EXP = 4,
+       AT_LAST_SUCCESS = 5,
+       AT_LAST_FAILED = 6,
+       AT_FAIL_AUTH_COUNT = 7,
+       AT_PRINC = 8,
+       AT_KEYDATA = 9,
+       AT_TL_DATA = 10,
+       AT_LEN = 11,
+       AT_MOD_PRINC = 12,
+       AT_MOD_TIME = 13,
+       AT_MOD_WHERE = 14,
+       AT_PW_LAST_CHANGE = 15,
+       AT_PW_POLICY = 16,
+       AT_PW_POLICY_SWITCH = 17,
+       AT_PW_HIST_KVNO = 18,
+       AT_PW_HIST = 19
+};
+
+/* KDB entry, Attribute=value */
+union kdbe_val_t switch (kdbe_attr_type_t av_type) {
+case AT_ATTRFLAGS:
+       uint32_t        av_attrflags;
+case AT_MAX_LIFE:
+       uint32_t        av_max_life;
+case AT_MAX_RENEW_LIFE:
+       uint32_t        av_max_renew_life;
+case AT_EXP:
+       uint32_t        av_exp;
+case AT_PW_EXP:
+       uint32_t        av_pw_exp;
+case AT_LAST_SUCCESS:
+       uint32_t        av_last_success;
+case AT_LAST_FAILED:
+       uint32_t        av_last_failed;
+case AT_FAIL_AUTH_COUNT:
+       uint32_t        av_fail_auth_count;
+case AT_PRINC:
+       kdbe_princ_t    av_princ;
+case AT_KEYDATA:
+       kdbe_key_t      av_keydata<>;   /* array of keys */
+case AT_TL_DATA:
+       kdbe_tl_t       av_tldata<>;    /* array of TL data */
+case AT_LEN:
+       int16_t         av_len;
+case AT_PW_LAST_CHANGE:
+       uint32_t        av_pw_last_change;
+case AT_MOD_PRINC:
+       kdbe_princ_t    av_mod_princ;
+case AT_MOD_TIME:
+       uint32_t        av_mod_time;
+case AT_MOD_WHERE:
+       utf8str_t       av_mod_where;
+case AT_PW_POLICY:
+       utf8str_t       av_pw_policy;
+case AT_PW_POLICY_SWITCH:
+       bool            av_pw_policy_switch;
+case AT_PW_HIST_KVNO:
+       uint32_t        av_pw_hist_kvno;
+case AT_PW_HIST:
+       kdbe_pw_hist_t  av_pw_hist<>;   /* array of pw history */
+default:
+       opaque          av_extension<>; /* futures */
+};
+
+typedef kdbe_val_t kdbe_t<>;       /* Array of attr/val makes a KDB entry */
+
+/*
+ * Incremental update
+ */
+struct kdb_incr_update_t {
+       utf8str_t       kdb_princ_name; /* Principal name */
+       kdb_sno_t       kdb_entry_sno;  /* Serial # of entry */
+       kdbe_time_t     kdb_time;       /* Timestamp of update */
+       kdbe_t          kdb_update;     /* Attributes modified */
+       bool            kdb_deleted;    /* Is this update a DELETION ? */
+       bool            kdb_commit;     /* Is the entry committed or not ? */
+       utf8str_t       kdb_kdcs_seen_by<>; /* Names of slaves that have */
+                                           /* seen this update - for */
+                                           /* future use */
+       opaque          kdb_futures<>;  /* futures */
+};
+
+/*
+ * Update log body
+ */
+typedef kdb_incr_update_t kdb_ulog_t<>;
+
+enum update_status_t {
+       UPDATE_OK = 0,
+       UPDATE_ERROR = 1,
+       UPDATE_FULL_RESYNC_NEEDED = 2,
+       UPDATE_BUSY = 3,
+       UPDATE_NIL = 4,
+       UPDATE_PERM_DENIED = 5
+};
+
+struct kdb_last_t {
+       kdb_sno_t       last_sno;
+       kdbe_time_t     last_time;
+};
+
+struct kdb_incr_result_t {
+       kdb_last_t              lastentry;
+       kdb_ulog_t              updates;
+       update_status_t         ret;
+};
+
+struct kdb_fullresync_result_t {
+       kdb_last_t              lastentry;
+       update_status_t         ret;
+};
+
+program KRB5_IPROP_PROG {
+       version KRB5_IPROP_VERS {
+               /*
+                * NULL procedure
+                */
+               void
+               IPROP_NULL(void) = 0;
+
+               /*
+                * Keep waiting for and get next incremental update(s)
+                *
+                * Will return latest kdb_vers on the master (if different),
+                * alongwith return value and affected db entries.
+                */
+               kdb_incr_result_t
+               IPROP_GET_UPDATES(kdb_last_t) = 1;
+
+               /*
+                * We need to do the full-resync of the db, since the
+                * serial nos./timestamps are way out-of-whack
+                */
+               kdb_fullresync_result_t
+               IPROP_FULL_RESYNC(void) = 2;
+       } = 1;
+} = 100423;
diff --git a/src/lib/kdb/iprop_xdr.c b/src/lib/kdb/iprop_xdr.c
new file mode 100644 (file)
index 0000000..a8b7685
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include "iprop.h"
+#include "iprop.h"
+
+bool_t
+xdr_int16_t (XDR *xdrs, int16_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_short (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_uint16_t (XDR *xdrs, uint16_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_u_short (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_int32_t (XDR *xdrs, int32_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_int (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_uint32_t (XDR *xdrs, uint32_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_u_int (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_utf8str_t (XDR *xdrs, utf8str_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_bytes (xdrs, (char **)&objp->utf8str_t_val, (u_int *) &objp->utf8str_t_len, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdb_sno_t (XDR *xdrs, kdb_sno_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32_t (xdrs, objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_time_t (XDR *xdrs, kdbe_time_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_uint32_t (xdrs, &objp->seconds))
+                return FALSE;
+        if (!xdr_uint32_t (xdrs, &objp->useconds))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_key_t (XDR *xdrs, kdbe_key_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_int32_t (xdrs, &objp->k_ver))
+                return FALSE;
+        if (!xdr_int32_t (xdrs, &objp->k_kvno))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->k_enctype.k_enctype_val, (u_int *) &objp->k_enctype.k_enctype_len, ~0,
+               sizeof (int32_t), (xdrproc_t) xdr_int32_t))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->k_contents.k_contents_val, (u_int *) &objp->k_contents.k_contents_len, ~0,
+               sizeof (utf8str_t), (xdrproc_t) xdr_utf8str_t))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_data_t (XDR *xdrs, kdbe_data_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_int32_t (xdrs, &objp->k_magic))
+                return FALSE;
+        if (!xdr_utf8str_t (xdrs, &objp->k_data))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_princ_t (XDR *xdrs, kdbe_princ_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_utf8str_t (xdrs, &objp->k_realm))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->k_components.k_components_val, (u_int *) &objp->k_components.k_components_len, ~0,
+               sizeof (kdbe_data_t), (xdrproc_t) xdr_kdbe_data_t))
+                return FALSE;
+        if (!xdr_int32_t (xdrs, &objp->k_nametype))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_tl_t (XDR *xdrs, kdbe_tl_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_int16_t (xdrs, &objp->tl_type))
+                return FALSE;
+        if (!xdr_bytes (xdrs, (char **)&objp->tl_data.tl_data_val, (u_int *) &objp->tl_data.tl_data_len, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_pw_hist_t (XDR *xdrs, kdbe_pw_hist_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->kdbe_pw_hist_t_val, (u_int *) &objp->kdbe_pw_hist_t_len, ~0,
+               sizeof (kdbe_key_t), (xdrproc_t) xdr_kdbe_key_t))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_attr_type_t (XDR *xdrs, kdbe_attr_type_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_val_t (XDR *xdrs, kdbe_val_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_kdbe_attr_type_t (xdrs, &objp->av_type))
+                return FALSE;
+       switch (objp->av_type) {
+       case AT_ATTRFLAGS:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_attrflags))
+                        return FALSE;
+               break;
+       case AT_MAX_LIFE:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_max_life))
+                        return FALSE;
+               break;
+       case AT_MAX_RENEW_LIFE:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_max_renew_life))
+                        return FALSE;
+               break;
+       case AT_EXP:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_exp))
+                        return FALSE;
+               break;
+       case AT_PW_EXP:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_pw_exp))
+                        return FALSE;
+               break;
+       case AT_LAST_SUCCESS:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_last_success))
+                        return FALSE;
+               break;
+       case AT_LAST_FAILED:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_last_failed))
+                        return FALSE;
+               break;
+       case AT_FAIL_AUTH_COUNT:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_fail_auth_count))
+                        return FALSE;
+               break;
+       case AT_PRINC:
+                if (!xdr_kdbe_princ_t (xdrs, &objp->kdbe_val_t_u.av_princ))
+                        return FALSE;
+               break;
+       case AT_KEYDATA:
+                if (!xdr_array (xdrs, (char **)&objp->kdbe_val_t_u.av_keydata.av_keydata_val, (u_int *) &objp->kdbe_val_t_u.av_keydata.av_keydata_len, ~0,
+                       sizeof (kdbe_key_t), (xdrproc_t) xdr_kdbe_key_t))
+                        return FALSE;
+               break;
+       case AT_TL_DATA:
+                if (!xdr_array (xdrs, (char **)&objp->kdbe_val_t_u.av_tldata.av_tldata_val, (u_int *) &objp->kdbe_val_t_u.av_tldata.av_tldata_len, ~0,
+                       sizeof (kdbe_tl_t), (xdrproc_t) xdr_kdbe_tl_t))
+                        return FALSE;
+               break;
+       case AT_LEN:
+                if (!xdr_int16_t (xdrs, &objp->kdbe_val_t_u.av_len))
+                        return FALSE;
+               break;
+       case AT_PW_LAST_CHANGE:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_pw_last_change))
+                        return FALSE;
+               break;
+       case AT_MOD_PRINC:
+                if (!xdr_kdbe_princ_t (xdrs, &objp->kdbe_val_t_u.av_mod_princ))
+                        return FALSE;
+               break;
+       case AT_MOD_TIME:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_mod_time))
+                        return FALSE;
+               break;
+       case AT_MOD_WHERE:
+                if (!xdr_utf8str_t (xdrs, &objp->kdbe_val_t_u.av_mod_where))
+                        return FALSE;
+               break;
+       case AT_PW_POLICY:
+                if (!xdr_utf8str_t (xdrs, &objp->kdbe_val_t_u.av_pw_policy))
+                        return FALSE;
+               break;
+       case AT_PW_POLICY_SWITCH:
+                if (!xdr_bool (xdrs, &objp->kdbe_val_t_u.av_pw_policy_switch))
+                        return FALSE;
+               break;
+       case AT_PW_HIST_KVNO:
+                if (!xdr_uint32_t (xdrs, &objp->kdbe_val_t_u.av_pw_hist_kvno))
+                        return FALSE;
+               break;
+       case AT_PW_HIST:
+                if (!xdr_array (xdrs, (char **)&objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_val, (u_int *) &objp->kdbe_val_t_u.av_pw_hist.av_pw_hist_len, ~0,
+                       sizeof (kdbe_pw_hist_t), (xdrproc_t) xdr_kdbe_pw_hist_t))
+                        return FALSE;
+               break;
+       default:
+                if (!xdr_bytes (xdrs, (char **)&objp->kdbe_val_t_u.av_extension.av_extension_val, (u_int *) &objp->kdbe_val_t_u.av_extension.av_extension_len, ~0))
+                        return FALSE;
+               break;
+       }
+       return TRUE;
+}
+
+bool_t
+xdr_kdbe_t (XDR *xdrs, kdbe_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->kdbe_t_val, (u_int *) &objp->kdbe_t_len, ~0,
+               sizeof (kdbe_val_t), (xdrproc_t) xdr_kdbe_val_t))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdb_incr_update_t (XDR *xdrs, kdb_incr_update_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_utf8str_t (xdrs, &objp->kdb_princ_name))
+                return FALSE;
+        if (!xdr_kdb_sno_t (xdrs, &objp->kdb_entry_sno))
+                return FALSE;
+        if (!xdr_kdbe_time_t (xdrs, &objp->kdb_time))
+                return FALSE;
+        if (!xdr_kdbe_t (xdrs, &objp->kdb_update))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->kdb_deleted))
+                return FALSE;
+        if (!xdr_bool (xdrs, &objp->kdb_commit))
+                return FALSE;
+        if (!xdr_array (xdrs, (char **)&objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val, (u_int *) &objp->kdb_kdcs_seen_by.kdb_kdcs_seen_by_len, ~0,
+               sizeof (utf8str_t), (xdrproc_t) xdr_utf8str_t))
+                return FALSE;
+        if (!xdr_bytes (xdrs, (char **)&objp->kdb_futures.kdb_futures_val, (u_int *) &objp->kdb_futures.kdb_futures_len, ~0))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdb_ulog_t (XDR *xdrs, kdb_ulog_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_array (xdrs, (char **)&objp->kdb_ulog_t_val, (u_int *) &objp->kdb_ulog_t_len, ~0,
+               sizeof (kdb_incr_update_t), (xdrproc_t) xdr_kdb_incr_update_t))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_update_status_t (XDR *xdrs, update_status_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_enum (xdrs, (enum_t *) objp))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdb_last_t (XDR *xdrs, kdb_last_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_kdb_sno_t (xdrs, &objp->last_sno))
+                return FALSE;
+        if (!xdr_kdbe_time_t (xdrs, &objp->last_time))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdb_incr_result_t (XDR *xdrs, kdb_incr_result_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_kdb_last_t (xdrs, &objp->lastentry))
+                return FALSE;
+        if (!xdr_kdb_ulog_t (xdrs, &objp->updates))
+                return FALSE;
+        if (!xdr_update_status_t (xdrs, &objp->ret))
+                return FALSE;
+       return TRUE;
+}
+
+bool_t
+xdr_kdb_fullresync_result_t (XDR *xdrs, kdb_fullresync_result_t *objp)
+{
+       register int32_t *buf;
+
+        if (!xdr_kdb_last_t (xdrs, &objp->lastentry))
+                return FALSE;
+        if (!xdr_update_status_t (xdrs, &objp->ret))
+                return FALSE;
+       return TRUE;
+}
index bdade990477141e153b1dbdddb99dfe43d277aa9..d782d0f413e615f570d649078787cd9c175afde4 100644 (file)
@@ -37,6 +37,7 @@
 #include <osconf.h>
 #include "kdb5.h"
 #include <assert.h>
+#include "kdb_log.h"
 
 /* Currently DB2 policy related errors are exported from DAL.  But
    other databases should set_err function to return string.  */
@@ -935,29 +936,35 @@ krb5_db_free_principal(krb5_context kcontext, krb5_db_entry * entry, int count)
     return status;
 }
 
-krb5_error_code
-krb5_db_put_principal(krb5_context kcontext,
-                     krb5_db_entry * entries, int *nentries)
+static void
+free_db_args(krb5_context kcontext, char **db_args)
 {
-    krb5_error_code status = 0;
-    kdb5_dal_handle *dal_handle;
-    char  **db_args = NULL;
-    krb5_tl_data *prev, *curr, *next;
-    int     db_args_size = 0;
-
-    if (kcontext->dal_handle == NULL) {
-       status = kdb_setup_lib_handle(kcontext);
-       if (status) {
-           goto clean_n_exit;
-       }
+    int i;
+    if (db_args) {
+       /* XXX Is this right?  Or are we borrowing storage from
+          the caller?  */
+       for (i = 0; db_args[i]; i++)
+           krb5_db_free(kcontext, db_args[i]);
+       free(db_args);
     }
+}
+
+static krb5_error_code
+extract_db_args_from_tl_data(krb5_context kcontext,
+                            krb5_tl_data **start, krb5_int16 *count,
+                            char ***db_argsp)
+{
+    char **db_args = NULL;
+    int db_args_size = 0;
+    krb5_tl_data *prev, *curr, *next;
+    krb5_error_code status;
 
     /* Giving db_args as part of tl data causes db2 to store the
        tl_data as such.  To prevent this, tl_data is collated and
        passed as a separate argument.  Currently supports only one
        principal, but passing it as a separate argument makes it
        difficult for kadmin remote to pass arguments to server.  */
-    prev = NULL, curr = entries->tl_data;
+    prev = NULL, curr = *start;
     while (curr) {
        if (curr->tl_data_type == KRB5_TL_DB_ARGS) {
            char  **t;
@@ -985,11 +992,11 @@ krb5_db_put_principal(krb5_context kcontext,
            next = curr->tl_data_next;
            if (prev == NULL) {
                /* current node is the first in the linked list. remove it */
-               entries->tl_data = curr->tl_data_next;
+               *start = curr->tl_data_next;
            } else {
                prev->tl_data_next = curr->tl_data_next;
            }
-           entries->n_tl_data--;
+           (*count)--;
            krb5_db_free(kcontext, curr);
 
            /* previous does not change */
@@ -999,6 +1006,67 @@ krb5_db_put_principal(krb5_context kcontext,
            curr = curr->tl_data_next;
        }
     }
+    status = 0;
+clean_n_exit:
+    if (status != 0) {
+       free_db_args(kcontext, db_args);
+       db_args = NULL;
+    }
+    *db_argsp = db_args;
+    return status;
+}
+
+krb5_error_code
+krb5int_put_principal_no_log(krb5_context kcontext,
+                            krb5_db_entry *entries, int *nentries)
+{
+    kdb5_dal_handle *dal_handle;
+    krb5_error_code status;
+    char **db_args;
+
+    status = extract_db_args_from_tl_data(kcontext, &entries->tl_data,
+                                         &entries->n_tl_data,
+                                         &db_args);
+    if (status)
+       return status;
+    assert (kcontext->dal_handle != NULL); /* XXX */
+    dal_handle = kcontext->dal_handle;
+    /* XXX Locking?  */
+    status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
+                                                            nentries,
+                                                            db_args);
+    get_errmsg(kcontext, status);
+    free_db_args(kcontext, db_args);
+    return status;
+}
+
+krb5_error_code
+krb5_db_put_principal(krb5_context kcontext,
+                     krb5_db_entry * entries, int *nentries)
+{
+    krb5_error_code status = 0;
+    kdb5_dal_handle *dal_handle;
+    char  **db_args = NULL;
+    kdb_incr_update_t *upd, *fupd;
+    char *princ_name = NULL;
+    kdb_log_context *log_ctx;
+    int i;
+    int ulog_locked = 0;
+
+    log_ctx = kcontext->kdblog_context;
+
+    if (kcontext->dal_handle == NULL) {
+       status = kdb_setup_lib_handle(kcontext);
+       if (status) {
+           goto clean_n_exit;
+       }
+    }
+
+    status = extract_db_args_from_tl_data(kcontext, &entries->tl_data,
+                                         &entries->n_tl_data,
+                                         &db_args);
+    if (status)
+       goto clean_n_exit;
 
     dal_handle = kcontext->dal_handle;
     status = kdb_lock_lib_lock(dal_handle->lib_handle, FALSE);
@@ -1006,23 +1074,89 @@ krb5_db_put_principal(krb5_context kcontext,
        goto clean_n_exit;
     }
 
+    /*
+     * We need the lock since ulog_conv_2logentry() does a get
+     */
+    if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
+       if (!(upd = (kdb_incr_update_t *)
+         malloc(sizeof (kdb_incr_update_t)* *nentries))) {
+           status = errno;
+           goto err_lock;
+       }
+       fupd = upd;
+
+       (void) memset(upd, 0, sizeof(kdb_incr_update_t)* *nentries);
+
+        if ((status = ulog_conv_2logentry(kcontext, entries, upd, *nentries))) {
+           goto err_lock;
+       }
+    }
+
+    status = ulog_lock(kcontext, KRB5_LOCKMODE_EXCLUSIVE);
+    if (status != 0)
+       goto err_lock;
+    ulog_locked = 1;
+
+    for (i = 0; i < *nentries; i++) {
+       /*
+        * We'll be sharing the same locks as db for logging
+        */
+        if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
+               if ((status = krb5_unparse_name(kcontext, entries->princ,
+                   &princ_name)))
+                       goto err_lock;
+
+               upd->kdb_princ_name.utf8str_t_val = princ_name;
+               upd->kdb_princ_name.utf8str_t_len = strlen(princ_name);
+
+                if (status = ulog_add_update(kcontext, upd))
+                       goto err_lock;
+        }
+       upd++;
+    }
+
     status = dal_handle->lib_handle->vftabl.db_put_principal(kcontext, entries,
                                                             nentries,
                                                             db_args);
     get_errmsg(kcontext, status);
+    if (status == 0 && log_ctx && log_ctx->iproprole == IPROP_MASTER) {
+       upd = fupd;
+       for (i = 0; i < *nentries; i++) {
+           (void) ulog_finish_update(kcontext, upd);
+           upd++;
+       }
+    }
+err_lock:
+    if (ulog_locked)
+       ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
+
     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
 
   clean_n_exit:
-    while (db_args_size) {
-       if (db_args[db_args_size - 1])
-           krb5_db_free(kcontext, db_args[db_args_size - 1]);
+    free_db_args(kcontext, db_args);
 
-       db_args_size--;
-    }
+    if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
+       ulog_free_entries(fupd, *nentries);
 
-    if (db_args)
-       free(db_args);
+    return status;
+}
+
+krb5_error_code
+krb5int_delete_principal_no_log(krb5_context kcontext,
+                               krb5_principal search_for,
+                               int *nentries)
+{
+    kdb5_dal_handle *dal_handle;
+    krb5_error_code status;
+
+    assert (kcontext->dal_handle != NULL); /* XXX */
 
+    dal_handle = kcontext->dal_handle;
+    /* XXX Locking?  */
+    status = dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
+                                                                search_for,
+                                                                nentries);
+    get_errmsg(kcontext, status);
     return status;
 }
 
@@ -1032,6 +1166,11 @@ krb5_db_delete_principal(krb5_context kcontext,
 {
     krb5_error_code status = 0;
     kdb5_dal_handle *dal_handle;
+    kdb_incr_update_t upd;
+    char *princ_name = NULL;
+    kdb_log_context *log_ctx;
+
+    log_ctx = kcontext->kdblog_context;
 
     if (kcontext->dal_handle == NULL) {
        status = kdb_setup_lib_handle(kcontext);
@@ -1046,11 +1185,50 @@ krb5_db_delete_principal(krb5_context kcontext,
        goto clean_n_exit;
     }
 
-    status =
-       dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
-                                                          search_for,
-                                                          nentries);
+    status = ulog_lock(kcontext, KRB5_LOCKMODE_EXCLUSIVE);
+    if (status) {
+       kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+       return status;
+    }
+
+    /*
+     * We'll be sharing the same locks as db for logging
+     */
+    if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) {
+       if ((status = krb5_unparse_name(kcontext, search_for, &princ_name))) {
+           ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
+           (void) kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+           return status;
+       }
+
+       (void) memset(&upd, 0, sizeof (kdb_incr_update_t));
+
+       upd.kdb_princ_name.utf8str_t_val = princ_name;
+       upd.kdb_princ_name.utf8str_t_len = strlen(princ_name);
+
+       if ((status = ulog_delete_update(kcontext, &upd)) != 0) {
+               ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
+               free(princ_name);
+               (void) kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
+               return status;
+       }
+
+       free(princ_name);
+    }
+
+    status = dal_handle->lib_handle->vftabl.db_delete_principal(kcontext,
+                                                                search_for,
+                                                                nentries);
     get_errmsg(kcontext, status);
+
+    /*
+     * We need to commit our update upon success
+     */
+    if (!status)
+       if (log_ctx && (log_ctx->iproprole == IPROP_MASTER))
+               (void) ulog_finish_update(kcontext, &upd);
+
+    ulog_lock(kcontext, KRB5_LOCKMODE_UNLOCK);
     kdb_unlock_lib_lock(dal_handle->lib_handle, FALSE);
 
   clean_n_exit:
diff --git a/src/lib/kdb/kdb_convert.c b/src/lib/kdb/kdb_convert.c
new file mode 100644 (file)
index 0000000..61b2b97
--- /dev/null
@@ -0,0 +1,1017 @@
+/*
+ * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)kdb_convert.c      1.3     05/01/05 SMI" */
+
+/*
+ * This file contains api's for conversion of the kdb_incr_update_t
+ * struct(s) into krb5_db_entry struct(s) and vice-versa.
+ */
+#include <sys/types.h>
+#include <com_err.h>
+#include <locale.h>
+#include <errno.h>
+#include <iprop_hdr.h>
+#include "iprop.h"
+#include <k5-int.h>
+#include <kdb.h>
+
+/* BEGIN CSTYLED */
+#define        ULOG_ENTRY_TYPE(upd, i) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i]
+
+#define        ULOG_ENTRY(upd, i) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u
+
+#define        ULOG_ENTRY_KEYVAL(upd, i, j) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u.av_keydata.av_keydata_val[j]
+
+#define        ULOG_ENTRY_PRINC(upd, i, j) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u.av_princ.k_components.k_components_val[j]
+
+#define        ULOG_ENTRY_MOD_PRINC(upd, i, j) ((kdb_incr_update_t *)upd)->kdb_update.kdbe_t_val[i].kdbe_val_t_u.av_mod_princ.k_components.k_components_val[j]
+/* END CSTYLED */
+
+typedef enum {
+    REG_PRINC = 0,
+    MOD_PRINC = 1
+} princ_type;
+
+
+/*
+ * This routine tracks the krb5_db_entry fields that have been modified
+ * (by comparing it to the db_entry currently present in principal.db)
+ * in the update.
+ */
+static void
+find_changed_attrs(krb5_db_entry *current, krb5_db_entry *new,
+                  kdbe_attr_type_t *attrs, int *nattrs)
+{
+    int i = 0, j = 0;
+
+    krb5_tl_data *first, *second;
+
+    if (current->attributes != new->attributes)
+       attrs[i++] = AT_ATTRFLAGS;
+
+    if (current->max_life != new->max_life)
+       attrs[i++] = AT_MAX_LIFE;
+
+    if (current->max_renewable_life != new->max_renewable_life)
+       attrs[i++] = AT_MAX_RENEW_LIFE;
+
+    if (current->expiration != new->expiration)
+       attrs[i++] = AT_EXP;
+
+    if (current->pw_expiration != new->pw_expiration)
+       attrs[i++] = AT_PW_EXP;
+
+    if (current->last_success != new->last_success)
+       attrs[i++] = AT_LAST_SUCCESS;
+
+    if (current->last_failed != new->last_failed)
+       attrs[i++] = AT_LAST_FAILED;
+
+    if (current->fail_auth_count != new->fail_auth_count)
+       attrs[i++] = AT_FAIL_AUTH_COUNT;
+
+    if ((current->princ->type == new->princ->type) &&
+       (current->princ->length == new->princ->length)) {
+       if ((current->princ->realm.length ==
+            new->princ->realm.length) &&
+           strncmp(current->princ->realm.data,
+                   new->princ->realm.data,
+                   current->princ->realm.length)) {
+           for (j = 0; j < current->princ->length; j++) {
+               if ((current->princ->data[j].data != NULL) &&
+                   (strncmp(current->princ->data[j].data,
+                            new->princ->data[j].data,
+                            current->princ->data[j].length))) {
+                   attrs[i++] = AT_PRINC;
+                   break;
+               }
+           }
+       } else {
+           attrs[i++] = AT_PRINC;
+       }
+    } else {
+       attrs[i++] = AT_PRINC;
+    }
+
+    if (current->n_key_data == new->n_key_data) {
+       /* Assuming key ordering is the same in new & current */
+       for (j = 0; j < new->n_key_data; j++) {
+           if (current->key_data[j].key_data_kvno !=
+               new->key_data[j].key_data_kvno) {
+               attrs[i++] = AT_KEYDATA;
+               break;
+           }
+       }
+    } else {
+       attrs[i++] = AT_KEYDATA;
+    }
+
+    if (current->n_tl_data == new->n_tl_data) {
+       /* Assuming we preserve the TL_DATA ordering between updates */
+       for (first = current->tl_data, second = new->tl_data;
+            first; first = first->tl_data_next,
+                second = second->tl_data_next) {
+           if ((first->tl_data_length == second->tl_data_length) &&
+               (first->tl_data_type == second->tl_data_type)) {
+               if ((memcmp((char *)first->tl_data_contents,
+                           (char *)second->tl_data_contents,
+                           first->tl_data_length)) != 0) {
+                   attrs[i++] = AT_TL_DATA;
+                   break;
+               }
+           } else {
+               attrs[i++] = AT_TL_DATA;
+               break;
+           }
+       }
+
+    } else {
+       attrs[i++] = AT_TL_DATA;
+    }
+
+    if (current->len != new->len)
+       attrs[i++] = AT_LEN;
+    /*
+     * Store the no. of (possibly :)) changed attributes
+     */
+    *nattrs = i;
+}
+
+
+/*
+ */
+static int
+data_to_utf8str(utf8str_t *u, krb5_data d)
+{
+    u->utf8str_t_len = d.length;
+    if (d.data) {
+       /* XXX Is the data always a nul-terminated string?  */
+       u->utf8str_t_val = strdup(d.data);
+       if (u->utf8str_t_val == NULL)
+           return -1;
+    } else
+       u->utf8str_t_val = NULL;
+    return 0;
+}
+
+/*
+ * Converts the krb5_principal struct from db2 to ulog format.
+ */
+krb5_error_code
+conv_princ_2ulog(krb5_principal princ, kdb_incr_update_t *upd,
+                int cnt, princ_type tp)
+{
+    int i = 0;
+    kdbe_princ_t *p;
+    kdbe_data_t *components;
+
+    if ((upd == NULL) || !princ)
+       return (KRB5KRB_ERR_GENERIC);
+
+    switch (tp) {
+    case REG_PRINC:
+    case MOD_PRINC:
+       p = &ULOG_ENTRY(upd, cnt).av_princ; /* or av_mod_princ */
+       p->k_nametype = (int32_t)princ->type;
+
+       if (data_to_utf8str(&p->k_realm, princ->realm) < 0) {
+           return ENOMEM;
+       }
+
+       p->k_components.k_components_len = princ->length;
+
+       p->k_components.k_components_val = components
+           = malloc(princ->length * sizeof (kdbe_data_t));
+       if (p->k_components.k_components_val == NULL) {
+           free(p->k_realm.utf8str_t_val);
+           p->k_realm.utf8str_t_val = NULL;
+           return (ENOMEM);
+       }
+
+       memset(components, 0, princ->length * sizeof(kdbe_data_t));
+       for (i = 0; i < princ->length; i++)
+           components[i].k_data.utf8str_t_val = NULL;
+       for (i = 0; i < princ->length; i++) {
+           components[i].k_magic = princ->data[i].magic;
+           if (data_to_utf8str(&components[i].k_data, princ->data[i]) < 0) {
+               int j;
+               for (j = 0; j < i; j++) {
+                   free(components[j].k_data.utf8str_t_val);
+                   components[j].k_data.utf8str_t_val = NULL;
+               }
+               free(components);
+               p->k_components.k_components_val = NULL;
+               free(p->k_realm.utf8str_t_val);
+               p->k_realm.utf8str_t_val = NULL;
+               return ENOMEM;
+           }
+       }
+       break;
+
+    default:
+       break;
+    }
+    return (0);
+}
+
+/*
+ * Copies a UTF-8 string from ulog to a krb5_data object, which may
+ * already have allocated storage associated with it.
+ *
+ * Maybe a return value should indicate success/failure?
+ */
+static void
+replace_with_utf8str(krb5_data *d, utf8str_t u)
+{
+    d->length = u.utf8str_t_len;
+    /* XXX Memory leak: old d->data if realloc failed.  */
+    /* XXX Overflow check?  d->length + 1.  */
+    d->data = realloc(d->data, d->length + 1);
+    if (d->data == NULL)
+       return;
+    if (u.utf8str_t_val)       /* May be null if length = 0.  */
+       strncpy(d->data, u.utf8str_t_val, d->length + 1);
+    d->data[d->length] = 0;
+}
+
+/*
+ * Converts the krb5_principal struct from ulog to db2 format.
+ */
+krb5_error_code
+conv_princ_2db(krb5_context context, krb5_principal *dbprinc,
+              kdb_incr_update_t *upd,
+              int cnt, princ_type tp,
+              int princ_exists)
+{
+    int i;
+    krb5_principal princ;
+    kdbe_princ_t *kdbe_princ;
+    kdbe_data_t *components;
+
+    if (upd == NULL)
+       return (KRB5KRB_ERR_GENERIC);
+
+    if (princ_exists == 0) {
+       princ = NULL;
+       princ = (krb5_principal)malloc(sizeof (krb5_principal_data));
+       if (princ == NULL) {
+           return (ENOMEM);
+       }
+    } else {
+       princ = *dbprinc;
+    }
+
+    switch (tp) {
+    case REG_PRINC:
+    case MOD_PRINC:
+       kdbe_princ = &ULOG_ENTRY(upd, cnt).av_princ; /* or av_mod_princ */
+       components = kdbe_princ->k_components.k_components_val;
+
+       princ->type = (krb5_int32)
+           kdbe_princ->k_nametype;
+       if (princ_exists == 0)
+           princ->realm.data = NULL;
+       replace_with_utf8str(&princ->realm, kdbe_princ->k_realm);
+       if (princ->realm.data == NULL)
+           goto error;
+
+       /* Free up old entries we're about to release.  */
+       if (princ_exists) {
+           for (i = kdbe_princ->k_components.k_components_len; i < princ->length; i++) {
+               free(princ->data[i].data);
+               princ->data[i].data = NULL;
+           }
+       } else
+           princ->data = NULL;
+       princ->data = (krb5_data *)realloc(princ->data,
+                                          (princ->length * sizeof (krb5_data)));
+       if (princ->data == NULL)
+           /* XXX Memory leak: old storage not freed.  */
+           goto error;
+       /* Initialize pointers in added component slots.  */
+       for (i = princ->length; i < kdbe_princ->k_components.k_components_len; i++) {
+           princ->data[i].data = NULL;
+       }
+       princ->length = (krb5_int32)kdbe_princ->k_components.k_components_len;
+
+       for (i = 0; i < princ->length; i++) {
+           princ->data[i].magic =
+               components[i].k_magic;
+           if (princ_exists == 0)
+               princ->data[i].data = NULL;
+           replace_with_utf8str(&princ->data[i],
+                                components[i].k_data);
+           if (princ->data[i].data == NULL)
+               goto error;
+       }
+       break;
+
+    default:
+       break;
+    }
+
+    *dbprinc = princ;
+    return (0);
+error:
+    krb5_free_principal(context, princ);
+    return (ENOMEM);
+}
+
+
+/*
+ * This routine converts one or more krb5 db2 records into update
+ * log (ulog) entry format. Space for the update log entries should
+ * be allocated prior to invocation of this routine.
+ */
+krb5_error_code
+ulog_conv_2logentry(krb5_context context, krb5_db_entry *entries,
+                   kdb_incr_update_t *updates,
+                   int nentries)
+{
+    int i, j, k, cnt, final, nattrs, tmpint, nprincs;
+    unsigned int more;
+    krb5_principal tmpprinc;
+    krb5_tl_data *newtl;
+    krb5_db_entry curr;
+    krb5_error_code ret;
+    kdbe_attr_type_t *attr_types;
+    kdb_incr_update_t *upd;
+    krb5_db_entry *ent;
+    int kadm_data_yes;
+
+    if ((updates == NULL) || (entries == NULL))
+       return (KRB5KRB_ERR_GENERIC);
+
+    upd = updates;
+    ent = entries;
+
+    for (k = 0; k < nentries; k++) {
+       nprincs = nattrs = tmpint = 0;
+       final = -1;
+       kadm_data_yes = 0;
+       attr_types = NULL;
+
+       if ((upd->kdb_update.kdbe_t_val = (kdbe_val_t *)
+            malloc(MAXENTRY_SIZE)) == NULL) {
+           return (ENOMEM);
+       }
+
+       /*
+        * Find out which attrs have been modified
+        */
+       if ((attr_types = (kdbe_attr_type_t *)malloc(
+                sizeof (kdbe_attr_type_t) * MAXATTRS_SIZE))
+           == NULL) {
+           return (ENOMEM);
+       }
+
+       if ((ret = krb5_db_get_principal(context, ent->princ, &curr,
+                                        &nprincs, &more)))
+           return (ret);
+
+       if (nprincs == 0) {
+           /*
+            * This is a new entry to the database, hence will
+            * include all the attribute-value pairs
+            *
+            * We leave out the TL_DATA types which we model as
+            * attrs in kdbe_attr_type_t, since listing AT_TL_DATA
+            * encompasses these other types-turned-attributes
+            *
+            * So, we do *NOT* consider AT_MOD_PRINC, AT_MOD_TIME,
+            * AT_MOD_WHERE, AT_PW_LAST_CHANGE, AT_PW_POLICY,
+            * AT_PW_POLICY_SWITCH, AT_PW_HIST_KVNO and AT_PW_HIST,
+            * totalling 8 attrs.
+            */
+           while (nattrs < MAXATTRS_SIZE - 8) {
+               attr_types[nattrs] = nattrs;
+               nattrs++;
+           }
+       } else {
+           find_changed_attrs(&curr, ent, attr_types, &nattrs);
+
+           krb5_db_free_principal(context, &curr, nprincs);
+       }
+
+       for (i = 0; i < nattrs; i++) {
+           switch (attr_types[i]) {
+           case AT_ATTRFLAGS:
+               if (ent->attributes >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_ATTRFLAGS;
+                   ULOG_ENTRY(upd, final).av_attrflags =
+                       (uint32_t)ent->attributes;
+               }
+               break;
+
+           case AT_MAX_LIFE:
+               if (ent->max_life >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_MAX_LIFE;
+                   ULOG_ENTRY(upd, final).av_max_life =
+                       (uint32_t)ent->max_life;
+               }
+               break;
+
+           case AT_MAX_RENEW_LIFE:
+               if (ent->max_renewable_life >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_MAX_RENEW_LIFE;
+                   ULOG_ENTRY(upd,
+                              final).av_max_renew_life =
+                       (uint32_t)ent->max_renewable_life;
+               }
+               break;
+
+           case AT_EXP:
+               if (ent->expiration >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_EXP;
+                   ULOG_ENTRY(upd, final).av_exp =
+                       (uint32_t)ent->expiration;
+               }
+               break;
+
+           case AT_PW_EXP:
+               if (ent->pw_expiration >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_PW_EXP;
+                   ULOG_ENTRY(upd, final).av_pw_exp =
+                       (uint32_t)ent->pw_expiration;
+               }
+               break;
+
+           case AT_LAST_SUCCESS:
+               if (ent->last_success >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_LAST_SUCCESS;
+                   ULOG_ENTRY(upd,
+                              final).av_last_success =
+                       (uint32_t)ent->last_success;
+               }
+               break;
+
+           case AT_LAST_FAILED:
+               if (ent->last_failed >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_LAST_FAILED;
+                   ULOG_ENTRY(upd,
+                              final).av_last_failed =
+                       (uint32_t)ent->last_failed;
+               }
+               break;
+
+           case AT_FAIL_AUTH_COUNT:
+               if (ent->fail_auth_count >= (krb5_kvno)0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_FAIL_AUTH_COUNT;
+                   ULOG_ENTRY(upd,
+                              final).av_fail_auth_count =
+                       (uint32_t)ent->fail_auth_count;
+               }
+               break;
+
+           case AT_PRINC:
+               if (ent->princ->length > 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_PRINC;
+                   if ((ret = conv_princ_2ulog(ent->princ,
+                                               upd, final, REG_PRINC)))
+                       return (ret);
+               }
+               break;
+
+           case AT_KEYDATA:
+/* BEGIN CSTYLED */
+               if (ent->n_key_data >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_KEYDATA;
+                   ULOG_ENTRY(upd, final).av_keydata.av_keydata_len = ent->n_key_data;
+
+                   ULOG_ENTRY(upd, final).av_keydata.av_keydata_val = malloc(ent->n_key_data * sizeof (kdbe_key_t));
+                   if (ULOG_ENTRY(upd, final).av_keydata.av_keydata_val == NULL)
+                       return (ENOMEM);
+
+                   for (j = 0; j < ent->n_key_data; j++) {
+                       ULOG_ENTRY_KEYVAL(upd, final, j).k_ver = ent->key_data[j].key_data_ver;
+                       ULOG_ENTRY_KEYVAL(upd, final, j).k_kvno = ent->key_data[j].key_data_kvno;
+                       ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_len = ent->key_data[j].key_data_ver;
+                       ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_len = ent->key_data[j].key_data_ver;
+
+                       ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_val = malloc(ent->key_data[j].key_data_ver * sizeof(int32_t));
+                       if (ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_val == NULL)
+                           return (ENOMEM);
+
+                       ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val = malloc(ent->key_data[j].key_data_ver * sizeof(utf8str_t));
+                       if (ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val == NULL)
+                           return (ENOMEM);
+
+                       for (cnt = 0; cnt < ent->key_data[j].key_data_ver; cnt++) {
+                           ULOG_ENTRY_KEYVAL(upd, final, j).k_enctype.k_enctype_val[cnt] = ent->key_data[j].key_data_type[cnt];
+                           ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_len = ent->key_data[j].key_data_length[cnt];
+                           ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_val = malloc(ent->key_data[j].key_data_length[cnt] * sizeof (char));
+                           if (ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_val == NULL)
+                               return (ENOMEM);
+                           (void) memcpy(ULOG_ENTRY_KEYVAL(upd, final, j).k_contents.k_contents_val[cnt].utf8str_t_val, ent->key_data[j].key_data_contents[cnt], ent->key_data[j].key_data_length[cnt]);
+                       }
+                   }
+               }
+               break;
+
+           case AT_TL_DATA:
+               ret = krb5_dbe_lookup_last_pwd_change(context,
+                                                     ent, &tmpint);
+               if (ret == 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_PW_LAST_CHANGE;
+                   ULOG_ENTRY(upd, final).av_pw_last_change = tmpint;
+               }
+               tmpint = 0;
+
+               if(!(ret = krb5_dbe_lookup_mod_princ_data(
+                        context, ent, &tmpint, &tmpprinc))) {
+
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_MOD_PRINC;
+
+                   ret = conv_princ_2ulog(tmpprinc,
+                                          upd, final, MOD_PRINC);
+                   krb5_free_principal(context, tmpprinc);
+                   if (ret)
+                       return (ret);
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_MOD_TIME;
+                   ULOG_ENTRY(upd, final).av_mod_time =
+                       tmpint;
+               }
+
+               newtl = ent->tl_data;
+               while (newtl) {
+                   switch (newtl->tl_data_type) {
+                   case KRB5_TL_LAST_PWD_CHANGE:
+                   case KRB5_TL_MOD_PRINC:
+                       break;
+
+                   case KRB5_TL_KADM_DATA:
+                   default:
+                       if (kadm_data_yes == 0) {
+                           ULOG_ENTRY_TYPE(upd, ++final).av_type = AT_TL_DATA;
+                           ULOG_ENTRY(upd, final).av_tldata.av_tldata_len = 0;
+                           ULOG_ENTRY(upd, final).av_tldata.av_tldata_val = malloc(ent->n_tl_data * sizeof(kdbe_tl_t));
+
+                           if (ULOG_ENTRY(upd, final).av_tldata.av_tldata_val == NULL)
+                               return (ENOMEM);
+                           kadm_data_yes = 1;
+                       }
+
+                       tmpint = ULOG_ENTRY(upd, final).av_tldata.av_tldata_len;
+                       ULOG_ENTRY(upd, final).av_tldata.av_tldata_len++;
+                       ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_type = newtl->tl_data_type;
+                       ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_len = newtl->tl_data_length;
+                       ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_val = malloc(newtl->tl_data_length * sizeof (char));
+                       if (ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_val == NULL)
+                           return (ENOMEM);
+                       (void) memcpy(ULOG_ENTRY(upd, final).av_tldata.av_tldata_val[tmpint].tl_data.tl_data_val, newtl->tl_data_contents, newtl->tl_data_length);
+                       break;
+                   }
+                   newtl = newtl->tl_data_next;
+               }
+               break;
+/* END CSTYLED */
+
+           case AT_LEN:
+               if (ent->len >= 0) {
+                   ULOG_ENTRY_TYPE(upd, ++final).av_type =
+                       AT_LEN;
+                   ULOG_ENTRY(upd, final).av_len =
+                       (int16_t)ent->len;
+               }
+               break;
+
+           default:
+               break;
+           }
+
+       }
+
+       free(attr_types);
+
+       /*
+        * Update len field in kdb_update
+        */
+       upd->kdb_update.kdbe_t_len = ++final;
+
+       /*
+        * Bump up to next struct
+        */
+       upd++;
+       ent++;
+    }
+    return (0);
+}
+
+/*
+ * This routine converts one or more update log (ulog) entries into
+ * kerberos db2 records. Required memory should be allocated
+ * for the db2 records (pointed to by krb5_db_entry *ent), prior
+ * to calling this routine.
+ */
+krb5_error_code
+ulog_conv_2dbentry(krb5_context context, krb5_db_entry *entries,
+                  kdb_incr_update_t *updates,
+                  int nentries)
+{
+    int i, j, k, cnt, mod_time = 0, nattrs, nprincs;
+    krb5_principal mod_princ = NULL;
+    krb5_principal dbprinc;
+    char *dbprincstr = NULL;
+
+    krb5_db_entry *ent;
+    kdb_incr_update_t *upd;
+
+    krb5_tl_data *newtl = NULL;
+    krb5_error_code ret;
+    unsigned int more;
+    unsigned int prev_n_keys = 0;
+
+    if ((updates == NULL) || (entries == NULL))
+       return (KRB5KRB_ERR_GENERIC);
+
+    ent = entries;
+    upd = updates;
+
+    for (k = 0; k < nentries; k++) {
+       cnt = nprincs = 0;
+
+       /*
+        * If the ulog entry represents a DELETE update,
+        * just skip to the next entry.
+        */
+       if (upd->kdb_deleted == TRUE)
+           goto next;
+
+       /*
+        * Store the no. of changed attributes in nattrs
+        */
+       nattrs = upd->kdb_update.kdbe_t_len;
+
+       dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len + 1)
+                           * sizeof (char));
+       if (dbprincstr == NULL)
+           return (ENOMEM);
+       strncpy(dbprincstr, (char *)upd->kdb_princ_name.utf8str_t_val,
+               (upd->kdb_princ_name.utf8str_t_len + 1));
+       dbprincstr[upd->kdb_princ_name.utf8str_t_len] = 0;
+
+       ret = krb5_parse_name(context, dbprincstr, &dbprinc);
+       free(dbprincstr);
+       if (ret)
+           return (ret);
+
+       ret = krb5_db_get_principal(context, dbprinc, ent, &nprincs,
+                                   &more);
+       krb5_free_principal(context, dbprinc);
+       if (ret)
+           return (ret);
+
+       /*
+        * Set ent->n_tl_data = 0 initially, if this is an ADD update
+        */
+       if (nprincs == 0)
+           ent->n_tl_data = 0;
+
+       for (i = 0; i < nattrs; i++) {
+           switch (ULOG_ENTRY_TYPE(upd, i).av_type) {
+           case AT_ATTRFLAGS:
+               ent->attributes = (krb5_flags)
+                   ULOG_ENTRY(upd, i).av_attrflags;
+               break;
+
+           case AT_MAX_LIFE:
+               ent->max_life = (krb5_deltat)
+                   ULOG_ENTRY(upd, i).av_max_life;
+               break;
+
+           case AT_MAX_RENEW_LIFE:
+               ent->max_renewable_life = (krb5_deltat)
+                   ULOG_ENTRY(upd, i).av_max_renew_life;
+               break;
+
+           case AT_EXP:
+               ent->expiration = (krb5_timestamp)
+                   ULOG_ENTRY(upd, i).av_exp;
+               break;
+
+           case AT_PW_EXP:
+               ent->pw_expiration = (krb5_timestamp)
+                   ULOG_ENTRY(upd, i).av_pw_exp;
+               break;
+
+           case AT_LAST_SUCCESS:
+               ent->last_success = (krb5_timestamp)
+                   ULOG_ENTRY(upd, i).av_last_success;
+               break;
+
+           case AT_LAST_FAILED:
+               ent->last_failed = (krb5_timestamp)
+                   ULOG_ENTRY(upd, i).av_last_failed;
+               break;
+
+           case AT_FAIL_AUTH_COUNT:
+               ent->fail_auth_count = (krb5_kvno)
+                   ULOG_ENTRY(upd, i).av_fail_auth_count;
+               break;
+
+           case AT_PRINC:
+               if ((ret = conv_princ_2db(context,
+                                         &(ent->princ), upd,
+                                         i, REG_PRINC, nprincs)))
+                   return (ret);
+               break;
+
+           case AT_KEYDATA:
+               if (nprincs != 0)
+                   prev_n_keys = ent->n_key_data;
+               ent->n_key_data = (krb5_int16)ULOG_ENTRY(upd,
+                                                        i).av_keydata.av_keydata_len;
+               if (nprincs == 0)
+                   ent->key_data = NULL;
+
+               ent->key_data = (krb5_key_data *)realloc(
+                   ent->key_data,
+                   (ent->n_key_data *
+                    sizeof (krb5_key_data)));
+               /* XXX Memory leak: Old key data in
+                  records eliminated by resizing to
+                  smaller size.  */
+               if (ent->key_data == NULL)
+                   /* XXX Memory leak: old storage.  */
+                   return (ENOMEM);
+
+/* BEGIN CSTYLED */
+               for (j = 0; j < ent->n_key_data; j++) {
+                   ent->key_data[j].key_data_ver = (krb5_int16)ULOG_ENTRY_KEYVAL(upd, i, j).k_ver;
+                   ent->key_data[j].key_data_kvno = (krb5_int16)ULOG_ENTRY_KEYVAL(upd, i, j).k_kvno;
+
+                   for (cnt = 0; cnt < ent->key_data[j].key_data_ver; cnt++) {
+                       ent->key_data[j].key_data_type[cnt] =  (krb5_int16)ULOG_ENTRY_KEYVAL(upd, i, j).k_enctype.k_enctype_val[cnt];
+                       ent->key_data[j].key_data_length[cnt] = (krb5_int16)ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val[cnt].utf8str_t_len;
+                       if ((nprincs == 0) || (j >= prev_n_keys))
+                           ent->key_data[j].key_data_contents[cnt] = NULL;
+
+                       ent->key_data[j].key_data_contents[cnt] = (krb5_octet *)realloc(ent->key_data[j].key_data_contents[cnt], ent->key_data[j].key_data_length[cnt]);
+                       if (ent->key_data[j].key_data_contents[cnt] == NULL)
+                           /* XXX Memory leak: old storage.  */
+                           return (ENOMEM);
+
+                       (void) memset(ent->key_data[j].key_data_contents[cnt], 0, (ent->key_data[j].key_data_length[cnt] * sizeof (krb5_octet)));
+                       (void) memcpy(ent->key_data[j].key_data_contents[cnt], ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val[cnt].utf8str_t_val, ent->key_data[j].key_data_length[cnt]);
+                   }
+               }
+               break;
+
+           case AT_TL_DATA:
+               cnt = ULOG_ENTRY(upd, i).av_tldata.av_tldata_len;
+               newtl = malloc(cnt * sizeof (krb5_tl_data));
+               (void) memset(newtl, 0, (cnt * sizeof (krb5_tl_data)));
+               if (newtl == NULL)
+                   return (ENOMEM);
+
+               for (j = 0; j < cnt; j++){
+                   newtl[j].tl_data_type = (krb5_int16)ULOG_ENTRY(upd, i).av_tldata.av_tldata_val[j].tl_type;
+                   newtl[j].tl_data_length = (krb5_int16)ULOG_ENTRY(upd, i).av_tldata.av_tldata_val[j].tl_data.tl_data_len;
+                   newtl[j].tl_data_contents = NULL;
+                   newtl[j].tl_data_contents = malloc(newtl[j].tl_data_length * sizeof (krb5_octet));
+                   if (newtl[j].tl_data_contents == NULL)
+                       /* XXX Memory leak: newtl
+                          and previously
+                          allocated elements.  */
+                       return (ENOMEM);
+
+                   (void) memset(newtl[j].tl_data_contents, 0, (newtl[j].tl_data_length * sizeof (krb5_octet)));
+                   (void) memcpy(newtl[j].tl_data_contents, ULOG_ENTRY(upd, i).av_tldata.av_tldata_val[j].tl_data.tl_data_val, newtl[j].tl_data_length);
+                   newtl[j].tl_data_next = NULL;
+                   if (j > 0)
+                       newtl[j - 1].tl_data_next =
+                           &newtl[j];
+               }
+
+               if ((ret = krb5_dbe_update_tl_data(context,
+                                                  ent, newtl)))
+                   return (ret);
+               for (j = 0; j < cnt; j++)
+                   if (newtl[j].tl_data_contents) {
+                       free(newtl[j].tl_data_contents);
+                       newtl[j].tl_data_contents = NULL;
+                   }
+               if (newtl) {
+                   free(newtl);
+                   newtl = NULL;
+               }
+               break;
+/* END CSTYLED */
+
+           case AT_PW_LAST_CHANGE:
+               if ((ret = krb5_dbe_update_last_pwd_change(
+                        context, ent,
+                        ULOG_ENTRY(upd, i).av_pw_last_change)))
+                   return (ret);
+               break;
+
+           case AT_MOD_PRINC:
+               if ((ret = conv_princ_2db(context,
+                                         &mod_princ, upd,
+                                         i, MOD_PRINC, 0)))
+                   return (ret);
+               break;
+
+           case AT_MOD_TIME:
+               mod_time = ULOG_ENTRY(upd, i).av_mod_time;
+               break;
+
+           case AT_LEN:
+               ent->len = (krb5_int16)
+                   ULOG_ENTRY(upd, i).av_len;
+               break;
+
+           default:
+               break;
+           }
+
+       }
+
+       /*
+        * process mod_princ_data request
+        */
+       if (mod_time && mod_princ) {
+           ret = krb5_dbe_update_mod_princ_data(context, ent,
+                                                mod_time, mod_princ);
+           krb5_free_principal(context, mod_princ);
+           if (ret)
+               return (ret);
+       }
+
+    next:
+       /*
+        * Bump up to next struct
+        */
+       upd++;
+       ent++;
+    }
+    return (0);
+}
+
+
+
+/*
+ * This routine frees up memory associated with the bunched ulog entries.
+ */
+void
+ulog_free_entries(kdb_incr_update_t *updates, int no_of_updates)
+{
+
+    kdb_incr_update_t *upd;
+    int i, j, k, cnt;
+
+    if (updates == NULL)
+       return;
+
+    upd = updates;
+
+    /*
+     * Loop thru each ulog entry
+     */
+    for (cnt = 0; cnt < no_of_updates; cnt++) {
+
+       /*
+        * ulog entry - kdb_princ_name
+        */
+       free(upd->kdb_princ_name.utf8str_t_val);
+
+/* BEGIN CSTYLED */
+
+       /*
+        * ulog entry - kdb_kdcs_seen_by
+        */
+       if (upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val) {
+           for (i = 0; i < upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_len; i++)
+               free(upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val[i].utf8str_t_val);
+           free(upd->kdb_kdcs_seen_by.kdb_kdcs_seen_by_val);
+       }
+
+       /*
+        * ulog entry - kdb_futures
+        */
+       free(upd->kdb_futures.kdb_futures_val);
+
+       /*
+        * ulog entry - kdb_update
+        */
+       if (upd->kdb_update.kdbe_t_val) {
+           /*
+            * Loop thru all the attributes and free up stuff
+            */
+           for (i = 0; i < upd->kdb_update.kdbe_t_len; i++) {
+
+               /*
+                * Free av_key_data
+                */
+               if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_KEYDATA) && ULOG_ENTRY(upd, i).av_keydata.av_keydata_val) {
+
+                   for (j = 0; j < ULOG_ENTRY(upd, i).av_keydata.av_keydata_len; j++) {
+                       free(ULOG_ENTRY_KEYVAL(upd, i, j).k_enctype.k_enctype_val);
+                       if (ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val) {
+                           for (k = 0; k < ULOG_ENTRY_KEYVAL(upd, i, j).k_ver; k++) {
+                               free(ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val[k].utf8str_t_val);
+                           }
+                           free(ULOG_ENTRY_KEYVAL(upd, i, j).k_contents.k_contents_val);
+                       }
+                   }
+                   free(ULOG_ENTRY(upd, i).av_keydata.av_keydata_val);
+               }
+
+
+               /*
+                * Free av_tl_data
+                */
+               if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_TL_DATA) && ULOG_ENTRY(upd, i).av_tldata.av_tldata_val) {
+                   for (j = 0; j < ULOG_ENTRY(upd, i).av_tldata.av_tldata_len; j++) {
+                       free(ULOG_ENTRY(upd, i).av_tldata.av_tldata_val[j].tl_data.tl_data_val);
+                   }
+                   free(ULOG_ENTRY(upd, i).av_tldata.av_tldata_val);
+               }
+
+               /*
+                * Free av_princ
+                */
+               if (ULOG_ENTRY_TYPE(upd, i).av_type == AT_PRINC) {
+                   free(ULOG_ENTRY(upd, i).av_princ.k_realm.utf8str_t_val);
+                   if (ULOG_ENTRY(upd, i).av_princ.k_components.k_components_val) {
+                       for (j = 0; j < ULOG_ENTRY(upd, i).av_princ.k_components.k_components_len; j++) {
+                           free(ULOG_ENTRY_PRINC(upd, i, j).k_data.utf8str_t_val);
+                       }
+                       free(ULOG_ENTRY(upd, i).av_princ.k_components.k_components_val);
+                   }
+               }
+
+               /*
+                * Free av_mod_princ
+                */
+               if (ULOG_ENTRY_TYPE(upd, i).av_type == AT_MOD_PRINC) {
+                   free(ULOG_ENTRY(upd, i).av_mod_princ.k_realm.utf8str_t_val);
+                   if (ULOG_ENTRY(upd, i).av_mod_princ.k_components.k_components_val) {
+                       for (j = 0; j < ULOG_ENTRY(upd, i).av_mod_princ.k_components.k_components_len; j++) {
+                           free(ULOG_ENTRY_MOD_PRINC(upd, i, j).k_data.utf8str_t_val);
+                       }
+                       free(ULOG_ENTRY(upd, i).av_mod_princ.k_components.k_components_val);
+                   }
+               }
+
+               /*
+                * Free av_mod_where
+                */
+               if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_MOD_WHERE) && ULOG_ENTRY(upd, i).av_mod_where.utf8str_t_val)
+                   free(ULOG_ENTRY(upd, i).av_mod_where.utf8str_t_val);
+
+               /*
+                * Free av_pw_policy
+                */
+               if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_PW_POLICY) && ULOG_ENTRY(upd, i).av_pw_policy.utf8str_t_val)
+                   free(ULOG_ENTRY(upd, i).av_pw_policy.utf8str_t_val);
+
+               /* 
+                * XXX: Free av_pw_hist
+                *
+                * For now, we just free the pointer
+                * to av_pw_hist_val, since we aren't
+                * populating this union member in
+                * the conv api function(s) anyways.
+                */
+               if ((ULOG_ENTRY_TYPE(upd, i).av_type == AT_PW_HIST) && ULOG_ENTRY(upd, i).av_pw_hist.av_pw_hist_val)
+                   free(ULOG_ENTRY(upd, i).av_pw_hist.av_pw_hist_val);
+
+           }
+
+           /*
+            * Free up the pointer to kdbe_t_val
+            */
+           free(upd->kdb_update.kdbe_t_val);
+       }
+
+/* END CSTYLED */
+
+       /*
+        * Bump up to next struct
+        */
+       upd++;
+    }
+
+
+    /*
+     * Finally, free up the pointer to the bunched ulog entries
+     */
+    free(updates);
+}
diff --git a/src/lib/kdb/kdb_log.c b/src/lib/kdb/kdb_log.c
new file mode 100644 (file)
index 0000000..c4efc41
--- /dev/null
@@ -0,0 +1,928 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)kdb_log.c  1.3     04/02/23 SMI" */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <k5-int.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <syslog.h>
+#include "kdb5.h"
+#include "kdb_log.h"
+
+/*
+ * This modules includes all the necessary functions that create and
+ * modify the Kerberos principal update and header logs.
+ */
+
+#define        getpagesize()   sysconf(_SC_PAGESIZE)
+
+static int             pagesize = 0;
+
+#define        INIT_ULOG(ctx)                          \
+       log_ctx = ctx->kdblog_context;          \
+       assert(log_ctx != NULL);                \
+       ulog = log_ctx->ulog;                   \
+       assert(ulog != NULL)
+
+/* XXX */
+typedef unsigned long ulong_t;
+typedef unsigned int uint_t;
+
+static int extend_file_to(int fd, uint_t new_size);
+
+krb5_error_code
+ulog_lock(krb5_context ctx, int mode)
+{
+    kdb_log_context *log_ctx = NULL;
+    kdb_hlog_t *ulog = NULL;
+
+    if (ctx == NULL)
+       return KRB5_LOG_ERROR;
+    if (ctx->kdblog_context == NULL || ctx->kdblog_context->iproprole == IPROP_NULL)
+       return 0;
+    INIT_ULOG(ctx);
+    return krb5_lock_file(ctx, log_ctx->ulogfd, mode);
+}
+
+/*
+ * Sync update entry to disk.
+ */
+static krb5_error_code
+ulog_sync_update(kdb_hlog_t *ulog, kdb_ent_header_t *upd)
+{
+    ulong_t            start, end, size;
+    krb5_error_code    retval;
+
+    if (ulog == NULL)
+       return (KRB5_LOG_ERROR);
+
+    if (!pagesize)
+       pagesize = getpagesize();
+
+    start = ((ulong_t)upd) & (~(pagesize-1));
+
+    end = (((ulong_t)upd) + ulog->kdb_block +
+          (pagesize-1)) & (~(pagesize-1));
+
+    size = end - start;
+    if (retval = msync((caddr_t)start, size, MS_SYNC)) {
+       return (retval);
+    }
+
+    return (0);
+}
+
+/*
+ * Sync memory to disk for the update log header.
+ */
+static void
+ulog_sync_header(kdb_hlog_t *ulog)
+{
+
+    if (!pagesize)
+       pagesize = getpagesize();
+
+    if (msync((caddr_t)ulog, pagesize, MS_SYNC)) {
+       /*
+        * Couldn't sync to disk, let's panic
+        */
+       syslog(LOG_ERR, "ulog_sync_header: could not sync to disk");
+       abort();
+    }
+}
+
+/*
+ * Resizes the array elements.  We reinitialize the update log rather than
+ * unrolling the the log and copying it over to a temporary log for obvious
+ * performance reasons.  Slaves will subsequently do a full resync, but
+ * the need for resizing should be very small.
+ */
+static krb5_error_code
+ulog_resize(kdb_hlog_t *ulog, uint32_t ulogentries, int ulogfd, uint_t recsize)
+{
+    uint_t             new_block, new_size;
+
+    if (ulog == NULL)
+       return (KRB5_LOG_ERROR);
+
+    new_size = sizeof (kdb_hlog_t);
+
+    new_block = (recsize / ULOG_BLOCK) + 1;
+    new_block *= ULOG_BLOCK;
+
+    new_size += ulogentries * new_block;
+
+    if (new_size <= MAXLOGLEN) {
+       /*
+        * Reinit log with new block size
+        */
+       (void) memset(ulog, 0, sizeof (kdb_hlog_t));
+
+       ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
+       ulog->db_version_num = KDB_VERSION;
+       ulog->kdb_state = KDB_STABLE;
+       ulog->kdb_block = new_block;
+
+       ulog_sync_header(ulog);
+
+       /*
+        * Time to expand log considering new block size
+        */
+       if (extend_file_to(ulogfd, new_size) < 0)
+           return errno;
+    } else {
+       /*
+        * Can't map into file larger than MAXLOGLEN
+        */
+       return (KRB5_LOG_ERROR);
+    }
+
+    return (0);
+}
+
+/*
+ * Adds an entry to the update log.
+ * The layout of the update log looks like:
+ *
+ * header log -> [ update header -> xdr(kdb_incr_update_t) ], ...
+ */
+krb5_error_code
+ulog_add_update(krb5_context context, kdb_incr_update_t *upd)
+{
+    XDR                xdrs;
+    kdbe_time_t        ktime;
+    struct timeval     timestamp;
+    kdb_ent_header_t *indx_log;
+    uint_t             i, recsize;
+    ulong_t            upd_size;
+    krb5_error_code    retval;
+    kdb_sno_t  cur_sno;
+    kdb_log_context    *log_ctx;
+    kdb_hlog_t *ulog = NULL;
+    uint32_t   ulogentries;
+    int                ulogfd;
+
+    INIT_ULOG(context);
+    ulogentries = log_ctx->ulogentries;
+    ulogfd = log_ctx->ulogfd;
+
+    if (upd == NULL)
+       return (KRB5_LOG_ERROR);
+
+    (void) gettimeofday(&timestamp, NULL);
+    ktime.seconds = timestamp.tv_sec;
+    ktime.useconds = timestamp.tv_usec;
+
+    upd_size = xdr_sizeof((xdrproc_t)xdr_kdb_incr_update_t, upd);
+
+    recsize = sizeof (kdb_ent_header_t) + upd_size;
+
+    if (recsize > ulog->kdb_block) {
+       if (retval = ulog_resize(ulog, ulogentries, ulogfd, recsize)) {
+           /* Resize element array failed */
+           return (retval);
+       }
+    }
+
+    cur_sno = ulog->kdb_last_sno;
+
+    /*
+     * We need to overflow our sno, replicas will do full
+     * resyncs once they see their sno > than the masters.
+     */
+    if (cur_sno == ULONG_MAX)
+       cur_sno = 1;
+    else
+       cur_sno++;
+
+    /*
+     * We squirrel this away for finish_update() to index
+     */
+    upd->kdb_entry_sno = cur_sno;
+
+    i = (cur_sno - 1) % ulogentries;
+
+    indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
+
+    (void) memset(indx_log, 0, ulog->kdb_block);
+
+    indx_log->kdb_umagic = KDB_ULOG_MAGIC;
+    indx_log->kdb_entry_size = upd_size;
+    indx_log->kdb_entry_sno = cur_sno;
+    indx_log->kdb_time = upd->kdb_time = ktime;
+    indx_log->kdb_commit = upd->kdb_commit = FALSE;
+
+    ulog->kdb_state = KDB_UNSTABLE;
+
+    xdrmem_create(&xdrs, (char *)indx_log->entry_data,
+                 indx_log->kdb_entry_size, XDR_ENCODE);
+    if (!xdr_kdb_incr_update_t(&xdrs, upd))
+       return (KRB5_LOG_CONV);
+
+    if (retval = ulog_sync_update(ulog, indx_log))
+       return (retval);
+
+    if (ulog->kdb_num < ulogentries)
+       ulog->kdb_num++;
+
+    ulog->kdb_last_sno = cur_sno;
+    ulog->kdb_last_time = ktime;
+
+    /*
+     * Since this is a circular array, once we circled, kdb_first_sno is
+     * always kdb_entry_sno + 1.
+     */
+    if (cur_sno > ulogentries) {
+       i = upd->kdb_entry_sno % ulogentries;
+       indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
+       ulog->kdb_first_sno = indx_log->kdb_entry_sno;
+       ulog->kdb_first_time = indx_log->kdb_time;
+    } else if (cur_sno == 1) {
+       ulog->kdb_first_sno = 1;
+       ulog->kdb_first_time = indx_log->kdb_time;
+    }
+
+    ulog_sync_header(ulog);
+
+    return (0);
+}
+
+/*
+ * Mark the log entry as committed and sync the memory mapped log
+ * to file.
+ */
+krb5_error_code
+ulog_finish_update(krb5_context context, kdb_incr_update_t *upd)
+{
+    krb5_error_code    retval;
+    kdb_ent_header_t   *indx_log;
+    uint_t             i;
+    kdb_log_context    *log_ctx;
+    kdb_hlog_t         *ulog = NULL;
+    uint32_t           ulogentries;
+
+    INIT_ULOG(context);
+    ulogentries = log_ctx->ulogentries;
+
+    i = (upd->kdb_entry_sno - 1) % ulogentries;
+
+    indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
+
+    indx_log->kdb_commit = TRUE;
+
+    ulog->kdb_state = KDB_STABLE;
+
+    if (retval = ulog_sync_update(ulog, indx_log))
+       return (retval);
+
+    ulog_sync_header(ulog);
+
+    return (0);
+}
+
+/*
+ * Set the header log details on the slave and sync it to file.
+ */
+static void
+ulog_finish_update_slave(kdb_hlog_t *ulog, kdb_last_t lastentry)
+{
+
+    ulog->kdb_last_sno = lastentry.last_sno;
+    ulog->kdb_last_time = lastentry.last_time;
+
+    ulog_sync_header(ulog);
+}
+
+/*
+ * Delete an entry to the update log.
+ */
+krb5_error_code
+ulog_delete_update(krb5_context context, kdb_incr_update_t *upd)
+{
+
+    upd->kdb_deleted = TRUE;
+
+    return (ulog_add_update(context, upd));
+}
+
+/*
+ * Used by the slave or master (during ulog_check) to update it's hash db from
+ * the incr update log.
+ *
+ * Must be called with lock held.
+ */
+krb5_error_code
+ulog_replay(krb5_context context, kdb_incr_result_t *incr_ret, char **db_args)
+{
+    krb5_db_entry      *entry = NULL;
+    kdb_incr_update_t  *upd = NULL, *fupd;
+    int                        i, no_of_updates;
+    krb5_error_code    retval;
+    krb5_principal     dbprinc = NULL;
+    kdb_last_t         errlast;
+    char               *dbprincstr = NULL;
+    kdb_log_context    *log_ctx;
+    kdb_hlog_t         *ulog = NULL;
+
+    INIT_ULOG(context);
+
+    no_of_updates = incr_ret->updates.kdb_ulog_t_len;
+    upd = incr_ret->updates.kdb_ulog_t_val;
+    fupd = upd;
+
+    /*
+     * We reset last_sno and last_time to 0, if krb5_db2_db_put_principal
+     * or krb5_db2_db_delete_principal fail.
+     */
+    errlast.last_sno = (unsigned int)0;
+    errlast.last_time.seconds = (unsigned int)0;
+    errlast.last_time.useconds = (unsigned int)0;
+
+    if ((retval = krb5_db_open(context, db_args,
+                              KRB5_KDB_OPEN_RW|KRB5_KDB_SRV_TYPE_ADMIN)))
+       goto cleanup;
+
+    for (i = 0; i < no_of_updates; i++) {
+       int nentry = 1;
+
+       if (!upd->kdb_commit)
+           continue;
+
+       if (upd->kdb_deleted) {
+           dbprincstr = malloc((upd->kdb_princ_name.utf8str_t_len
+                                + 1) * sizeof (char));
+
+           if (dbprincstr == NULL) {
+               retval = ENOMEM;
+               goto cleanup;
+           }
+
+           (void) strncpy(dbprincstr,
+                          (char *)upd->kdb_princ_name.utf8str_t_val,
+                          (upd->kdb_princ_name.utf8str_t_len + 1));
+           dbprincstr[upd->kdb_princ_name.utf8str_t_len] = 0;
+
+           if (retval = krb5_parse_name(context, dbprincstr,
+                                        &dbprinc)) {
+               goto cleanup;
+           }
+
+           free(dbprincstr);
+
+           retval = krb5int_delete_principal_no_log(context,
+                                                    dbprinc,
+                                                    &nentry);
+
+           if (dbprinc)
+               krb5_free_principal(context, dbprinc);
+
+           if (retval)
+               goto cleanup;
+       } else {
+           entry = (krb5_db_entry *)malloc(sizeof (krb5_db_entry));
+
+           if (!entry) {
+               retval = errno;
+               goto cleanup;
+           }
+
+           (void) memset(entry, 0, sizeof (krb5_db_entry));
+
+           if (retval = ulog_conv_2dbentry(context, entry, upd, 1))
+               goto cleanup;
+
+           retval = krb5int_put_principal_no_log(context, entry,
+                                                 &nentry);
+
+           if (entry) {
+               krb5_db_free_principal(context, entry, nentry);
+               free(entry);
+               entry = NULL;
+           }
+           if (retval)
+               goto cleanup;
+       }
+
+       upd++;
+    }
+
+cleanup:
+    if (fupd)
+       ulog_free_entries(fupd, no_of_updates);
+
+    if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
+       if (retval)
+           ulog_finish_update_slave(ulog, errlast);
+       else
+           ulog_finish_update_slave(ulog, incr_ret->lastentry);
+    }
+
+    return (retval);
+}
+
+/*
+ * Validate the log file and resync any uncommitted update entries
+ * to the principal database.
+ *
+ * Must be called with lock held.
+ */
+static krb5_error_code
+ulog_check(krb5_context context, kdb_hlog_t *ulog, char **db_args)
+{
+    XDR                        xdrs;
+    krb5_error_code    retval = 0;
+    int                        i;
+    kdb_ent_header_t   *indx_log;
+    kdb_incr_update_t  *upd = NULL;
+    kdb_incr_result_t  *incr_ret = NULL;
+
+    ulog->kdb_state = KDB_STABLE;
+
+    for (i = 0; i < ulog->kdb_num; i++) {
+       indx_log = (kdb_ent_header_t *)INDEX(ulog, i);
+
+       if (indx_log->kdb_umagic != KDB_ULOG_MAGIC) {
+           /*
+            * Update entry corrupted we should scream and die
+            */
+           ulog->kdb_state = KDB_CORRUPT;
+           retval = KRB5_LOG_CORRUPT;
+           break;
+       }
+
+       if (indx_log->kdb_commit == FALSE) {
+           ulog->kdb_state = KDB_UNSTABLE;
+
+           incr_ret = (kdb_incr_result_t *)
+               malloc(sizeof (kdb_incr_result_t));
+           if (incr_ret == NULL) {
+               retval = errno;
+               goto error;
+           }
+
+           upd = (kdb_incr_update_t *)
+               malloc(sizeof (kdb_incr_update_t));
+           if (upd == NULL) {
+               retval = errno;
+               goto error;
+           }
+
+           (void) memset(upd, 0, sizeof (kdb_incr_update_t));
+           xdrmem_create(&xdrs, (char *)indx_log->entry_data,
+                         indx_log->kdb_entry_size, XDR_DECODE);
+           if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
+               retval = KRB5_LOG_CONV;
+               goto error;
+           }
+
+           incr_ret->updates.kdb_ulog_t_len = 1;
+           incr_ret->updates.kdb_ulog_t_val = upd;
+
+           upd->kdb_commit = TRUE;
+
+           /*
+            * We don't want to readd this update and just use the
+            * existing update to be propagated later on
+            */
+           ulog_set_role(context, IPROP_NULL);
+           retval = ulog_replay(context, incr_ret, db_args);
+
+           /*
+            * upd was freed by ulog_replay, we NULL
+            * the pointer in case we subsequently break from loop.
+            */
+           upd = NULL;
+           if (incr_ret) {
+               free(incr_ret);
+               incr_ret = NULL;
+           }
+           ulog_set_role(context, IPROP_MASTER);
+
+           if (retval)
+               goto error;
+
+           /*
+            * We flag this as committed since this was
+            * the last entry before kadmind crashed, ergo
+            * the slaves have not seen this update before
+            */
+           indx_log->kdb_commit = TRUE;
+           retval = ulog_sync_update(ulog, indx_log);
+           if (retval)
+               goto error;
+
+           ulog->kdb_state = KDB_STABLE;
+       }
+    }
+
+error:
+    if (upd)
+       ulog_free_entries(upd, 1);
+
+    free(incr_ret);
+
+    ulog_sync_header(ulog);
+
+    return (retval);
+}
+
+/*
+ * Map the log file to memory for performance and simplicity.
+ *
+ * Called by: if iprop_enabled then ulog_map();
+ * Assumes that the caller will terminate on ulog_map, hence munmap and
+ * closing of the fd are implicitly performed by the caller.
+ * Returns 0 on success else failure.
+ */
+krb5_error_code
+ulog_map(krb5_context context, const char *logname, uint32_t ulogentries,
+        int caller, char **db_args)
+{
+    struct stat        st;
+    krb5_error_code    retval;
+    uint32_t   ulog_filesize;
+    kdb_log_context    *log_ctx;
+    kdb_hlog_t *ulog = NULL;
+    int                ulogfd = -1;
+
+    ulog_filesize = sizeof (kdb_hlog_t);
+
+    if (stat(logname, &st) == -1) {
+
+       if (caller == FKPROPLOG) {
+           /*
+            * File doesn't exist so we exit with kproplog
+            */
+           return (errno);
+       }
+
+       if ((ulogfd = open(logname, O_RDWR+O_CREAT, 0600)) == -1) {
+           return (errno);
+       }
+
+       if (lseek(ulogfd, 0L, SEEK_CUR) == -1) {
+           return (errno);
+       }
+
+       if ((caller == FKADMIND) || (caller == FKCOMMAND))
+           ulog_filesize += ulogentries * ULOG_BLOCK;
+
+       if (extend_file_to(ulogfd, ulog_filesize) < 0)
+           return errno;
+    } else {
+
+       ulogfd = open(logname, O_RDWR, 0600);
+       if (ulogfd == -1)
+           /*
+            * Can't open existing log file
+            */
+           return errno;
+    }
+
+    if (caller == FKPROPLOG) {
+       fstat(ulogfd, &st);
+       ulog_filesize = st.st_size;
+
+       ulog = (kdb_hlog_t *)mmap(0, ulog_filesize,
+                                 PROT_READ+PROT_WRITE, MAP_PRIVATE, ulogfd, 0);
+    } else {
+       /*
+        * else kadmind, kpropd, & kcommands should udpate stores
+        */
+       ulog = (kdb_hlog_t *)mmap(0, MAXLOGLEN,
+                                 PROT_READ+PROT_WRITE, MAP_SHARED, ulogfd, 0);
+    }
+
+    if ((int)(ulog) == -1) {
+       /*
+        * Can't map update log file to memory
+        */
+       return (errno);
+    }
+
+    if (!context->kdblog_context) {
+       if (!(log_ctx = malloc(sizeof (kdb_log_context))))
+           return (errno);
+       memset(log_ctx, 0, sizeof(*log_ctx));
+       context->kdblog_context = log_ctx;
+    } else
+       log_ctx = context->kdblog_context;
+    log_ctx->ulog = ulog;
+    log_ctx->ulogentries = ulogentries;
+    log_ctx->ulogfd = ulogfd;
+
+    if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) {
+       if (ulog->kdb_hmagic == 0) {
+           /*
+            * New update log
+            */
+           (void) memset(ulog, 0, sizeof (kdb_hlog_t));
+
+           ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
+           ulog->db_version_num = KDB_VERSION;
+           ulog->kdb_state = KDB_STABLE;
+           ulog->kdb_block = ULOG_BLOCK;
+           if (!(caller == FKPROPLOG))
+               ulog_sync_header(ulog);
+       } else {
+           return (KRB5_LOG_CORRUPT);
+       }
+    }
+
+    if (caller == FKADMIND) {
+       retval = ulog_lock(context, KRB5_LOCKMODE_EXCLUSIVE);
+       if (retval)
+           return retval;
+       switch (ulog->kdb_state) {
+       case KDB_STABLE:
+       case KDB_UNSTABLE:
+           /*
+            * Log is currently un/stable, check anyway
+            */
+           retval = ulog_check(context, ulog, db_args);
+           ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+           if (retval == KRB5_LOG_CORRUPT) {
+               return (retval);
+           }
+           break;
+       case KDB_CORRUPT:
+           ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+           return (KRB5_LOG_CORRUPT);
+       default:
+           /*
+            * Invalid db state
+            */
+           ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+           return (KRB5_LOG_ERROR);
+       }
+    } else if ((caller == FKPROPLOG) || (caller == FKPROPD)) {
+       /*
+        * kproplog and kpropd don't need to do anything else
+        */
+       return (0);
+    }
+
+    /*
+     * Reinit ulog if the log is being truncated or expanded after
+     * we have circled.
+     */
+    retval = ulog_lock(context, KRB5_LOCKMODE_EXCLUSIVE);
+    if (retval)
+       return retval;
+    if (ulog->kdb_num != ulogentries) {
+       if ((ulog->kdb_num != 0) &&
+           ((ulog->kdb_last_sno > ulog->kdb_num) ||
+            (ulog->kdb_num > ulogentries))) {
+           
+           (void) memset(ulog, 0, sizeof (kdb_hlog_t));
+
+           ulog->kdb_hmagic = KDB_ULOG_HDR_MAGIC;
+           ulog->db_version_num = KDB_VERSION;
+           ulog->kdb_state = KDB_STABLE;
+           ulog->kdb_block = ULOG_BLOCK;
+
+           ulog_sync_header(ulog);
+       }
+
+       /*
+        * Expand ulog if we have specified a greater size
+        */
+       if (ulog->kdb_num < ulogentries) {
+           ulog_filesize += ulogentries * ulog->kdb_block;
+
+           if (extend_file_to(ulogfd, ulog_filesize) < 0) {
+               ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+               return errno;
+           }
+       }
+    }
+    ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+
+    return (0);
+}
+
+/*
+ * Get the last set of updates seen, (last+1) to n is returned.
+ */
+krb5_error_code
+ulog_get_entries(krb5_context context,         /* input - krb5 lib config */
+                kdb_last_t last,               /* input - slave's last sno */
+                kdb_incr_result_t *ulog_handle) /* output - incr result for slave */
+{
+    XDR                        xdrs;
+    kdb_ent_header_t   *indx_log;
+    kdb_incr_update_t  *upd;
+    uint_t             indx, count, tdiff;
+    uint32_t           sno;
+    krb5_error_code    retval;
+    struct timeval     timestamp;
+    kdb_log_context    *log_ctx;
+    kdb_hlog_t         *ulog = NULL;
+    uint32_t           ulogentries;
+
+    INIT_ULOG(context);
+    ulogentries = log_ctx->ulogentries;
+
+    retval = ulog_lock(context, KRB5_LOCKMODE_SHARED);
+    if (retval)
+       return retval;
+
+    /*
+     * Check to make sure we don't have a corrupt ulog first.
+     */
+    if (ulog->kdb_state == KDB_CORRUPT) {
+       ulog_handle->ret = UPDATE_ERROR;
+       (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+       return (KRB5_LOG_CORRUPT);
+    }
+
+    gettimeofday(&timestamp, NULL);
+
+    tdiff = timestamp.tv_sec - ulog->kdb_last_time.seconds;
+    if (tdiff <= ULOG_IDLE_TIME) {
+       ulog_handle->ret = UPDATE_BUSY;
+       (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+       return (0);
+    }
+
+    /*
+     * We need to lock out other processes here, such as kadmin.local,
+     * since we are looking at the last_sno and looking up updates.  So
+     * we can share with other readers.
+     */
+    retval = krb5_db_lock(context, KRB5_LOCKMODE_SHARED);
+    if (retval) {
+       (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+       return (retval);
+    }
+
+    /*
+     * We may have overflowed the update log or we shrunk the log, or
+     * the client's ulog has just been created.
+     */
+    if ((last.last_sno > ulog->kdb_last_sno) ||
+       (last.last_sno < ulog->kdb_first_sno) ||
+       (last.last_sno == 0)) {
+       ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
+       (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+       (void) krb5_db_unlock(context);
+       ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
+       return (0);
+    } else if (last.last_sno <= ulog->kdb_last_sno) {
+       sno = last.last_sno;
+
+       indx = (sno - 1) % ulogentries;
+
+       indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+
+       /*
+        * Validate the time stamp just to make sure it was the same sno
+        */
+       if ((indx_log->kdb_time.seconds == last.last_time.seconds) &&
+           (indx_log->kdb_time.useconds == last.last_time.useconds)) {
+
+           /*
+            * If we have the same sno we return success
+            */
+           if (last.last_sno == ulog->kdb_last_sno) {
+               (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+               (void) krb5_db_unlock(context);
+               ulog_handle->ret = UPDATE_NIL;
+               return (0);
+           }
+
+           count = ulog->kdb_last_sno - sno;
+
+           ulog_handle->updates.kdb_ulog_t_val =
+               (kdb_incr_update_t *)malloc(
+                   sizeof (kdb_incr_update_t) * count);
+
+           upd = ulog_handle->updates.kdb_ulog_t_val;
+
+           if (upd == NULL) {
+               (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+               (void) krb5_db_unlock(context);
+               ulog_handle->ret = UPDATE_ERROR;
+               return (errno);
+           }
+
+           while (sno < ulog->kdb_last_sno) {
+               indx = sno % ulogentries;
+
+               indx_log = (kdb_ent_header_t *)
+                   INDEX(ulog, indx);
+
+               (void) memset(upd, 0,
+                             sizeof (kdb_incr_update_t));
+               xdrmem_create(&xdrs,
+                             (char *)indx_log->entry_data,
+                             indx_log->kdb_entry_size, XDR_DECODE);
+               if (!xdr_kdb_incr_update_t(&xdrs, upd)) {
+                   (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+                   (void) krb5_db_unlock(context);
+                   ulog_handle->ret = UPDATE_ERROR;
+                   return (KRB5_LOG_CONV);
+               }
+               /*
+                * Mark commitment since we didn't
+                * want to decode and encode the
+                * incr update record the first time.
+                */
+               upd->kdb_commit = indx_log->kdb_commit;
+
+               upd++;
+               sno++;
+           } /* while */
+
+           ulog_handle->updates.kdb_ulog_t_len = count;
+
+           ulog_handle->lastentry.last_sno = ulog->kdb_last_sno;
+           ulog_handle->lastentry.last_time.seconds =
+               ulog->kdb_last_time.seconds;
+           ulog_handle->lastentry.last_time.useconds =
+               ulog->kdb_last_time.useconds;
+           ulog_handle->ret = UPDATE_OK;
+
+           (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+           (void) krb5_db_unlock(context);
+
+           return (0);
+       } else {
+           /*
+            * We have time stamp mismatch or we no longer have
+            * the slave's last sno, so we brute force it
+            */
+           (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+           (void) krb5_db_unlock(context);
+           ulog_handle->ret = UPDATE_FULL_RESYNC_NEEDED;
+
+           return (0);
+       }
+    }
+
+    /*
+     * Should never get here, return error
+     */
+    (void) ulog_lock(context, KRB5_LOCKMODE_UNLOCK);
+    ulog_handle->ret = UPDATE_ERROR;
+    return (KRB5_LOG_ERROR);
+}
+
+krb5_error_code
+ulog_set_role(krb5_context ctx, iprop_role role)
+{
+    kdb_log_context    *log_ctx;
+
+    if (!ctx->kdblog_context) {
+       if (!(log_ctx = malloc(sizeof (kdb_log_context))))
+           return (errno);
+       memset(log_ctx, 0, sizeof(*log_ctx));
+       ctx->kdblog_context = log_ctx;
+    } else
+       log_ctx = ctx->kdblog_context;
+
+    log_ctx->iproprole = role;
+
+    return (0);
+}
+
+/*
+ * Extend update log file.
+ */
+static int extend_file_to(int fd, uint_t new_size)
+{
+    int current_offset;
+    static const char zero[512] = { 0, };
+
+    current_offset = lseek(fd, 0, SEEK_END);
+    if (current_offset < 0)
+       return -1;
+    if (new_size > INT_MAX) {
+       errno = EINVAL;
+       return -1;
+    }
+    while (current_offset < new_size) {
+       int write_size, wrote_size;
+       write_size = new_size - current_offset;
+       if (write_size > 512)
+           write_size = 512;
+       wrote_size = write(fd, zero, write_size);
+       if (wrote_size < 0)
+           return -1;
+       if (wrote_size == 0) {
+           errno = EINVAL;     /* XXX ?? */
+           return -1;
+       }
+       current_offset += wrote_size;
+       write_size = new_size - current_offset;
+    }
+    return 0;
+}
index dd9f95288b76fc3520e9acfa55cef5b36ea84d71..6157ec3571d7ceaf4a18e2f6f9492722122235ad 100644 (file)
@@ -49,3 +49,12 @@ krb5_db_delete_policy
 krb5_db_free_policy
 krb5_def_store_mkey
 krb5_db_promote
+ulog_map
+ulog_set_role
+ulog_free_entries
+xdr_kdb_last_t
+xdr_kdb_incr_result_t
+xdr_kdb_fullresync_result_t
+ulog_get_entries
+ulog_replay
+xdr_kdb_incr_update_t
index d6014acec2e4f530db4796943d927fa767c79ecb..b15af8c87321ca38600145fc8f705d7382dd9f0a 100644 (file)
@@ -1,7 +1,7 @@
 #
 # lib/krb5/error_tables/kdb5_err.et
 #
-# Copyright 1990 by the Massachusetts Institute of Technology.
+# Copyright 1990, 2008 by the Massachusetts Institute of Technology.
 # All Rights Reserved.
 #
 # Export of this software from the United States of America may
@@ -75,6 +75,9 @@ ec KRB5_KDB_SERVER_INTERNAL_ERR,   "Server error"
 ec KRB5_KDB_ACCESS_ERROR,       "Unable to access Kerberos database"
 ec KRB5_KDB_INTERNAL_ERROR,     "Kerberos database internal error"
 ec KRB5_KDB_CONSTRAINT_VIOLATION,  "Kerberos database constraints violated"
-
+ec KRB5_LOG_CONV,              "Update log conversion error"
+ec KRB5_LOG_UNSTABLE,          "Update log is unstable"
+ec KRB5_LOG_CORRUPT,           "Update log is corrupt"
+ec KRB5_LOG_ERROR,             "Generic update log error"
 
 end
index 5e4e9cbddc962e1858576aadba870dec7d074e5c..2437f84385845ff1d180cb0b98439d6586ca1367 100644 (file)
@@ -6,25 +6,31 @@ PROG_LIBPATH=-L$(TOPLIBD)
 PROG_RPATH=$(KRB5_LIBDIR)
 DEFS=
 
-all::  kprop kpropd
+all::  kprop kpropd kproplog
 
 CLIENTSRCS= $(srcdir)/kprop.c 
 CLIENTOBJS= kprop.o 
 
-SERVERSRCS= $(srcdir)/kpropd.c 
-SERVEROBJS= kpropd.o
+SERVERSRCS= $(srcdir)/kpropd.c $(srcdir)/kpropd_rpc.c
+SERVEROBJS= kpropd.o kpropd_rpc.o
 
-SRCS= $(CLIENTSRCS) $(SERVERSRCS)
+LOGSRCS= $(srcdir)/kproplog.c
+LOGOBJS= kproplog.o
+
+SRCS= $(CLIENTSRCS) $(SERVERSRCS) $(LOGSRCS)
 
 
 kprop: $(CLIENTOBJS) $(KRB5_BASE_DEPLIBS)
        $(CC_LINK) -o kprop $(CLIENTOBJS) $(KRB5_BASE_LIBS) @LIBUTIL@
 
-kpropd: $(SERVEROBJS) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB)
-       $(CC_LINK) -o kpropd $(SERVEROBJS) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) @LIBUTIL@
+kpropd: $(SERVEROBJS) $(KADMCLNT_DEPLIBS) $(KDB5_DEPLIB) $(KRB5_BASE_DEPLIBS) $(APPUTILS_DEPLIB)
+       $(CC_LINK) -o kpropd $(SERVEROBJS) $(KADMCLNT_LIBS) $(KDB5_LIB) $(KRB5_BASE_LIBS) $(APPUTILS_LIB) @LIBUTIL@
+
+kproplog: $(LOGOBJS)
+       $(CC_LINK) -o kproplog $(LOGOBJS) $(KADMSRV_LIBS) $(KRB5_BASE_LIBS)
 
 install::
-       for f in kprop kpropd; do \
+       for f in kprop kpropd kproplog; do \
          $(INSTALL_PROGRAM) $$f \
                $(DESTDIR)$(SERVER_BINDIR)/`echo $$f|sed '$(transform)'`; \
          $(INSTALL_DATA) $(srcdir)/$$f.M \
@@ -52,11 +58,46 @@ $(OUTPRE)kprop.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
   kprop.c kprop.h
 $(OUTPRE)kpropd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(SRCTOP)/include/k5-err.h \
-  $(SRCTOP)/include/k5-int-pkinit.h $(SRCTOP)/include/k5-int.h \
-  $(SRCTOP)/include/k5-platform.h $(SRCTOP)/include/k5-plugin.h \
-  $(SRCTOP)/include/k5-thread.h $(SRCTOP)/include/krb5.h \
-  $(SRCTOP)/include/krb5/locate_plugin.h $(SRCTOP)/include/krb5/preauth_plugin.h \
-  $(SRCTOP)/include/port-sockets.h $(SRCTOP)/include/socket-utils.h \
-  kprop.h kpropd.c
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+  $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+  $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h $(SRCTOP)/include/gssrpc/auth_gss.h \
+  $(SRCTOP)/include/gssrpc/auth_unix.h $(SRCTOP)/include/gssrpc/clnt.h \
+  $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
+  $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
+  $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+  $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+  $(SRCTOP)/include/socket-utils.h kprop.h kpropd.c
+$(OUTPRE)kpropd_rpc.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(SRCTOP)/include/gssrpc/auth.h \
+  $(SRCTOP)/include/gssrpc/auth_gss.h $(SRCTOP)/include/gssrpc/auth_unix.h \
+  $(SRCTOP)/include/gssrpc/clnt.h $(SRCTOP)/include/gssrpc/rename.h \
+  $(SRCTOP)/include/gssrpc/rpc.h $(SRCTOP)/include/gssrpc/rpc_msg.h \
+  $(SRCTOP)/include/gssrpc/svc.h $(SRCTOP)/include/gssrpc/svc_auth.h \
+  $(SRCTOP)/include/gssrpc/xdr.h $(SRCTOP)/include/iprop.h \
+  kpropd_rpc.c
+$(OUTPRE)kproplog.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
+  $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
+  $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/chpass_util_strings.h \
+  $(BUILDTOP)/include/kadm5/kadm_err.h $(BUILDTOP)/include/krb5/krb5.h \
+  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
+  $(COM_ERR_DEPS) $(SRCTOP)/include/gssrpc/auth.h $(SRCTOP)/include/gssrpc/auth_gss.h \
+  $(SRCTOP)/include/gssrpc/auth_unix.h $(SRCTOP)/include/gssrpc/clnt.h \
+  $(SRCTOP)/include/gssrpc/rename.h $(SRCTOP)/include/gssrpc/rpc.h \
+  $(SRCTOP)/include/gssrpc/rpc_msg.h $(SRCTOP)/include/gssrpc/svc.h \
+  $(SRCTOP)/include/gssrpc/svc_auth.h $(SRCTOP)/include/gssrpc/xdr.h \
+  $(SRCTOP)/include/iprop.h $(SRCTOP)/include/iprop_hdr.h \
+  $(SRCTOP)/include/k5-err.h $(SRCTOP)/include/k5-int-pkinit.h \
+  $(SRCTOP)/include/k5-int.h $(SRCTOP)/include/k5-platform.h \
+  $(SRCTOP)/include/k5-plugin.h $(SRCTOP)/include/k5-thread.h \
+  $(SRCTOP)/include/kdb.h $(SRCTOP)/include/kdb_log.h \
+  $(SRCTOP)/include/krb5.h $(SRCTOP)/include/krb5/locate_plugin.h \
+  $(SRCTOP)/include/krb5/preauth_plugin.h $(SRCTOP)/include/port-sockets.h \
+  $(SRCTOP)/include/socket-utils.h kproplog.c
index 41791f263ffc8ed99843a3f4d1bca6c8903193d7..e2d0e76183590203826888ef8b5537b8d2dc85d8 100644 (file)
@@ -1,6 +1,6 @@
 .\" slave/kpropd.M
 .\"
-.\" Copyright 1992 by the Massachusetts Institute of Technology.
+.\" Copyright 1992, 2008 by the Massachusetts Institute of Technology.
 .\"
 .\" Export of this software from the United States of America may
 .\"   require a specific license from the United States Government.
@@ -49,10 +49,15 @@ kpropd \- Kerberos V5 slave KDC update server
 ]
 .br
 .SH DESCRIPTION
+The
 .I kpropd 
-is the server which accepts connections from the 
+command runs on the slave KDC server.  It listens for update requests
+made by the
 .IR kprop (8)
-program.  
+program, and periodically requests incremental updates from the
+master KDC.
+
+When the slave receives a kprop request from the master,
 .I kpropd 
 accepts the dumped KDC database and places it in a file, and then runs 
 .IR kdb5_util (8)
@@ -76,6 +81,25 @@ However, kpropd can also run as a standalone deamon, if the
 option is turned on.  This is done for debugging purposes, or if for
 some reason the system administrator just doesn't want to run it out of
 .IR inetd (8).
+
+When the slave periodically requests incremental updates,
+.I kpropd
+updates its
+.I principal.ulog
+file with any updates from the master.
+.IR kproplog (8)
+can be used to view a summary of the update entry log on the slave
+KDC.  Incremental propagation is not enabled by default; it can be
+enabled using the
+.I iprop_enable
+and
+.I iprop_slave_poll
+settings in
+.IR kdc.conf (5).
+The principal "kiprop/slavehostname@REALM" (where "slavehostname" is
+the name of the slave KDC host, and "REALM" is the name of the
+Kerberos realm) must be present in the slave's keytab file.
+
 .SH OPTIONS
 .TP
 \fB\-r\fP \fIrealm\fP
index d7900556101cdd68ccaf1ac373ff39a83f8e256c..fd46819a1826e74e00ea673a7c48132a50354682 100644 (file)
 #include <errno.h>
 
 #include "kprop.h"
+#include <iprop_hdr.h>
+#include "iprop.h"
+#include <kadm5/admin.h>
+#include <kdb_log.h>
 
 #ifndef GETSOCKNAME_ARG3_TYPE
 #define GETSOCKNAME_ARG3_TYPE unsigned int
@@ -95,6 +99,28 @@ extern int daemon(int, int);
 
 #define SYSLOG_CLASS LOG_DAEMON
 
+char *def_realm = NULL;
+int runonce = 0;
+
+/*
+ * This struct simulates the use of _kadm5_server_handle_t
+ *
+ * This is a COPY of kadm5_server_handle_t from
+ * lib/kadm5/clnt/client_internal.h!
+ */
+typedef struct _kadm5_iprop_handle_t {
+       krb5_ui_4       magic_number;
+       krb5_ui_4       struct_version;
+       krb5_ui_4       api_version;
+       char            *cache_name;
+       int             destroy_cache;
+       CLIENT          *clnt;
+       krb5_context    context;
+       kadm5_config_params params;
+       struct _kadm5_iprop_handle_t *lhandle;
+} *kadm5_iprop_handle_t;
+
+
 static char *kprop_version = KPROP_PROT_VERSION;
 
 char   *progname;
@@ -117,12 +143,16 @@ krb5_address      sender_addr;
 krb5_address   receiver_addr;
 short          port = 0;
 
+char **db_args = NULL;
+int db_args_size = 0;
+
 void   PRS
        (char**);
-void   do_standalone
-       (void);
+int    do_standalone
+       (iprop_role iproprole);
 void   doit
        (int);
+krb5_error_code do_iprop(kdb_log_context *log_ctx);
 void   kerberos_authenticate
        (krb5_context,
                   int,
@@ -150,6 +180,12 @@ void       send_error
 void   recv_error
        (krb5_context,
                   krb5_data *);
+unsigned int backoff_from_master(int *);
+
+static kadm5_ret_t
+kadm5_get_kiprop_host_srv_name(krb5_context context,
+                              const char *realm,
+                              char **host_service_name);
 
 static void usage()
 {
@@ -157,7 +193,7 @@ static void usage()
                "\nUsage: %s [-r realm] [-s srvtab] [-dS] [-f slave_file]\n",
                progname);
        fprintf(stderr, "\t[-F kerberos_db_file ] [-p kdb5_util_pathname]\n");
-       fprintf(stderr, "\t[-P port] [-a acl_file]\n");
+       fprintf(stderr, "\t[-x db_args]* [-P port] [-a acl_file]\n");
        exit(1);
 }
 
@@ -166,23 +202,55 @@ main(argc, argv)
        int     argc;
        char    **argv;
 {
-       PRS(argv);
+    krb5_error_code retval;
+    int ret = 0;
+    kdb_log_context *log_ctx;
+
+    PRS(argv);
+
+    log_ctx = kpropd_context->kdblog_context;
+
+    {
+#ifdef POSIX_SIGNALS
+       struct sigaction s_action;
+       memset(&s_action, 0, sizeof(s_action));
+       sigemptyset(&s_action.sa_mask);
+       s_action.sa_handler = SIG_IGN;
+       sigaction(SIGPIPE, &s_action, NULL);
+#else
+       signal(SIGPIPE, SIG_IGN);
+#endif
+    }
+
+    if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE)) {
+       /*
+        * We wanna do iprop !
+        */
+       retval = do_iprop(log_ctx);
+       if (retval) {
+           com_err(progname, retval,
+                   _("do_iprop failed.\n"));
+           exit(1);
+       }
 
+    } else {
        if (standalone)
-               do_standalone();
+           ret = do_standalone(IPROP_NULL);
        else
-               doit(0);
-       exit(0);
+           doit(0);
+    }
+
+    exit(ret);
 }
 
-void do_standalone()
+int do_standalone(iprop_role iproprole)
 {
        struct  sockaddr_in     my_sin, frominet;
        struct servent *sp;
        int     finet, s;
        GETPEERNAME_ARG3_TYPE fromlen;
        int     ret;
-       
+
        finet = socket(AF_INET, SOCK_STREAM, 0);
        if (finet < 0) {
                com_err(progname, errno, "while obtaining socket");
@@ -200,6 +268,27 @@ void do_standalone()
                my_sin.sin_port = port;
        }
        my_sin.sin_family = AF_INET;
+
+       /*
+        * We need to close the socket immediately if iprop is enabled,
+        * since back-to-back full resyncs are possible, so we do not
+        * linger around for too long
+        */
+       if (iproprole == IPROP_SLAVE) {
+           int on = 1;
+           struct linger linger;
+
+           if (setsockopt(finet, SOL_SOCKET, SO_REUSEADDR,
+                          (char *)&on, sizeof(on)) < 0)
+               com_err(progname, errno,
+                       _("in setsockopt(SO_REUSEADDR)"));
+           linger.l_onoff = 1;
+           linger.l_linger = 2;
+           if (setsockopt(finet, SOL_SOCKET, SO_LINGER,
+                          (void *)&linger, sizeof(linger)) < 0)
+               com_err(progname, errno,
+                       _("in setsockopt(SO_LINGER)"));
+       }
        if ((ret = bind(finet, (struct sockaddr *) &my_sin, sizeof(my_sin))) < 0) {
            if (debug) {
                int on = 1;
@@ -217,7 +306,7 @@ void do_standalone()
                exit(1);
            }
        }
-       if (!debug)
+       if (!debug && iproprole != IPROP_SLAVE)
                daemon(1, 0);       
 #ifdef PID_FILE
        if ((pidfile = fopen(PID_FILE, "w")) != NULL) {
@@ -233,9 +322,12 @@ void do_standalone()
        }
        while (1) {
                int child_pid;
+               int status;
            
                memset((char *)&frominet, 0, sizeof(frominet));
                fromlen = sizeof(frominet);
+               if (debug)
+                   fprintf(stderr, "waiting for a kprop connection\n");
                s = accept(finet, (struct sockaddr *) &frominet, &fromlen);
 
                if (s < 0) {
@@ -244,7 +336,7 @@ void do_standalone()
                                        "from accept system call");
                        continue;
                }
-               if (debug)
+               if (debug && iproprole != IPROP_SLAVE)
                        child_pid = 0;
                else
                        child_pid = fork();
@@ -259,11 +351,23 @@ void do_standalone()
                        close(s);
                        _exit(0);
                default:
-                       wait(0);
-                       close(s);
-                       
+                   if (wait(&status) < 0) {
+                       com_err(progname, errno,
+                               _("while waiting to receive database"));
+                       exit(1);
+                   }
+
+                   close(s);
+                   if (iproprole == IPROP_SLAVE)
+                       close(finet);
+                   if ((ret = WEXITSTATUS(status)) != 0)
+                       return (ret);
                }
+               if (iproprole == IPROP_SLAVE)
+                   break;
        }
+       return 0;
 }
 
 void doit(fd)
@@ -328,6 +432,10 @@ void doit(fd)
                                "While unparsing client name");
                        exit(1);
                }
+               if (debug)
+                   fprintf(stderr,
+                           "Rejected connection from unauthorized principal %s\n",
+                           name);
                syslog(LOG_WARNING,
                       "Rejected connection from unauthorized principal %s",
                       name);
@@ -392,6 +500,411 @@ void doit(fd)
        exit(0);
 }
 
+/*
+ * Routine to handle incremental update transfer(s) from master KDC
+ */
+kadm5_config_params params;
+krb5_error_code do_iprop(kdb_log_context *log_ctx)
+{
+       kadm5_ret_t retval;
+       krb5_ccache cc;
+       krb5_principal iprop_svc_principal;
+       void *server_handle = NULL;
+       char *iprop_svc_princstr = NULL;
+       char *master_svc_princstr = NULL;
+       char *keytab_name = NULL;
+       unsigned int pollin, backoff_time;
+       int backoff_cnt = 0;
+       int reinit_cnt = 0;
+       int ret;
+       int frdone = 0;
+
+       kdb_incr_result_t *incr_ret;
+       static kdb_last_t mylast;
+
+       kdb_fullresync_result_t *full_ret;
+       char *full_resync_arg = NULL;
+
+       kadm5_iprop_handle_t handle;
+       kdb_hlog_t *ulog;
+
+       if (!debug)
+               daemon(0, 0);
+
+       ulog = log_ctx->ulog;
+
+       pollin = params.iprop_poll_time;
+       if (pollin < 10)
+           pollin = 10;
+
+       /*
+        * Grab the realm info and check if iprop is enabled.
+        */
+       if (def_realm == NULL) {
+               retval = krb5_get_default_realm(kpropd_context, &def_realm);
+               if (retval) {
+                       com_err(progname, retval,
+                               _("Unable to get default realm"));
+                       exit(1);
+               }
+       }
+
+       params.mask |= KADM5_CONFIG_REALM;
+       params.realm = def_realm;
+
+       if (master_svc_princstr == NULL) {
+               if (retval = kadm5_get_kiprop_host_srv_name(kpropd_context,
+                                       def_realm, &master_svc_princstr)) {
+                       com_err(progname, retval,
+                               _("%s: unable to get kiprop host based "
+                                       "service name for realm %s\n"),
+                                       progname, def_realm);
+                       exit(1);
+               }
+       }
+
+       /*
+        * Set cc to the default credentials cache
+        */
+       if (retval = krb5_cc_default(kpropd_context, &cc)) {
+               com_err(progname, retval,
+                       _("while opening default "
+                               "credentials cache"));
+               exit(1);
+       }
+
+       retval = krb5_sname_to_principal(kpropd_context, NULL, KIPROP_SVC_NAME,
+                               KRB5_NT_SRV_HST, &iprop_svc_principal);
+       if (retval) {
+               com_err(progname, retval,
+                       _("while trying to construct host service principal"));
+               exit(1);
+       }
+
+       /* XXX referrals? */
+       if (krb5_is_referral_realm(krb5_princ_realm(kpropd_context,
+                                                   iprop_svc_principal))) {
+           krb5_data *r = krb5_princ_realm(kpropd_context,
+                                           iprop_svc_principal);
+           assert(def_realm != NULL);
+           r->length = strlen(def_realm);
+           r->data = strdup(def_realm);
+           if (r->data == NULL) {
+               com_err(progname, retval,
+                       _("while determining local service principal name"));
+               exit(1);
+           }
+           /* XXX Memory leak: Old r->data value.  */
+       }
+       if (retval = krb5_unparse_name(kpropd_context, iprop_svc_principal,
+                               &iprop_svc_princstr)) {
+               com_err(progname, retval,
+                       _("while canonicalizing principal name"));
+               krb5_free_principal(kpropd_context, iprop_svc_principal);
+               exit(1);
+       }
+       krb5_free_principal(kpropd_context, iprop_svc_principal);
+
+reinit:
+       /*
+        * Authentication, initialize rpcsec_gss handle etc.
+        */
+       retval = kadm5_init_with_skey(iprop_svc_princstr, keytab_name,
+                                     master_svc_princstr,
+                                     &params,
+                                     KADM5_STRUCT_VERSION,
+                                     KADM5_API_VERSION_2,
+                                     db_args,
+                                     &server_handle);
+
+       if (retval) {
+               if (retval == KADM5_RPC_ERROR) {
+                       reinit_cnt++;
+                       if (server_handle)
+                               kadm5_destroy((void *) server_handle);
+                       server_handle = (void *)NULL;
+                       handle = (kadm5_iprop_handle_t)NULL;
+
+                       com_err(progname, retval, _(
+                                       "while attempting to connect"
+                                       " to master KDC ... retrying"));
+                       backoff_time = backoff_from_master(&reinit_cnt);
+                       (void) sleep(backoff_time);
+                       goto reinit;
+               } else {
+                       if (retval == KADM5_BAD_CLIENT_PARAMS ||
+                           retval == KADM5_BAD_SERVER_PARAMS) {
+                           com_err(progname, retval,
+                                   _("while initializing %s interface"),
+                                   progname);
+
+                           usage();
+                       }
+                       reinit_cnt++;
+                       com_err(progname, retval,
+                                _("while initializing %s interface, retrying"),
+                               progname);
+                       backoff_time = backoff_from_master(&reinit_cnt);
+                       sleep(backoff_time);
+                       goto reinit;
+                }
+       }
+
+       /*
+        * Reset re-initialization count to zero now.
+        */
+       reinit_cnt = backoff_time = 0;
+
+       /*
+        * Reset the handle to the correct type for the RPC call
+        */
+       handle = server_handle;
+       
+       for (;;) {
+               incr_ret = NULL;
+               full_ret = NULL;
+
+               /*
+                * Get the most recent ulog entry sno + ts, which
+                * we package in the request to the master KDC
+                */
+               mylast.last_sno = ulog->kdb_last_sno;
+               mylast.last_time = ulog->kdb_last_time;
+
+               /*
+                * Loop continuously on an iprop_get_updates_1(),
+                * so that we can keep probing the master for updates
+                * or (if needed) do a full resync of the krb5 db.
+                */
+
+               incr_ret = iprop_get_updates_1(&mylast, handle->clnt);
+               if (incr_ret == (kdb_incr_result_t *)NULL) {
+                       clnt_perror(handle->clnt,
+                                   "iprop_get_updates call failed");
+                       if (server_handle)
+                               kadm5_destroy((void *)server_handle);
+                       server_handle = (void *)NULL;
+                       handle = (kadm5_iprop_handle_t)NULL;
+                       goto reinit;
+               }
+
+               switch (incr_ret->ret) {
+
+               case UPDATE_FULL_RESYNC_NEEDED:
+                       /*
+                        * We dont do a full resync again, if the last
+                        * X'fer was a resync and if the master sno is
+                        * still "0", i.e. no updates so far.
+                        */
+                       if ((frdone == 1) && (incr_ret->lastentry.last_sno
+                                               == 0)) {
+                               break;
+                       } else {
+
+                               full_ret = iprop_full_resync_1((void *)
+                                               &full_resync_arg, handle->clnt);
+
+                               if (full_ret == (kdb_fullresync_result_t *)
+                                                       NULL) {
+                                       clnt_perror(handle->clnt,
+                                           "iprop_full_resync call failed");
+                                       if (server_handle)
+                                               kadm5_destroy((void *)
+                                                       server_handle);
+                                       server_handle = (void *)NULL;
+                                       handle = (kadm5_iprop_handle_t)NULL;
+                                       goto reinit;
+                               }
+                       }
+
+                       switch (full_ret->ret) {
+                       case UPDATE_OK:
+                               backoff_cnt = 0;
+                               /* 
+                                * We now listen on the kprop port for
+                                * the full dump
+                                */
+                               ret = do_standalone(log_ctx->iproprole);
+                               if (ret)
+                                       syslog(LOG_WARNING,
+                                           _("kpropd: Full resync, "
+                                           "invalid return."));
+                               if (debug) {
+                                       if (ret)
+                                               fprintf(stderr,
+                                                   _("Full resync "
+                                                   "was unsuccessful\n"));
+                                       else
+                                               fprintf(stderr,
+                                                   _("Full resync "
+                                                   "was successful\n"));
+                               }
+                               frdone = 1;
+                               break;
+
+                       case UPDATE_BUSY:
+                               /*
+                                * Exponential backoff
+                                */
+                               backoff_cnt++;
+                               break;
+
+                       case UPDATE_FULL_RESYNC_NEEDED:
+                       case UPDATE_NIL:
+                       default:
+                               backoff_cnt = 0;
+                               frdone = 0;
+                               syslog(LOG_ERR, _("kpropd: Full resync,"
+                                       " invalid return from master KDC."));
+                               break;
+
+                       case UPDATE_PERM_DENIED:
+                               syslog(LOG_ERR, _("kpropd: Full resync,"
+                                       " permission denied."));
+                               goto error;
+
+                       case UPDATE_ERROR:
+                               syslog(LOG_ERR, _("kpropd: Full resync,"
+                                       " error returned from master KDC."));
+                               goto error;
+                       }
+                       break;
+
+               case UPDATE_OK:
+                       backoff_cnt = 0;
+                       frdone = 0;
+
+                       /*
+                        * ulog_replay() will convert the ulog updates to db
+                        * entries using the kdb conv api and will commit
+                        * the entries to the slave kdc database
+                        */
+                       retval = ulog_replay(kpropd_context, incr_ret,
+                                            db_args);
+
+                       if (retval) {
+                               syslog(LOG_ERR, _("kpropd: ulog_replay"
+                                       " failed, updates not registered."));
+                               break;
+                       }
+
+                       if (debug)
+                               fprintf(stderr, _("Update transfer "
+                                       "from master was OK\n"));
+                       break;
+
+               case UPDATE_PERM_DENIED:
+                       syslog(LOG_ERR, _("kpropd: get_updates,"
+                                               " permission denied."));
+                       goto error;
+
+               case UPDATE_ERROR:
+                       syslog(LOG_ERR, _("kpropd: get_updates, error "
+                                               "returned from master KDC."));
+                       goto error;
+
+               case UPDATE_BUSY:
+                       /*
+                        * Exponential backoff
+                        */
+                       backoff_cnt++;
+                       break;
+
+               case UPDATE_NIL:
+                       /*
+                        * Master-slave are in sync
+                        */
+                       if (debug)
+                               fprintf(stderr, _("Master, slave KDC's "
+                                       "are in-sync, no updates\n"));
+                       backoff_cnt = 0;
+                       frdone = 0;
+                       break;
+
+               default:
+                       backoff_cnt = 0;
+                       syslog(LOG_ERR, _("kpropd: get_updates,"
+                                       " invalid return from master KDC."));
+                       break;
+               }
+
+               if (runonce == 1)
+                       goto done;
+
+               /*
+                * Sleep for the specified poll interval (Default is 2 mts),
+                * or do a binary exponential backoff if we get an
+                * UPDATE_BUSY signal
+                */
+               if (backoff_cnt > 0) {
+                       backoff_time = backoff_from_master(&backoff_cnt);
+                       if (debug)
+                               fprintf(stderr, _("Busy signal received "
+                                       "from master, backoff for %d secs\n"),
+                                       backoff_time);
+                       (void) sleep(backoff_time);
+               }
+               else
+                       (void) sleep(pollin);
+
+       }
+
+
+error:
+       if (debug)
+               fprintf(stderr, _("ERROR returned by master, bailing\n"));
+       syslog(LOG_ERR, _("kpropd: ERROR returned by master KDC,"
+                       " bailing.\n"));
+done:
+       if(iprop_svc_princstr)
+               free(iprop_svc_princstr);
+       if (master_svc_princstr)
+               free(master_svc_princstr);
+       if (retval = krb5_cc_close(kpropd_context, cc)) {
+               com_err(progname, retval,
+                       _("while closing default ccache"));
+               exit(1);
+       }
+       if (def_realm)
+               free(def_realm);
+       if (server_handle)
+               kadm5_destroy((void *)server_handle);
+       if (kpropd_context)
+               krb5_free_context(kpropd_context);
+
+       if (runonce == 1)
+               return (0);
+       else
+               exit(1);
+}
+
+
+/*
+ * Do exponential backoff, since master KDC is BUSY or down
+ */
+unsigned int backoff_from_master(int *cnt) {
+       unsigned int btime;
+
+       btime = (unsigned int)(2<<(*cnt));
+       if (btime > MAX_BACKOFF) {
+               btime = MAX_BACKOFF;
+               *cnt--;
+       }
+
+       return (btime);
+}
+
+
+static char *
+copy_leading_substring(char *src, size_t len)
+{
+    char *result;
+    result = malloc((len + 1) * sizeof(char));
+    (void) strncpy(result, src, len+1);
+    result[len] = 0;
+    return result;
+}
+
 static void
 kpropd_com_err_proc(whoami, code, fmt, args)
        const char      *whoami;
@@ -414,7 +927,10 @@ void PRS(argv)
        register char   *word, ch;
        krb5_error_code retval;
        static const char       tmp[] = ".temp";
-       
+       kdb_log_context *log_ctx;
+
+       (void) memset((char *)&params, 0, sizeof (params));
+
        retval = krb5_init_context(&kpropd_context);
        if (retval) {
                com_err(argv[0], retval, "while initializing krb5");
@@ -496,6 +1012,38 @@ void PRS(argv)
                                             usage();
                                        word = 0;
                                        break;
+
+                               case 't':
+                                   /*
+                                    * Undocumented option - for testing only.
+                                    *
+                                    * Option to run the kpropd server exactly
+                                    * once (this is true only if iprop is enabled).
+                                    */
+                                   runonce = 1;
+                                   break;
+
+                               case 'x':
+                                   {
+                                       char **new_db_args;
+                                       new_db_args = realloc(db_args,
+                                                             (db_args_size+2)*sizeof(*db_args));
+                                       if (new_db_args == NULL) {
+                                           com_err(argv[0], errno, "copying db args");
+                                           exit(1);
+                                       }
+                                       db_args = new_db_args;
+                                       if (*word)
+                                           db_args[db_args_size] = word;
+                                       else
+                                           db_args[db_args_size] = *argv++;
+                                       word = 0;
+                                       if (db_args[db_args_size] == NULL)
+                                           usage();
+                                       db_args[db_args_size+1] = NULL;
+                                       db_args_size++;
+                                   }
+
                                default:
                                        usage();
                                }
@@ -542,6 +1090,25 @@ void PRS(argv)
        }
        strcpy(temp_file_name, file);
        strcat(temp_file_name, tmp);
+
+       retval = kadm5_get_config_params(kpropd_context, 1, &params, &params);
+       if (retval) {
+               com_err(progname, retval, _("while initializing"));
+               exit(1);
+       }
+       if (params.iprop_enabled == TRUE) {
+               ulog_set_role(kpropd_context, IPROP_SLAVE);
+
+               if (ulog_map(kpropd_context, params.iprop_logfile,
+                            params.iprop_ulogsize, FKPROPD, db_args)) { 
+                       com_err(progname, errno,
+                           _("Unable to map log!\n")); 
+                       exit(1); 
+               }
+       }
+       log_ctx = kpropd_context->kdblog_context;
+       if (log_ctx && (log_ctx->iproprole == IPROP_SLAVE))
+               ulog_set_role(kpropd_context, IPROP_SLAVE);
 }
 
 /*
@@ -939,10 +1506,13 @@ load_database(context, kdb_util, database_file_name)
        int     waitb;
 #endif
        krb5_error_code retval;
+       kdb_log_context *log_ctx;
 
        if (debug)
                printf("calling kdb5_util to load database\n");
 
+       log_ctx = context->kdblog_context;
+
        edit_av[0] = kdb_util;
        count = 1;
        if (realm) {
@@ -954,6 +1524,9 @@ load_database(context, kdb_util, database_file_name)
                edit_av[count++] = "-d";
                edit_av[count++] = kerb_database;
        }
+       if (log_ctx && log_ctx->iproprole == IPROP_SLAVE) {
+           edit_av[count++] = "-i";
+       }
        edit_av[count++] = database_file_name;
        edit_av[count++] = NULL;
 
@@ -999,3 +1572,30 @@ load_database(context, kdb_util, database_file_name)
        }
        return;
 }
+
+/*
+ * Get the host base service name for the kiprop principal. Returns
+ * KADM5_OK on success. Caller must free the storage allocated
+ * for host_service_name.
+ */
+static kadm5_ret_t
+kadm5_get_kiprop_host_srv_name(krb5_context context,
+                              const char *realm,
+                              char **host_service_name)
+{
+       kadm5_ret_t ret;
+       char *name;
+       char *host;
+
+       host = params.admin_server; /* XXX */
+
+       name = malloc(strlen(KADM5_KIPROP_HOST_SERVICE) + strlen(host) + 2);
+       if (name == NULL) {
+               free(host);
+               return (ENOMEM);
+       }
+       sprintf(name, "%s/%s", KADM5_KIPROP_HOST_SERVICE, host);
+       *host_service_name = name;
+
+       return (KADM5_OK);
+}
diff --git a/src/slave/kpropd_rpc.c b/src/slave/kpropd_rpc.c
new file mode 100644 (file)
index 0000000..e5713b2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Please do not edit this file.
+ * It was generated using rpcgen.
+ */
+
+#include <memory.h> /* for memset */
+#include "iprop.h"
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+void *
+iprop_null_1(void *argp, CLIENT *clnt)
+{
+       static char clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call (clnt, IPROP_NULL,
+               (xdrproc_t) xdr_void, (caddr_t) argp,
+               (xdrproc_t) xdr_void, (caddr_t) &clnt_res,
+               TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return ((void *)&clnt_res);
+}
+
+kdb_incr_result_t *
+iprop_get_updates_1(kdb_last_t *argp, CLIENT *clnt)
+{
+       static kdb_incr_result_t clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call (clnt, IPROP_GET_UPDATES,
+               (xdrproc_t) xdr_kdb_last_t, (caddr_t) argp,
+               (xdrproc_t) xdr_kdb_incr_result_t, (caddr_t) &clnt_res,
+               TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
+
+kdb_fullresync_result_t *
+iprop_full_resync_1(void *argp, CLIENT *clnt)
+{
+       static kdb_fullresync_result_t clnt_res;
+
+       memset((char *)&clnt_res, 0, sizeof(clnt_res));
+       if (clnt_call (clnt, IPROP_FULL_RESYNC,
+               (xdrproc_t) xdr_void, (caddr_t) argp,
+               (xdrproc_t) xdr_kdb_fullresync_result_t, (caddr_t) &clnt_res,
+               TIMEOUT) != RPC_SUCCESS) {
+               return (NULL);
+       }
+       return (&clnt_res);
+}
diff --git a/src/slave/kproplog.M b/src/slave/kproplog.M
new file mode 100644 (file)
index 0000000..b7081a9
--- /dev/null
@@ -0,0 +1,96 @@
+.\" slave/kprop.M
+.\"
+.\" Copyright 2008 by the Massachusetts Institute of Technology.
+.\"
+.\" Export of this software from the United States of America may
+.\"   require a specific license from the United States Government.
+.\"   It is the responsibility of any person or organization contemplating
+.\"   export to obtain such a license before exporting.
+.\" 
+.\" WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+.\" distribute this software and its documentation for any purpose and
+.\" without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" the name of M.I.T. not be used in advertising or publicity pertaining
+.\" to distribution of the software without specific, written prior
+.\" permission.  Furthermore if you modify this software you must label
+.\" your software as modified software and not distribute it in such a
+.\" fashion that it might be confused with the original M.I.T. software.
+.\" M.I.T. makes no representations about the suitability of
+.\" this software for any purpose.  It is provided "as is" without express
+.\" or implied warranty.
+.\" 
+.\"
+.\" Copyright (c) 2003, Sun Microsystems, Inc.  All Rights Reserved
+.\"
+.TH KPROPLOG 1
+.SH NAME
+kproplog \- display the contents of the Kerberos principal update log
+.SH SYNOPSIS
+.B kproplog
+[\fB\-h\fP] [\fB\-e\fP \fInum\fP]
+.br
+.SH DESCRIPTION
+The
+.I kproplog
+command displays the contents of the Kerberos principal update log to
+standard output.  It can be used to keep track of the incremental
+updates to the principal database, when enabled.  The update log
+file contains the update log maintained by the
+.I kadmind
+process on the master KDC server and the kpropd process on the slave
+KDC servers.  When updates occur, they are logged to this
+file.  Subsequently any KDC slave configured for incremental updates
+will request the current data from the master KDC and update their
+.I principal.ulog
+file with any updates returned.
+
+The
+.I kproplog
+command can only be run on a KDC server by someone with privileges
+comparable to the superuser.  It will display update entries for that
+server only.
+
+If no options are specified, the summary of the update log is
+displayed.  If invoked on the master, all of the update entries are
+also displayed.  When invoked on a slave KDC server, only a summary of
+the updates are displayed, which includes the serial number of the
+last update received and the associated time stamp of the last update.
+
+.SH OPTIONS
+.TP
+\fB\-h\fP
+Display a summary of the update log.  This information includes the
+database version number, state of the database, the number of updates
+in the log, the time stamp of the first and last update, and the
+version number of the first and last update entry.
+.TP
+\fB\-e\fP \fInum\fP
+Display the last
+.I num
+update entries in the log.  This is useful when debugging
+synchronization between KDC servers.
+.TP
+\fB\-v\fP
+Display individual attributes per update.
+An example of the output generated for one entry:
+.nf
+ Update Entry
+    Update serial # : 4
+    Update operation : Add
+    Update principal : test@EXAMPLE.COM
+    Update size : 424
+    Update committed : True
+    Update time stamp : Fri Feb 20 23:37:42 2004
+    Attributes changed : 6
+          Principal
+          Key data
+          Password last changed
+          Modifying principal
+          Modification time
+          TL data
+.fi
+
+.SH SEE ALSO
+kpropd(8)
diff --git a/src/slave/kproplog.c b/src/slave/kproplog.c
new file mode 100644 (file)
index 0000000..58ce70f
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/* #pragma ident       "@(#)kproplog.c 1.4     04/03/19 SMI" */
+
+/*
+ * This module will parse the update logs on the master or slave servers.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#include <limits.h>
+#include <locale.h>
+#include <syslog.h>
+#include "k5-int.h"
+#include <kdb_log.h>
+#include <kadm5/admin.h>
+
+#ifndef gettext
+#define textdomain(X) 0
+#endif
+
+static char    *progname;
+
+static void
+usage()
+{
+    (void) fprintf(stderr, _("\nUsage: %s [-h] [-v] [-e num]\n\n"),
+                  progname);
+    exit(1);
+}
+
+/*
+ * Print the individual types if verbose mode was specified.
+ */
+static void
+print_attr(kdbe_attr_type_t type)
+{
+    switch (type) {
+    case AT_ATTRFLAGS:
+       (void) printf(_("\t\tAttribute flags\n"));
+       break;
+    case AT_MAX_LIFE:
+       (void) printf(_("\t\tMaximum ticket life\n"));
+       break;
+    case AT_MAX_RENEW_LIFE:
+       (void) printf(_("\t\tMaximum renewable life\n"));
+       break;
+    case AT_EXP:
+       (void) printf(_("\t\tPrincipal expiration\n"));
+       break;
+    case AT_PW_EXP:
+       (void) printf(_("\t\tPassword expiration\n"));
+       break;
+    case AT_LAST_SUCCESS:
+       (void) printf(_("\t\tLast successful auth\n"));
+       break;
+    case AT_LAST_FAILED:
+       (void) printf(_("\t\tLast failed auth\n"));
+       break;
+    case AT_FAIL_AUTH_COUNT:
+       (void) printf(_("\t\tFailed passwd attempt\n"));
+       break;
+    case AT_PRINC:
+       (void) printf(_("\t\tPrincipal\n"));
+       break;
+    case AT_KEYDATA:
+       (void) printf(_("\t\tKey data\n"));
+       break;
+    case AT_TL_DATA:
+       (void) printf(_("\t\tTL data\n"));
+       break;
+    case AT_LEN:
+       (void) printf(_("\t\tLength\n"));
+       break;
+    case AT_MOD_PRINC:
+       (void) printf(_("\t\tModifying principal\n"));
+       break;
+    case AT_MOD_TIME:
+       (void) printf(_("\t\tModification time\n"));
+       break;
+    case AT_MOD_WHERE:
+       (void) printf(_("\t\tModified where\n"));
+       break;
+    case AT_PW_LAST_CHANGE:
+       (void) printf(_("\t\tPassword last changed\n"));
+       break;
+    case AT_PW_POLICY:
+       (void) printf(_("\t\tPassword policy\n"));
+       break;
+    case AT_PW_POLICY_SWITCH:
+       (void) printf(_("\t\tPassword policy switch\n"));
+       break;
+    case AT_PW_HIST_KVNO:
+       (void) printf(_("\t\tPassword history KVNO\n"));
+       break;
+    case AT_PW_HIST:
+       (void) printf(_("\t\tPassword history\n"));
+       break;
+    } /* switch */
+
+}
+/*
+ * Print the update entry information
+ */
+static void
+print_update(kdb_hlog_t *ulog, uint32_t entry, bool_t verbose)
+{
+    XDR                        xdrs;
+    uint32_t           start_sno, i, j, indx;
+    char               *dbprinc;
+    kdb_ent_header_t   *indx_log;
+    kdb_incr_update_t  upd;
+
+    if (entry && (entry < ulog->kdb_num))
+       start_sno = ulog->kdb_last_sno - entry;
+    else
+       start_sno = ulog->kdb_first_sno - 1;
+
+    for (i = start_sno; i < ulog->kdb_last_sno; i++) {
+       indx = i % ulog->kdb_num;
+
+       indx_log = (kdb_ent_header_t *)INDEX(ulog, indx);
+
+       /*
+        * Check for corrupt update entry
+        */
+       if (indx_log->kdb_umagic != KDB_ULOG_MAGIC) {
+           (void) fprintf(stderr,
+                          _("Corrupt update entry\n\n"));
+           exit(1);
+       }
+
+       (void) memset((char *)&upd, 0, sizeof (kdb_incr_update_t));
+       xdrmem_create(&xdrs, (char *)indx_log->entry_data,
+                     indx_log->kdb_entry_size, XDR_DECODE);
+       if (!xdr_kdb_incr_update_t(&xdrs, &upd)) {
+           (void) printf(_("Entry data decode failure\n\n"));
+           exit(1);
+       }
+
+       (void) printf("---\n");
+       (void) printf(_("Update Entry\n"));
+
+       (void) printf(_("\tUpdate serial # : %u\n"),
+                     indx_log->kdb_entry_sno);
+
+       (void) printf(_("\tUpdate operation : "));
+       if (upd.kdb_deleted)
+           (void) printf(_("Delete\n"));
+       else
+           (void) printf(_("Add\n"));
+
+       dbprinc = malloc(upd.kdb_princ_name.utf8str_t_len + 1);
+       if (dbprinc == NULL) {
+           (void) printf(_("Could not allocate "
+                           "principal name\n\n"));
+           exit(1);
+       }
+       (void) strncpy(dbprinc, upd.kdb_princ_name.utf8str_t_val,
+                      (upd.kdb_princ_name.utf8str_t_len + 1));
+       dbprinc[upd.kdb_princ_name.utf8str_t_len] = 0;
+       (void) printf(_("\tUpdate principal : %s\n"), dbprinc);
+
+       (void) printf(_("\tUpdate size : %u\n"),
+                     indx_log->kdb_entry_size);
+
+       (void) printf(_("\tUpdate committed : %s\n"),
+                     indx_log->kdb_commit ? "True" : "False");
+
+       if (indx_log->kdb_time.seconds == 0L)
+           (void) printf(_("\tUpdate time stamp : None\n"));
+       else
+           (void) printf(_("\tUpdate time stamp : %s"),
+                         ctime((time_t *)&(indx_log->kdb_time.seconds)));
+
+       (void) printf(_("\tAttributes changed : %d\n"),
+                     upd.kdb_update.kdbe_t_len);
+
+       if (verbose)
+           for (j = 0; j < upd.kdb_update.kdbe_t_len; j++)
+               print_attr(
+                   upd.kdb_update.kdbe_t_val[j].av_type);
+
+       xdr_free(xdr_kdb_incr_update_t, (char *)&upd);
+       free(dbprinc);
+    } /* for */
+}
+
+int
+main(int argc, char **argv)
+{
+    int                        c;
+    bool_t             verbose = FALSE;
+    bool_t             headeronly = FALSE;
+    uint32_t           entry = 0;
+    krb5_context       context;
+    kadm5_config_params        params;
+    kdb_log_context    *log_ctx;
+    kdb_hlog_t         *ulog = NULL;
+    char               **db_args = NULL; /* XXX */
+
+    (void) setlocale(LC_ALL, "");
+
+#if !defined(TEXT_DOMAIN)
+#define        TEXT_DOMAIN "SYS_TEST"
+#endif /* TEXT_DOMAIN */
+
+    (void) textdomain(TEXT_DOMAIN);
+
+    progname = argv[0];
+
+    while ((c = getopt(argc, argv, "vhe:")) != -1) {
+       switch (c) {
+       case 'h':
+           headeronly = TRUE;
+           break;
+       case 'e':
+           entry = atoi(optarg);
+           break;
+       case 'v':
+           verbose = TRUE;
+           break;
+       default:
+           usage();
+       }
+    }
+
+    if (krb5_init_context(&context)) {
+       (void) fprintf(stderr,
+                      _("Unable to initialize Kerberos\n\n"));
+       exit(1);
+    }
+
+    (void) memset((char *)&params, 0, sizeof (params));
+
+    if (kadm5_get_config_params(context, 1, &params, &params)) {
+       (void) fprintf(stderr,
+                      _("Couldn't read database_name\n\n"));
+       exit(1);
+    }
+
+    (void) printf(_("\nKerberos update log (%s.ulog)\n"),
+                 params.dbname);
+
+    if (ulog_map(context, params.iprop_logfile, 0, FKPROPLOG, db_args)) {
+       (void) fprintf(stderr, _("Unable to map log file "
+                                "%s.ulog\n\n"), params.dbname);
+       exit(1);
+    }
+
+    log_ctx = context->kdblog_context;
+    if (log_ctx)
+       ulog = log_ctx->ulog;
+    else {
+       (void) fprintf(stderr, _("Unable to map log file "
+                                "%s.ulog\n\n"), params.dbname);
+       exit(1);
+    }
+
+    if (ulog->kdb_hmagic != KDB_ULOG_HDR_MAGIC) {
+       (void) fprintf(stderr,
+                      _("Corrupt header log, exiting\n\n"));
+       exit(1);
+    }
+
+    (void) printf(_("Update log dump :\n"));
+    (void) printf(_("\tLog version # : %u\n"), ulog->db_version_num);
+    (void) printf(_("\tLog state : "));
+    switch (ulog->kdb_state) {
+    case KDB_STABLE:
+       (void) printf(_("Stable\n"));
+       break;
+    case KDB_UNSTABLE:
+       (void) printf(_("Unstable\n"));
+       break;
+    case KDB_CORRUPT:
+       (void) printf(_("Corrupt\n"));
+       break;
+    default:
+       (void) printf(_("Unknown state: %d\n"),
+                     ulog->kdb_state);
+       break;
+    }
+    (void) printf(_("\tEntry block size : %u\n"), ulog->kdb_block);
+    (void) printf(_("\tNumber of entries : %u\n"), ulog->kdb_num);
+
+    if (ulog->kdb_last_sno == 0)
+       (void) printf(_("\tLast serial # : None\n"));
+    else {
+       if (ulog->kdb_first_sno == 0)
+           (void) printf(_("\tFirst serial # : None\n"));
+       else {
+           (void) printf(_("\tFirst serial # : "));
+           (void) printf("%u\n", ulog->kdb_first_sno);
+       }
+
+       (void) printf(_("\tLast serial # : "));
+       (void) printf("%u\n", ulog->kdb_last_sno);
+    }
+
+    if (ulog->kdb_last_time.seconds == 0L) {
+       (void) printf(_("\tLast time stamp : None\n"));
+    } else {
+       if (ulog->kdb_first_time.seconds == 0L)
+           (void) printf(_("\tFirst time stamp : None\n"));
+       else {
+           (void) printf(_("\tFirst time stamp : %s"),
+                         ctime((time_t *)
+                               &(ulog->kdb_first_time.seconds)));
+       }
+
+       (void) printf(_("\tLast time stamp : %s\n"),
+                     ctime((time_t *)&(ulog->kdb_last_time.seconds)));
+    }
+
+    if ((!headeronly) && ulog->kdb_num) {
+       print_update(ulog, entry, verbose);
+    }
+
+    (void) printf("\n");
+
+    return (0);
+}