From 5661d1290f74312a405db970aea097da77706f71 Mon Sep 17 00:00:00 2001 From: Ken Raeburn Date: Tue, 24 Jun 2008 05:04:29 +0000 Subject: [PATCH] Merge from branch sun-iprop git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@20465 dc483132-0cff-0310-8789-dd5450dbe970 --- README | 15 +- doc/copyright.texinfo | 17 +- doc/install.texinfo | 130 ++- doc/iprop-notes.txt | 120 +++ src/config-files/kdc.conf.M | 20 +- src/include/iprop.h | 269 ++++++ src/include/iprop_hdr.h | 42 + src/include/k5-int.h | 4 + src/include/k5-platform.h | 7 +- src/include/kdb_log.h | 124 +++ src/include/osconf.hin | 3 +- src/kadmin/cli/kadmin.M | 14 + src/kadmin/cli/kadmin.c | 13 +- src/kadmin/dbutil/Makefile.in | 42 +- src/kadmin/dbutil/dump.c | 193 ++++- src/kadmin/dbutil/kadm5_create.c | 1 + src/kadmin/dbutil/kdb5_create.c | 39 +- src/kadmin/dbutil/kdb5_destroy.c | 6 +- src/kadmin/dbutil/kdb5_util.c | 7 +- src/kadmin/dbutil/kdb5_util.h | 5 +- src/kadmin/server/Makefile.in | 35 +- src/kadmin/server/ipropd_svc.c | 615 ++++++++++++++ src/kadmin/server/kadm_rpc_svc.c | 9 +- src/kadmin/server/kadmind.M | 19 + src/kadmin/server/misc.h | 4 + src/kadmin/server/ovsec_kadmd.c | 269 +++++- src/kadmin/server/server_stubs.c | 5 +- src/lib/kadm5/Makefile.in | 4 +- src/lib/kadm5/admin.h | 61 +- src/lib/kadm5/alt_prof.c | 63 +- src/lib/kadm5/clnt/Makefile.in | 7 +- src/lib/kadm5/clnt/client_init.c | 52 +- src/lib/kadm5/clnt/libkadm5clnt.exports | 1 + src/lib/kadm5/srv/Makefile.in | 8 +- src/lib/kadm5/srv/libkadm5srv.exports | 1 + src/lib/kadm5/srv/server_acl.c | 3 +- src/lib/kadm5/srv/server_acl.h | 4 +- src/lib/kadm5/srv/server_init.c | 38 + src/lib/kdb/Makefile.in | 66 +- src/lib/kdb/iprop.x | 223 +++++ src/lib/kdb/iprop_xdr.c | 351 ++++++++ src/lib/kdb/kdb5.c | 234 +++++- src/lib/kdb/kdb_convert.c | 1017 +++++++++++++++++++++++ src/lib/kdb/kdb_log.c | 928 +++++++++++++++++++++ src/lib/kdb/libkdb5.exports | 9 + src/lib/krb5/error_tables/kdb5_err.et | 7 +- src/slave/Makefile.in | 71 +- src/slave/kpropd.M | 30 +- src/slave/kpropd.c | 630 +++++++++++++- src/slave/kpropd_rpc.c | 55 ++ src/slave/kproplog.M | 96 +++ src/slave/kproplog.c | 327 ++++++++ 52 files changed, 6122 insertions(+), 191 deletions(-) create mode 100644 doc/iprop-notes.txt create mode 100644 src/include/iprop.h create mode 100644 src/include/iprop_hdr.h create mode 100644 src/include/kdb_log.h create mode 100644 src/kadmin/server/ipropd_svc.c create mode 100644 src/lib/kdb/iprop.x create mode 100644 src/lib/kdb/iprop_xdr.c create mode 100644 src/lib/kdb/kdb_convert.c create mode 100644 src/lib/kdb/kdb_log.c create mode 100644 src/slave/kpropd_rpc.c create mode 100644 src/slave/kproplog.M create mode 100644 src/slave/kproplog.c diff --git a/README b/README index 7dcc5b23b..6955f1482 100644 --- 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, diff --git a/doc/copyright.texinfo b/doc/copyright.texinfo index 5d239b561..cc6b577fc 100644 --- a/doc/copyright.texinfo +++ b/doc/copyright.texinfo @@ -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 diff --git a/doc/install.texinfo b/doc/install.texinfo index 64cdac70e..54f183d43 100644 --- a/doc/install.texinfo +++ b/doc/install.texinfo @@ -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 index 000000000..2b1ee43c2 --- /dev/null +++ b/doc/iprop-notes.txt @@ -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 "" 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. diff --git a/src/config-files/kdc.conf.M b/src/config-files/kdc.conf.M index f19c6e008..21f173bbe 100644 --- a/src/config-files/kdc.conf.M +++ b/src/config-files/kdc.conf.M @@ -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 index 000000000..208f4dce0 --- /dev/null +++ b/src/include/iprop.h @@ -0,0 +1,269 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#ifndef _IPROP_H_RPCGEN +#define _IPROP_H_RPCGEN + +#include + + +#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 index 000000000..d629542bb --- /dev/null +++ b/src/include/iprop_hdr.h @@ -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 */ diff --git a/src/include/k5-int.h b/src/include/k5-int.h index 5e1fd113a..3763399d9 100644 --- a/src/include/k5-int.h +++ b/src/include/k5-int.h @@ -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 */ diff --git a/src/include/k5-platform.h b/src/include/k5-platform.h index c9bcb8a52..2a4463a20 100644 --- a/src/include/k5-platform.h +++ b/src/include/k5-platform.h @@ -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 index 000000000..804b7328a --- /dev/null +++ b/src/include/kdb_log.h @@ -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 +#include +#include +#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 */ diff --git a/src/include/osconf.hin b/src/include/osconf.hin index 48bf8b1f9..11e721b37 100644 --- a/src/include/osconf.hin +++ b/src/include/osconf.hin @@ -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 @@ -133,6 +133,7 @@ #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" diff --git a/src/kadmin/cli/kadmin.M b/src/kadmin/cli/kadmin.M index 76dee5c48..165bf0c73 100644 --- a/src/kadmin/cli/kadmin.M +++ b/src/kadmin/cli/kadmin.M @@ -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 diff --git a/src/kadmin/cli/kadmin.c b/src/kadmin/cli/kadmin.c index 98ff99500..02394e7f0 100644 --- a/src/kadmin/cli/kadmin.c +++ b/src/kadmin/cli/kadmin.c @@ -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 @@ -24,7 +24,13 @@ * 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 #include #include @@ -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; } diff --git a/src/kadmin/dbutil/Makefile.in b/src/kadmin/dbutil/Makefile.in index a7fa1778e..5d480df4c 100644 --- a/src/kadmin/dbutil/Makefile.in +++ b/src/kadmin/dbutil/Makefile.in @@ -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 diff --git a/src/kadmin/dbutil/dump.c b/src/kadmin/dbutil/dump.c index 44675a6b2..8fcb56e5a 100644 --- a/src/kadmin/dbutil/dump.c +++ b/src/kadmin/dbutil/dump.c @@ -26,6 +26,10 @@ * * Dump a KDC database */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ #include #include @@ -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, diff --git a/src/kadmin/dbutil/kadm5_create.c b/src/kadmin/dbutil/kadm5_create.c index 3f6f6557f..c02b40287 100644 --- a/src/kadmin/dbutil/kadm5_create.c +++ b/src/kadmin/dbutil/kadm5_create.c @@ -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; } diff --git a/src/kadmin/dbutil/kdb5_create.c b/src/kadmin/dbutil/kdb5_create.c index da192256e..b8959e102 100644 --- a/src/kadmin/dbutil/kdb5_create.c +++ b/src/kadmin/dbutil/kdb5_create.c @@ -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 @@ -52,6 +52,10 @@ * 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 #include @@ -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); diff --git a/src/kadmin/dbutil/kdb5_destroy.c b/src/kadmin/dbutil/kdb5_destroy.c index 22b75eef6..160268f24 100644 --- a/src/kadmin/dbutil/kdb5_destroy.c +++ b/src/kadmin/dbutil/kdb5_destroy.c @@ -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; diff --git a/src/kadmin/dbutil/kdb5_util.c b/src/kadmin/dbutil/kdb5_util.c index 1807d1ad0..1b2aa60c5 100644 --- a/src/kadmin/dbutil/kdb5_util.c +++ b/src/kadmin/dbutil/kdb5_util.c @@ -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 ) diff --git a/src/kadmin/dbutil/kdb5_util.h b/src/kadmin/dbutil/kdb5_util.h index 90b7b43ef..69dfba986 100644 --- a/src/kadmin/dbutil/kdb5_util.h +++ b/src/kadmin/dbutil/kdb5_util.h @@ -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 + +#define MAX_HEADER 1024 #define REALM_SEP '@' #define REALM_SEP_STR "@" diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in index bc3052b25..63b4941b4 100644 --- a/src/kadmin/server/Makefile.in +++ b/src/kadmin/server/Makefile.in @@ -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 index 000000000..673d2a9af --- /dev/null +++ b/src/kadmin/server/ipropd_svc.c @@ -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 +#include /* getenv, exit */ +#include +#include +#include /* rlimit */ +#include + +#include "k5-platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 = ""; + +#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, + "", 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, + "", 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, + "", + "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 diff --git a/src/kadmin/server/kadm_rpc_svc.c b/src/kadmin/server/kadm_rpc_svc.c index 733602052..c56eedd0b 100644 --- a/src/kadmin/server/kadm_rpc_svc.c +++ b/src/kadmin/server/kadm_rpc_svc.c @@ -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; diff --git a/src/kadmin/server/kadmind.M b/src/kadmin/server/kadmind.M index ad810e6f2..2a227fb4e 100644 --- a/src/kadmin/server/kadmind.M +++ b/src/kadmin/server/kadmind.M @@ -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 . diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h index a020874fd..74d703c4a 100644 --- a/src/kadmin/server/misc.h +++ b/src/kadmin/server/misc.h @@ -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); diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c index 659ebfba3..88a60e026 100644 --- a/src/kadmin/server/ovsec_kadmd.c +++ b/src/kadmin/server/ovsec_kadmd.c @@ -53,6 +53,7 @@ #include "kdb_kt.h" /* for krb5_ktkdb_set_context */ #include #include "kadm5/server_internal.h" /* XXX for kadm5_server_handle_t */ +#include #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, ¶ms, ¶ms))) { 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 : ""); + + fprintf(stderr, + _("%s: Unable to set RPCSEC_GSS service name (`%s'), failing.\n"), + whoami, + kiprop_name ? kiprop_name : ""); + + 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(¶ms); 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 */ } diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c index 5e50971f1..cf93e8667 100644 --- a/src/kadmin/server/server_stubs.c +++ b/src/kadmin/server/server_stubs.c @@ -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) { diff --git a/src/lib/kadm5/Makefile.in b/src/lib/kadm5/Makefile.in index bcb46cede..8cc931aca 100644 --- a/src/lib/kadm5/Makefile.in +++ b/src/lib/kadm5/Makefile.in @@ -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): \ diff --git a/src/lib/kadm5/admin.h b/src/lib/kadm5/admin.h index 06a77dce6..9013305b6 100644 --- a/src/lib/kadm5/admin.h +++ b/src/lib/kadm5/admin.h @@ -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; /*********************************************************************** diff --git a/src/lib/kadm5/alt_prof.c b/src/lib/kadm5/alt_prof.c index c56ceac2f..c8bc6b052 100644 --- a/src/lib/kadm5/alt_prof.c +++ b/src/lib/kadm5/alt_prof.c @@ -24,6 +24,10 @@ * 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 #include +#include 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(¶ms.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: diff --git a/src/lib/kadm5/clnt/Makefile.in b/src/lib/kadm5/clnt/Makefile.in index 8c0bce105..69679a9c8 100644 --- a/src/lib/kadm5/clnt/Makefile.in +++ b/src/lib/kadm5/clnt/Makefile.in @@ -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 \ diff --git a/src/lib/kadm5/clnt/client_init.c b/src/lib/kadm5/clnt/client_init.c index 8fc3dc1e2..d5131cfda 100644 --- a/src/lib/kadm5/clnt/client_init.c +++ b/src/lib/kadm5/clnt/client_init.c @@ -48,6 +48,8 @@ #include #include #include "client_internal.h" +#include +#include "iprop.h" #include #include @@ -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); +} diff --git a/src/lib/kadm5/clnt/libkadm5clnt.exports b/src/lib/kadm5/clnt/libkadm5clnt.exports index f7f873e29..7f11f320a 100644 --- a/src/lib/kadm5/clnt/libkadm5clnt.exports +++ b/src/lib/kadm5/clnt/libkadm5clnt.exports @@ -129,3 +129,4 @@ xdr_setkey3_arg xdr_setkey_arg xdr_setv4key_arg xdr_ui_4 +kadm5_init_iprop diff --git a/src/lib/kadm5/srv/Makefile.in b/src/lib/kadm5/srv/Makefile.in index 21628bd7e..9889c1e22 100644 --- a/src/lib/kadm5/srv/Makefile.in +++ b/src/lib/kadm5/srv/Makefile.in @@ -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 \ diff --git a/src/lib/kadm5/srv/libkadm5srv.exports b/src/lib/kadm5/srv/libkadm5srv.exports index 23a4ee1e5..a4d2156f7 100644 --- a/src/lib/kadm5/srv/libkadm5srv.exports +++ b/src/lib/kadm5/srv/libkadm5srv.exports @@ -161,3 +161,4 @@ xdr_setkey3_arg xdr_setkey_arg xdr_setv4key_arg xdr_ui_4 +kadm5_init_iprop diff --git a/src/lib/kadm5/srv/server_acl.c b/src/lib/kadm5/srv/server_acl.c index bcfe35f84..6cc9492c3 100644 --- a/src/lib/kadm5/srv/server_acl.c +++ b/src/lib/kadm5/srv/server_acl.c @@ -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 }, diff --git a/src/lib/kadm5/srv/server_acl.h b/src/lib/kadm5/srv/server_acl.h index 5024730cf..b0ed0bf3d 100644 --- a/src/lib/kadm5/srv/server_acl.h +++ b/src/lib/kadm5/srv/server_acl.h @@ -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 { diff --git a/src/lib/kadm5/srv/server_init.c b/src/lib/kadm5/srv/server_init.c index dbb7ff78e..febf79bdf 100644 --- a/src/lib/kadm5/srv/server_init.c +++ b/src/lib/kadm5/srv/server_init.c @@ -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 #include +#include #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); +} diff --git a/src/lib/kdb/Makefile.in b/src/lib/kdb/Makefile.in index 65ee8e770..9935a450a 100644 --- a/src/lib/kdb/Makefile.in +++ b/src/lib/kdb/Makefile.in @@ -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 index 000000000..840e7a2e5 --- /dev/null +++ b/src/lib/kdb/iprop.x @@ -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 index 000000000..a8b7685ff --- /dev/null +++ b/src/lib/kdb/iprop_xdr.c @@ -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; +} diff --git a/src/lib/kdb/kdb5.c b/src/lib/kdb/kdb5.c index bdade9904..d782d0f41 100644 --- a/src/lib/kdb/kdb5.c +++ b/src/lib/kdb/kdb5.c @@ -37,6 +37,7 @@ #include #include "kdb5.h" #include +#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 index 000000000..61b2b9747 --- /dev/null +++ b/src/lib/kdb/kdb_convert.c @@ -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 +#include +#include +#include +#include +#include "iprop.h" +#include +#include + +/* 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 index 000000000..c4efc41e0 --- /dev/null +++ b/src/lib/kdb/kdb_log.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#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(×tamp, 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(×tamp, 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; +} diff --git a/src/lib/kdb/libkdb5.exports b/src/lib/kdb/libkdb5.exports index dd9f95288..6157ec357 100644 --- a/src/lib/kdb/libkdb5.exports +++ b/src/lib/kdb/libkdb5.exports @@ -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 diff --git a/src/lib/krb5/error_tables/kdb5_err.et b/src/lib/krb5/error_tables/kdb5_err.et index d6014acec..b15af8c87 100644 --- a/src/lib/krb5/error_tables/kdb5_err.et +++ b/src/lib/krb5/error_tables/kdb5_err.et @@ -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 diff --git a/src/slave/Makefile.in b/src/slave/Makefile.in index 5e4e9cbdd..2437f8438 100644 --- a/src/slave/Makefile.in +++ b/src/slave/Makefile.in @@ -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 diff --git a/src/slave/kpropd.M b/src/slave/kpropd.M index 41791f263..e2d0e7618 100644 --- a/src/slave/kpropd.M +++ b/src/slave/kpropd.M @@ -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 diff --git a/src/slave/kpropd.c b/src/slave/kpropd.c index d79005561..fd46819a1 100644 --- a/src/slave/kpropd.c +++ b/src/slave/kpropd.c @@ -81,6 +81,10 @@ #include #include "kprop.h" +#include +#include "iprop.h" +#include +#include #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, + ¶ms, + 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 *)¶ms, 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, ¶ms, ¶ms); + 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 index 000000000..e5713b2e1 --- /dev/null +++ b/src/slave/kpropd_rpc.c @@ -0,0 +1,55 @@ +/* + * Please do not edit this file. + * It was generated using rpcgen. + */ + +#include /* 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 index 000000000..b7081a956 --- /dev/null +++ b/src/slave/kproplog.M @@ -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 index 000000000..58ce70ffe --- /dev/null +++ b/src/slave/kproplog.c @@ -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 +#include +#include +#include +#include +#include +#include "k5-int.h" +#include +#include + +#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 *)¶ms, 0, sizeof (params)); + + if (kadm5_get_config_params(context, 1, ¶ms, ¶ms)) { + (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); +} -- 2.26.2