summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/kadm5
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/kadm5')
-rw-r--r--third_party/heimdal/lib/kadm5/ChangeLog1389
-rw-r--r--third_party/heimdal/lib/kadm5/Makefile.am233
-rw-r--r--third_party/heimdal/lib/kadm5/NTMakefile296
-rw-r--r--third_party/heimdal/lib/kadm5/acl.c242
-rw-r--r--third_party/heimdal/lib/kadm5/ad.c1525
-rw-r--r--third_party/heimdal/lib/kadm5/admin.h272
-rw-r--r--third_party/heimdal/lib/kadm5/bump_pw_expire.c64
-rw-r--r--third_party/heimdal/lib/kadm5/check-cracklib.pl112
-rw-r--r--third_party/heimdal/lib/kadm5/chpass_c.c175
-rw-r--r--third_party/heimdal/lib/kadm5/chpass_s.c466
-rw-r--r--third_party/heimdal/lib/kadm5/client_glue.c150
-rw-r--r--third_party/heimdal/lib/kadm5/common_glue.c452
-rw-r--r--third_party/heimdal/lib/kadm5/context_s.c316
-rw-r--r--third_party/heimdal/lib/kadm5/create_c.c114
-rw-r--r--third_party/heimdal/lib/kadm5/create_s.c363
-rw-r--r--third_party/heimdal/lib/kadm5/default_keys.c121
-rw-r--r--third_party/heimdal/lib/kadm5/delete_c.c88
-rw-r--r--third_party/heimdal/lib/kadm5/delete_s.c145
-rw-r--r--third_party/heimdal/lib/kadm5/destroy_c.c58
-rw-r--r--third_party/heimdal/lib/kadm5/destroy_s.c95
-rw-r--r--third_party/heimdal/lib/kadm5/ent_setup.c276
-rw-r--r--third_party/heimdal/lib/kadm5/error.c48
-rw-r--r--third_party/heimdal/lib/kadm5/flush.c48
-rw-r--r--third_party/heimdal/lib/kadm5/flush_c.c41
-rw-r--r--third_party/heimdal/lib/kadm5/flush_s.c41
-rw-r--r--third_party/heimdal/lib/kadm5/free.c92
-rw-r--r--third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.binbin0 -> 191 bytes
-rw-r--r--third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.binbin0 -> 139 bytes
-rw-r--r--third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.binbin0 -> 65 bytes
-rw-r--r--third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.binbin0 -> 46 bytes
-rw-r--r--third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt101
-rw-r--r--third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt54
-rw-r--r--third_party/heimdal/lib/kadm5/get_c.c96
-rw-r--r--third_party/heimdal/lib/kadm5/get_princs_c.c299
-rw-r--r--third_party/heimdal/lib/kadm5/get_princs_s.c207
-rw-r--r--third_party/heimdal/lib/kadm5/get_s.c412
-rw-r--r--third_party/heimdal/lib/kadm5/init_c.c920
-rw-r--r--third_party/heimdal/lib/kadm5/init_s.c280
-rw-r--r--third_party/heimdal/lib/kadm5/iprop-commands.in187
-rw-r--r--third_party/heimdal/lib/kadm5/iprop-log-version.rc36
-rw-r--r--third_party/heimdal/lib/kadm5/iprop-log.8254
-rw-r--r--third_party/heimdal/lib/kadm5/iprop-log.c653
-rw-r--r--third_party/heimdal/lib/kadm5/iprop.8246
-rw-r--r--third_party/heimdal/lib/kadm5/iprop.h80
-rw-r--r--third_party/heimdal/lib/kadm5/ipropd-master-version.rc36
-rw-r--r--third_party/heimdal/lib/kadm5/ipropd-slave-version.rc36
-rw-r--r--third_party/heimdal/lib/kadm5/ipropd_common.c266
-rw-r--r--third_party/heimdal/lib/kadm5/ipropd_master.c1890
-rw-r--r--third_party/heimdal/lib/kadm5/ipropd_slave.c1186
-rw-r--r--third_party/heimdal/lib/kadm5/kadm5-hook.h151
-rw-r--r--third_party/heimdal/lib/kadm5/kadm5-pwcheck.h73
-rw-r--r--third_party/heimdal/lib/kadm5/kadm5_err.et114
-rw-r--r--third_party/heimdal/lib/kadm5/kadm5_locl.h91
-rw-r--r--third_party/heimdal/lib/kadm5/kadm5_pwcheck.3159
-rw-r--r--third_party/heimdal/lib/kadm5/keys.c132
-rw-r--r--third_party/heimdal/lib/kadm5/libkadm5srv-exports.def93
-rw-r--r--third_party/heimdal/lib/kadm5/libkadm5srv-version.rc36
-rw-r--r--third_party/heimdal/lib/kadm5/log.c2756
-rw-r--r--third_party/heimdal/lib/kadm5/marshall.c962
-rw-r--r--third_party/heimdal/lib/kadm5/modify_c.c93
-rw-r--r--third_party/heimdal/lib/kadm5/modify_s.c215
-rw-r--r--third_party/heimdal/lib/kadm5/password_quality.c508
-rw-r--r--third_party/heimdal/lib/kadm5/private.h210
-rw-r--r--third_party/heimdal/lib/kadm5/privs_c.c89
-rw-r--r--third_party/heimdal/lib/kadm5/privs_s.c44
-rw-r--r--third_party/heimdal/lib/kadm5/prune_c.c73
-rw-r--r--third_party/heimdal/lib/kadm5/prune_s.c149
-rw-r--r--third_party/heimdal/lib/kadm5/randkey_c.c154
-rw-r--r--third_party/heimdal/lib/kadm5/randkey_s.c204
-rw-r--r--third_party/heimdal/lib/kadm5/rename_c.c94
-rw-r--r--third_party/heimdal/lib/kadm5/rename_s.c188
-rw-r--r--third_party/heimdal/lib/kadm5/sample_hook.c286
-rw-r--r--third_party/heimdal/lib/kadm5/sample_passwd_check.c87
-rw-r--r--third_party/heimdal/lib/kadm5/send_recv.c99
-rw-r--r--third_party/heimdal/lib/kadm5/server_glue.c150
-rw-r--r--third_party/heimdal/lib/kadm5/server_hooks.c97
-rw-r--r--third_party/heimdal/lib/kadm5/set_keys.c420
-rw-r--r--third_party/heimdal/lib/kadm5/set_modifier.c54
-rw-r--r--third_party/heimdal/lib/kadm5/setkey3_s.c220
-rw-r--r--third_party/heimdal/lib/kadm5/test_pw_quality.c99
-rw-r--r--third_party/heimdal/lib/kadm5/version-script-client.map69
-rw-r--r--third_party/heimdal/lib/kadm5/version-script.map98
82 files changed, 22728 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/kadm5/ChangeLog b/third_party/heimdal/lib/kadm5/ChangeLog
new file mode 100644
index 0000000..5016827
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ChangeLog
@@ -0,0 +1,1389 @@
+2008-04-23 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c: Only log "sending AYT" once, pointed out by Dr
+ A V Le Blanc.
+
+
+2008-01-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * default_keys.c: Use hdb_free_keys().
+
+2008-01-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: add check-cracklib.pl, flush.c,
+ sample_passwd_check.c
+
+2007-12-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * use hdb_db_dir() and hdb_default_db()
+
+2007-10-18 Love <lha@stacken.kth.se>
+
+ * init_c.c: We are getting default_client, not client. this way
+ the user can override the result.
+
+2007-09-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop.8: fix spelling, From Antoine Jacoutt.
+
+2007-08-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * version-script.map: export _kadm5_unmarshal_params,
+ _kadm5_acl_check_permission
+
+ * version-script.map: export kadm5_log_ symbols.
+
+ * log.c: Unexport the specific log replay operations.
+
+2007-08-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: build sample_passwd_check.la as part of noinst.
+
+ * sample_passwd_check.c: Add missing prototype for check_length().
+
+2007-08-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * log.c: Sprinkle krb5_set_error_string().
+
+ * ipropd_slave.c: Provide better error why kadm5_log_replay
+ failed.
+
+2007-08-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c: - don't push whole database to the new client
+ every time. - make slaves get the whole new database if they have
+ a newer log the the master (and thus have them go back in time).
+
+2007-08-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c: make more sane.
+
+ * ipropd_slave.c: more paranoid check that the log entires are
+ self consistant
+
+ * log.c (kadm5_log_foreach): check that the postamble contains the
+ right data.
+
+ * ipropd_master.c: Sprinkle more info about what versions the
+ master thinks about the client versions.
+
+ * ipropd_master.c: Start the server at the current version, not 0.
+
+2007-08-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c: Add more logging, to figure out what is
+ happening in the master.
+
+2007-08-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: add version-script for libkadm5srv.la
+
+ * version-script.map: version script fro kadm5 server libary.
+
+ * log.c: only free the orignal entries extentions if there was
+ any. Bug reported by Peter Meinecke.
+
+ * add configuration for signal file and acl file, let user select
+ hostname, catch signals and print why we are quiting, make nop
+ cause one new version, not two
+
+2007-07-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c (send_diffs): make current slave's version
+ uptodate when diff have been sent.
+
+2007-07-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c: More comments and some more error checking.
+
+2007-07-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c (get_cache_principal): make sure id is reset if we
+ fail. From Benjamin Bennet.
+
+2007-07-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * context_s.c (find_db_spec): match realm-less as the default
+ realm.
+
+ * Makefile.am: New library version.
+
+2007-07-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * context_s.c: Use hdb_get_dbinfo to pick up configuration.
+ ctx->config.realm can be NULL, check for that, from Bjorn S.
+
+2007-07-04 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c: Try harder to use the right principal.
+
+2007-06-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c: Catch return value from krb5_program_setup. From
+ Steven Luo.
+
+2007-05-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * delete_s.c: Write log entry after store is successful, rename
+ out goto statments.
+
+ * randkey_s.c: Write log entry after store is successful.
+
+ * modify_s.c: Write log entry after store is successful.
+
+ * rename_s.c: indent.
+
+ * chpass_s.c: Write log entry after store is successful.
+
+ * create_s.c: Write log entry after store is successful.
+
+2007-05-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop-commands.in: Add default values to make this working
+ again.
+
+ * iprop-log.c (iprop_replay): create the database with more
+ liberal mode.
+
+ * log.c: make it slightly more working.
+
+ * iprop-log.8: Document last-version.
+
+ * iprop-log.c: (last_version): print last version of the log.
+
+ * iprop-commands.in: new command last-version: print last version
+ of the log.
+
+ * log.c (kadm5_log_previous): document assumptions and make less
+ broken. Bug report from Ronny Blomme.
+
+2007-02-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * admin.h: add support to get aliases
+
+ * get_s.c: add support to get aliases
+
+2007-02-11 David Love <fx@gnu.org>
+
+ * iprop-log.8: Small fixes, from David Love.
+
+2006-12-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c: if the user have a kadmin/admin initial ticket, don't
+ ask for password, just use the credential instead.
+
+2006-12-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c: Use strcspn to remove \n from string returned
+ by fgets. From Björn Sandell
+
+2006-11-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c (kadm_connect): clear error string before trying to
+ print a errno, this way we don't pick up a random failure code
+
+2006-11-20 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c: Make krb5_get_init_creds_opt_free take a context
+ argument.
+
+ * init_c.c: Make krb5_get_init_creds_opt_free take a context
+ argument.
+
+2006-10-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ent_setup.c: Try to not leak memory.
+
+2006-10-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: split build files into dist_ and noinst_ SOURCES
+
+2006-08-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * get_s.c: Add KRB5_KDB_ALLOW_DIGEST
+
+ * ent_setup.c: Add KRB5_KDB_ALLOW_DIGEST
+
+ * admin.h: Add KRB5_KDB_ALLOW_DIGEST
+
+2006-06-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * check-cracklib.pl: Add password reuse checking. From Harald
+ Barth.
+
+2006-06-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ent_setup.c (attr_to_flags): Add KRB5_KDB_ALLOW_KERBEROS4
+
+ * get_s.c (kadm5_s_get_principal): Add KRB5_KDB_ALLOW_KERBEROS4
+
+ * admin.h: Add KRB5_KDB_ALLOW_KERBEROS4
+
+2006-06-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ent_setup.c (attr_to_flags): Add KRB5_KDB_TRUSTED_FOR_DELEGATION
+
+2006-05-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * password_quality.c (kadm5_check_password_quality): set error
+ message in context.
+
+2006-05-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop-log.c: Avoid shadowing.
+
+ * rename_s.c: Avoid shadowing.
+
+2006-05-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * privs_c.c (kadm5_c_get_privs): privs is a uint32_t, let copy it
+ that way.
+
+2006-05-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Rename u_intXX_t to uintXX_t
+
+2006-04-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * chpass_s.c,delete_s.c,get_s.c,log.c,modify_s.c,randkey_s.c,rename_s.c:
+ Pass in HDB_F_GET_ANY to all ->hdb fetch to hint what entries we are looking for
+
+ * send_recv.c: set and clear error string
+
+ * rename_s.c: Break out the that we request from principal from
+ the entry and pass it in as a separate argument.
+
+ * randkey_s.c: Break out the that we request from principal from
+ the entry and pass it in as a separate argument.
+
+ * modify_s.c: Break out the that we request from principal from
+ the entry and pass it in as a separate argument.
+
+ * log.c: Break out the that we request from principal from the
+ entry and pass it in as a separate argument.
+
+ * get_s.c: Break out the that we request from principal from the
+ entry and pass it in as a separate argument.
+
+ * delete_s.c: Break out the that we request from principal from
+ the entry and pass it in as a separate argument.
+
+ * chpass_s.c: Break out the that we request from principal from
+ the entry and pass it in as a separate argument.
+
+2006-04-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * create_s.c (create_principal*): If client doesn't send kvno,
+ make sure to set it to 1.
+
+2006-04-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * log.c: (kadm5_log_rename): handle errors better
+ Fixes Coverity, NetBSD CID#628
+
+ * log.c (kadm5_log_delete): add error handling Coverity, NetBSD
+ CID#626
+ (kadm5_log_modify): add error handling Coverity, NetBSD CID#627
+
+ * init_c.c (_kadm5_c_get_cred_cache): handle ccache case better in
+ case no client name was passed in. Coverity, NetBSD CID#919
+
+ * init_c.c (_kadm5_c_get_cred_cache): Free client principal in
+ case of error. Coverity NetBSD CID#1908
+
+2006-02-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadm5_err.et: (PASS_REUSE): Spelling,
+ from Václav H?la <ax@natur.cuni.cz>
+
+2006-01-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * send_recv.c: Clear error-string when introducing new errors.
+
+ * *_c.c: Clear error-string when introducing new errors.
+
+2006-01-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am (libkadm5clnt.la) doesn't depend on libhdb, remove
+ dependency
+
+2005-12-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * memset hdb_entry_ex before use
+
+2005-12-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Wrap hdb_entry with hdb_entry_ex, patch originally
+ from Andrew Bartlet
+
+2005-11-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * context_s.c (set_field): try another way to calculate the path
+ to the database/logfile/signal-socket
+
+ * log.c (kadm5_log_init): set error string on failures
+
+2005-09-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Constify password.
+
+ * admin.h: Add KRB5_TL_PKINIT_ACL.
+
+ * marshall.c (_kadm5_unmarshal_params): avoid signed-ness warnings
+
+ * get_s.c (kadm5_s_get_principal): clear error string
+
+2005-08-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop-log.8: More text about iprop-log.
+
+2005-08-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop.8: SEE ALSO iprop-log.
+
+ * Makefile.am: man_MANS += iprop-log.8
+
+ * iprop-log.8: Basic for documentation of iprop-log.
+
+ * remove replay_log.c, dump_log.c, and truncate_log.c, folded into
+ iprop-log.
+
+ * log.c (kadm5_log_foreach): add a context variable and pass it
+ down to `func´.
+
+ * iprop-commands.in: Move truncate_log and replay_log into
+ iprop-log.
+
+ * iprop-log.c: Move truncate_log and replay_log into iprop-log.
+
+ * Makefile.am: Move truncate_log and replay_log into iprop-log.
+
+ * Makefile.am: Make this work with a clean directory.
+
+ * ipropd_master.c: Make compile.
+
+ * ipropd_master.c: Update to new signature of kadm5_log_previous.
+
+ * log.c (kadm5_log_previous): catch errors instead of asserting
+ and set error string.
+
+ * iprop-commands.in: New program iprop-log that incorperates
+ dump_log as a subcommand, truncate_log and replay_log soon to come
+ after.
+
+ * iprop-log.c: New program iprop-log that incorperates dump_log as
+ a subcommand, truncate_log and replay_log soon to come after.
+
+ * Makefile.am: New program iprop-log that incorperates dump_log as
+ a subcommand, truncate_log and replay_log soon to come after.
+
+2005-08-11 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * get_s.c: Implement KADM5_LAST_PWD_CHANGE.
+
+ * set_keys.c: Set and clear password where appropriate.
+
+ * randkey_s.c: Operation modifies tl_data.
+
+ * log.c (kadm5_log_replay_modify): Check return values of
+ malloc(), replace all extensions.
+
+ * kadm5_err.et: Make BAD_TL_TYPE error more helpful.
+
+ * get_s.c: Expose KADM5_TL_DATA options to the client.
+
+ * ent_setup.c: Merge in KADM5_TL_DATA in the database.
+
+ * chpass_s.c: Operations modify extensions, mark that with
+ TL_DATA.
+
+ * admin.h: Add more TL types (password and extension).
+
+2005-06-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * constify
+
+ * ipropd_slave.c: avoid shadowing
+
+ * ipropd_master.c: rename local variable slave to s, optind ->
+ optidx
+
+ * get_princs_c.c: rename variable exp to expression
+
+ * ad.c: rename variable exp to expression
+
+ * log.c: rename shadowing len to num
+
+ * get_princs_s.c: rename variable exp to expression
+
+ * context_s.c: const poison
+
+ * common_glue.c: rename variable exp to expression
+
+2005-05-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ent_setup.c (attr_to_flags): check for KRB5_KDB_OK_AS_DELEGATE
+
+ * get_s.c (kadm5_s_get_principal): set KRB5_KDB_OK_AS_DELEGATE
+
+ * admin.h: add KRB5_KDB_OK_AS_DELEGATE, sync KRB5_TL_ flags
+
+2005-05-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadm5_pwcheck.3: please mdoclint
+
+2005-05-25 Dave Love <fx@gnu.org>
+
+ * kadm5_pwcheck.3: document kadm5_add_passwd_quality_verifier,
+ improve text
+
+2005-05-24 Dave Love <fx@gnu.org>
+
+ * iprop.8: Added some info about defaults, fixed some markup.
+
+2005-05-23 Dave Love <fx@gnu.org>
+
+ * ipropd_slave.c: Don't test HAVE_DAEMON since roken supplies it.
+
+ * ipropd_master.c: Don't test HAVE_DAEMON since roken supplies it.
+
+2005-05-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c (_kadm5_c_init_context): fix memory leak in case of
+ failure
+
+2005-05-09 Dave Love <fx@gnu.org>
+
+ * password_quality.c (find_func): Fix off-by-one and logic error.
+ (external_passwd_quality): Improve messages.
+
+ * test_pw_quality.c (main): Call kadm5_setup_passwd_quality_check
+ and kadm5_add_passwd_quality_verifier.
+
+2005-04-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * default_keys.c: #include <err.h>, only print salt it its longer
+ then 0, use krb5_err instead of errx where appropriate
+
+2005-04-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c: add the documented option --port
+
+ * ipropd_master.c: add the documented option --port
+
+ * dump_log.c: use the newly generated units function
+
+2005-04-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * dump_log.c: use strlcpy
+
+ * password_quality.c: don't use sizeof(pointer)
+
+2005-04-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * check-cracklib.pl: external password verifier sample
+
+ * password_quality.c (kadm5_add_passwd_quality_verifier): if NULL
+ is passed in, load defaults
+
+2005-04-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * password_quality.c: add an end tag to the external password
+ quality check protocol
+
+2005-04-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * password_quality.c: add external passsword quality check builtin
+ module
+
+ [password_quality]
+ policies = external-check
+ external-program = /bin/false
+
+ To approve password a, make the test program return APPROVED on
+ stderr and fail with exit code 0.
+
+2004-10-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: bump version to 7:7:0 and 6:5:2
+
+ * default_keys.c (parse_file): use hdb_generate_key_set
+
+ * keys.c,set_keys.c: Move keyset parsing and password based keyset
+ generation into hdb. Requested by Andrew Bartlett <abartlet@samba.org>
+ for hdb-ldb backend.
+
+2004-09-23 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_master.c: add help strings to some options
+
+2004-09-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * chpass_s.c: deal with changed prototype for _kadm5_free_keys
+
+ * keys.c (_kadm5_free_keys): change prototype, make it use
+ krb5_context instead of a kadm5_server_context
+
+ * set_keys.c (parse_key_set): do way with static returning
+ (function) static variable and returned allocated memory
+ (_kadm5_generate_key_set): free enctypes returned by parse_key_set
+
+2004-09-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c: Fix memory leak, don't return stack variables From
+ Andrew Bartlett
+
+ * set_keys.c: make all_etypes const and move outside function to
+ avoid returning data on stack
+
+2004-08-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * acl.c (fetch_acl): use " \t\n" instead of just "\n" for the
+ delim of the third element, this is so we can match
+ "foo@REALM<SPC>all<SPC><SPC>*@REALM", before it just matched
+ "foo@REALM<SPC>all<SPC>*@REALM", but that is kind of lucky since
+ what really happen was that the last <SPC> was stamped out, and
+ the it never strtok_r never needed to parse over it.
+
+2004-08-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c (_kadm5_generate_key_set): since arcfour-hmac-md5 is
+ without salting, some people tries to add the string
+ "arcfour-hmac-md5" when they really should have used
+ "arcfour-hmac-md5:pw-salt", help them and add glue for that
+
+2004-08-18 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_slave.c: add --detach
+
+2004-07-06 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: use new tsasl interface remove debug printf add upn to
+ computer-accounts
+
+2004-06-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: implement kadm5_ad_init_with_password_ctx set more error
+ strings
+
+2004-06-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: man_MANS = kadm5_pwcheck.3
+
+ * kadm5_pwcheck.3: document new password quality api
+
+ * password_quality.c: new password check interface (old still
+ supported)
+
+ * kadm5-pwcheck.h: new password check interface
+
+2004-06-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c (main): process all slaves, not just up to the
+ last slave sending data
+ (bug report from Björn Sandell <biorn@dce.chalmers.se>)
+ (*): only send one ARE_YOU_THERE
+
+2004-06-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: use krb5_set_password_using_ccache
+
+2004-06-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: try handle spn's better
+
+2004-05-31 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: add expiration time
+
+ * ad.c: add modify operations
+
+ * ad.c: handle create and delete
+
+2004-05-27 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: more code for get, handle attributes
+
+ * ad.c: more code for get, handle time stamps and bad password
+ counter
+
+ * ad.c: more code for get, only fetches kvno for now
+
+2004-05-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ad.c: add support for tsasl
+
+ * private.h: add kadm5_ad_context
+
+ * ipropd_master.c (prop_one): store the opcode in the begining of
+ the blob, not the end
+
+ * ad.c: try all ldap servers in dns, generate a random password,
+ base64(random_block(64)), XXX must make it support other then
+ ARCFOUR
+
+ * ad.c: framework for windows AD backend
+
+2004-03-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * create_s.c (kadm5_s_create_principal): remove old XXX command
+ and related code, _kadm5_set_keys will do all this now
+
+2004-02-29 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c (_kadm5_set_keys_randomly): make sure enctype to copy
+ enctype for des keys From: Andrew Bartlett <abartlet@samba.org>
+
+ * create_s.c (kadm5_s_create_principal_with_key): don't call
+ _kadm5_set_keys2, create_principal will do that for us. Set kvno
+ to 1.
+
+ * chpass_s.c (change): bump kvno
+ (kadm5_s_chpass_principal_with_key): bump kvno
+
+ * randkey_s.c (kadm5_s_randkey_principal): bump kvno
+
+ * set_keys.c (_kadm5_set_*): don't change the kvno, let the callee
+ to that
+
+2003-12-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * chpass_s.c (change): fix same-password-again by decrypting keys
+ and setting an error code From: Buck Huppmann <buckh@pobox.com>
+
+2003-12-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c (_kadm5_c_init_context): catch errors from strdup and
+ other krb5_ functions
+
+2003-12-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * rename_s.c (kadm5_s_rename_principal): allow principal to change
+ realm From Panasas Inc
+
+2003-12-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * destroy_c.c (kadm5_c_destroy): fix memory leaks, From Panasas,
+ Inc
+
+2003-11-23 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop.h: don't include <krb5-private.h>
+
+ * ipropd_slave.c: stop using krb5 lib private byte-frobbing
+ functions and replace them with with krb5_storage
+
+ * ipropd_master.c: stop using krb5 lib private byte-frobbing
+ functions and replace them with with krb5_storage
+
+2003-11-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c (receive_loop): when seeking over the entries we
+ already have, skip over the trailer. From: Jeffrey Hutzelman
+ <jhutz@cmu.edu>
+
+ * dump_log.c,ipropd_master.c,ipropd_slave.c,
+ replay_log.c,truncate_log.c: parse kdc.conf
+ From: Jeffrey Hutzelman <jhutz@cmu.edu>
+
+2003-10-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: += test_pw_quality
+
+ * test_pw_quality.c: test program for verifying password quality
+ function
+
+2003-09-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: add and enable check program default_keys
+
+ * default_keys.c: test program for _kadm5_generate_key_set
+
+ * init_c.c: use
+ krb5_get_init_creds_opt_alloc/krb5_get_init_creds_opt_free
+
+2003-08-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c (_kadm5_set_keys_randomly): remove dup return
+
+ * ipropd_master.c (main): make sure current_version is initialized
+
+2003-08-15 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c: use default_keys for the both random keys and
+ password derived keys if its defined
+
+2003-07-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_slave.c (receive_everything): switch close and rename
+ From: Alf Wachsmann <alfw@SLAC.Stanford.EDU>
+
+2003-07-03 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * iprop.h, ipropd_master.c, ipropd_slave.c:
+ Add probing from the server that the client is still there, also
+ make the client check that the server is probing.
+
+2003-07-02 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * truncate_log.c (main): add missing ``if (ret)''
+
+2003-06-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c (make_keys): add AES support
+
+ * set_keys.c: fix off by one in the aes case, pointed out by Ken
+ Raeburn
+
+2003-04-30 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * set_keys.c (_kadm5_set_keys_randomly): add
+ ETYPE_AES256_CTS_HMAC_SHA1_96 key when configuried with aes
+ support
+
+2003-04-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * send_recv.c: check return values from krb5_data_alloc
+ * log.c: check return values from krb5_data_alloc
+
+2003-04-16 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * dump_log.c (print_entry): check return values from
+ krb5_data_alloc
+
+2003-04-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * init_c.c (kadm_connect): if a context realm was passed in, use
+ that to form the kadmin/admin principal
+
+2003-03-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * ipropd_master.c (main): make sure we don't consider dead slave
+ for select processing
+ (write_stats): use slave_stats_file variable,
+ check return value of strftime
+ (args): allow specifying slave stats file
+ (slave_dead): close the fd when the slave dies
+
+2002-10-21 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_slave.c (from Derrick Brashear): Propagating a large
+ database without this means the slave kdcs can get erroneous
+ HDB_NOENTRY and return the resulting errors. This creates a new db
+ handle, populates it, and moves it into place.
+
+2002-08-26 Assar Westerlund <assar@kth.se>
+
+ * ipropd_slave.c (receive_everything): type-correctness calling
+ _krb5_get_int
+
+ * context_s.c (find_db_spec): const-correctness in parameters to
+ krb5_config_get_next
+
+2002-08-16 Johan Danielsson <joda@pdc.kth.se>
+
+ * private.h: rename header file flag macro
+
+ * Makefile.am: generate kadm5-{protos,private}.h
+
+2002-08-15 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_master.c: check return value of krb5_sockaddr2address
+
+2002-07-04 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_master.c: handle slaves that come and go; add status
+ reporting (both from Love)
+
+ * iprop.h: KADM5_SLAVE_STATS
+
+2002-03-25 Jacques Vidrine <n@nectar.com>
+
+ * init_c.c (get_cred_cache): bug fix: the default credentials
+ cache was not being used if a client name was specified.
+
+2002-03-25 Johan Danielsson <joda@pdc.kth.se>
+
+ * init_c.c (get_cred_cache): when getting the default_client from
+ the cred cache, make sure the instance part is "admin"; this
+ should require fewer uses of -p
+
+2002-03-11 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5srv_la_LDFLAGS): set version to 7:5:0
+ (libkadm5clnt_la_LDFLAGS): set version to 6:3:2
+
+2002-02-08 Johan Danielsson <joda@pdc.kth.se>
+
+ * init_c.c: we have to create our own param struct before
+ marshaling
+
+2001-09-05 Johan Danielsson <joda@pdc.kth.se>
+
+ * Makefile.am: link with LIB_pidfile
+
+ * iprop.h: include util.h for pidfile
+
+2001-08-31 Assar Westerlund <assar@sics.se>
+
+ * ipropd_slave.c (main): syslog with the correct name
+
+2001-08-30 Jacques Vidrine <n@nectar.com>
+
+ * ipropd_slave.c, ipropd_master.c (main): call pidfile
+
+2001-08-28 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5srv_la_LDFLAGS): set version to 7:4:0
+
+2001-08-24 Assar Westerlund <assar@sics.se>
+
+ * acl.c (fetch_acl): do not return bogus flags and re-organize
+ function
+
+ * Makefile.am: rename variable name to avoid error from current
+ automake
+
+2001-08-13 Johan Danielsson <joda@pdc.kth.se>
+
+ * set_keys.c: add easier afs configuration, defaulting to the
+ local realm in lower case; also try to remove duplicate salts
+
+2001-07-12 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: add required library dependencies
+
+2001-07-03 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5clnt_la_LDFLAGS): set version to 6:2:2
+
+2001-06-29 Johan Danielsson <joda@pdc.kth.se>
+
+ * init_c.c: call krb5_get_init_creds_opt_set_default_flags
+
+2001-02-19 Johan Danielsson <joda@pdc.kth.se>
+
+ * replay_log.c: add --{start-end}-version flags to replay just
+ part of the log
+
+2001-02-15 Assar Westerlund <assar@sics.se>
+
+ * ipropd_master.c (main): fix select-loop to decrement ret
+ correctly. from "Brandon S. Allbery KF8NH" <allbery@ece.cmu.edu>
+
+2001-01-30 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: bump versions
+
+2000-12-31 Assar Westerlund <assar@sics.se>
+
+ * init_s.c (*): handle krb5_init_context failure consistently
+ * init_c.c (init_context): handle krb5_init_context failure
+ consistently
+
+2000-12-11 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5srv_la_LDFLAGS): bump version to 7:2:0
+
+2000-11-16 Assar Westerlund <assar@sics.se>
+
+ * set_keys.c (make_keys): clean-up salting loop and try not to
+ leak memory
+
+ * ipropd_master.c (main): check for fd's being too large to select
+ on
+
+2000-08-16 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5srv_la_LDFLAGS): bump version to 7:1:0
+
+2000-08-10 Assar Westerlund <assar@sics.se>
+
+ * acl.c (fetch_acl): fix wrong cases, use krb5_principal_match
+
+2000-08-07 Assar Westerlund <assar@sics.se>
+
+ * ipropd_master.c (main): ignore SIGPIPE
+
+2000-08-06 Assar Westerlund <assar@sics.se>
+
+ * ipropd_slave.c (receive_everything): make `fd' an int instead of
+ a pointer. From Derrick J Brashear <shadow@dementia.org>
+
+2000-08-04 Johan Danielsson <joda@pdc.kth.se>
+
+ * admin.h: change void** to void*
+
+2000-07-25 Johan Danielsson <joda@pdc.kth.se>
+
+ * Makefile.am: bump versions to 7:0:0 and 6:0:2
+
+2000-07-24 Assar Westerlund <assar@sics.se>
+
+ * log.c (kadm5_log_get_version): rename kadm5_log_get_version_fd
+ and make a new that takes a context
+ (kadm5_log_nop): add logging of missing lengths
+ (kadm5_log_truncate): new function
+
+ * dump_log.c (print_entry): update and correct
+ * randkey_s.c: call _kadm5_bump_pw_expire
+ * truncate_log.c: new program for truncating the log
+ * Makefile.am (sbin_PROGRAMS): add truncate_log
+ (C_SOURCES): add bump_pw_expire.c
+ * bump_pw_expire.c: new function for extending password expiration
+
+2000-07-22 Assar Westerlund <assar@sics.se>
+
+ * keys.c: new file with _kadm5_free_keys, _kadm5_init_keys
+
+ * set_keys.c (free_keys, init_keys): elevate to internal kadm5
+ functions
+
+ * chpass_s.c (kadm5_s_chpass_principal_cond): new function
+ * Makefile.am (C_SOURCES): add keys.c
+ * init_c.c: remove unused variable and handle some parameters
+ being NULL
+
+2000-07-22 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_slave.c: use krb5_read_priv_message
+
+ * ipropd_master.c: use krb5_{read,write}_priv_message
+
+ * init_c.c: use krb5_write_priv_message
+
+2000-07-11 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_slave.c: no need to call gethostname, since
+ sname_to_principal will
+
+ * send_recv.c: assert that we have a connected socket
+
+ * get_princs_c.c: call _kadm5_connect
+
+ * rename_c.c: call _kadm5_connect
+
+ * randkey_c.c: call _kadm5_connect
+
+ * privs_c.c: call _kadm5_connect
+
+ * modify_c.c: call _kadm5_connect
+
+ * get_c.c: call _kadm5_connect
+
+ * delete_c.c: call _kadm5_connect
+
+ * create_c.c: call _kadm5_connect
+
+ * chpass_c.c: call _kadm5_connect
+
+ * private.h: add more fields to client context; remove prototypes
+
+ * admin.h: remove prototypes
+
+ * kadm5-protos.h: move public prototypes here
+
+ * kadm5-private.h: move private prototypes here
+
+ * init_c.c: break out connection code to separate function, and
+ defer calling it until we actually do something
+
+2000-07-07 Assar Westerlund <assar@sics.se>
+
+ * set_keys.c (make_keys): also support `[kadmin]use_v4_salt' for
+ backwards compatability
+
+2000-06-26 Johan Danielsson <joda@pdc.kth.se>
+
+ * set_keys.c (_kadm5_set_keys): rewrite this to be more easily
+ adaptable to different salts
+
+2000-06-19 Johan Danielsson <joda@pdc.kth.se>
+
+ * get_s.c: pa_* -> KRB5_PADATA_*
+
+2000-06-16 Assar Westerlund <assar@sics.se>
+
+ * ipropd_slave.c: change default keytab to default keytab (as in
+ typically FILE:/etc/krb5.keytab)
+
+2000-06-08 Assar Westerlund <assar@sics.se>
+
+ * ipropd_slave.c: bug fixes, for actually writing the full dump to
+ the database. based on a patch from Love <lha@stacken.kth.se>
+
+2000-06-07 Assar Westerlund <assar@sics.se>
+
+ * acl.c: add support for patterns of principals
+ * log.c (kadm5_log_replay_create): handle more NULL pointers
+ (should they really happen?)
+ * log.c (kadm5_log_replay_modify): handle max_life == NULL and
+ max_renew == NULL
+
+ * ipropd_master.c: use syslog. be less verbose
+ * ipropd_slave.c: use syslog
+
+2000-06-05 Assar Westerlund <assar@sics.se>
+
+ * private.h (kadm_ops): add kadm_nop more prototypes
+ * log.c (kadm5_log_set_version, kadm5_log_reinit, kadm5_log_nop,
+ kadm5_log_replay_nop): add
+ * ipropd_slave.c: and some more improvements
+ * ipropd_master.c: lots of improvements
+ * iprop.h (IPROP_PORT, IPROP_SERVICE): add
+ (iprop_cmd): add new commands
+
+ * dump_log.c: add nop
+
+2000-05-15 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5clnt_la_LDFLAGS): set version to 5:1:1
+
+2000-05-12 Assar Westerlund <assar@sics.se>
+
+ * get_s.c (kadm5_s_get_principal): set life, rlife to INT_MAX as a
+ fallback. handle not having any creator.
+ * destroy_s.c (kadm5_s_destroy): free all allocated memory
+ * context_s.c (set_field): free variable if it's already set
+ (find_db_spec): malloc space for all strings
+
+2000-04-05 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (LDADD): add LIB_openldap
+
+2000-04-03 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5srv_la_LDFLAGS): set version to 6:0:1
+ (libkadm5clnt_la_LDFLAGS): set version to 5:0:1
+
+2000-03-24 Assar Westerlund <assar@sics.se>
+
+ * set_keys.c (_kadm5_set_keys2): rewrite
+ (_kadm5_set_keys3): add
+
+ * private.h (struct kadm_func): add chpass_principal_with_key
+ * init_c.c (set_funcs): add chpass_principal_with_key
+
+2000-03-23 Assar Westerlund <assar@sics.se>
+
+ * context_s.c (set_funcs): add chpass_principal_with_key
+ * common_glue.c (kadm5_chpass_principal_with_key): add
+ * chpass_s.c: comment-ize and change calling convention for
+ _kadm5_set_keys*
+ * chpass_c.c (kadm5_c_chpass_principal_with_key): add
+
+2000-02-07 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5clnt_la_LDFLAGS): set version to 4:2:0
+
+2000-01-28 Assar Westerlund <assar@sics.se>
+
+ * init_c.c (get_new_cache): make sure to request non-forwardable,
+ non-proxiable
+
+2000-01-06 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5srv.la): bump version to 5:1:0
+
+ * context_s.c (_kadm5_s_init_context): handle params == NULL
+
+1999-12-26 Assar Westerlund <assar@sics.se>
+
+ * get_s.c (kadm5_s_get_principal): handle modified_by->principal
+ == NULL
+
+1999-12-20 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (libkadm5clnt_la_LDFLAGS): bump version to 4:1:0
+
+ * init_c.c (_kadm5_c_init_context): handle getting back port
+ number from admin host
+ (kadm5_c_init_with_context): remove `proto/' part before doing
+ getaddrinfo()
+
+1999-12-06 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: bump version to 5:0:0 and 4:0:0
+
+ * init_c.c (kadm5_c_init_with_context): don't use unitialized
+ stuff
+
+1999-12-04 Assar Westerlund <assar@sics.se>
+
+ * replay_log.c: adapt to changed kadm5_log_foreach
+
+ * log.c (kadm5_log_foreach): change to take a
+ `kadm5_server_context'
+
+ * init_c.c: use krb5_warn{,x}
+
+ * dump_log.c: adapt to changed kadm5_log_foreach
+
+ * init_c.c: re-write to use getaddrinfo
+ * Makefile.am (install-build-headers): add dependency
+
+1999-12-03 Johan Danielsson <joda@pdc.kth.se>
+
+ * log.c (kadm5_log_foreach): pass context
+
+ * dump_log.c: print more interesting things
+
+1999-12-02 Johan Danielsson <joda@pdc.kth.se>
+
+ * ipropd_master.c (process_msg): check for short reads
+
+1999-11-25 Assar Westerlund <assar@sics.se>
+
+ * modify_s.c (kadm5_s_modify_principal): support key_data
+ (kadm5_s_modify_principal_with_key): remove
+
+ * admin.h (kadm5_s_modify_principal_with_key): remove
+
+1999-11-20 Assar Westerlund <assar@sics.se>
+
+ * context_s.c (find_db_spec): ugly cast work-around.
+
+1999-11-14 Assar Westerlund <assar@sics.se>
+
+ * context_s.c (_kadm5_s_init_context): call krb5_add_et_list so
+ that we aren't dependent on the layout of krb5_context_data
+ * init_c.c (_kadm5_c_init_context): call krb5_add_et_list so that
+ we aren't dependent on the layout of krb5_context_data
+
+1999-11-13 Assar Westerlund <assar@sics.se>
+
+ * password_quality.c (kadm5_setup_passwd_quality_check): use
+ correct types for function pointers
+
+1999-11-09 Johan Danielsson <joda@pdc.kth.se>
+
+ * randkey_s.c: always bail out if the fetch fails
+
+ * admin.h (kadm5_config_params): remove fields we're not using
+
+ * ipropd_slave.c: allow passing a realm
+
+ * ipropd_master.c: allow passing a realm
+
+ * dump_log.c: allow passing a realm
+
+ * acl.c: correctly get acl file
+
+ * private.h (kadm5_server_context): add config_params struct and
+ remove acl_file; bump protocol version number
+
+ * marshall.c: marshalling of config parameters
+
+ * init_c.c (kadm5_c_init_with_context): try to cope with old
+ servers
+
+ * init_s.c (kadm5_s_init_with_context): actually use some passed
+ values
+
+ * context_s.c (_kadm5_s_init_context): get dbname, acl_file, and
+ stash_file from the config parameters, try to figure out these if
+ they're not provided
+
+1999-11-05 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (install-build-headers): use `cp' instead of
+ INSTALL_DATA
+
+1999-11-04 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: bump version to 4:0:0 and 3:0:0 (they access fields
+ directly in libkrb5's context - bad functions)
+
+ * set_keys.c (_kadm5_set_keys_randomly): set enctypes correctly in
+ the copied keys
+
+1999-10-20 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: set version of kadm5srv to 3:0:2 (new password
+ quality functions).
+ set version of kdam5clnt to 2:1:1 (no interface changes)
+
+ * Makefile.am (LDADD): add $(LIB_dlopen)
+
+1999-10-17 Assar Westerlund <assar@sics.se>
+
+ * randkey_s.c (kadm5_s_randkey_principal): use
+ _kadm5_set_keys_randomly
+
+ * set_keys.c (free_keys): free more memory
+ (_kadm5_set_keys): a little bit more generic
+ (_kadm5_set_keys_randomly): new function for setting random keys.
+
+1999-10-14 Assar Westerlund <assar@sics.se>
+
+ * set_keys.c (_kadm5_set_keys): ignore old keys when setting new
+ ones and always add 3 DES keys and one 3DES key
+
+1999-10-03 Assar Westerlund <assar@sics.se>
+
+ * init_c.c (_kadm5_c_init_context): use `krb5_get_krb_admin_hst'.
+ check return value from strdup
+
+1999-09-26 Assar Westerlund <assar@sics.se>
+
+ * acl.c (_kadm5_privs_to_string): forgot one strcpy_truncate ->
+ strlcpy
+
+1999-09-24 Johan Danielsson <joda@pdc.kth.se>
+
+ * dump_log.c: remove unused `optind'
+
+ * replay_log.c: remove unused `optind'
+
+1999-09-13 Assar Westerlund <assar@sics.se>
+
+ * chpass_c.c (kadm5_c_chpass_principal): new _kadm5_client_recv
+
+ * send_recv.c (_kadm5_client_recv): return result in a `krb5_data'
+ so that we avoid copying it and don't need to dimension in
+ advance. change all callers.
+
+1999-09-10 Assar Westerlund <assar@sics.se>
+
+ * password_quality.c: new file
+
+ * admin.h
+ (kadm5_setup_passwd_quality_check,kadm5_check_password_quality):
+ add prototypes
+
+ * Makefile.am (S_SOURCES): add password_quality.c
+
+1999-07-26 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: update versions to 2:0:1
+
+1999-07-24 Assar Westerlund <assar@sics.se>
+
+ * ent_setup.c (_kadm5_setup_entry): make princ_expire_time == 0
+ and pw_expiration == 0 mean never
+
+1999-07-22 Assar Westerlund <assar@sics.se>
+
+ * log.c (kadm5_log_flush): extra cast
+
+1999-07-07 Assar Westerlund <assar@sics.se>
+
+ * marshall.c (store_principal_ent): encoding princ_expire_time and
+ pw_expiration in correct order
+
+1999-06-28 Assar Westerlund <assar@sics.se>
+
+ * randkey_s.c (kadm5_s_randkey_principal): nuke old mkvno,
+ otherwise hdb will think that the new random keys are already
+ encrypted which will cause lots of confusion later.
+
+1999-06-23 Assar Westerlund <assar@sics.se>
+
+ * ent_setup.c (_kadm5_setup_entry): handle 0 == unlimited
+ correctly. From Michal Vocu <michal@karlin.mff.cuni.cz>
+
+1999-06-15 Assar Westerlund <assar@sics.se>
+
+ * init_c.c (get_cred_cache): use get_default_username
+
+1999-05-23 Assar Westerlund <assar@sics.se>
+
+ * create_s.c (create_principal): if there's no default entry the
+ mask should be zero.
+
+1999-05-21 Assar Westerlund <assar@sics.se>
+
+ * init_c.c (get_cred_cache): use $USERNAME
+
+1999-05-17 Johan Danielsson <joda@pdc.kth.se>
+
+ * init_c.c (get_cred_cache): figure out principal
+
+1999-05-05 Johan Danielsson <joda@pdc.kth.se>
+
+ * send_recv.c: cleanup _kadm5_client_{send,recv}
+
+1999-05-04 Assar Westerlund <assar@sics.se>
+
+ * set_keys.c (_kadm5_set_keys2): don't check the recently created
+ memory for NULL pointers
+
+ * private.h (_kadm5_setup_entry): change prototype
+
+ * modify_s.c: call new _kadm5_setup_entry
+
+ * ent_setup.c (_kadm5_setup_entry): change so that it takes three
+ masks, one for what bits to set and one for each of principal and
+ def containing the bits that are set there.
+
+ * create_s.c: call new _kadm5_setup_entry
+
+ * create_s.c (get_default): check return value
+ (create_principal): send wider mask to _kadm5_setup_entry
+
+1999-05-04 Johan Danielsson <joda@pdc.kth.se>
+
+ * send_recv.c (_kadm5_client_recv): handle arbitrarily sized
+ packets, check for errors
+
+ * get_c.c: check for failure from _kadm5_client_{send,recv}
+
+1999-05-04 Assar Westerlund <assar@sics.se>
+
+ * init_c.c (get_new_cache): don't abort when interrupted from
+ password prompt
+
+ * destroy_c.c (kadm5_c_destroy): check if we should destroy the
+ auth context
+
+1999-05-03 Johan Danielsson <joda@pdc.kth.se>
+
+ * chpass_s.c: fix arguments to _kadm5_set_keys2
+
+ * private.h: proto
+
+ * set_keys.c: clear mkvno
+
+ * rename_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * randkey_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * modify_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * log.c: add flags to fetch and store; seal keys before logging
+
+ * get_s.c: add flags to fetch and store; seal keys before logging
+
+ * get_princs_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * delete_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * create_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * chpass_s.c: add flags to fetch and store; seal keys before
+ logging
+
+ * Makefile.am: remove server.c
+
+ * admin.h: add prototypes
+
+ * ent_setup.c (_kadm5_setup_entry): set key_data
+
+ * set_keys.c: add _kadm5_set_keys2 to sey keys from key_data
+
+ * modify_s.c: add kadm5_s_modify_principal_with_key
+
+ * create_s.c: add kadm5_s_create_principal_with_key
+
+ * chpass_s.c: add kadm5_s_chpass_principal_with_key
+
+ * kadm5_locl.h: move stuff to private.h
+
+ * private.h: move stuff from kadm5_locl.h
+
diff --git a/third_party/heimdal/lib/kadm5/Makefile.am b/third_party/heimdal/lib/kadm5/Makefile.am
new file mode 100644
index 0000000..6b58c5f
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/Makefile.am
@@ -0,0 +1,233 @@
+# $Id$
+
+include $(top_srcdir)/Makefile.am.common
+
+libkadm5srv_la_CPPFLAGS = -I$(srcdir)/../krb5
+libkadm5clnt_la_CPPFLAGS = -I$(srcdir)/../krb5
+
+lib_LTLIBRARIES = libkadm5srv.la libkadm5clnt.la
+libkadm5srv_la_LDFLAGS = -version-info 8:1:0
+libkadm5clnt_la_LDFLAGS = -version-info 7:1:0
+
+if versionscript
+libkadm5clnt_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script-client.map
+libkadm5srv_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+
+sbin_PROGRAMS = iprop-log
+check_PROGRAMS = default_keys test_marshall
+noinst_PROGRAMS = test_pw_quality
+
+noinst_LTLIBRARIES = sample_passwd_check.la sample_hook.la
+
+sample_passwd_check_la_SOURCES = sample_passwd_check.c
+sample_passwd_check_la_LDFLAGS = -module
+
+sample_hook_la_SOURCES = sample_hook.c
+sample_hook_la_LDFLAGS = -module
+
+libkadm5srv_la_LIBADD = \
+ $(LIB_com_err) ../krb5/libkrb5.la \
+ ../hdb/libhdb.la $(LIBADD_roken)
+libkadm5clnt_la_LIBADD = \
+ $(LIB_com_err) ../krb5/libkrb5.la $(LIBADD_roken)
+
+libexec_PROGRAMS = ipropd-master ipropd-slave
+
+default_keys_SOURCES = default_keys.c
+default_keys_CPPFLAGS = -I$(srcdir)/../krb5
+
+test_marshall_SOURCES = marshall.c
+test_marshall_CPPFLAGS = -I$(srcdir)/../krb5 -DTEST
+
+kadm5includedir = $(includedir)/kadm5
+buildkadm5include = $(buildinclude)/kadm5
+
+dist_kadm5include_HEADERS = admin.h private.h kadm5-hook.h kadm5-pwcheck.h
+dist_kadm5include_HEADERS += $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
+
+nodist_kadm5include_HEADERS = kadm5_err.h
+
+install-build-headers:: $(dist_kadm5include_HEADERS) $(nodist_kadm5include_HEADERS)
+ @foo='$(dist_kadm5include_HEADERS) $(nodist_kadm5include_HEADERS)'; \
+ for f in $$foo; do \
+ f=`basename $$f`; \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ if cmp -s $$file $(buildkadm5include)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo "cp $$file $(buildkadm5include)/$$f";\
+ cp $$file $(buildkadm5include)/$$f; \
+ fi ; \
+ done
+
+dist_libkadm5clnt_la_SOURCES = \
+ ad.c \
+ chpass_c.c \
+ client_glue.c \
+ common_glue.c \
+ create_c.c \
+ delete_c.c \
+ destroy_c.c \
+ flush_c.c \
+ free.c \
+ get_c.c \
+ get_princs_c.c \
+ init_c.c \
+ kadm5_locl.h \
+ marshall.c \
+ modify_c.c \
+ private.h \
+ privs_c.c \
+ prune_c.c \
+ randkey_c.c \
+ rename_c.c \
+ send_recv.c \
+ admin.h
+
+nodist_libkadm5clnt_la_SOURCES = \
+ kadm5_err.c \
+ kadm5_err.h
+
+dist_libkadm5srv_la_SOURCES = \
+ acl.c \
+ admin.h \
+ bump_pw_expire.c \
+ chpass_s.c \
+ common_glue.c \
+ context_s.c \
+ create_s.c \
+ delete_s.c \
+ destroy_s.c \
+ ent_setup.c \
+ error.c \
+ flush_s.c \
+ free.c \
+ get_princs_s.c \
+ get_s.c \
+ init_s.c \
+ kadm5_locl.h \
+ keys.c \
+ log.c \
+ marshall.c \
+ modify_s.c \
+ password_quality.c \
+ private.h \
+ privs_s.c \
+ prune_s.c \
+ randkey_s.c \
+ rename_s.c \
+ server_glue.c \
+ server_hooks.c \
+ setkey3_s.c \
+ set_keys.c \
+ set_modifier.c \
+ admin.h
+
+nodist_libkadm5srv_la_SOURCES = \
+ kadm5_err.c \
+ kadm5_err.h
+
+libkadm5srv_la_DEPENDENCIES = \
+ version-script.map
+
+dist_iprop_log_SOURCES = iprop-log.c
+nodist_iprop_log_SOURCES = iprop-commands.c
+
+ipropd_master_SOURCES = ipropd_master.c ipropd_common.c iprop.h kadm5_locl.h
+ipropd_master_CPPFLAGS = -I$(srcdir)/../krb5
+
+ipropd_slave_SOURCES = ipropd_slave.c ipropd_common.c iprop.h kadm5_locl.h
+ipropd_slave_CPPFLAGS = -I$(srcdir)/../krb5
+
+man_MANS = kadm5_pwcheck.3 iprop.8 iprop-log.8
+
+LDADD = \
+ libkadm5srv.la \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_hcrypto) \
+ $(LIB_roken) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
+ $(LIB_dlopen) \
+ $(LIB_pidfile)
+
+
+iprop_log_LDADD = \
+ libkadm5srv.la \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/sl/libsl.la \
+ $(LIB_readline) \
+ $(LIB_roken) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
+ $(LIB_dlopen) \
+ $(LIB_pidfile)
+
+iprop_log_CPPFLAGS = -I$(srcdir)/../krb5
+
+iprop-commands.c iprop-commands.h: iprop-commands.in
+ $(SLC) $(srcdir)/iprop-commands.in
+
+$(libkadm5srv_la_OBJECTS): kadm5_err.h
+$(libkadm5clnt_la_OBJECTS): kadm5_err.h
+$(iprop_log_OBJECTS): iprop-commands.h
+
+client_glue.lo server_glue.lo: $(srcdir)/common_glue.c
+
+CLEANFILES = kadm5_err.c kadm5_err.h iprop-commands.h iprop-commands.c
+
+# to help stupid solaris make
+
+kadm5_err.h: kadm5_err.et
+
+ALL_OBJECTS = $(libkadm5clnt_la_OBJECTS)
+ALL_OBJECTS += $(libkadm5srv_la_OBJECTS)
+ALL_OBJECTS += $(ipropd_master_OBJECTS)
+ALL_OBJECTS += $(ipropd_slave_OBJECTS)
+ALL_OBJECTS += $(iprop_log_OBJECTS)
+ALL_OBJECTS += $(test_pw_quality_OBJECTS)
+ALL_OBJECTS += $(sample_passwd_check_la_OBJECTS)
+ALL_OBJECTS += $(sample_hook_la_OBJECTS)
+ALL_OBJECTS += $(default_keys_OBJECTS)
+
+$(ALL_OBJECTS): $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
+$(ALL_OBJECTS): kadm5_err.h
+
+KADM5_PROTOS_SRCS = $(dist_libkadm5clnt_la_SOURCES)
+KADM5_PROTOS_SRCS += $(dist_libkadm5srv_la_SOURCES)
+
+proto_opts = -q -R '^(_|kadm5_c_|kadm5_s_|kadm5_log)' -P comment
+$(srcdir)/kadm5-protos.h: $(KADM5_PROTOS_SRCS)
+ cd $(srcdir); perl ../../cf/make-proto.pl $(proto_opts) \
+ -o kadm5-protos.h \
+ $(dist_libkadm5clnt_la_SOURCES) \
+ $(dist_libkadm5srv_la_SOURCES) \
+ || rm -f kadm5-protos.h
+
+$(srcdir)/kadm5-private.h: $(KADM5_PROTOS_SRCS)
+ cd $(srcdir); perl ../../cf/make-proto.pl $(proto_opts) \
+ -p kadm5-private.h \
+ $(dist_libkadm5clnt_la_SOURCES) \
+ $(dist_libkadm5srv_la_SOURCES) \
+ || rm -f kadm5-private.h
+
+EXTRA_DIST = \
+ NTMakefile \
+ iprop-log-version.rc \
+ ipropd-master-version.rc \
+ ipropd-slave-version.rc \
+ libkadm5srv-version.rc \
+ libkadm5srv-exports.def \
+ kadm5_err.et \
+ iprop-commands.in \
+ $(man_MANS) \
+ check-cracklib.pl \
+ flush.c \
+ sample_passwd_check.c \
+ sample_hook.c \
+ version-script.map \
+ version-script-client.map
diff --git a/third_party/heimdal/lib/kadm5/NTMakefile b/third_party/heimdal/lib/kadm5/NTMakefile
new file mode 100644
index 0000000..9357a7f
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/NTMakefile
@@ -0,0 +1,296 @@
+########################################################################
+#
+# Copyright (c) 2009, Secure Endpoints Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+RELDIR=lib\kadm5
+
+!include ../../windows/NTMakefile.w32
+
+dist_libkadm5clnt_la_SOURCES = \
+ ad.c \
+ chpass_c.c \
+ client_glue.c \
+ common_glue.c \
+ create_c.c \
+ delete_c.c \
+ destroy_c.c \
+ flush_c.c \
+ free.c \
+ get_c.c \
+ get_princs_c.c \
+ init_c.c \
+ kadm5_locl.h \
+ marshall.c \
+ modify_c.c \
+ private.h \
+ privs_c.c \
+ prune_c.c \
+ randkey_c.c \
+ rename_c.c \
+ send_recv.c \
+ kadm5-pwcheck.h \
+ admin.h \
+ kadm5-hook.h
+
+dist_libkadm5srv_la_SOURCES = \
+ acl.c \
+ admin.h \
+ bump_pw_expire.c \
+ chpass_s.c \
+ common_glue.c \
+ context_s.c \
+ create_s.c \
+ delete_s.c \
+ destroy_s.c \
+ ent_setup.c \
+ error.c \
+ flush_s.c \
+ free.c \
+ get_princs_s.c \
+ get_s.c \
+ init_s.c \
+ kadm5_locl.h \
+ keys.c \
+ log.c \
+ marshall.c \
+ modify_s.c \
+ password_quality.c \
+ private.h \
+ privs_s.c \
+ prune_s.c \
+ randkey_s.c \
+ rename_s.c \
+ server_glue.c \
+ server_hooks.c \
+ set_keys.c \
+ setkey3_s.c \
+ set_modifier.c \
+ kadm5-pwcheck.h \
+ admin.h \
+ kadm5-hook.h
+
+LIBKADM5CLNT_OBJS= \
+ $(OBJ)\ad.obj \
+ $(OBJ)\chpass_c.obj \
+ $(OBJ)\client_glue.obj \
+ $(OBJ)\common_glue.obj \
+ $(OBJ)\create_c.obj \
+ $(OBJ)\delete_c.obj \
+ $(OBJ)\destroy_c.obj \
+ $(OBJ)\flush_c.obj \
+ $(OBJ)\free.obj \
+ $(OBJ)\get_c.obj \
+ $(OBJ)\get_princs_c.obj \
+ $(OBJ)\init_c.obj \
+ $(OBJ)\marshall.obj \
+ $(OBJ)\modify_c.obj \
+ $(OBJ)\privs_c.obj \
+ $(OBJ)\prune_c.obj \
+ $(OBJ)\randkey_c.obj \
+ $(OBJ)\rename_c.obj \
+ $(OBJ)\send_recv.obj \
+ $(OBJ)\kadm5_err.obj
+
+LIBKADM5SRV_OBJS= \
+ $(OBJ)\acl.obj \
+ $(OBJ)\bump_pw_expire.obj \
+ $(OBJ)\chpass_s.obj \
+ $(OBJ)\common_glue.obj \
+ $(OBJ)\context_s.obj \
+ $(OBJ)\create_s.obj \
+ $(OBJ)\delete_s.obj \
+ $(OBJ)\destroy_s.obj \
+ $(OBJ)\ent_setup.obj \
+ $(OBJ)\error.obj \
+ $(OBJ)\flush_s.obj \
+ $(OBJ)\free.obj \
+ $(OBJ)\get_princs_s.obj \
+ $(OBJ)\get_s.obj \
+ $(OBJ)\init_s.obj \
+ $(OBJ)\keys.obj \
+ $(OBJ)\log.obj \
+ $(OBJ)\marshall.obj \
+ $(OBJ)\modify_s.obj \
+ $(OBJ)\password_quality.obj \
+ $(OBJ)\privs_s.obj \
+ $(OBJ)\prune_s.obj \
+ $(OBJ)\randkey_s.obj \
+ $(OBJ)\rename_s.obj \
+ $(OBJ)\server_glue.obj \
+ $(OBJ)\server_hooks.obj \
+ $(OBJ)\set_keys.obj \
+ $(OBJ)\setkey3_s.obj \
+ $(OBJ)\set_modifier.obj \
+ $(OBJ)\kadm5_err.obj
+
+
+proto_opts = -q -R "^(_|kadm5_c_|kadm5_s_|kadm5_log)" -P remove
+
+$(OBJ)\kadm5-protos.h: $(dist_libkadm5srv_la_SOURCES) $(dist_libkadm5clnt_la_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl $(proto_opts) \
+ -o $@ \
+ $(dist_libkadm5srv_la_SOURCES) \
+ $(dist_libkadm5clnt_la_SOURCES) \
+ || $(RM) $@
+
+$(OBJ)\kadm5-private.h: $(dist_libkadm5srv_la_SOURCES) $(dist_libkadm5clnt_la_SOURCES)
+ $(PERL) ..\..\cf\make-proto.pl $(proto_opts) \
+ -p $@ \
+ $(dist_libkadm5srv_la_SOURCES) \
+ $(dist_libkadm5clnt_la_SOURCES) \
+ || $(RM) $@
+
+$(OBJ)\iprop-commands.c $(OBJ)\iprop-commands.h: iprop-commands.in
+ cd $(OBJ)
+ $(CP) $(SRCDIR)\iprop-commands.in $(OBJ)
+ $(BINDIR)\slc.exe iprop-commands.in
+ cd $(SRCDIR)
+
+$(OBJ)\kadm5_err.h $(OBJ)kadm5_err.c: kadm5_err.et
+ cd $(OBJ)
+ $(BINDIR)\compile_et.exe $(SRCDIR)\kadm5_err.et
+ cd $(SRCDIR)
+
+$(KADM5INCDIR)\kadm5_err.h: $(OBJ)\kadm5_err.h
+
+KADM5INCDIR=$(INCDIR)\kadm5
+
+INCFILES=\
+ $(KADM5INCDIR)\kadm5_err.h \
+ $(KADM5INCDIR)\admin.h \
+ $(KADM5INCDIR)\private.h \
+ $(KADM5INCDIR)\kadm5-hook.h \
+ $(KADM5INCDIR)\kadm5-protos.h \
+ $(KADM5INCDIR)\kadm5-private.h \
+ $(OBJ)\iprop-commands.h
+
+SBINPROGRAMS=$(SBINDIR)\iprop-log.exe
+
+LIBEXECPROGRAMS=$(LIBEXECDIR)\ipropd-master.exe $(LIBEXECDIR)\ipropd-slave.exe
+
+EXELIBDEPS= \
+ $(LIBKADM5SRV) \
+ $(LIBROKEN) \
+ $(LIBHEIMDAL) \
+ $(LIBHDB) \
+ $(LIBSQLITE) \
+ $(LIBSL) \
+ $(LIBCOMERR) \
+ $(LIBVERS)
+
+$(SBINDIR)\iprop-log.exe: $(OBJ)\iprop-log.obj $(OBJ)\iprop-commands.obj $(EXELIBDEPS) \
+ $(OBJ)\iprop-log-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(LIBEXECDIR)\ipropd-master.exe: $(OBJ)\ipropd_master.obj $(OBJ)\ipropd_common.obj \
+ $(EXELIBDEPS) $(OBJ)\ipropd-master-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(LIBEXECDIR)\ipropd-slave.exe: $(OBJ)\ipropd_slave.obj $(OBJ)\ipropd_common.obj \
+ $(EXELIBDEPS) $(OBJ)\ipropd-slave-version.res
+ $(EXECONLINK)
+ $(EXEPREP)
+
+$(LIBKADM5CLNT): $(LIBKADM5CLNT_OBJS)
+ $(LIBCON)
+
+LIBKADM5SRVRES=$(OBJ)\libkadm5srv-version.res
+
+$(LIBKADM5SRV): $(BINDIR)\libkadm5srv.dll
+
+$(BINDIR)\libkadm5srv.dll: $(LIBKADM5SRV_OBJS) $(LIBHEIMDAL) $(LIBROKEN) $(LIBHDB) $(LIBCOMERR) $(LIBSQLITE) $(LIBKADM5SRVRES) $(LIBHEIMBASE)
+ $(DLLGUILINK) -implib:$(LIBKADM5SRV) -def:libkadm5srv-exports.def
+ $(DLLPREP_NODIST)
+
+all:: $(INCFILES) $(LIBKADM5SRV) $(LIBKADM5CLNT)
+
+all-tools:: $(SBINPROGRAMS) $(LIBEXECPROGRAMS)
+
+clean::
+ -$(RM) $(INCFILES)
+ -$(RM) $(LIBKADM5CLNT)
+ -$(RM) $(LIBKADM5SRV)
+ -$(RM) $(BINDIR)\libkadm5srv.*
+ -$(RM) $(SBINPROGRAMS:.exe=.*)
+ -$(RM) $(LIBEXECPROGRAMS:.exe=.*)
+
+test:: test-binaries test-run
+
+test-binaries: \
+ $(OBJ)\default_keys.exe \
+ $(OBJ)\test_pw_quality.exe \
+ $(OBJ)\sample_passwd_check.dll \
+ $(OBJ)\sample_hook.dll
+
+$(OBJ)\default_keys.exe: $(OBJ)\default_keys.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHDB)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+$(OBJ)\test_pw_quality.exe: $(OBJ)\test_pw_quality.obj \
+ $(LIBROKEN) $(LIBKADM5SRV) $(LIBVERS) $(LIBHEIMDAL)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+$(OBJ)\sample_passwd_check.dll: $(OBJ)\sample_passwd_check.obj $(LIBHEIMDAL)
+ $(DLLGUILINK) /DEF:<<
+EXPORTS
+ version DATA
+ check_length
+<<
+ $(DLLPREP_NODIST)
+
+$(OBJ)\sample_hook.dll: $(OBJ)\sample_hook.obj $(LIBKADM5SRV) $(LIBHEIMDAL)
+ $(DLLGUILINK) /DEF:<<
+EXPORTS
+ kadm5_hook_plugin_load
+<<
+ $(DLLPREP_NODIST)
+
+test-run:
+ cd $(OBJ)
+ -default_keys.exe
+ -test_pw_quality.exe
+ cd $(SRCDIR)
+
+{$(OBJ)}.h{$(KADM5INCDIR)}.h:
+ $(CP) $< $@
+
+{}.h{$(KADM5INCDIR)}.h:
+ $(CP) $< $@
+
+{}.c{$(OBJ)}.obj::
+ $(C2OBJ_P) -I$(OBJ) -I$(KADM5INCDIR)
+
+test-exports:
+ $(PERL) ..\..\cf\w32-check-exported-symbols.pl --vs version-script.map --def libkadm5srv-exports.def
+
+test:: test-exports
diff --git a/third_party/heimdal/lib/kadm5/acl.c b/third_party/heimdal/lib/kadm5/acl.c
new file mode 100644
index 0000000..f71ed99
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/acl.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+static struct units acl_units[] = {
+ { "all", KADM5_PRIV_ALL },
+ { "change-password",KADM5_PRIV_CPW },
+ { "cpw", KADM5_PRIV_CPW },
+ { "list", KADM5_PRIV_LIST },
+ { "delete", KADM5_PRIV_DELETE },
+ { "modify", KADM5_PRIV_MODIFY },
+ { "add", KADM5_PRIV_ADD },
+ { "get", KADM5_PRIV_GET },
+ { "get-keys", KADM5_PRIV_GET_KEYS },
+ { NULL, 0 }
+};
+
+kadm5_ret_t
+_kadm5_string_to_privs(const char *s, uint32_t* privs)
+{
+ int flags;
+ flags = parse_flags(s, acl_units, 0);
+ if(flags < 0)
+ return KADM5_FAILURE;
+ *privs = flags;
+ return 0;
+}
+
+kadm5_ret_t
+_kadm5_privs_to_string(uint32_t privs, char *string, size_t len)
+{
+ if(privs == 0)
+ strlcpy(string, "none", len);
+ else
+ unparse_flags(privs, acl_units + 1, string, len);
+ return 0;
+}
+
+/*
+ * retrieve the right for the current caller on `princ' (NULL means all)
+ * and store them in `ret_flags'
+ * return 0 or an error.
+ */
+
+static kadm5_ret_t
+fetch_acl (kadm5_server_context *context,
+ krb5_const_principal princ,
+ unsigned *ret_flags)
+{
+ FILE *f;
+ krb5_error_code ret = 0;
+ char buf[256];
+
+ *ret_flags = 0;
+
+ /* no acl file -> no rights */
+ f = fopen(context->config.acl_file, "r");
+ if (f == NULL)
+ return 0;
+
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ char *foo = NULL, *p;
+ krb5_principal this_princ;
+ unsigned flags = 0;
+
+ p = strtok_r(buf, " \t\n", &foo);
+ if(p == NULL)
+ continue;
+ if (*p == '#') /* comment */
+ continue;
+ ret = krb5_parse_name(context->context, p, &this_princ);
+ if(ret)
+ break;
+ if(!krb5_principal_compare(context->context,
+ context->caller, this_princ)) {
+ krb5_free_principal(context->context, this_princ);
+ continue;
+ }
+ krb5_free_principal(context->context, this_princ);
+ p = strtok_r(NULL, " \t\n", &foo);
+ if(p == NULL)
+ continue;
+ ret = _kadm5_string_to_privs(p, &flags);
+ if (ret)
+ break;
+ p = strtok_r(NULL, " \t\n", &foo);
+ if (p == NULL) {
+ *ret_flags = flags;
+ break;
+ }
+ if (princ != NULL) {
+ krb5_principal pattern_princ;
+ krb5_boolean match;
+ const char *c0 = krb5_principal_get_comp_string(context->context,
+ princ, 0);
+ const char *pat_c0;
+
+ ret = krb5_parse_name(context->context, p, &pattern_princ);
+ if (ret)
+ break;
+ pat_c0 = krb5_principal_get_comp_string(context->context,
+ pattern_princ, 0);
+ match = krb5_principal_match(context->context,
+ princ, pattern_princ);
+
+ /*
+ * If `princ' is a WELLKNOWN name, then require the WELLKNOWN label
+ * be matched exactly.
+ *
+ * FIXME: We could do something similar for krbtgt and kadmin other
+ * principal types.
+ */
+ if (match && c0 && strcmp(c0, "WELLKNOWN") == 0 &&
+ (!pat_c0 || strcmp(pat_c0, "WELLKNOWN") != 0))
+ match = FALSE;
+ krb5_free_principal(context->context, pattern_princ);
+ if (match) {
+ *ret_flags = flags;
+ break;
+ }
+ }
+ }
+ fclose(f);
+ return ret;
+}
+
+krb5_boolean
+_kadm5_is_kadmin_service_p(kadm5_server_context *context)
+{
+ krb5_boolean ret;
+ krb5_principal princ;
+
+ if (krb5_parse_name(context->context, KADM5_ADMIN_SERVICE, &princ) != 0)
+ return FALSE;
+
+ ret = krb5_principal_compare(context->context, context->caller, princ);
+ krb5_free_principal(context->context, princ);
+
+ return ret;
+}
+
+/*
+ * set global acl flags in `context' for the current caller.
+ * return 0 on success or an error
+ */
+
+kadm5_ret_t
+_kadm5_acl_init(kadm5_server_context *context)
+{
+ if (_kadm5_is_kadmin_service_p(context)) {
+ context->acl_flags = KADM5_PRIV_ALL;
+ return 0;
+ }
+
+ return fetch_acl (context, NULL, &context->acl_flags);
+}
+
+/*
+ * check if `flags' allows `op'
+ * return 0 if OK or an error
+ */
+
+static kadm5_ret_t
+check_flags (unsigned op,
+ unsigned flags)
+{
+ unsigned res = ~flags & op;
+
+ if(res & KADM5_PRIV_GET)
+ return KADM5_AUTH_GET;
+ if(res & KADM5_PRIV_GET_KEYS)
+ return KADM5_AUTH_GET_KEYS;
+ if(res & KADM5_PRIV_ADD)
+ return KADM5_AUTH_ADD;
+ if(res & KADM5_PRIV_MODIFY)
+ return KADM5_AUTH_MODIFY;
+ if(res & KADM5_PRIV_DELETE)
+ return KADM5_AUTH_DELETE;
+ if(res & KADM5_PRIV_CPW)
+ return KADM5_AUTH_CHANGEPW;
+ if(res & KADM5_PRIV_LIST)
+ return KADM5_AUTH_LIST;
+ if(res)
+ return KADM5_AUTH_INSUFFICIENT;
+ return 0;
+}
+
+/*
+ * return 0 if the current caller in `context' is allowed to perform
+ * `op' on `princ' and otherwise an error
+ * princ == NULL if it's not relevant.
+ */
+
+kadm5_ret_t
+_kadm5_acl_check_permission(kadm5_server_context *context,
+ unsigned op,
+ krb5_const_principal princ)
+{
+ kadm5_ret_t ret;
+ unsigned princ_flags;
+
+ ret = check_flags (op, context->acl_flags);
+ if (ret == 0)
+ return ret;
+ ret = fetch_acl (context, princ, &princ_flags);
+ if (ret)
+ return ret;
+ return check_flags (op, princ_flags);
+}
diff --git a/third_party/heimdal/lib/kadm5/ad.c b/third_party/heimdal/lib/kadm5/ad.c
new file mode 100644
index 0000000..b9b9c90
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ad.c
@@ -0,0 +1,1525 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define HAVE_TSASL 1
+
+#include "kadm5_locl.h"
+#if 1
+#undef OPENLDAP
+#undef HAVE_TSASL
+#endif
+#ifdef OPENLDAP
+#include <ldap.h>
+#ifdef HAVE_TSASL
+#include <tsasl.h>
+#endif
+#include <resolve.h>
+#include <base64.h>
+#endif
+
+RCSID("$Id$");
+
+#ifdef OPENLDAP
+
+#define CTX2LP(context) ((LDAP *)((context)->ldap_conn))
+#define CTX2BASE(context) ((context)->base_dn)
+
+/*
+ * userAccountControl
+ */
+
+#define UF_SCRIPT 0x00000001
+#define UF_ACCOUNTDISABLE 0x00000002
+#define UF_UNUSED_0 0x00000004
+#define UF_HOMEDIR_REQUIRED 0x00000008
+#define UF_LOCKOUT 0x00000010
+#define UF_PASSWD_NOTREQD 0x00000020
+#define UF_PASSWD_CANT_CHANGE 0x00000040
+#define UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED 0x00000080
+#define UF_TEMP_DUPLICATE_ACCOUNT 0x00000100
+#define UF_NORMAL_ACCOUNT 0x00000200
+#define UF_UNUSED_1 0x00000400
+#define UF_INTERDOMAIN_TRUST_ACCOUNT 0x00000800
+#define UF_WORKSTATION_TRUST_ACCOUNT 0x00001000
+#define UF_SERVER_TRUST_ACCOUNT 0x00002000
+#define UF_UNUSED_2 0x00004000
+#define UF_UNUSED_3 0x00008000
+#define UF_PASSWD_NOT_EXPIRE 0x00010000
+#define UF_MNS_LOGON_ACCOUNT 0x00020000
+#define UF_SMARTCARD_REQUIRED 0x00040000
+#define UF_TRUSTED_FOR_DELEGATION 0x00080000
+#define UF_NOT_DELEGATED 0x00100000
+#define UF_USE_DES_KEY_ONLY 0x00200000
+#define UF_DONT_REQUIRE_PREAUTH 0x00400000
+#define UF_UNUSED_4 0x00800000
+#define UF_UNUSED_5 0x01000000
+#define UF_UNUSED_6 0x02000000
+#define UF_UNUSED_7 0x04000000
+#define UF_UNUSED_8 0x08000000
+#define UF_UNUSED_9 0x10000000
+#define UF_UNUSED_10 0x20000000
+#define UF_UNUSED_11 0x40000000
+#define UF_UNUSED_12 0x80000000
+
+/*
+ *
+ */
+
+#ifndef HAVE_TSASL
+static int
+sasl_interact(LDAP *ld, unsigned flags, void *defaults, void *interact)
+{
+ return LDAP_SUCCESS;
+}
+#endif
+
+#if 0
+static Sockbuf_IO ldap_tsasl_io = {
+ NULL, /* sbi_setup */
+ NULL, /* sbi_remove */
+ NULL, /* sbi_ctrl */
+ NULL, /* sbi_read */
+ NULL, /* sbi_write */
+ NULL /* sbi_close */
+};
+#endif
+
+#ifdef HAVE_TSASL
+static int
+ldap_tsasl_bind_s(LDAP *ld,
+ LDAP_CONST char *dn,
+ LDAPControl **serverControls,
+ LDAPControl **clientControls,
+ const char *host)
+{
+ char *attrs[] = { "supportedSASLMechanisms", NULL };
+ struct tsasl_peer *peer = NULL;
+ struct tsasl_buffer in, out;
+ struct berval ccred, *scred;
+ LDAPMessage *m, *m0;
+ const char *mech;
+ char **vals;
+ int ret, rc;
+
+ ret = tsasl_peer_init(TSASL_FLAGS_INITIATOR | TSASL_FLAGS_CLEAR,
+ "ldap", host, &peer);
+ if (ret != TSASL_DONE) {
+ rc = LDAP_LOCAL_ERROR;
+ goto out;
+ }
+
+ rc = ldap_search_s(ld, "", LDAP_SCOPE_BASE, NULL, attrs, 0, &m0);
+ if (rc != LDAP_SUCCESS)
+ goto out;
+
+ m = ldap_first_entry(ld, m0);
+ if (m == NULL) {
+ ldap_msgfree(m0);
+ goto out;
+ }
+
+ vals = ldap_get_values(ld, m, "supportedSASLMechanisms");
+ if (vals == NULL) {
+ ldap_msgfree(m0);
+ goto out;
+ }
+
+ ret = tsasl_find_best_mech(peer, vals, &mech);
+ if (ret) {
+ ldap_msgfree(m0);
+ goto out;
+ }
+
+ ldap_msgfree(m0);
+
+ ret = tsasl_select_mech(peer, mech);
+ if (ret != TSASL_DONE) {
+ rc = LDAP_LOCAL_ERROR;
+ goto out;
+ }
+
+ in.tb_data = NULL;
+ in.tb_size = 0;
+
+ do {
+ ret = tsasl_request(peer, &in, &out);
+ if (in.tb_size != 0) {
+ free(in.tb_data);
+ in.tb_data = NULL;
+ in.tb_size = 0;
+ }
+ if (ret != TSASL_DONE && ret != TSASL_CONTINUE) {
+ rc = LDAP_AUTH_UNKNOWN;
+ goto out;
+ }
+
+ ccred.bv_val = out.tb_data;
+ ccred.bv_len = out.tb_size;
+
+ rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
+ serverControls, clientControls, &scred);
+ tsasl_buffer_free(&out);
+
+ if (rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS) {
+ if(scred && scred->bv_len)
+ ber_bvfree(scred);
+ goto out;
+ }
+
+ in.tb_data = malloc(scred->bv_len);
+ if (in.tb_data == NULL) {
+ rc = LDAP_LOCAL_ERROR;
+ goto out;
+ }
+ memcpy(in.tb_data, scred->bv_val, scred->bv_len);
+ in.tb_size = scred->bv_len;
+ ber_bvfree(scred);
+
+ } while (rc == LDAP_SASL_BIND_IN_PROGRESS);
+
+ out:
+ if (rc == LDAP_SUCCESS) {
+#if 0
+ ber_sockbuf_add_io(ld->ld_conns->lconn_sb, &ldap_tsasl_io,
+ LBER_SBIOD_LEVEL_APPLICATION, peer);
+
+#endif
+ } else if (peer != NULL)
+ tsasl_peer_free(peer);
+
+ return rc;
+}
+#endif /* HAVE_TSASL */
+
+
+static int
+check_ldap(kadm5_ad_context *context, int ret)
+{
+ switch (ret) {
+ case LDAP_SUCCESS:
+ return 0;
+ case LDAP_SERVER_DOWN: {
+ LDAP *lp = CTX2LP(context);
+ ldap_unbind(lp);
+ context->ldap_conn = NULL;
+ free(context->base_dn);
+ context->base_dn = NULL;
+ return 1;
+ }
+ default:
+ return 1;
+ }
+}
+
+/*
+ *
+ */
+
+static void
+laddattr(char ***al, int *attrlen, char *attr)
+{
+ char **a;
+ a = realloc(*al, (*attrlen + 2) * sizeof(**al));
+ if (a == NULL)
+ return;
+ a[*attrlen] = attr;
+ a[*attrlen + 1] = NULL;
+ (*attrlen)++;
+ *al = a;
+}
+
+static kadm5_ret_t
+_kadm5_ad_connect(void *server_handle)
+{
+ kadm5_ad_context *context = server_handle;
+ struct {
+ char *server;
+ int port;
+ } *s, *servers = NULL;
+ int i, num_servers = 0;
+
+ if (context->ldap_conn)
+ return 0;
+
+ {
+ struct dns_reply *r;
+ struct resource_record *rr;
+ char *domain;
+
+ asprintf(&domain, "_ldap._tcp.%s", context->realm);
+ if (domain == NULL) {
+ krb5_set_error_message(context->context, KADM5_NO_SRV, "malloc");
+ return KADM5_NO_SRV;
+ }
+
+ r = dns_lookup(domain, "SRV");
+ free(domain);
+ if (r == NULL) {
+ krb5_set_error_message(context->context, KADM5_NO_SRV, "Didn't find ldap dns");
+ return KADM5_NO_SRV;
+ }
+
+ for (rr = r->head ; rr != NULL; rr = rr->next) {
+ if (rr->type != rk_ns_t_srv)
+ continue;
+ s = realloc(servers, sizeof(*servers) * (num_servers + 1));
+ if (s == NULL) {
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "malloc");
+ dns_free_data(r);
+ goto fail;
+ }
+ servers = s;
+ num_servers++;
+ servers[num_servers - 1].port = rr->u.srv->port;
+ servers[num_servers - 1].server = strdup(rr->u.srv->target);
+ }
+ dns_free_data(r);
+ }
+
+ if (num_servers == 0) {
+ krb5_set_error_message(context->context, KADM5_NO_SRV, "No AD server found in DNS");
+ return KADM5_NO_SRV;
+ }
+
+ for (i = 0; i < num_servers; i++) {
+ int lret, version = LDAP_VERSION3;
+ LDAP *lp;
+
+ lp = ldap_init(servers[i].server, servers[i].port);
+ if (lp == NULL)
+ continue;
+
+ if (ldap_set_option(lp, LDAP_OPT_PROTOCOL_VERSION, &version)) {
+ ldap_unbind(lp);
+ continue;
+ }
+
+ if (ldap_set_option(lp, LDAP_OPT_REFERRALS, LDAP_OPT_OFF)) {
+ ldap_unbind(lp);
+ continue;
+ }
+
+#ifdef HAVE_TSASL
+ lret = ldap_tsasl_bind_s(lp, NULL, NULL, NULL, servers[i].server);
+
+#else
+ lret = ldap_sasl_interactive_bind_s(lp, NULL, NULL, NULL, NULL,
+ LDAP_SASL_QUIET,
+ sasl_interact, NULL);
+#endif
+ if (lret != LDAP_SUCCESS) {
+ krb5_set_error_message(context->context, 0,
+ "Couldn't contact any AD servers: %s",
+ ldap_err2string(lret));
+ ldap_unbind(lp);
+ continue;
+ }
+
+ context->ldap_conn = lp;
+ break;
+ }
+ if (i >= num_servers) {
+ goto fail;
+ }
+
+ {
+ LDAPMessage *m, *m0;
+ char **attr = NULL;
+ int attrlen = 0;
+ char **vals;
+ int ret;
+
+ laddattr(&attr, &attrlen, "defaultNamingContext");
+
+ ret = ldap_search_s(CTX2LP(context), "", LDAP_SCOPE_BASE,
+ "objectclass=*", attr, 0, &m);
+ free(attr);
+ if (check_ldap(context, ret))
+ goto fail;
+
+ if (ldap_count_entries(CTX2LP(context), m) > 0) {
+ m0 = ldap_first_entry(CTX2LP(context), m);
+ if (m0 == NULL) {
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR,
+ "Error in AD ldap responce");
+ ldap_msgfree(m);
+ goto fail;
+ }
+ vals = ldap_get_values(CTX2LP(context),
+ m0, "defaultNamingContext");
+ if (vals == NULL) {
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR,
+ "No naming context found");
+ goto fail;
+ }
+ context->base_dn = strdup(vals[0]);
+ } else
+ goto fail;
+ ldap_msgfree(m);
+ }
+
+ for (i = 0; i < num_servers; i++)
+ free(servers[i].server);
+ free(servers);
+
+ return 0;
+
+ fail:
+ for (i = 0; i < num_servers; i++)
+ free(servers[i].server);
+ free(servers);
+
+ if (context->ldap_conn) {
+ ldap_unbind(CTX2LP(context));
+ context->ldap_conn = NULL;
+ }
+ return KADM5_RPC_ERROR;
+}
+
+#define NTTIME_EPOCH 0x019DB1DED53E8000LL
+
+static time_t
+nt2unixtime(const char *str)
+{
+ unsigned long long t;
+ t = strtoll(str, NULL, 10);
+ t = ((t - NTTIME_EPOCH) / (long long)10000000);
+ if (t > (((time_t)(~(long long)0)) >> 1))
+ return 0;
+ return (time_t)t;
+}
+
+static long long
+unix2nttime(time_t unix_time)
+{
+ long long wt;
+ wt = unix_time * (long long)10000000 + (long long)NTTIME_EPOCH;
+ return wt;
+}
+
+/* XXX create filter in a better way */
+
+static int
+ad_find_entry(kadm5_ad_context *context,
+ const char *fqdn,
+ const char *pn,
+ char **name)
+{
+ LDAPMessage *m, *m0;
+ char *attr[] = { "distinguishedName", NULL };
+ char *filter;
+ int ret;
+
+ if (name)
+ *name = NULL;
+
+ if (fqdn)
+ asprintf(&filter,
+ "(&(objectClass=computer)(|(dNSHostName=%s)(servicePrincipalName=%s)))",
+ fqdn, pn);
+ else if(pn)
+ asprintf(&filter, "(&(objectClass=account)(userPrincipalName=%s))", pn);
+ else
+ return KADM5_RPC_ERROR;
+
+ ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
+ LDAP_SCOPE_SUBTREE,
+ filter, attr, 0, &m);
+ free(filter);
+ if (check_ldap(context, ret))
+ return KADM5_RPC_ERROR;
+
+ if (ldap_count_entries(CTX2LP(context), m) > 0) {
+ char **vals;
+ m0 = ldap_first_entry(CTX2LP(context), m);
+ vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
+ if (vals == NULL || vals[0] == NULL) {
+ ldap_msgfree(m);
+ return KADM5_RPC_ERROR;
+ }
+ if (name)
+ *name = strdup(vals[0]);
+ ldap_msgfree(m);
+ } else
+ return KADM5_UNK_PRINC;
+
+ return 0;
+}
+
+#endif /* OPENLDAP */
+
+static kadm5_ret_t
+ad_get_cred(kadm5_ad_context *context, const char *password)
+{
+ kadm5_ret_t ret;
+ krb5_ccache cc;
+ char *service;
+ int aret;
+
+ if (context->ccache)
+ return 0;
+
+ aret = asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME,
+ context->realm, context->realm);
+ if (aret == -1 || service == NULL)
+ return krb5_enomem(context->context);
+
+ ret = _kadm5_c_get_cred_cache(context->context,
+ context->client_name,
+ service,
+ password, krb5_prompter_posix,
+ NULL, NULL, &cc);
+ free(service);
+ if(ret)
+ return ret; /* XXX */
+ context->ccache = cc;
+ return 0;
+}
+
+static kadm5_ret_t
+kadm5_ad_chpass_principal(void *server_handle,
+ krb5_principal principal,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ kadm5_ad_context *context = server_handle;
+ krb5_data result_code_string, result_string;
+ int result_code;
+ kadm5_ret_t ret;
+
+ if (keepold)
+ return KADM5_KEEPOLD_NOSUPP;
+
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
+ ret = ad_get_cred(context, NULL);
+ if (ret)
+ return ret;
+
+ krb5_data_zero (&result_code_string);
+ krb5_data_zero (&result_string);
+
+ ret = krb5_set_password_using_ccache (context->context,
+ context->ccache,
+ password,
+ principal,
+ &result_code,
+ &result_code_string,
+ &result_string);
+
+ krb5_data_free (&result_code_string);
+ krb5_data_free (&result_string);
+
+ /* XXX do mapping here on error codes */
+
+ return ret;
+}
+
+#ifdef OPENLDAP
+static const char *
+get_fqdn(krb5_context context, const krb5_principal p)
+{
+ const char *s, *hosttypes[] = { "host", "ldap", "gc", "cifs", "dns" };
+ int i;
+
+ s = krb5_principal_get_comp_string(context, p, 0);
+ if (p == NULL)
+ return NULL;
+
+ for (i = 0; i < sizeof(hosttypes)/sizeof(hosttypes[0]); i++) {
+ if (strcasecmp(s, hosttypes[i]) == 0)
+ return krb5_principal_get_comp_string(context, p, 1);
+ }
+ return 0;
+}
+#endif
+
+
+static kadm5_ret_t
+kadm5_ad_create_principal(void *server_handle,
+ kadm5_principal_ent_t entry,
+ uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ kadm5_ad_context *context = server_handle;
+
+ /*
+ * KADM5_PRINC_EXPIRE_TIME
+ *
+ * return 0 || KADM5_DUP;
+ */
+
+#ifdef OPENLDAP
+ LDAPMod *attrs[8], rattrs[7], *a;
+ char *useraccvals[2] = { NULL, NULL },
+ *samvals[2], *dnsvals[2], *spnvals[5], *upnvals[2], *tv[2];
+ char *ocvals_spn[] = { "top", "person", "organizationalPerson",
+ "user", "computer", NULL};
+ char *p, *realmless_p, *p_msrealm = NULL, *dn = NULL;
+ const char *fqdn;
+ char *s, *samname = NULL, *short_spn = NULL;
+ int ret, i;
+ int32_t uf_flags = 0;
+
+ if ((mask & KADM5_PRINCIPAL) == 0)
+ return KADM5_BAD_MASK;
+
+ /*
+ * We should get around to implementing this... At the moment, the
+ * the server side API is implemented but the wire protocol has not
+ * been updated.
+ */
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
+ for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
+ attrs[i] = &rattrs[i];
+ attrs[i] = NULL;
+
+ ret = ad_get_cred(context, NULL);
+ if (ret)
+ return ret;
+
+ ret = _kadm5_ad_connect(server_handle);
+ if (ret)
+ return ret;
+
+ fqdn = get_fqdn(context->context, entry->principal);
+
+ ret = krb5_unparse_name(context->context, entry->principal, &p);
+ if (ret)
+ return ret;
+
+ if (ad_find_entry(context, fqdn, p, NULL) == 0) {
+ free(p);
+ return KADM5_DUP;
+ }
+
+ if (mask & KADM5_ATTRIBUTES) {
+ if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
+ uf_flags |= UF_ACCOUNTDISABLE|UF_LOCKOUT;
+ if ((entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH) == 0)
+ uf_flags |= UF_DONT_REQUIRE_PREAUTH;
+ if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
+ uf_flags |= UF_SMARTCARD_REQUIRED;
+ }
+
+ realmless_p = strdup(p);
+ if (realmless_p == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ s = strrchr(realmless_p, '@');
+ if (s)
+ *s = '\0';
+
+ if (fqdn) {
+ /* create computer account */
+ asprintf(&samname, "%s$", fqdn);
+ if (samname == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ s = strchr(samname, '.');
+ if (s) {
+ s[0] = '$';
+ s[1] = '\0';
+ }
+
+ short_spn = strdup(p);
+ if (short_spn == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ s = strchr(short_spn, '.');
+ if (s) {
+ *s = '\0';
+ } else {
+ free(short_spn);
+ short_spn = NULL;
+ }
+
+ p_msrealm = strdup(p);
+ if (p_msrealm == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ s = strrchr(p_msrealm, '@');
+ if (s) {
+ *s = '/';
+ } else {
+ free(p_msrealm);
+ p_msrealm = NULL;
+ }
+
+ asprintf(&dn, "cn=%s, cn=Computers, %s", fqdn, CTX2BASE(context));
+ if (dn == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+
+ a = &rattrs[0];
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "objectClass";
+ a->mod_values = ocvals_spn;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "userAccountControl";
+ a->mod_values = useraccvals;
+ asprintf(&useraccvals[0], "%d",
+ uf_flags |
+ UF_PASSWD_NOT_EXPIRE |
+ UF_WORKSTATION_TRUST_ACCOUNT);
+ useraccvals[1] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "sAMAccountName";
+ a->mod_values = samvals;
+ samvals[0] = samname;
+ samvals[1] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "dNSHostName";
+ a->mod_values = dnsvals;
+ dnsvals[0] = (char *)fqdn;
+ dnsvals[1] = NULL;
+ a++;
+
+ /* XXX add even more spn's */
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "servicePrincipalName";
+ a->mod_values = spnvals;
+ i = 0;
+ spnvals[i++] = p;
+ spnvals[i++] = realmless_p;
+ if (short_spn)
+ spnvals[i++] = short_spn;
+ if (p_msrealm)
+ spnvals[i++] = p_msrealm;
+ spnvals[i++] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "userPrincipalName";
+ a->mod_values = upnvals;
+ upnvals[0] = p;
+ upnvals[1] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "accountExpires";
+ a->mod_values = tv;
+ tv[0] = "9223372036854775807"; /* "never" */
+ tv[1] = NULL;
+ a++;
+
+ } else {
+ /* create user account */
+
+ a = &rattrs[0];
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "userAccountControl";
+ a->mod_values = useraccvals;
+ asprintf(&useraccvals[0], "%d",
+ uf_flags |
+ UF_PASSWD_NOT_EXPIRE);
+ useraccvals[1] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "sAMAccountName";
+ a->mod_values = samvals;
+ samvals[0] = realmless_p;
+ samvals[1] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "userPrincipalName";
+ a->mod_values = upnvals;
+ upnvals[0] = p;
+ upnvals[1] = NULL;
+ a++;
+
+ a->mod_op = LDAP_MOD_ADD;
+ a->mod_type = "accountExpires";
+ a->mod_values = tv;
+ tv[0] = "9223372036854775807"; /* "never" */
+ tv[1] = NULL;
+ a++;
+ }
+
+ attrs[a - &rattrs[0]] = NULL;
+
+ ret = ldap_add_s(CTX2LP(context), dn, attrs);
+
+ out:
+ if (useraccvals[0])
+ free(useraccvals[0]);
+ if (realmless_p)
+ free(realmless_p);
+ if (samname)
+ free(samname);
+ if (short_spn)
+ free(short_spn);
+ if (p_msrealm)
+ free(p_msrealm);
+ free(p);
+
+ if (check_ldap(context, ret))
+ return KADM5_RPC_ERROR;
+
+ return 0;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
+kadm5_ad_delete_principal(void *server_handle, krb5_principal principal)
+{
+ kadm5_ad_context *context = server_handle;
+#ifdef OPENLDAP
+ char *p, *dn = NULL;
+ const char *fqdn;
+ int ret;
+
+ ret = ad_get_cred(context, NULL);
+ if (ret)
+ return ret;
+
+ ret = _kadm5_ad_connect(server_handle);
+ if (ret)
+ return ret;
+
+ fqdn = get_fqdn(context->context, principal);
+
+ ret = krb5_unparse_name(context->context, principal, &p);
+ if (ret)
+ return ret;
+
+ if (ad_find_entry(context, fqdn, p, &dn) != 0) {
+ free(p);
+ return KADM5_UNK_PRINC;
+ }
+
+ ret = ldap_delete_s(CTX2LP(context), dn);
+
+ free(dn);
+ free(p);
+
+ if (check_ldap(context, ret))
+ return KADM5_RPC_ERROR;
+ return 0;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
+kadm5_ad_destroy(void *server_handle)
+{
+ kadm5_ad_context *context = server_handle;
+
+ if (context->ccache)
+ krb5_cc_destroy(context->context, context->ccache);
+
+#ifdef OPENLDAP
+ {
+ LDAP *lp = CTX2LP(context);
+ if (lp)
+ ldap_unbind(lp);
+ if (context->base_dn)
+ free(context->base_dn);
+ }
+#endif
+ free(context->realm);
+ free(context->client_name);
+ krb5_free_principal(context->context, context->caller);
+ if(context->my_context)
+ krb5_free_context(context->context);
+ return 0;
+}
+
+static kadm5_ret_t
+kadm5_ad_flush(void *server_handle)
+{
+ kadm5_ad_context *context = server_handle;
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+}
+
+static kadm5_ret_t
+kadm5_ad_get_principal(void *server_handle,
+ krb5_principal principal,
+ kadm5_principal_ent_t entry,
+ uint32_t mask)
+{
+ kadm5_ad_context *context = server_handle;
+#ifdef OPENLDAP
+ LDAPMessage *m, *m0;
+ char **attr = NULL;
+ int attrlen = 0;
+ char *filter, *p, *q, *u;
+ int ret;
+
+ /*
+ * principal
+ * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
+ */
+
+ /*
+ * return 0 || KADM5_DUP;
+ */
+
+ memset(entry, 0, sizeof(*entry));
+
+ if (mask & KADM5_KVNO)
+ laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
+
+ if (mask & KADM5_PRINCIPAL) {
+ laddattr(&attr, &attrlen, "userPrincipalName");
+ laddattr(&attr, &attrlen, "servicePrincipalName");
+ }
+ laddattr(&attr, &attrlen, "objectClass");
+ laddattr(&attr, &attrlen, "lastLogon");
+ laddattr(&attr, &attrlen, "badPwdCount");
+ laddattr(&attr, &attrlen, "badPasswordTime");
+ laddattr(&attr, &attrlen, "pwdLastSet");
+ laddattr(&attr, &attrlen, "accountExpires");
+ laddattr(&attr, &attrlen, "userAccountControl");
+
+ krb5_unparse_name_short(context->context, principal, &p);
+ krb5_unparse_name(context->context, principal, &u);
+
+ /* replace @ in domain part with a / */
+ q = strrchr(p, '@');
+ if (q && (p != q && *(q - 1) != '\\'))
+ *q = '/';
+
+ asprintf(&filter,
+ "(|(userPrincipalName=%s)(servicePrincipalName=%s)(servicePrincipalName=%s))",
+ u, p, u);
+ free(p);
+ free(u);
+
+ ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
+ LDAP_SCOPE_SUBTREE,
+ filter, attr, 0, &m);
+ free(attr);
+ if (check_ldap(context, ret))
+ return KADM5_RPC_ERROR;
+
+ if (ldap_count_entries(CTX2LP(context), m) > 0) {
+ char **vals;
+ m0 = ldap_first_entry(CTX2LP(context), m);
+ if (m0 == NULL) {
+ ldap_msgfree(m);
+ goto fail;
+ }
+#if 0
+ vals = ldap_get_values(CTX2LP(context), m0, "servicePrincipalName");
+ if (vals)
+ printf("servicePrincipalName %s\n", vals[0]);
+ vals = ldap_get_values(CTX2LP(context), m0, "userPrincipalName");
+ if (vals)
+ printf("userPrincipalName %s\n", vals[0]);
+ vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
+ if (vals)
+ printf("userAccountControl %s\n", vals[0]);
+#endif
+ entry->princ_expire_time = 0;
+ if (mask & KADM5_PRINC_EXPIRE_TIME) {
+ vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
+ if (vals)
+ entry->princ_expire_time = nt2unixtime(vals[0]);
+ }
+ entry->last_success = 0;
+ if (mask & KADM5_LAST_SUCCESS) {
+ vals = ldap_get_values(CTX2LP(context), m0, "lastLogon");
+ if (vals)
+ entry->last_success = nt2unixtime(vals[0]);
+ }
+ if (mask & KADM5_LAST_FAILED) {
+ vals = ldap_get_values(CTX2LP(context), m0, "badPasswordTime");
+ if (vals)
+ entry->last_failed = nt2unixtime(vals[0]);
+ }
+ if (mask & KADM5_LAST_PWD_CHANGE) {
+ vals = ldap_get_values(CTX2LP(context), m0, "pwdLastSet");
+ if (vals)
+ entry->last_pwd_change = nt2unixtime(vals[0]);
+ }
+ if (mask & KADM5_FAIL_AUTH_COUNT) {
+ vals = ldap_get_values(CTX2LP(context), m0, "badPwdCount");
+ if (vals)
+ entry->fail_auth_count = atoi(vals[0]);
+ }
+ if (mask & KADM5_ATTRIBUTES) {
+ vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
+ if (vals) {
+ uint32_t i;
+ i = atoi(vals[0]);
+ if (i & (UF_ACCOUNTDISABLE|UF_LOCKOUT))
+ entry->attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if ((i & UF_DONT_REQUIRE_PREAUTH) == 0)
+ entry->attributes |= KRB5_KDB_REQUIRES_PRE_AUTH;
+ if (i & UF_SMARTCARD_REQUIRED)
+ entry->attributes |= KRB5_KDB_REQUIRES_HW_AUTH;
+ if ((i & UF_WORKSTATION_TRUST_ACCOUNT) == 0)
+ entry->attributes |= KRB5_KDB_DISALLOW_SVR;
+ }
+ }
+ if (mask & KADM5_KVNO) {
+ vals = ldap_get_values(CTX2LP(context), m0,
+ "msDS-KeyVersionNumber");
+ if (vals)
+ entry->kvno = atoi(vals[0]);
+ else
+ entry->kvno = 0;
+ }
+ ldap_msgfree(m);
+ } else {
+ return KADM5_UNK_PRINC;
+ }
+
+ if (mask & KADM5_PRINCIPAL)
+ krb5_copy_principal(context->context, principal, &entry->principal);
+
+ return 0;
+ fail:
+ return KADM5_RPC_ERROR;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
+kadm5_ad_get_principals(void *server_handle,
+ const char *expression,
+ char ***principals,
+ int *count)
+{
+ kadm5_ad_context *context = server_handle;
+
+ /*
+ * KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES
+ */
+
+#ifdef OPENLDAP
+ kadm5_ret_t ret;
+
+ ret = ad_get_cred(context, NULL);
+ if (ret)
+ return ret;
+
+ ret = _kadm5_ad_connect(server_handle);
+ if (ret)
+ return ret;
+
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
+kadm5_ad_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ kadm5_ad_context *context = server_handle;
+
+#ifdef OPENLDAP
+ kadm5_ret_t ret;
+
+ ret = ad_get_cred(context, NULL);
+ if (ret)
+ return ret;
+
+ ret = _kadm5_ad_connect(server_handle);
+ if (ret)
+ return ret;
+
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
+kadm5_ad_get_privs(void *server_handle, uint32_t*privs)
+{
+ kadm5_ad_context *context = server_handle;
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+}
+
+static kadm5_ret_t
+kadm5_ad_modify_principal(void *server_handle,
+ kadm5_principal_ent_t entry,
+ uint32_t mask)
+{
+ kadm5_ad_context *context = server_handle;
+
+ /*
+ * KADM5_ATTRIBUTES
+ * KRB5_KDB_DISALLOW_ALL_TIX (| KADM5_KVNO)
+ */
+
+#ifdef OPENLDAP
+ LDAPMessage *m = NULL, *m0;
+ kadm5_ret_t ret;
+ char **attr = NULL;
+ int attrlen = 0;
+ char *p = NULL, *s = NULL, *q;
+ char **vals;
+ LDAPMod *attrs[4], rattrs[3], *a;
+ char *uaf[2] = { NULL, NULL };
+ char *kvno[2] = { NULL, NULL };
+ char *tv[2] = { NULL, NULL };
+ char *filter, *dn;
+ int i;
+
+ for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
+ attrs[i] = &rattrs[i];
+ attrs[i] = NULL;
+ a = &rattrs[0];
+
+ ret = _kadm5_ad_connect(server_handle);
+ if (ret)
+ return ret;
+
+ if (mask & KADM5_KVNO)
+ laddattr(&attr, &attrlen, "msDS-KeyVersionNumber");
+ if (mask & KADM5_PRINC_EXPIRE_TIME)
+ laddattr(&attr, &attrlen, "accountExpires");
+ if (mask & KADM5_ATTRIBUTES)
+ laddattr(&attr, &attrlen, "userAccountControl");
+ laddattr(&attr, &attrlen, "distinguishedName");
+
+ krb5_unparse_name(context->context, entry->principal, &p);
+
+ s = strdup(p);
+
+ q = strrchr(s, '@');
+ if (q && (p != q && *(q - 1) != '\\'))
+ *q = '\0';
+
+ asprintf(&filter,
+ "(|(userPrincipalName=%s)(servicePrincipalName=%s))",
+ s, s);
+ free(p);
+ free(s);
+
+ ret = ldap_search_s(CTX2LP(context), CTX2BASE(context),
+ LDAP_SCOPE_SUBTREE,
+ filter, attr, 0, &m);
+ free(attr);
+ free(filter);
+ if (check_ldap(context, ret))
+ return KADM5_RPC_ERROR;
+
+ if (ldap_count_entries(CTX2LP(context), m) <= 0) {
+ ret = KADM5_RPC_ERROR;
+ goto out;
+ }
+
+ m0 = ldap_first_entry(CTX2LP(context), m);
+
+ if (mask & KADM5_ATTRIBUTES) {
+ int32_t i;
+
+ vals = ldap_get_values(CTX2LP(context), m0, "userAccountControl");
+ if (vals == NULL) {
+ ret = KADM5_RPC_ERROR;
+ goto out;
+ }
+
+ i = atoi(vals[0]);
+ if (i == 0)
+ return KADM5_RPC_ERROR;
+
+ if (entry->attributes & KRB5_KDB_DISALLOW_ALL_TIX)
+ i |= (UF_ACCOUNTDISABLE|UF_LOCKOUT);
+ else
+ i &= ~(UF_ACCOUNTDISABLE|UF_LOCKOUT);
+ if (entry->attributes & KRB5_KDB_REQUIRES_PRE_AUTH)
+ i &= ~UF_DONT_REQUIRE_PREAUTH;
+ else
+ i |= UF_DONT_REQUIRE_PREAUTH;
+ if (entry->attributes & KRB5_KDB_REQUIRES_HW_AUTH)
+ i |= UF_SMARTCARD_REQUIRED;
+ else
+ i &= ~UF_SMARTCARD_REQUIRED;
+ if (entry->attributes & KRB5_KDB_DISALLOW_SVR)
+ i &= ~UF_WORKSTATION_TRUST_ACCOUNT;
+ else
+ i |= UF_WORKSTATION_TRUST_ACCOUNT;
+
+ asprintf(&uaf[0], "%d", i);
+
+ a->mod_op = LDAP_MOD_REPLACE;
+ a->mod_type = "userAccountControl";
+ a->mod_values = uaf;
+ a++;
+ }
+
+ if (mask & KADM5_KVNO) {
+ vals = ldap_get_values(CTX2LP(context), m0, "msDS-KeyVersionNumber");
+ if (vals == NULL) {
+ entry->kvno = 0;
+ } else {
+ asprintf(&kvno[0], "%d", entry->kvno);
+
+ a->mod_op = LDAP_MOD_REPLACE;
+ a->mod_type = "msDS-KeyVersionNumber";
+ a->mod_values = kvno;
+ a++;
+ }
+ }
+
+ if (mask & KADM5_PRINC_EXPIRE_TIME) {
+ long long wt;
+ vals = ldap_get_values(CTX2LP(context), m0, "accountExpires");
+ if (vals == NULL) {
+ ret = KADM5_RPC_ERROR;
+ goto out;
+ }
+
+ wt = unix2nttime(entry->princ_expire_time);
+
+ asprintf(&tv[0], "%llu", wt);
+
+ a->mod_op = LDAP_MOD_REPLACE;
+ a->mod_type = "accountExpires";
+ a->mod_values = tv;
+ a++;
+ }
+
+ vals = ldap_get_values(CTX2LP(context), m0, "distinguishedName");
+ if (vals == NULL) {
+ ret = KADM5_RPC_ERROR;
+ goto out;
+ }
+ dn = vals[0];
+
+ attrs[a - &rattrs[0]] = NULL;
+
+ ret = ldap_modify_s(CTX2LP(context), dn, attrs);
+ if (check_ldap(context, ret))
+ return KADM5_RPC_ERROR;
+
+ out:
+ if (m)
+ ldap_msgfree(m);
+ if (uaf[0])
+ free(uaf[0]);
+ if (kvno[0])
+ free(kvno[0]);
+ if (tv[0])
+ free(tv[0]);
+ return ret;
+#else
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+/*ARGSUSED*/
+static kadm5_ret_t
+kadm5_ad_randkey_principal(void *server_handle,
+ krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **keys,
+ int *n_keys)
+{
+ kadm5_ad_context *context = server_handle;
+
+ if (keepold)
+ return KADM5_KEEPOLD_NOSUPP;
+
+ /*
+ * random key
+ */
+
+#ifdef OPENLDAP
+ krb5_data result_code_string, result_string;
+ int result_code, plen;
+ kadm5_ret_t ret;
+ char *password;
+
+ *keys = NULL;
+ *n_keys = 0;
+
+ {
+ char p[64];
+ krb5_generate_random_block(p, sizeof(p));
+ plen = rk_base64_encode(p, sizeof(p), &password);
+ if (plen < 0)
+ return krb5_enomem(context->context);
+ }
+
+ ret = ad_get_cred(context, NULL);
+ if (ret) {
+ free(password);
+ return ret;
+ }
+
+ krb5_data_zero(&result_code_string);
+ krb5_data_zero(&result_string);
+
+ ret = krb5_set_password_using_ccache(context->context,
+ context->ccache,
+ password,
+ principal,
+ &result_code,
+ &result_code_string,
+ &result_string);
+ krb5_data_free(&result_code_string);
+ krb5_data_free(&result_string);
+
+ if (ret)
+ goto out;
+
+ *keys = malloc(sizeof(**keys) * 1);
+ if (*keys == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ *n_keys = 1;
+
+ ret = krb5_string_to_key(context->context,
+ ENCTYPE_ARCFOUR_HMAC_MD5,
+ password,
+ principal,
+ &(*keys)[0]);
+ if (ret) {
+ free(*keys);
+ *keys = NULL;
+ *n_keys = 0;
+ goto out;
+ }
+
+ out:
+ memset(password, 0, plen);
+ free(password);
+ return ret;
+#else
+ *keys = NULL;
+ *n_keys = 0;
+
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+#endif
+}
+
+static kadm5_ret_t
+kadm5_ad_rename_principal(void *server_handle,
+ krb5_principal from,
+ krb5_principal to)
+{
+ kadm5_ad_context *context = server_handle;
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+}
+
+static kadm5_ret_t
+kadm5_ad_chpass_principal_with_key(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_key_data,
+ krb5_key_data *key_data)
+{
+ kadm5_ad_context *context = server_handle;
+ krb5_set_error_message(context->context, KADM5_RPC_ERROR, "Function not implemented");
+ return KADM5_RPC_ERROR;
+}
+
+static kadm5_ret_t
+kadm5_ad_lock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
+static kadm5_ret_t
+kadm5_ad_unlock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
+static void
+set_funcs(kadm5_ad_context *c)
+{
+#define SET(C, F) (C)->funcs.F = kadm5_ad_ ## F
+#define SETNOTIMP(C, F) (C)->funcs.F = 0
+ SET(c, chpass_principal);
+ SET(c, chpass_principal_with_key);
+ SET(c, create_principal);
+ SET(c, delete_principal);
+ SET(c, destroy);
+ SET(c, flush);
+ SET(c, get_principal);
+ SET(c, get_principals);
+ SET(c, get_privs);
+ SET(c, modify_principal);
+ SET(c, randkey_principal);
+ SET(c, rename_principal);
+ SET(c, lock);
+ SET(c, unlock);
+ SETNOTIMP(c, setkey_principal_3);
+ SETNOTIMP(c, prune_principal);
+ SET(c, iter_principals);
+ SET(c, dup_context);
+}
+
+kadm5_ret_t
+kadm5_ad_init_with_password_ctx(krb5_context context,
+ const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ kadm5_ret_t ret;
+ kadm5_ad_context *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return krb5_enomem(context);
+ memset(ctx, 0, sizeof(*ctx));
+ set_funcs(ctx);
+
+ ctx->context = context;
+ krb5_add_et_list (context, initialize_kadm5_error_table_r);
+
+ ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
+ if(ret) {
+ free(ctx);
+ return ret;
+ }
+
+ if(realm_params->mask & KADM5_CONFIG_REALM) {
+ ret = 0;
+ ctx->realm = strdup(realm_params->realm);
+ if (ctx->realm == NULL)
+ ret = krb5_enomem(context);
+ } else
+ ret = krb5_get_default_realm(ctx->context, &ctx->realm);
+ if (ret) {
+ free(ctx);
+ return ret;
+ }
+
+ ctx->client_name = strdup(client_name);
+
+ if(password != NULL && *password != '\0')
+ ret = ad_get_cred(ctx, password);
+ else
+ ret = ad_get_cred(ctx, NULL);
+ if(ret) {
+ kadm5_ad_destroy(ctx);
+ free(ctx);
+ return ret;
+ }
+
+#ifdef OPENLDAP
+ ret = _kadm5_ad_connect(ctx);
+ if (ret) {
+ kadm5_ad_destroy(ctx);
+ free(ctx);
+ return ret;
+ }
+#endif
+
+ *server_handle = ctx;
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_ad_dup_context(void *in, void **out)
+{
+ return ENOTSUP;
+}
+
+kadm5_ret_t
+kadm5_ad_init_with_password(const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ krb5_context context;
+ kadm5_ret_t ret;
+ kadm5_ad_context *ctx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return ret;
+ ret = kadm5_ad_init_with_password_ctx(context,
+ client_name,
+ password,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+ if(ret) {
+ krb5_free_context(context);
+ return ret;
+ }
+ ctx = *server_handle;
+ ctx->my_context = 1;
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/admin.h b/third_party/heimdal/lib/kadm5/admin.h
new file mode 100644
index 0000000..530070c
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/admin.h
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1997-2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $Id$ */
+
+#ifndef __KADM5_ADMIN_H__
+#define __KADM5_ADMIN_H__
+
+#define KADM5_API_VERSION_1 1
+#define KADM5_API_VERSION_2 2
+
+#ifndef USE_KADM5_API_VERSION
+#define USE_KADM5_API_VERSION KADM5_API_VERSION_2
+#endif
+
+#if USE_KADM5_API_VERSION != KADM5_API_VERSION_2
+#error No support for API versions other than 2
+#endif
+
+#define KADM5_STRUCT_VERSION 0
+
+/* For kadm5_log_get_version_fd() */
+#define LOG_VERSION_LAST -1
+#define LOG_VERSION_FIRST 1
+#define LOG_VERSION_UBER 0
+
+#include <krb5.h>
+
+#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
+#define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002
+#define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004
+#define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008
+#define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010
+#define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020
+#define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040
+#define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080
+#define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100
+#define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200
+#define KRB5_KDB_DISALLOW_SVR 0x00001000
+#define KRB5_KDB_PWCHANGE_SERVICE 0x00002000
+#define KRB5_KDB_SUPPORT_DESMD5 0x00004000
+#define KRB5_KDB_NEW_PRINC 0x00008000
+#define KRB5_KDB_OK_AS_DELEGATE 0x00010000 /* 0x00100000 in MIT */
+#define KRB5_KDB_TRUSTED_FOR_DELEGATION 0x00020000 /* MIT has as 0x00200000 */
+#define KRB5_KDB_ALLOW_KERBEROS4 0x00040000 /* MIT doesn't have this; XXX remove */
+#define KRB5_KDB_ALLOW_DIGEST 0x00080000 /* MIT doesn't have this */
+#define KRB5_KDB_MATERIALIZE 0x00100000 /* MIT doesn't have this */
+#define KRB5_KDB_VIRTUAL_KEYS 0x00200000 /* MIT doesn't have this */
+#define KRB5_KDB_VIRTUAL 0x00400000 /* MIT doesn't have this */
+#define KRB5_KDB_DISALLOW_CLIENT 0x00800000 /* MIT doesn't have this */
+#define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x01000000 /* 0x00400000 in MIT */
+#define KRB5_KDB_AUTH_DATA_REQUIRED 0x02000000
+
+/*
+ * MIT has:
+ *
+ * - Same as our KRB5_KDB_TRUSTED_FOR_DELEGATION:
+ *
+ * #define KRB5_KDB_OK_TO_AUTH_AS_DELEGATE 0x00200000 // S4U2Self OK
+ *
+ * - Misc:
+ *
+ * #define KRB5_KDB_NO_AUTH_DATA_REQUIRED 0x00400000 // Don't lookup / add
+ * // authz data
+ * #define KRB5_KDB_LOCKDOWN_KEYS 0x00800000 // Don't allow
+ * // deletion of princ
+ */
+
+
+#define KADM5_PRINCIPAL 0x000001U
+#define KADM5_PRINC_EXPIRE_TIME 0x000002U
+#define KADM5_PW_EXPIRATION 0x000004U
+#define KADM5_LAST_PWD_CHANGE 0x000008U
+#define KADM5_ATTRIBUTES 0x000010U
+#define KADM5_MAX_LIFE 0x000020U
+#define KADM5_MOD_TIME 0x000040U
+#define KADM5_MOD_NAME 0x000080U
+#define KADM5_KVNO 0x000100U
+#define KADM5_MKVNO 0x000200U
+#define KADM5_AUX_ATTRIBUTES 0x000400U
+#define KADM5_POLICY 0x000800U
+#define KADM5_POLICY_CLR 0x001000U
+#define KADM5_MAX_RLIFE 0x002000U
+#define KADM5_LAST_SUCCESS 0x004000U
+#define KADM5_LAST_FAILED 0x008000U
+#define KADM5_FAIL_AUTH_COUNT 0x010000U
+#define KADM5_KEY_DATA 0x020000U
+#define KADM5_TL_DATA 0x040000U
+
+#define KADM5_PRINCIPAL_NORMAL_MASK (~(KADM5_KEY_DATA | KADM5_TL_DATA))
+
+#define KADM5_PW_MAX_LIFE 0x004000
+#define KADM5_PW_MIN_LIFE 0x008000
+#define KADM5_PW_MIN_LENGTH 0x010000
+#define KADM5_PW_MIN_CLASSES 0x020000
+#define KADM5_PW_HISTORY_NUM 0x040000
+#define KADM5_REF_COUNT 0x080000
+
+#define KADM5_POLICY_NORMAL_MASK (~0)
+
+#define KADM5_ADMIN_SERVICE "kadmin/admin"
+#define KADM5_HIST_PRINCIPAL "kadmin/history"
+#define KADM5_CHANGEPW_SERVICE "kadmin/changepw"
+
+typedef struct {
+ int16_t key_data_ver; /* Version */
+ int16_t key_data_kvno; /* Key Version */
+ int16_t key_data_type[2]; /* Array of types */
+ int16_t key_data_length[2]; /* Array of lengths */
+ void* key_data_contents[2];/* Array of pointers */
+} krb5_key_data;
+
+typedef struct _krb5_keysalt {
+ int16_t type;
+ krb5_data data; /* Length, data */
+} krb5_keysalt;
+
+typedef struct _krb5_tl_data {
+ struct _krb5_tl_data* tl_data_next;
+ int16_t tl_data_type;
+ int16_t tl_data_length;
+ void* tl_data_contents;
+} krb5_tl_data;
+
+#define KRB5_TL_LAST_PWD_CHANGE 0x0001
+#define KRB5_TL_MOD_PRINC 0x0002
+#define KRB5_TL_KADM_DATA 0x0003
+#define KRB5_TL_KADM5_E_DATA 0x0004
+#define KRB5_TL_RB1_CHALLENGE 0x0005
+#define KRB5_TL_SECURID_STATE 0x0006
+#define KRB5_TL_PASSWORD 0x0007
+#define KRB5_TL_EXTENSION 0x0008
+#define KRB5_TL_PKINIT_ACL 0x0009
+#define KRB5_TL_ALIASES 0x000a
+#define KRB5_TL_HIST_KVNO_DIFF_CLNT 0x000b
+#define KRB5_TL_HIST_KVNO_DIFF_SVC 0x000c
+#define KRB5_TL_ETYPES 0x000d
+#define KRB5_TL_KEY_ROTATION 0x000e
+#define KRB5_TL_KRB5_CONFIG 0x000f
+
+typedef struct _kadm5_principal_ent_t {
+ krb5_principal principal;
+
+ krb5_timestamp princ_expire_time;
+ krb5_timestamp last_pwd_change;
+ krb5_timestamp pw_expiration;
+ krb5_deltat max_life;
+ krb5_principal mod_name;
+ krb5_timestamp mod_date;
+ krb5_flags attributes;
+ krb5_kvno kvno;
+ krb5_kvno mkvno;
+
+ char * policy;
+ uint32_t aux_attributes;
+
+ krb5_deltat max_renewable_life;
+ krb5_timestamp last_success;
+ krb5_timestamp last_failed;
+ krb5_kvno fail_auth_count;
+ int16_t n_key_data;
+ int16_t n_tl_data;
+ krb5_tl_data *tl_data;
+ krb5_key_data *key_data;
+} kadm5_principal_ent_rec, *kadm5_principal_ent_t;
+
+typedef struct _kadm5_policy_ent_t {
+ char *policy;
+
+ uint32_t pw_min_life;
+ uint32_t pw_max_life;
+ uint32_t pw_min_length;
+ uint32_t pw_min_classes;
+ uint32_t pw_history_num;
+ uint32_t policy_refcnt;
+} kadm5_policy_ent_rec, *kadm5_policy_ent_t;
+
+#define KADM5_CONFIG_REALM (1 << 0)
+#define KADM5_CONFIG_PROFILE (1 << 1)
+#define KADM5_CONFIG_KADMIND_PORT (1 << 2)
+#define KADM5_CONFIG_ADMIN_SERVER (1 << 3)
+#define KADM5_CONFIG_DBNAME (1 << 4)
+#define KADM5_CONFIG_ADBNAME (1 << 5)
+#define KADM5_CONFIG_ADB_LOCKFILE (1 << 6)
+#define KADM5_CONFIG_ACL_FILE (1 << 7)
+#define KADM5_CONFIG_DICT_FILE (1 << 8)
+#define KADM5_CONFIG_ADMIN_KEYTAB (1 << 9)
+#define KADM5_CONFIG_MKEY_FROM_KEYBOARD (1 << 10)
+#define KADM5_CONFIG_STASH_FILE (1 << 11)
+#define KADM5_CONFIG_MKEY_NAME (1 << 12)
+#define KADM5_CONFIG_ENCTYPE (1 << 13)
+#define KADM5_CONFIG_MAX_LIFE (1 << 14)
+#define KADM5_CONFIG_MAX_RLIFE (1 << 15)
+#define KADM5_CONFIG_EXPIRATION (1 << 16)
+#define KADM5_CONFIG_FLAGS (1 << 17)
+#define KADM5_CONFIG_ENCTYPES (1 << 18)
+#define KADM5_CONFIG_READONLY_ADMIN_SERVER (1 << 19)
+#define KADM5_CONFIG_READONLY_KADMIN_PORT (1 << 20)
+
+#define KADM5_PRIV_GET (1 << 0)
+#define KADM5_PRIV_ADD (1 << 1)
+#define KADM5_PRIV_MODIFY (1 << 2)
+#define KADM5_PRIV_DELETE (1 << 3)
+#define KADM5_PRIV_LIST (1 << 4)
+#define KADM5_PRIV_CPW (1 << 5)
+#define KADM5_PRIV_GET_KEYS (1 << 6)
+
+/* Note: KADM5_PRIV_GET_KEYS not included */
+#define KADM5_PRIV_ALL (KADM5_PRIV_GET | KADM5_PRIV_ADD | KADM5_PRIV_MODIFY | KADM5_PRIV_DELETE | KADM5_PRIV_LIST | KADM5_PRIV_CPW)
+
+#define KADM5_BOGUS_KEY_DATA "\xe5\xe5\xe5\xe5"
+
+/*
+ * ABI NOTE: We can add fields at the end of this provided that we define new
+ * mask bits that must be set in the mask field when setting the new fields.
+ */
+typedef struct _kadm5_config_params {
+ uint32_t mask;
+
+ /* Client and server fields */
+ char *realm;
+ int kadmind_port;
+
+ /* client fields */
+ char *admin_server;
+
+ /* server fields */
+ char *dbname;
+ char *acl_file;
+
+ /* server library (database) fields */
+ char *stash_file;
+
+ /* read-only kadmin server */
+ char *readonly_admin_server;
+ int readonly_kadmind_port;
+} kadm5_config_params;
+
+typedef krb5_error_code kadm5_ret_t;
+
+#include "kadm5-protos.h"
+
+#endif /* __KADM5_ADMIN_H__ */
diff --git a/third_party/heimdal/lib/kadm5/bump_pw_expire.c b/third_party/heimdal/lib/kadm5/bump_pw_expire.c
new file mode 100644
index 0000000..1938c75
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/bump_pw_expire.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * extend password_expiration if it's defined
+ */
+
+kadm5_ret_t
+_kadm5_bump_pw_expire(kadm5_server_context *context,
+ hdb_entry *ent)
+{
+ if (ent->pw_end != NULL) {
+ time_t life;
+
+ life = krb5_config_get_time_default(context->context,
+ NULL,
+ 365 * 24 * 60 * 60,
+ "kadmin",
+ "password_lifetime",
+ NULL);
+
+ if (life != 0)
+ *(ent->pw_end) = time(NULL) + life;
+ else {
+ free(ent->pw_end);
+ ent->pw_end = NULL;
+ }
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/check-cracklib.pl b/third_party/heimdal/lib/kadm5/check-cracklib.pl
new file mode 100644
index 0000000..a6fbd4c
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/check-cracklib.pl
@@ -0,0 +1,112 @@
+#!/usr/pkg/bin/perl
+#
+# Sample password verifier for Heimdals external password
+# verifier, see the chapter "Password changing" in the the info
+# documentation for more information about the protocol used.
+#
+# Three checks
+# 1. Check that password is not the principal name
+# 2. Check that the password passes cracklib
+# 3. Check that password isn't repeated for this principal
+#
+# The repeat check must be last because some clients ask
+# twice when getting "no" back and thus the error message
+# would be wrong.
+#
+# Prereqs (example versions):
+#
+# * perl (5.8.5) http://www.perl.org/
+# * cracklib (2.8.5) http://sourceforge.net/projects/cracklib
+# * Crypt-Cracklib perlmodule (0.01) http://search.cpan.org/~daniel/
+#
+# Sample dictionaries:
+# cracklib-words (1.1) http://sourceforge.net/projects/cracklib
+# miscfiles (1.4.2) http://directory.fsf.org/miscfiles.html
+#
+# Configuration for krb5.conf or kdc.conf
+#
+# [password_quality]
+# policies = builtin:external-check
+# external_program = <your-path>/check-cracklib.pl
+#
+# $Id$
+
+use strict;
+use Crypt::Cracklib;
+use Digest::MD5;
+
+# NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
+my $database = '/usr/lib/cracklib_dict';
+my $historydb = '/var/heimdal/historydb';
+# NEED TO CHANGE THESE TO MATCH YOUR SYSTEM
+
+# seconds password reuse allowed (to catch retries from clients)
+my $reusetime = 60;
+
+my %params;
+
+sub check_basic
+{
+ my $principal = shift;
+ my $passwd = shift;
+
+ if ($principal eq $passwd) {
+ return "Principal name as password is not allowed";
+ }
+ return "ok";
+}
+
+sub check_repeat
+{
+ my $principal = shift;
+ my $passwd = shift;
+ my $result = 'Do not reuse passwords';
+ my %DB;
+ my $md5context = new Digest::MD5;
+ my $timenow = scalar(time());
+
+ $md5context->reset();
+ $md5context->add($principal, ":", $passwd);
+
+ my $key=$md5context->hexdigest();
+
+ dbmopen(%DB,$historydb,0600) or die "Internal: Could not open $historydb";
+ if (!$DB{$key} || ($timenow - $DB{$key} < $reusetime)) {
+ $result = "ok";
+ $DB{$key}=$timenow;
+ }
+ dbmclose(%DB) or die "Internal: Could not close $historydb";
+ return $result;
+}
+
+sub badpassword
+{
+ my $reason = shift;
+ print "$reason\n";
+ exit 0
+}
+
+while (<STDIN>) {
+ last if /^end$/;
+ if (!/^([^:]+): (.+)$/) {
+ die "key value pair not correct: $_";
+ }
+ $params{$1} = $2;
+}
+
+die "missing principal" if (!defined $params{'principal'});
+die "missing password" if (!defined $params{'new-password'});
+
+my $reason;
+
+$reason = check_basic($params{'principal'}, $params{'new-password'});
+badpassword($reason) if ($reason ne "ok");
+
+$reason = fascist_check($params{'new-password'}, $database);
+badpassword($reason) if ($reason ne "ok");
+
+$reason = check_repeat($params{'principal'}, $params{'new-password'});
+badpassword($reason) if ($reason ne "ok");
+
+print "APPROVED\n";
+exit 0
diff --git a/third_party/heimdal/lib/kadm5/chpass_c.c b/third_party/heimdal/lib/kadm5/chpass_c.c
new file mode 100644
index 0000000..1036918
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/chpass_c.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 1997-2000, 2005-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_chpass_principal(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ /*
+ * We should get around to implementing this... At the moment, the
+ * the server side API is implemented but the wire protocol has not
+ * been updated.
+ */
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_chpass);
+ if (ret)
+ goto out;
+ ret = krb5_store_principal(sp, princ);
+ if (ret)
+ goto out;
+ ret = krb5_store_string(sp, password);
+ if (ret)
+ goto out;
+ ret = krb5_store_int32(sp, keepold); /* extension */
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_c_chpass_principal_with_key(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_key_data,
+ krb5_key_data *key_data)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+ int i;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_chpass_with_key);
+ if (ret)
+ goto out;
+ ret = krb5_store_principal(sp, princ);
+ if (ret)
+ goto out;
+ ret = krb5_store_int32(sp, n_key_data);
+ if (ret)
+ goto out;
+ for (i = 0; i < n_key_data; ++i) {
+ ret = kadm5_store_key_data (sp, &key_data[i]);
+ if (ret)
+ goto out;
+ }
+ ret = krb5_store_int32(sp, keepold); /* extension */
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data (&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/chpass_s.c b/third_party/heimdal/lib/kadm5/chpass_s.c
new file mode 100644
index 0000000..c89448f
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/chpass_s.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct chpass_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal princ;
+ uint32_t flags;
+ size_t n_ks_tuple;
+ krb5_key_salt_tuple *ks_tuple;
+ const char *password;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+chpass_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct chpass_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->chpass(context, hookctx,
+ ctx->stage, ctx->code, ctx->princ,
+ ctx->flags, ctx->n_ks_tuple, ctx->ks_tuple,
+ ctx->password);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "chpass",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+chpass_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ krb5_error_code ret;
+ struct chpass_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+ ctx.flags = flags;
+ ctx.n_ks_tuple = n_ks_tuple;
+ ctx.ks_tuple = ks_tuple;
+ ctx.password = password;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, chpass_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+static kadm5_ret_t
+change(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password,
+ int cond)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry ent;
+ kadm5_ret_t ret;
+ Key *keys;
+ size_t num_keys;
+ int existsp = 0;
+ uint32_t hook_flags = 0;
+
+ memset(&ent, 0, sizeof(ent));
+
+ if (krb5_principal_compare(context->context, princ, context->caller) ||
+ _kadm5_enforce_pwqual_on_admin_set_p(context)) {
+ krb5_data pwd_data;
+ const char *pwd_reason;
+
+ pwd_data.data = rk_UNCONST(password);
+ pwd_data.length = strlen(password);
+
+ pwd_reason = kadm5_check_password_quality(context->context,
+ princ, &pwd_data);
+ if (pwd_reason != NULL) {
+ krb5_set_error_message(context->context, KADM5_PASS_Q_GENERIC, "%s", pwd_reason);
+ return KADM5_PASS_Q_GENERIC;
+ }
+ }
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if (ret)
+ goto out2;
+
+ if (keepold)
+ hook_flags |= KADM5_HOOK_FLAG_KEEPOLD;
+ if (cond)
+ hook_flags |= KADM5_HOOK_FLAG_CONDITIONAL;
+ ret = chpass_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, princ, hook_flags,
+ n_ks_tuple, ks_tuple, password);
+ if (ret)
+ goto out3;
+
+ if (keepold || cond) {
+ /*
+ * We save these for now so we can handle password history checking;
+ * we handle keepold further below.
+ */
+ ret = hdb_add_current_keys_to_history(context->context, &ent);
+ if (ret)
+ goto out3;
+ }
+
+ if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) {
+ ret = context->db->hdb_password(context->context, context->db,
+ &ent, password, cond);
+ if (ret)
+ goto out3;
+ } else {
+
+ num_keys = ent.keys.len;
+ keys = ent.keys.val;
+
+ ent.keys.len = 0;
+ ent.keys.val = NULL;
+
+ ret = _kadm5_set_keys(context, &ent, n_ks_tuple, ks_tuple,
+ password);
+ if(ret) {
+ _kadm5_free_keys(context->context, num_keys, keys);
+ goto out3;
+ }
+ _kadm5_free_keys(context->context, num_keys, keys);
+
+ if (cond) {
+ HDB_extension *ext;
+
+ ext = hdb_find_extension(&ent, choice_HDB_extension_data_hist_keys);
+ if (ext != NULL)
+ existsp = _kadm5_exists_keys_hist(ent.keys.val,
+ ent.keys.len,
+ &ext->data.u.hist_keys);
+ }
+
+ if (existsp) {
+ ret = KADM5_PASS_REUSE;
+ krb5_set_error_message(context->context, ret,
+ "Password reuse forbidden");
+ goto out3;
+ }
+ }
+ ent.kvno++;
+
+ ent.flags.require_pwchange = 0;
+
+ if (!keepold) {
+ HDB_extension ext;
+
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ ret = hdb_replace_extension(context->context, &ent, &ext);
+ if (ret)
+ goto out3;
+ }
+
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out3;
+
+ ret = _kadm5_set_modifier(context, &ent);
+ if(ret)
+ goto out3;
+
+ ret = _kadm5_bump_pw_expire(context, &ent);
+ if (ret)
+ goto out3;
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent,
+ KADM5_ATTRIBUTES | KADM5_PRINCIPAL |
+ KADM5_MOD_NAME | KADM5_MOD_TIME |
+ KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
+
+ (void) chpass_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, princ, hook_flags,
+ n_ks_tuple, ks_tuple, password);
+
+ out3:
+ hdb_free_entry(context->context, context->db, &ent);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
+
+
+
+/*
+ * change the password of `princ' to `password' if it's not already that.
+ */
+
+kadm5_ret_t
+kadm5_s_chpass_principal_cond(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ const char *password)
+{
+ return change (server_handle, princ, keepold, 0, NULL, password, 1);
+}
+
+/*
+ * change the password of `princ' to `password'
+ */
+
+kadm5_ret_t
+kadm5_s_chpass_principal(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ return change (server_handle, princ, keepold,
+ n_ks_tuple, ks_tuple, password, 0);
+}
+
+struct chpass_principal_with_key_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal princ;
+ uint32_t flags;
+ size_t n_key_data;
+ krb5_key_data *key_data;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+chpass_principal_with_key_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct chpass_principal_with_key_hook_ctx *ctx = userctx;
+
+ ret = ftable->chpass_with_key(context, hookctx,
+ ctx->stage, ctx->code, ctx->princ,
+ ctx->flags, ctx->n_key_data, ctx->key_data);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "chpass_with_key",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+chpass_principal_with_key_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_key_data,
+ krb5_key_data *key_data)
+{
+ krb5_error_code ret;
+ struct chpass_principal_with_key_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+ ctx.flags = flags;
+ ctx.n_key_data = n_key_data;
+ ctx.key_data = key_data;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, chpass_principal_with_key_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+/*
+ * change keys for `princ' to `keys'
+ */
+
+kadm5_ret_t
+kadm5_s_chpass_principal_with_key(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_key_data,
+ krb5_key_data *key_data)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry ent;
+ kadm5_ret_t ret;
+ uint32_t hook_flags = 0;
+
+ memset(&ent, 0, sizeof(ent));
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
+ if (ret == HDB_ERR_NOENTRY)
+ goto out2;
+
+ if (keepold)
+ hook_flags |= KADM5_HOOK_FLAG_KEEPOLD;
+ ret = chpass_principal_with_key_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, princ, hook_flags,
+ n_key_data, key_data);
+ if (ret)
+ goto out3;
+
+ if (keepold) {
+ ret = hdb_add_current_keys_to_history(context->context, &ent);
+ if (ret)
+ goto out3;
+ }
+ ret = _kadm5_set_keys2(context, &ent, n_key_data, key_data);
+ if (ret)
+ goto out3;
+ ent.kvno++;
+ ret = _kadm5_set_modifier(context, &ent);
+ if (ret)
+ goto out3;
+ ret = _kadm5_bump_pw_expire(context, &ent);
+ if (ret)
+ goto out3;
+
+ if (keepold) {
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out3;
+ } else {
+ HDB_extension ext;
+
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ ext.data.u.hist_keys.len = 0;
+ ext.data.u.hist_keys.val = NULL;
+ hdb_replace_extension(context->context, &ent, &ext);
+ }
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent,
+ KADM5_PRINCIPAL | KADM5_MOD_NAME |
+ KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
+
+ (void) chpass_principal_with_key_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, princ, hook_flags,
+ n_key_data, key_data);
+
+ out3:
+ hdb_free_entry(context->context, context->db, &ent);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
+
+/*
+ * Returns TRUE if password quality should be checked when passwords are
+ * being set or changed by administrators. This includes principal creation.
+ */
+krb5_boolean
+_kadm5_enforce_pwqual_on_admin_set_p(kadm5_server_context *contextp)
+{
+ if (_kadm5_is_kadmin_service_p(contextp))
+ return FALSE;
+
+ return krb5_config_get_bool_default(contextp->context, NULL, TRUE,
+ "password_quality",
+ "enforce_on_admin_set", NULL);
+}
diff --git a/third_party/heimdal/lib/kadm5/client_glue.c b/third_party/heimdal/lib/kadm5/client_glue.c
new file mode 100644
index 0000000..2783a9a
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/client_glue.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_init_with_password(const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_password(client_name,
+ password,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_password_ctx(krb5_context context,
+ const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_password_ctx(context,
+ client_name,
+ password,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_skey(const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_skey(client_name,
+ keytab,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_skey_ctx(krb5_context context,
+ const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_skey_ctx(context,
+ client_name,
+ keytab,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_creds(const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_creds(client_name,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_creds_ctx(krb5_context context,
+ const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_creds_ctx(context,
+ client_name,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
diff --git a/third_party/heimdal/lib/kadm5/common_glue.c b/third_party/heimdal/lib/kadm5/common_glue.c
new file mode 100644
index 0000000..210bf93
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/common_glue.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+#define __CALL(F, P) (*((kadm5_common_context*)server_handle)->funcs.F)P
+#define __CALLABLE(F) (((kadm5_common_context*)server_handle)->funcs.F != 0)
+
+kadm5_ret_t
+kadm5_dup_context(void *server_handle, void **dup_server_handle)
+{
+ return __CALL(dup_context, (server_handle, dup_server_handle));
+}
+
+kadm5_ret_t
+kadm5_chpass_principal(void *server_handle,
+ krb5_principal princ,
+ const char *password)
+{
+ return __CALL(chpass_principal, (server_handle, princ, 0,
+ 0, NULL, password));
+}
+
+kadm5_ret_t
+kadm5_chpass_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ return __CALL(chpass_principal, (server_handle, princ, keepold,
+ n_ks_tuple, ks_tuple, password));
+}
+
+kadm5_ret_t
+kadm5_chpass_principal_with_key(void *server_handle,
+ krb5_principal princ,
+ int n_key_data,
+ krb5_key_data *key_data)
+{
+ return __CALL(chpass_principal_with_key,
+ (server_handle, princ, 0, n_key_data, key_data));
+}
+
+kadm5_ret_t
+kadm5_chpass_principal_with_key_3(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_key_data,
+ krb5_key_data *key_data)
+{
+ return __CALL(chpass_principal_with_key,
+ (server_handle, princ, keepold, n_key_data, key_data));
+}
+
+kadm5_ret_t
+kadm5_create_principal_3(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ char *password)
+{
+ return __CALL(create_principal,
+ (server_handle, princ, mask, n_ks_tuple, ks_tuple, password));
+}
+
+kadm5_ret_t
+kadm5_create_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ const char *password)
+{
+ return __CALL(create_principal,
+ (server_handle, princ, mask, 0, NULL, password));
+}
+
+kadm5_ret_t
+kadm5_delete_principal(void *server_handle,
+ krb5_principal princ)
+{
+ return __CALL(delete_principal, (server_handle, princ));
+}
+
+kadm5_ret_t
+kadm5_destroy (void *server_handle)
+{
+ return __CALL(destroy, (server_handle));
+}
+
+kadm5_ret_t
+kadm5_flush (void *server_handle)
+{
+ return __CALL(flush, (server_handle));
+}
+
+kadm5_ret_t
+kadm5_get_principal(void *server_handle,
+ krb5_principal princ,
+ kadm5_principal_ent_t out,
+ uint32_t mask)
+{
+ return __CALL(get_principal, (server_handle, princ, out, mask));
+}
+
+/**
+ * Extract decrypted keys from kadm5_principal_ent_t object. Mostly a
+ * no-op for Heimdal because we fetch the entry with decrypted keys.
+ * Sadly this is not fully a no-op, as we have to allocate a copy.
+ *
+ * @server_handle is the kadm5 handle
+ * @entry is the HDB entry for the principal in question
+ * @ktype is the enctype to get a key for, or -1 to get the first one
+ * @stype is the salttype to get a key for, or -1 to get the first match
+ * @kvno is the kvno to search for, or -1 to get the first match (highest kvno)
+ * @keyblock is where the key will be placed
+ * @keysalt, if not NULL, is where the salt will be placed
+ * @kvnop, if not NULL, is where the selected kvno will be placed
+ */
+kadm5_ret_t
+kadm5_decrypt_key(void *server_handle,
+ kadm5_principal_ent_t entry,
+ int32_t ktype, int32_t stype,
+ int32_t kvno, krb5_keyblock *keyblock,
+ krb5_keysalt *keysalt, int *kvnop)
+{
+ size_t i;
+ kadm5_server_context *context = server_handle;
+
+ if (kvno < 1 || stype != -1)
+ return KADM5_DECRYPT_USAGE_NOSUPP;
+
+ for (i = 0; i < entry->n_key_data; i++) {
+ if (ktype != entry->key_data[i].key_data_kvno)
+ continue;
+
+ keyblock->keytype = ktype;
+ keyblock->keyvalue.length = entry->key_data[i].key_data_length[0];
+ keyblock->keyvalue.data = malloc(keyblock->keyvalue.length);
+ if (keyblock->keyvalue.data == NULL)
+ return krb5_enomem(context->context);
+ memcpy(keyblock->keyvalue.data,
+ entry->key_data[i].key_data_contents[0],
+ keyblock->keyvalue.length);
+ }
+
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_modify_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ return __CALL(modify_principal, (server_handle, princ, mask));
+}
+
+kadm5_ret_t
+kadm5_randkey_principal(void *server_handle,
+ krb5_principal princ,
+ krb5_keyblock **new_keys,
+ int *n_keys)
+{
+ return __CALL(randkey_principal, (server_handle, princ, FALSE, 0, NULL,
+ new_keys, n_keys));
+}
+
+kadm5_ret_t
+kadm5_randkey_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **new_keys,
+ int *n_keys)
+{
+ return __CALL(randkey_principal, (server_handle, princ, keepold,
+ n_ks_tuple, ks_tuple, new_keys, n_keys));
+}
+
+kadm5_ret_t
+kadm5_rename_principal(void *server_handle,
+ krb5_principal source,
+ krb5_principal target)
+{
+ return __CALL(rename_principal, (server_handle, source, target));
+}
+
+kadm5_ret_t
+kadm5_get_principals(void *server_handle,
+ const char *expression,
+ char ***princs,
+ int *count)
+{
+ return __CALL(get_principals, (server_handle, expression, princs, count));
+}
+
+kadm5_ret_t
+kadm5_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ return __CALL(iter_principals, (server_handle, expression, cb, cbdata));
+}
+
+kadm5_ret_t
+kadm5_get_privs(void *server_handle,
+ uint32_t *privs)
+{
+ return __CALL(get_privs, (server_handle, privs));
+}
+
+
+/**
+ * This function is allows the caller to set new keys for a principal.
+ * This is a trivial wrapper around kadm5_setkey_principal_3().
+ */
+kadm5_ret_t
+kadm5_setkey_principal(void *server_handle,
+ krb5_principal princ,
+ krb5_keyblock *new_keys,
+ int n_keys)
+{
+ return kadm5_setkey_principal_3(server_handle, princ, 0, 0, NULL,
+ new_keys, n_keys);
+}
+
+/**
+ * This function is allows the caller to set new keys for a principal.
+ * This is a simple wrapper around kadm5_get_principal() and
+ * kadm5_modify_principal().
+ */
+kadm5_ret_t
+kadm5_setkey_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock *keyblocks,
+ int n_keys)
+{
+ kadm5_principal_ent_rec princ_ent;
+ kadm5_ret_t ret;
+ krb5_key_data *new_key_data = NULL;
+ size_t i;
+ kadm5_server_context *context = server_handle;
+
+ if (n_keys < 1)
+ return EINVAL;
+ if (n_ks_tuple > 0 && n_ks_tuple != n_keys)
+ return KADM5_SETKEY3_ETYPE_MISMATCH;
+
+ /*
+ * If setkey_principal_3 is defined in the server handle, use that.
+ */
+ if (__CALLABLE(setkey_principal_3))
+ return __CALL(setkey_principal_3,
+ (server_handle, princ, keepold, n_ks_tuple, ks_tuple,
+ keyblocks, n_keys));
+
+ /*
+ * Otherwise, simulate it via a get, update, modify sequence.
+ */
+ ret = kadm5_get_principal(server_handle, princ, &princ_ent,
+ KADM5_KVNO | KADM5_PRINCIPAL | KADM5_KEY_DATA);
+ if (ret)
+ return ret;
+
+ if (keepold) {
+ new_key_data = calloc((n_keys + princ_ent.n_key_data),
+ sizeof(*new_key_data));
+ if (new_key_data == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+
+ memcpy(&new_key_data[n_keys], &princ_ent.key_data[0],
+ princ_ent.n_key_data * sizeof (princ_ent.key_data[0]));
+ } else {
+ new_key_data = calloc(n_keys, sizeof(*new_key_data));
+ if (new_key_data == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+
+ princ_ent.kvno++;
+ for (i = 0; i < n_keys; i++) {
+ new_key_data[i].key_data_ver = 2;
+
+ /* Key */
+ new_key_data[i].key_data_kvno = princ_ent.kvno;
+ new_key_data[i].key_data_type[0] = keyblocks[i].keytype;
+ new_key_data[i].key_data_length[0] = keyblocks[i].keyvalue.length;
+ new_key_data[i].key_data_contents[0] =
+ malloc(keyblocks[i].keyvalue.length);
+ if (new_key_data[i].key_data_contents[0] == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ memcpy(new_key_data[i].key_data_contents[0],
+ keyblocks[i].keyvalue.data,
+ keyblocks[i].keyvalue.length);
+
+ /*
+ * Salt (but there's no salt, just salttype, which is kinda
+ * silly -- what's the point of setkey_3() then, besides
+ * keepold?!)
+ */
+ new_key_data[i].key_data_type[1] = 0;
+ if (n_ks_tuple > 0) {
+ if (ks_tuple[i].ks_enctype != keyblocks[i].keytype) {
+ ret = KADM5_SETKEY3_ETYPE_MISMATCH;
+ goto out;
+ }
+ new_key_data[i].key_data_type[1] = ks_tuple[i].ks_salttype;
+ }
+ new_key_data[i].key_data_length[1] = 0;
+ new_key_data[i].key_data_contents[1] = NULL;
+ }
+
+ /* Free old keys */
+ if (!keepold) {
+ for (i = 0; i < princ_ent.n_key_data; i++) {
+ free(princ_ent.key_data[i].key_data_contents[0]);
+ free(princ_ent.key_data[i].key_data_contents[1]);
+ }
+ }
+ free(princ_ent.key_data);
+ princ_ent.key_data = new_key_data;
+ princ_ent.n_key_data = n_keys + (keepold ? princ_ent.n_key_data : 0);
+ new_key_data = NULL;
+
+ /* Modify the principal */
+ ret = kadm5_modify_principal(server_handle, &princ_ent, KADM5_KVNO | KADM5_KEY_DATA);
+
+out:
+ if (new_key_data != NULL) {
+ for (i = 0; i < n_keys; i++) {
+ free(new_key_data[i].key_data_contents[0]);
+ free(new_key_data[i].key_data_contents[1]);
+ }
+ free(new_key_data);
+ }
+ kadm5_free_principal_ent(server_handle, &princ_ent);
+ return ret;
+}
+
+
+kadm5_ret_t
+kadm5_lock(void *server_handle)
+{
+ return __CALL(lock, (server_handle));
+}
+
+kadm5_ret_t
+kadm5_unlock(void *server_handle)
+{
+ return __CALL(unlock, (server_handle));
+}
+
+
+kadm5_ret_t
+kadm5_create_policy(void *server_handle,
+ kadm5_policy_ent_t policy, long mask)
+{
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+kadm5_ret_t
+kadm5_delete_policy(void *server_handle, char *name)
+{
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+
+kadm5_ret_t
+kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t policy,
+ uint32_t mask)
+{
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+kadm5_ret_t
+kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent)
+{
+ memset(ent, 0, sizeof (*ent));
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+
+kadm5_ret_t
+kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count)
+{
+ *count = 0;
+ *pols = NULL;
+
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+kadm5_ret_t
+kadm5_free_policy_ent(kadm5_policy_ent_t ent)
+{
+ if (ent->policy)
+ free(ent->policy);
+ /*
+ * Not clear if we should free ent or not. It might be an automatic
+ * struct, so we don't free it for now, just in case.
+ */
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_prune_principal(void *server_handle,
+ krb5_principal princ,
+ int kvno)
+{
+ return __CALL(prune_principal, (server_handle, princ, kvno));
+}
diff --git a/third_party/heimdal/lib/kadm5/context_s.c b/third_party/heimdal/lib/kadm5/context_s.c
new file mode 100644
index 0000000..5c9b3e3
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/context_s.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+static kadm5_ret_t
+kadm5_s_lock(void *server_handle)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+
+ if (context->keep_open) {
+ /*
+ * We open/close around every operation, but we retain the DB
+ * open if the DB was locked with a prior call to kadm5_lock(),
+ * so if it's open here that must be because the DB is locked.
+ */
+ heim_assert(context->db->lock_count > 0,
+ "Internal error in tracking HDB locks");
+ return KADM5_ALREADY_LOCKED;
+ }
+
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret)
+ return ret;
+
+ ret = context->db->hdb_lock(context->context, context->db, HDB_WLOCK);
+ if (ret) {
+ (void) context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ /*
+ * Attempt to recover the log. This will generally fail on slaves,
+ * and we can't tell if we're on a slave here.
+ *
+ * Perhaps we could set a flag in the kadm5_server_context to
+ * indicate whether a read has been done without recovering the log,
+ * in which case we could fail any subsequent writes.
+ */
+ if (kadm5_log_init(context) == 0)
+ (void) kadm5_log_end(context);
+
+ context->keep_open = 1;
+ return 0;
+}
+
+static kadm5_ret_t
+kadm5_s_unlock(void *server_handle)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+
+ if (!context->keep_open)
+ return KADM5_NOT_LOCKED;
+
+ context->keep_open = 0;
+ ret = context->db->hdb_unlock(context->context, context->db);
+ (void) context->db->hdb_close(context->context, context->db);
+ return ret;
+}
+
+static void
+set_funcs(kadm5_server_context *c)
+{
+#define SET(C, F) (C)->funcs.F = kadm5_s_ ## F
+ SET(c, chpass_principal);
+ SET(c, chpass_principal_with_key);
+ SET(c, create_principal);
+ SET(c, delete_principal);
+ SET(c, destroy);
+ SET(c, flush);
+ SET(c, get_principal);
+ SET(c, get_principals);
+ SET(c, get_privs);
+ SET(c, modify_principal);
+ SET(c, prune_principal);
+ SET(c, randkey_principal);
+ SET(c, rename_principal);
+ SET(c, lock);
+ SET(c, unlock);
+ SET(c, setkey_principal_3);
+ SET(c, iter_principals);
+ SET(c, dup_context);
+}
+
+#ifndef NO_UNIX_SOCKETS
+
+static void
+set_socket_name(krb5_context context, struct sockaddr_un *un)
+{
+ const char *fn = kadm5_log_signal_socket(context);
+
+ memset(un, 0, sizeof(*un));
+ un->sun_family = AF_UNIX;
+ strlcpy (un->sun_path, fn, sizeof(un->sun_path));
+
+}
+#else
+
+static void
+set_socket_info(krb5_context context, struct addrinfo **info)
+{
+ kadm5_log_signal_socket_info(context, 0, info);
+}
+
+#endif
+
+static kadm5_ret_t
+find_db_spec(kadm5_server_context *ctx)
+{
+ krb5_context context = ctx->context;
+ struct hdb_dbinfo *info, *d;
+ krb5_error_code ret;
+ int aret;
+
+ if (ctx->config.realm) {
+ /* fetch the databases */
+ ret = hdb_get_dbinfo(context, &info);
+ if (ret)
+ return ret;
+
+ d = NULL;
+ while ((d = hdb_dbinfo_get_next(info, d)) != NULL) {
+ const char *p = hdb_dbinfo_get_realm(context, d);
+
+ /* match default (realm-less) */
+ if(p != NULL && strcmp(ctx->config.realm, p) != 0)
+ continue;
+
+ p = hdb_dbinfo_get_dbname(context, d);
+ if (p) {
+ ctx->config.dbname = strdup(p);
+ if (ctx->config.dbname == NULL) {
+ hdb_free_dbinfo(context, &info);
+ return krb5_enomem(context);
+ }
+ }
+
+ p = hdb_dbinfo_get_acl_file(context, d);
+ if (p) {
+ ctx->config.acl_file = strdup(p);
+ if (ctx->config.acl_file == NULL) {
+ hdb_free_dbinfo(context, &info);
+ return krb5_enomem(context);
+ }
+ }
+
+ p = hdb_dbinfo_get_mkey_file(context, d);
+ if (p) {
+ ctx->config.stash_file = strdup(p);
+ if (ctx->config.stash_file == NULL) {
+ hdb_free_dbinfo(context, &info);
+ return krb5_enomem(context);
+ }
+ }
+
+ p = hdb_dbinfo_get_log_file(context, d);
+ if (p) {
+ ctx->log_context.log_file = strdup(p);
+ if (ctx->log_context.log_file == NULL) {
+ hdb_free_dbinfo(context, &info);
+ return krb5_enomem(context);
+ }
+ }
+ break;
+ }
+ hdb_free_dbinfo(context, &info);
+ }
+
+ /* If any of the values was unset, pick up the default value */
+
+ if (ctx->config.dbname == NULL) {
+ ctx->config.dbname = strdup(hdb_default_db(context));
+ if (ctx->config.dbname == NULL)
+ return krb5_enomem(context);
+ }
+ if (ctx->config.acl_file == NULL) {
+ aret = asprintf(&ctx->config.acl_file, "%s/kadmind.acl",
+ hdb_db_dir(context));
+ if (aret == -1)
+ return krb5_enomem(context);
+ }
+ if (ctx->config.stash_file == NULL) {
+ aret = asprintf(&ctx->config.stash_file, "%s/m-key",
+ hdb_db_dir(context));
+ if (aret == -1)
+ return krb5_enomem(context);
+ }
+ if (ctx->log_context.log_file == NULL) {
+ aret = asprintf(&ctx->log_context.log_file, "%s/log",
+ hdb_db_dir(context));
+ if (aret == -1)
+ return krb5_enomem(context);
+ }
+
+#ifndef NO_UNIX_SOCKETS
+ set_socket_name(context, &ctx->log_context.socket_name);
+#else
+ set_socket_info(context, &ctx->log_context.socket_info);
+#endif
+
+ return 0;
+}
+
+kadm5_ret_t
+_kadm5_s_init_context(kadm5_server_context **ctx,
+ kadm5_config_params *params,
+ krb5_context context)
+{
+ kadm5_ret_t ret = 0;
+
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
+ return krb5_enomem(context);
+ (*ctx)->log_context.socket_fd = rk_INVALID_SOCKET;
+
+ set_funcs(*ctx);
+ (*ctx)->context = context;
+ krb5_add_et_list (context, initialize_kadm5_error_table_r);
+
+#define is_set(M) (params && params->mask & KADM5_CONFIG_ ## M)
+ if (params)
+ (*ctx)->config.mask = params->mask;
+ if (is_set(REALM)) {
+ (*ctx)->config.realm = strdup(params->realm);
+ if ((*ctx)->config.realm == NULL)
+ return krb5_enomem(context);
+ } else {
+ ret = krb5_get_default_realm(context, &(*ctx)->config.realm);
+ if (ret)
+ return ret;
+ }
+ if (is_set(DBNAME)) {
+ (*ctx)->config.dbname = strdup(params->dbname);
+ if ((*ctx)->config.dbname == NULL)
+ return krb5_enomem(context);
+ }
+ if (is_set(ACL_FILE)) {
+ (*ctx)->config.acl_file = strdup(params->acl_file);
+ if ((*ctx)->config.acl_file == NULL)
+ return krb5_enomem(context);
+ }
+ if (is_set(STASH_FILE)) {
+ (*ctx)->config.stash_file = strdup(params->stash_file);
+ if ((*ctx)->config.stash_file == NULL)
+ return krb5_enomem(context);
+ }
+
+ ret = find_db_spec(*ctx);
+ if (ret == 0)
+ ret = _kadm5_s_init_hooks(*ctx);
+ if (ret != 0) {
+ kadm5_s_destroy(*ctx);
+ *ctx = NULL;
+ return ret;
+ }
+
+ /* PROFILE can't be specified for now */
+ /* KADMIND_PORT is supposed to be used on the server also,
+ but this doesn't make sense */
+ /* ADMIN_SERVER is client only */
+ /* ADNAME is not used at all (as far as I can tell) */
+ /* ADB_LOCKFILE ditto */
+ /* DICT_FILE */
+ /* ADMIN_KEYTAB */
+ /* MKEY_FROM_KEYBOARD is not supported */
+ /* MKEY_NAME neither */
+ /* ENCTYPE */
+ /* MAX_LIFE */
+ /* MAX_RLIFE */
+ /* EXPIRATION */
+ /* FLAGS */
+ /* ENCTYPES */
+
+ return 0;
+}
+
+HDB *
+_kadm5_s_get_db(void *server_handle)
+{
+ kadm5_server_context *context = server_handle;
+ return context->db;
+}
diff --git a/third_party/heimdal/lib/kadm5/create_c.c b/third_party/heimdal/lib/kadm5/create_c.c
new file mode 100644
index 0000000..c239beb
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/create_c.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1997-2000, 2005-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_create_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ /*
+ * We should get around to implementing this... At the moment, the
+ * the server side API is implemented but the wire protocol has not
+ * been updated.
+ *
+ * Well, we have the etypes extension, which the kadmin ank command now
+ * adds, but that doesn't include salt types. We could, perhaps, make it
+ * so if the password is "" or NULL, we send the etypes but not the salt
+ * type, and then have the server side create random keys of just the
+ * etypes.
+ */
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ ret = krb5_store_int32(sp, kadm_create);
+ if (ret)
+ goto out;
+ ret = kadm5_store_principal_ent(sp, princ);
+ if (ret)
+ goto out;
+ ret = krb5_store_int32(sp, mask);
+ if (ret)
+ goto out;
+ ret = krb5_store_string(sp, password);
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
+
diff --git a/third_party/heimdal/lib/kadm5/create_s.c b/third_party/heimdal/lib/kadm5/create_s.c
new file mode 100644
index 0000000..26e4e4c
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/create_s.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+static kadm5_ret_t
+get_default(kadm5_server_context *context, krb5_principal princ,
+ kadm5_principal_ent_t def)
+{
+ kadm5_ret_t ret;
+ krb5_principal def_principal;
+ krb5_const_realm realm = krb5_principal_get_realm(context->context, princ);
+
+ ret = krb5_make_principal(context->context, &def_principal,
+ realm, "default", NULL);
+ if (ret)
+ return ret;
+ ret = kadm5_s_get_principal(context, def_principal, def,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ krb5_free_principal (context->context, def_principal);
+
+ if (ret) {
+ /* Copy defaults from kadmin/init.c */
+ memset(def, 0, sizeof(*def));
+ def->max_life = 24 * 60 * 60;
+ def->max_renewable_life = 7 * def->max_life;
+ def->attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+ }
+ return ret;
+}
+
+static kadm5_ret_t
+create_principal(kadm5_server_context *context,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ hdb_entry *ent,
+ uint32_t required_mask,
+ uint32_t forbidden_mask)
+{
+ kadm5_ret_t ret;
+ kadm5_principal_ent_rec defrec, *defent;
+ uint32_t def_mask;
+
+ memset(ent, 0, sizeof(*ent));
+ if((mask & required_mask) != required_mask)
+ return KADM5_BAD_MASK;
+ if((mask & forbidden_mask))
+ return KADM5_BAD_MASK;
+ if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
+ /* XXX no real policies for now */
+ return KADM5_UNK_POLICY;
+ ret = krb5_copy_principal(context->context, princ->principal,
+ &ent->principal);
+ if(ret)
+ return ret;
+
+ defent = &defrec;
+ ret = get_default(context, princ->principal, defent);
+ if(ret) {
+ defent = NULL;
+ def_mask = 0;
+ } else {
+ def_mask = KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE;
+ }
+
+ ret = _kadm5_setup_entry(context,
+ ent, mask | def_mask,
+ princ, mask,
+ defent, def_mask);
+ if(defent)
+ kadm5_free_principal_ent(context, defent);
+ if (ret)
+ return ret;
+
+ ent->created_by.time = time(NULL);
+
+ return krb5_copy_principal(context->context, context->caller,
+ &ent->created_by.principal);
+}
+
+struct create_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ kadm5_principal_ent_t princ;
+ uint32_t mask;
+ const char *password;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+create_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct create_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->create(context, hookctx,
+ ctx->stage, ctx->code, ctx->princ,
+ ctx->mask, ctx->password);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "create",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+create_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ const char *password)
+{
+ krb5_error_code ret;
+ struct create_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+ ctx.mask = mask;
+ ctx.password = password;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, create_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_create_principal_with_key(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ kadm5_ret_t ret;
+ hdb_entry ent;
+ kadm5_server_context *context = server_handle;
+
+ if ((mask & KADM5_KVNO) == 0) {
+ /* create_principal() through _kadm5_setup_entry(), will need this */
+ princ->kvno = 1;
+ mask |= KADM5_KVNO;
+ }
+
+ ret = create_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, princ, mask, NULL);
+ if (ret)
+ return ret;
+
+ ret = create_principal(context, princ, mask, &ent,
+ KADM5_PRINCIPAL | KADM5_KEY_DATA,
+ KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
+ | KADM5_MOD_NAME | KADM5_MKVNO
+ | KADM5_AUX_ATTRIBUTES
+ | KADM5_POLICY_CLR | KADM5_LAST_SUCCESS
+ | KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT);
+ if (ret)
+ return ret;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret) {
+ hdb_free_entry(context->context, context->db, &ent);
+ return ret;
+ }
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out2;
+
+ /*
+ * This logs the change for iprop and writes to the HDB.
+ *
+ * Creation of would-be virtual principals w/o the materialize flag will be
+ * rejected in kadm5_log_create().
+ */
+ ret = kadm5_log_create(context, &ent);
+
+ (void) create_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, princ, mask, NULL);
+
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ hdb_free_entry(context->context, context->db, &ent);
+ return _kadm5_error_code(ret);
+}
+
+
+kadm5_ret_t
+kadm5_s_create_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ kadm5_ret_t ret;
+ hdb_entry ent;
+ kadm5_server_context *context = server_handle;
+ int use_pw = 1;
+
+ if ((mask & KADM5_ATTRIBUTES) &&
+ (princ->attributes & (KRB5_KDB_VIRTUAL_KEYS | KRB5_KDB_VIRTUAL)) &&
+ !(princ->attributes & KRB5_KDB_MATERIALIZE)) {
+ return _kadm5_error_code(KADM5_DUP); /* XXX More like EINVAL */
+ }
+ if ((mask & KADM5_ATTRIBUTES) &&
+ (princ->attributes & KRB5_KDB_VIRTUAL_KEYS) &&
+ (princ->attributes & KRB5_KDB_VIRTUAL)) {
+ return _kadm5_error_code(KADM5_DUP); /* XXX More like EINVAL */
+ }
+
+ if ((mask & KADM5_ATTRIBUTES) &&
+ (princ->attributes & KRB5_KDB_VIRTUAL) &&
+ (princ->attributes & KRB5_KDB_MATERIALIZE))
+ princ->attributes &= ~(KRB5_KDB_MATERIALIZE | KRB5_KDB_VIRTUAL);
+
+ if (password[0] == '\0' && (mask & KADM5_KEY_DATA) && princ->n_key_data &&
+ !kadm5_all_keys_are_bogus(princ->n_key_data, princ->key_data))
+ use_pw = 0;
+
+ if (use_pw && _kadm5_enforce_pwqual_on_admin_set_p(context)) {
+ krb5_data pwd_data;
+ const char *pwd_reason;
+
+ pwd_data.data = rk_UNCONST(password);
+ pwd_data.length = strlen(password);
+
+ pwd_reason = kadm5_check_password_quality(context->context,
+ princ->principal, &pwd_data);
+ if (pwd_reason != NULL) {
+ krb5_set_error_message(context->context, KADM5_PASS_Q_DICT, "%s", pwd_reason);
+ return KADM5_PASS_Q_DICT;
+ }
+ }
+
+ if ((mask & KADM5_KVNO) == 0) {
+ /* create_principal() through _kadm5_setup_entry(), will need this */
+ princ->kvno = 1;
+ mask |= KADM5_KVNO;
+ }
+
+ ret = create_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, princ, mask, password);
+ if (ret)
+ return ret;
+
+ if (use_pw)
+ ret = create_principal(context, princ, mask, &ent,
+ KADM5_PRINCIPAL,
+ KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
+ | KADM5_MOD_NAME | KADM5_MKVNO
+ | KADM5_AUX_ATTRIBUTES | KADM5_KEY_DATA
+ | KADM5_POLICY_CLR | KADM5_LAST_SUCCESS
+ | KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT);
+ else
+ ret = create_principal(context, princ, mask, &ent,
+ KADM5_PRINCIPAL | KADM5_KEY_DATA,
+ KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
+ | KADM5_MOD_NAME | KADM5_MKVNO
+ | KADM5_AUX_ATTRIBUTES
+ | KADM5_POLICY_CLR | KADM5_LAST_SUCCESS
+ | KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT);
+ if (ret)
+ return ret;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret) {
+ hdb_free_entry(context->context, context->db, &ent);
+ return ret;
+ }
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ free_Keys(&ent.keys);
+
+ if (use_pw) {
+ ret = _kadm5_set_keys(context, &ent, n_ks_tuple, ks_tuple, password);
+ if (ret)
+ goto out2;
+ }
+
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out2;
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_create(context, &ent);
+
+ (void) create_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, princ, mask, password);
+
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ hdb_free_entry(context->context, context->db, &ent);
+ return _kadm5_error_code(ret);
+}
+
diff --git a/third_party/heimdal/lib/kadm5/default_keys.c b/third_party/heimdal/lib/kadm5/default_keys.c
new file mode 100644
index 0000000..c3494e5
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/default_keys.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+#include <err.h>
+
+RCSID("$Id$");
+
+static void
+print_keys(krb5_context context, Key *keys, size_t nkeys)
+{
+ krb5_error_code ret;
+ char *str;
+ int i;
+
+ printf("keys:\n");
+
+ for (i = 0; i < nkeys; i++) {
+
+ ret = krb5_enctype_to_string(context, keys[i].key.keytype, &str);
+ if (ret)
+ krb5_err(context, ret, 1, "krb5_enctype_to_string: %d\n",
+ (int)keys[i].key.keytype);
+
+ printf("\tenctype %s", str);
+ free(str);
+
+ if (keys[i].salt) {
+ printf(" salt: ");
+
+ switch (keys[i].salt->type) {
+ case KRB5_PW_SALT:
+ printf("pw-salt:");
+ break;
+ case KRB5_AFS3_SALT:
+ printf("afs3-salt:");
+ break;
+ default:
+ printf("unknown salt: %d", keys[i].salt->type);
+ break;
+ }
+ if (keys[i].salt->salt.length)
+ printf("%.*s", (int)keys[i].salt->salt.length,
+ (char *)keys[i].salt->salt.data);
+ }
+ printf("\n");
+ }
+ printf("end keys:\n");
+}
+
+static void
+parse_file(krb5_context context, krb5_principal principal, int no_salt)
+{
+ krb5_error_code ret;
+ size_t nkeys;
+ Key *keys;
+
+ ret = hdb_generate_key_set(context, principal, NULL, 0, &keys, &nkeys,
+ no_salt);
+ if (ret)
+ krb5_err(context, 1, ret, "hdb_generate_key_set");
+
+ print_keys(context, keys, nkeys);
+
+ hdb_free_keys(context, nkeys, keys);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal principal;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context");
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &principal);
+ if (ret)
+ krb5_err(context, ret, 1, "krb5_parse_name");
+
+ parse_file(context, principal, 0);
+ parse_file(context, principal, 1);
+
+ krb5_free_principal(context, principal);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/delete_c.c b/third_party/heimdal/lib/kadm5/delete_c.c
new file mode 100644
index 0000000..cf06b6f
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/delete_c.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_delete_principal(void *server_handle, krb5_principal princ)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_delete);
+ if (ret)
+ goto out;
+ ret = krb5_store_principal(sp, princ);
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/delete_s.c b/third_party/heimdal/lib/kadm5/delete_s.c
new file mode 100644
index 0000000..aa9fdb4
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/delete_s.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003, 2005 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct delete_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal princ;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+delete_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct delete_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->delete(context, hookctx,
+ ctx->stage, ctx->code, ctx->princ);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "delete",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+delete_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ)
+{
+ krb5_error_code ret;
+ struct delete_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, delete_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_delete_principal(void *server_handle, krb5_principal princ)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+ hdb_entry ent;
+
+ memset(&ent, 0, sizeof(ent));
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret) {
+ krb5_warn(context->context, ret, "opening database");
+ return ret;
+ }
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if (ret == HDB_ERR_NOENTRY)
+ goto out2;
+ if (ent.flags.immutable) {
+ ret = KADM5_PROTECT_PRINCIPAL;
+ goto out3;
+ }
+
+ ret = delete_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT, 0, princ);
+ if (ret)
+ goto out3;
+
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out3;
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_delete(context, princ);
+
+ (void) delete_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT, ret, princ);
+
+ out3:
+ hdb_free_entry(context->context, context->db, &ent);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
diff --git a/third_party/heimdal/lib/kadm5/destroy_c.c b/third_party/heimdal/lib/kadm5/destroy_c.c
new file mode 100644
index 0000000..5ed85f7
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/destroy_c.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_destroy(void *server_handle)
+{
+ kadm5_client_context *context = server_handle;
+
+ free(context->realm);
+ free(context->readonly_admin_server);
+ free(context->admin_server);
+ if (context->sock != rk_INVALID_SOCKET)
+ rk_closesocket(context->sock);
+ if (context->client_name)
+ free(context->client_name);
+ if (context->service_name)
+ free(context->service_name);
+ if (context->ac != NULL)
+ krb5_auth_con_free(context->context, context->ac);
+ if(context->my_context)
+ krb5_free_context(context->context);
+ free(context);
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/destroy_s.c b/third_party/heimdal/lib/kadm5/destroy_s.c
new file mode 100644
index 0000000..0558b45
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/destroy_s.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * dealloc a `kadm5_config_params'
+ */
+
+static void
+destroy_config (kadm5_config_params *c)
+{
+ if (!c)
+ return;
+ free(c->realm);
+ free(c->dbname);
+ free(c->acl_file);
+ free(c->stash_file);
+}
+
+/*
+ * dealloc a kadm5_log_context
+ */
+
+static void
+destroy_kadm5_log_context (kadm5_log_context *c)
+{
+ if (!c)
+ return;
+ free(c->log_file);
+ if (c->socket_fd != rk_INVALID_SOCKET)
+ rk_closesocket(c->socket_fd);
+#ifdef NO_UNIX_SOCKETS
+ if (c->socket_info) {
+ freeaddrinfo(c->socket_info);
+ c->socket_info = NULL;
+ }
+#endif
+}
+
+/*
+ * destroy a kadm5 handle
+ */
+
+kadm5_ret_t
+kadm5_s_destroy(void *server_handle)
+{
+ kadm5_ret_t ret = 0;
+ kadm5_server_context *context = server_handle;
+ krb5_context kcontext = context->context;
+
+ _kadm5_s_free_hooks(context);
+ if (context->db != NULL)
+ ret = context->db->hdb_destroy(kcontext, context->db);
+ destroy_kadm5_log_context(&context->log_context);
+ destroy_config(&context->config);
+ krb5_free_principal(kcontext, context->caller);
+ if (context->my_context)
+ krb5_free_context(kcontext);
+ free(context);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/ent_setup.c b/third_party/heimdal/lib/kadm5/ent_setup.c
new file mode 100644
index 0000000..03c4fb1
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ent_setup.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+#define set_value(X, V) do { if((X) == NULL) (X) = malloc(sizeof(*(X))); *(X) = V; } while(0)
+#define set_null(X) do { if((X) != NULL) free((X)); (X) = NULL; } while (0)
+
+static void
+attr_to_flags(unsigned attr, HDBFlags *flags)
+{
+ flags->postdate = !(attr & KRB5_KDB_DISALLOW_POSTDATED);
+ flags->forwardable = !(attr & KRB5_KDB_DISALLOW_FORWARDABLE);
+ flags->initial = !!(attr & KRB5_KDB_DISALLOW_TGT_BASED);
+ flags->renewable = !(attr & KRB5_KDB_DISALLOW_RENEWABLE);
+ flags->proxiable = !(attr & KRB5_KDB_DISALLOW_PROXIABLE);
+ /* DUP_SKEY */
+ flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);
+ flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);
+ flags->require_pwchange = !!(attr & KRB5_KDB_REQUIRES_PWCHANGE);
+ /* HW_AUTH */
+ flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);
+ flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);
+ flags->client = !(attr & KRB5_KDB_DISALLOW_CLIENT);
+ flags->ok_as_delegate = !!(attr & KRB5_KDB_OK_AS_DELEGATE);
+ flags->trusted_for_delegation = !!(attr & KRB5_KDB_TRUSTED_FOR_DELEGATION);
+ flags->allow_kerberos4 = !!(attr & KRB5_KDB_ALLOW_KERBEROS4);
+ flags->allow_digest = !!(attr & KRB5_KDB_ALLOW_DIGEST);
+ flags->materialize = !!(attr & KRB5_KDB_MATERIALIZE);
+ flags->virtual_keys = !!(attr & KRB5_KDB_VIRTUAL_KEYS);
+ flags->virtual = !!(attr & KRB5_KDB_VIRTUAL);
+ flags->no_auth_data_reqd = !!(attr & KRB5_KDB_NO_AUTH_DATA_REQUIRED);
+ flags->auth_data_reqd = !!(attr & KRB5_KDB_AUTH_DATA_REQUIRED);
+
+ if (flags->no_auth_data_reqd && flags->auth_data_reqd)
+ flags->auth_data_reqd = 0;
+}
+
+/*
+ * Modify the `ent' according to `tl_data'.
+ */
+
+static kadm5_ret_t
+perform_tl_data(krb5_context context,
+ HDB *db,
+ hdb_entry *ent,
+ const krb5_tl_data *tl_data)
+{
+ kadm5_ret_t ret = 0;
+
+ if (tl_data->tl_data_type == KRB5_TL_PASSWORD) {
+ heim_utf8_string pw = tl_data->tl_data_contents;
+
+ if (pw[tl_data->tl_data_length] != '\0')
+ return KADM5_BAD_TL_TYPE;
+
+ ret = hdb_entry_set_password(context, db, ent, pw);
+
+ } else if (tl_data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE) {
+ unsigned long t;
+ unsigned char *s;
+
+ if (tl_data->tl_data_length != 4)
+ return KADM5_BAD_TL_TYPE;
+
+ s = tl_data->tl_data_contents;
+
+ (void) _krb5_get_int(s, &t, tl_data->tl_data_length);
+ ret = hdb_entry_set_pw_change_time(context, ent, t);
+
+ } else if (tl_data->tl_data_type == KRB5_TL_KEY_ROTATION) {
+ HDB_Ext_KeyRotation *prev_kr = 0;
+ HDB_extension *prev_ext;
+ HDB_extension ext;
+
+ ext.mandatory = 0;
+ ext.data.element = choice_HDB_extension_data_key_rotation;
+ prev_ext = hdb_find_extension(ent, ext.data.element);
+ if (prev_ext)
+ prev_kr = &prev_ext->data.u.key_rotation;
+ ret = decode_HDB_Ext_KeyRotation(tl_data->tl_data_contents,
+ tl_data->tl_data_length,
+ &ext.data.u.key_rotation, NULL);
+ if (ret == 0)
+ ret = hdb_validate_key_rotations(context, prev_kr,
+ &ext.data.u.key_rotation);
+ if (ret == 0)
+ ret = hdb_replace_extension(context, ent, &ext);
+ free_HDB_extension(&ext);
+ } else if (tl_data->tl_data_type == KRB5_TL_EXTENSION) {
+ HDB_extension ext;
+
+ ret = decode_HDB_extension(tl_data->tl_data_contents,
+ tl_data->tl_data_length,
+ &ext,
+ NULL);
+ if (ret)
+ return KADM5_BAD_TL_TYPE;
+
+ if (ext.data.element == choice_HDB_extension_data_key_rotation) {
+ HDB_extension *prev_ext = hdb_find_extension(ent,
+ ext.data.element);
+ HDB_Ext_KeyRotation *prev_kr = 0;
+
+ if (prev_ext)
+ prev_kr = &prev_ext->data.u.key_rotation;
+ ret = hdb_validate_key_rotations(context, prev_kr,
+ &ext.data.u.key_rotation);
+ }
+ if (ret)
+ ret = KADM5_BAD_TL_TYPE; /* XXX Need new error code */
+ if (ret == 0)
+ ret = hdb_replace_extension(context, ent, &ext);
+ free_HDB_extension(&ext);
+ } else if (tl_data->tl_data_type == KRB5_TL_ETYPES) {
+ if (!ent->etypes &&
+ (ent->etypes = calloc(1,
+ sizeof(ent->etypes[0]))) == NULL)
+ ret = krb5_enomem(context);
+ if (ent->etypes)
+ free_HDB_EncTypeList(ent->etypes);
+ if (ret == 0)
+ ret = decode_HDB_EncTypeList(tl_data->tl_data_contents,
+ tl_data->tl_data_length,
+ ent->etypes, NULL);
+ if (ret)
+ return KADM5_BAD_TL_TYPE;
+ } else if (tl_data->tl_data_type == KRB5_TL_ALIASES) {
+ return 0;
+ } else {
+ return KADM5_BAD_TL_TYPE;
+ }
+ return ret;
+}
+
+static void
+default_flags(hdb_entry *ent)
+{
+ ent->flags.client = 1;
+ ent->flags.server = 1;
+ ent->flags.forwardable = 1;
+ ent->flags.proxiable = 1;
+ ent->flags.renewable = 1;
+ ent->flags.postdate = 1;
+}
+
+
+/*
+ * Create the hdb entry `ent' based on data from `princ' with
+ * `princ_mask' specifying what fields to be gotten from there and
+ * `mask' specifying what fields we want filled in.
+ */
+
+kadm5_ret_t
+_kadm5_setup_entry(kadm5_server_context *context,
+ hdb_entry *ent,
+ uint32_t mask,
+ kadm5_principal_ent_t princ,
+ uint32_t princ_mask,
+ kadm5_principal_ent_t def,
+ uint32_t def_mask)
+{
+ if(mask & KADM5_PRINC_EXPIRE_TIME
+ && princ_mask & KADM5_PRINC_EXPIRE_TIME) {
+ if (princ->princ_expire_time)
+ set_value(ent->valid_end, princ->princ_expire_time);
+ else
+ set_null(ent->valid_end);
+ }
+ if(mask & KADM5_PW_EXPIRATION
+ && princ_mask & KADM5_PW_EXPIRATION) {
+ if (princ->pw_expiration)
+ set_value(ent->pw_end, princ->pw_expiration);
+ else
+ set_null(ent->pw_end);
+ }
+ if(mask & KADM5_ATTRIBUTES) {
+ if (princ_mask & KADM5_ATTRIBUTES) {
+ attr_to_flags(princ->attributes, &ent->flags);
+ } else if(def_mask & KADM5_ATTRIBUTES) {
+ attr_to_flags(def->attributes, &ent->flags);
+ ent->flags.invalid = 0;
+ } else {
+ default_flags(ent);
+ }
+ }
+
+ if(mask & KADM5_MAX_LIFE) {
+ if(princ_mask & KADM5_MAX_LIFE) {
+ if(princ->max_life)
+ set_value(ent->max_life, princ->max_life);
+ else
+ set_null(ent->max_life);
+ } else if(def_mask & KADM5_MAX_LIFE) {
+ if(def->max_life)
+ set_value(ent->max_life, def->max_life);
+ else
+ set_null(ent->max_life);
+ }
+ }
+ if(mask & KADM5_KVNO
+ && (princ_mask & KADM5_KVNO)) {
+ krb5_error_code ret;
+
+ ret = hdb_change_kvno(context->context, princ->kvno, ent);
+ if (ret && ret != HDB_ERR_KVNO_NOT_FOUND)
+ return ret;
+ ent->kvno = princ->kvno; /* force it */
+ }
+ if(mask & KADM5_MAX_RLIFE) {
+ if(princ_mask & KADM5_MAX_RLIFE) {
+ if(princ->max_renewable_life)
+ set_value(ent->max_renew, princ->max_renewable_life);
+ else
+ set_null(ent->max_renew);
+ } else if(def_mask & KADM5_MAX_RLIFE) {
+ if(def->max_renewable_life)
+ set_value(ent->max_renew, def->max_renewable_life);
+ else
+ set_null(ent->max_renew);
+ }
+ }
+ if(mask & KADM5_KEY_DATA
+ && princ_mask & KADM5_KEY_DATA) {
+ _kadm5_set_keys2(context, ent,
+ princ->n_key_data, princ->key_data);
+ }
+ if(mask & KADM5_TL_DATA) {
+ krb5_tl_data *tl;
+
+ for (tl = princ->tl_data; tl != NULL; tl = tl->tl_data_next) {
+ kadm5_ret_t ret;
+ ret = perform_tl_data(context->context, context->db, ent, tl);
+ if (ret)
+ return ret;
+ }
+ }
+ if(mask & KADM5_FAIL_AUTH_COUNT) {
+ /* XXX */
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/error.c b/third_party/heimdal/lib/kadm5/error.c
new file mode 100644
index 0000000..e6a6dec
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/error.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+_kadm5_error_code(kadm5_ret_t code)
+{
+ switch(code){
+ case HDB_ERR_EXISTS:
+ return KADM5_DUP;
+ case HDB_ERR_NOENTRY:
+ return KADM5_UNK_PRINC;
+ }
+ return code;
+}
diff --git a/third_party/heimdal/lib/kadm5/flush.c b/third_party/heimdal/lib/kadm5/flush.c
new file mode 100644
index 0000000..4409fe6
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/flush.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_s_flush(void *server_handle)
+{
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_c_flush(void *server_handle)
+{
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/flush_c.c b/third_party/heimdal/lib/kadm5/flush_c.c
new file mode 100644
index 0000000..c1a2a0a
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/flush_c.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_flush(void *server_handle)
+{
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/flush_s.c b/third_party/heimdal/lib/kadm5/flush_s.c
new file mode 100644
index 0000000..9a52458
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/flush_s.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_s_flush(void *server_handle)
+{
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/free.c b/third_party/heimdal/lib/kadm5/free.c
new file mode 100644
index 0000000..e8bb0f9
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/free.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+void
+kadm5_free_key_data(void *server_handle,
+ int16_t *n_key_data,
+ krb5_key_data *key_data)
+{
+ int i;
+ for(i = 0; i < *n_key_data; i++){
+ if(key_data[i].key_data_contents[0]){
+ memset(key_data[i].key_data_contents[0],
+ 0,
+ key_data[i].key_data_length[0]);
+ free(key_data[i].key_data_contents[0]);
+ }
+ if(key_data[i].key_data_contents[1])
+ free(key_data[i].key_data_contents[1]);
+ }
+ *n_key_data = 0;
+}
+
+
+void
+kadm5_free_principal_ent(void *server_handle,
+ kadm5_principal_ent_t princ)
+{
+ kadm5_server_context *context = server_handle;
+
+ if (princ->principal)
+ krb5_free_principal(context->context, princ->principal);
+ if (princ->mod_name)
+ krb5_free_principal(context->context, princ->mod_name);
+ kadm5_free_key_data(server_handle, &princ->n_key_data, princ->key_data);
+ while (princ->n_tl_data && princ->tl_data) {
+ krb5_tl_data *tp;
+ tp = princ->tl_data;
+ princ->tl_data = tp->tl_data_next;
+ princ->n_tl_data--;
+ memset(tp->tl_data_contents, 0, tp->tl_data_length);
+ free(tp->tl_data_contents);
+ free(tp);
+ }
+ free(princ->key_data);
+ free(princ->policy);
+}
+
+void
+kadm5_free_name_list(void *server_handle,
+ char **names,
+ int *count)
+{
+ int i;
+ for(i = 0; i < *count; i++)
+ free(names[i]);
+ free(names);
+ *count = 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin
new file mode 100644
index 0000000..2c5f72c
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin
Binary files differ
diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin
new file mode 100644
index 0000000..ff0b705
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin
Binary files differ
diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin
new file mode 100644
index 0000000..645fea7
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin
Binary files differ
diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin
new file mode 100644
index 0000000..e64ec5a
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin
Binary files differ
diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt
new file mode 100644
index 0000000..9d78e8d
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt
@@ -0,0 +1,101 @@
+# The body of this file contains a representation of a very small and contrived
+# kadm5_principal_ent_rec meant for fuzzing. For fuzzing purposes, the smaller
+# the input, the better.
+#
+# To compile this input into a binary input file suitable for fuzzing:
+#
+# cd build
+# make -j4
+# cd lib/kadm5
+# make test_marshall
+# ./test_marshall --in-text --byte-order-in=packed \
+# --byte-order-out=packed \
+# --out-hex \
+# kadm5_principal_ent_rec \
+# ../../../lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt
+#
+# then decode the hex (e.g., with xxd -r -p) and save it in a file.
+#
+# Currently we have that saved in lib/kadm5/fuzz-inputs-packed/.
+#
+# To build and fuzz with this input:
+#
+# cd build
+# AFL_HARDEN=1 make -j4 CC=afl-clang all
+# cd lib/kadm5
+# AFL_HARDEN=1 make -j4 CC=afl-clang test_marshall
+# rm -rf f; mkdir f
+# ../../libtool --mode=execute afl-fuzz \
+# -i ../../../lib/kadm5/fuzz-inputs-packed \
+# -o $PWD/f \
+# ./test_marshall --byte-order-in=packed \
+# --byte-order-out=packed \
+# --out-hex \
+# kadm5_principal_ent_rec '@@'
+#
+# A kadm5_principal_ent_rec follows:
+#
+# principal name
+int32 0
+int32 1
+string T
+string f
+# expiration
+int32 2
+# pw expiration
+int32 3
+# last pw change
+int32 4
+# max life
+int32 5
+# mod name optional (boolean, principal name)
+int32 1
+int32 0
+int32 1
+string T
+string b
+# mod time
+int32 6
+# attrs
+int32 7
+# kvno
+int32 8
+# master kvno
+int32 9
+# policy (boolean, string)
+int32 1
+string default
+# aux attrs
+int32 10
+# max renew life
+int32 11
+# last success
+int32 12
+# last fail
+int32 13
+# fail count
+int32 14
+# nkeydata
+int32 2
+# keydata[0] (ver, kvno, type, data, type, data)
+int32 15
+int32 16
+int32 17
+data 1122
+int32 18
+data 2233
+# keydata[1]
+int32 19
+int32 21
+int32 22
+data 3344
+int32 23
+data 4455
+# ntldata
+int32 2
+# ntldata[0] (type, data)
+int32 24
+data 5566
+# ntldata[1]
+int32 25
+data 6677
diff --git a/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt
new file mode 100644
index 0000000..332c759
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt
@@ -0,0 +1,54 @@
+# See lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt for instructions on how
+# to fuzz krb5/kadm5 marshalling.
+#
+# A truncated kadm5_principal_ent_rec follows:
+#
+# principal name
+int32 0
+int32 1
+string T
+string f
+# expiration
+int32 2
+# pw expiration
+int32 3
+# last pw change
+int32 4
+# max life
+int32 5
+# mod name optional (boolean, principal name)
+int32 1
+int32 0
+int32 1
+string T
+string b
+# mod time
+int32 6
+# attrs
+int32 7
+# kvno
+int32 8
+# master kvno
+int32 9
+# policy (boolean, string)
+int32 1
+string default
+# aux attrs
+int32 10
+# max renew life
+int32 11
+# last success
+int32 12
+# last fail
+int32 13
+# fail count
+int32 14
+# nkeydata
+int32 2
+# keydata[0] (ver, kvno, type, data, type, data)
+int32 15
+int32 16
+int32 17
+data 1122
+int32 18
+data 2233
diff --git a/third_party/heimdal/lib/kadm5/get_c.c b/third_party/heimdal/lib/kadm5/get_c.c
new file mode 100644
index 0000000..1c1fd9d
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/get_c.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1997 - 2000, 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_get_principal(void *server_handle,
+ krb5_principal princ,
+ kadm5_principal_ent_t ent,
+ uint32_t mask)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ ret = _kadm5_connect(server_handle, 0 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_get);
+ if (ret)
+ goto out;
+ ret = krb5_store_principal(sp, princ);
+ if (ret)
+ goto out;
+ ret = krb5_store_int32(sp, mask);
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ if (ret == 0)
+ ret = kadm5_ret_principal_ent(sp, ent);
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/get_princs_c.c b/third_party/heimdal/lib/kadm5/get_princs_c.c
new file mode 100644
index 0000000..be86cfa
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/get_princs_c.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_get_principals(void *server_handle,
+ const char *expression,
+ char ***princs,
+ int *count)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+ int i;
+
+ *count = 0;
+ *princs = NULL;
+
+ ret = _kadm5_connect(server_handle, 0 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_get_princs);
+ if (ret)
+ goto out;
+ ret = krb5_store_int32(sp, expression != NULL ? 1 : 0);
+ if (ret)
+ goto out;
+ if (expression) {
+ ret = krb5_store_string(sp, expression);
+ if (ret)
+ goto out;
+ }
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data (&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ if (ret)
+ goto out;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret)
+ goto out;
+
+ *princs = calloc(tmp + 1, sizeof(**princs));
+ if (*princs == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ for (i = 0; i < tmp; i++) {
+ ret = krb5_ret_string(sp, &(*princs)[i]);
+ if (ret)
+ goto out;
+ }
+ *count = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_c_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+ size_t i;
+ int stop = 0;
+
+ ret = _kadm5_connect(server_handle, 0 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_get_princs);
+ if (ret)
+ goto out;
+
+ /*
+ * Our protocol has an int boolean for this operation to indicate whether
+ * there's an expression. What we'll do here is that instead of sending
+ * just false or trueish, for online iteration we'll send a number other
+ * than 0 or 1 -- a magic value > 0 and < INT_MAX.
+ *
+ * In response we'll expect multiple replies, each with up to some small
+ * number of principal names. See kadmin/server.c.
+ */
+ ret = krb5_store_int32(sp, 0x55555555);
+ if (ret)
+ goto out;
+ ret = krb5_store_string(sp, expression ? expression : "");
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data (&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ if (ret)
+ goto out;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret)
+ goto out;
+
+ if (tmp < 0) {
+ size_t n = -tmp;
+ int more = 1;
+
+ /* The server supports online iteration, hooray! */
+
+ while (more) {
+ /*
+ * We expect any number of chunks, each having `n' names, except
+ * the last one would have fewer than `n' (possibly zero, even).
+ *
+ * After that we expect one more reply with just a final return
+ * code.
+ */
+ krb5_data_free(&reply);
+ krb5_storage_free(sp);
+ sp = NULL;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret)
+ goto out;
+
+ /* Every chunk begins with a status code */
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (ret)
+ goto out;
+
+ /* We expect up to -tmp principals per reply */
+ for (i = 0; i < n; i++) {
+ char *princ = NULL;
+
+ ret = krb5_ret_string(sp, &princ);
+ if (ret == HEIM_ERR_EOF) {
+ /* This was the last reply */
+ more = 0;
+ ret = 0;
+ break;
+ }
+ if (ret)
+ goto out;
+ if (!stop) {
+ stop = cb(cbdata, princ);
+ if (stop) {
+ /*
+ * Tell the server to stop.
+ *
+ * We use a NOP for this, but with a payload that says
+ * "don't reply to the NOP" just in case the NOP
+ * arrives and is processed _after_ the LISTing has
+ * finished.
+ */
+ krb5_storage_free(sp);
+ if ((sp = krb5_storage_emem()) &&
+ krb5_store_int32(sp, kadm_nop) == 0 &&
+ krb5_store_int32(sp, 0))
+ (void) _kadm5_client_send(context, sp);
+ }
+ }
+ free(princ);
+ }
+ }
+ /* Get the final result code */
+ krb5_data_free(&reply);
+ krb5_storage_free(sp);
+ sp = NULL;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret)
+ goto out;
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (!stop) {
+ /*
+ * Send our "interrupt" after the last chunk if we hand't
+ * interrupted already.
+ */
+ krb5_storage_free(sp);
+ if ((sp = krb5_storage_emem()) &&
+ krb5_store_int32(sp, kadm_nop) == 0)
+ (void) _kadm5_client_send(context, sp);
+ }
+ } else {
+ size_t n = tmp;
+
+ /* Old server -- listing not online */
+ for (i = 0; i < n; i++) {
+ char *princ = NULL;
+
+ ret = krb5_ret_string(sp, &princ);
+ if (ret)
+ goto out;
+ cb(cbdata, princ);
+ free(princ);
+ }
+ }
+
+out:
+ krb5_clear_error_message(context->context);
+
+out_keep_error:
+ if (stop)
+ ret = stop;
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/get_princs_s.c b/third_party/heimdal/lib/kadm5/get_princs_s.c
new file mode 100644
index 0000000..14d3907
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/get_princs_s.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct foreach_data {
+ const char *exp;
+ char *exp2;
+ char **princs;
+ size_t nalloced;
+ size_t count;
+};
+
+static krb5_error_code
+add_princ(krb5_context context, struct foreach_data *d, char *princ)
+{
+
+ if (d->count == INT_MAX)
+ return ERANGE;
+ if (d->nalloced == d->count) {
+ size_t n = d->nalloced + (d->nalloced >> 1) + 128; /* No O(N^2) pls */
+ char **tmp;
+
+ if (SIZE_MAX / sizeof(*tmp) <= n)
+ return ERANGE;
+ if ((tmp = realloc(d->princs, n * sizeof(*tmp))) == NULL)
+ return krb5_enomem(context);
+ d->princs = tmp;
+ d->nalloced = n;
+ }
+ d->princs[d->count++] = princ;
+ return 0;
+}
+
+static krb5_error_code
+foreach(krb5_context context, HDB *db, hdb_entry *ent, void *data)
+{
+ struct foreach_data *d = data;
+ char *princ;
+ krb5_error_code ret;
+ ret = krb5_unparse_name(context, ent->principal, &princ);
+ if(ret)
+ return ret;
+ if(d->exp){
+ if(fnmatch(d->exp, princ, 0) == 0 || fnmatch(d->exp2, princ, 0) == 0)
+ ret = add_princ(context, d, princ);
+ else
+ free(princ);
+ }else{
+ ret = add_princ(context, d, princ);
+ }
+ if(ret)
+ free(princ);
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_get_principals(void *server_handle,
+ const char *expression,
+ char ***princs,
+ int *count)
+{
+ struct foreach_data d;
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret = 0;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
+ if (ret) {
+ krb5_warn(context->context, ret, "opening database");
+ return ret;
+ }
+ }
+ d.exp = expression;
+ d.exp2 = NULL;
+ if (expression) {
+ krb5_realm r;
+ int aret;
+
+ ret = krb5_get_default_realm(context->context, &r);
+ if (ret == 0) {
+ aret = asprintf(&d.exp2, "%s@%s", expression, r);
+ free(r);
+ if (aret == -1 || d.exp2 == NULL)
+ ret = krb5_enomem(context->context);
+ }
+ }
+ d.princs = NULL;
+ d.nalloced = 0;
+ d.count = 0;
+ if (ret == 0)
+ ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA,
+ foreach, &d);
+
+ if (ret == 0)
+ ret = add_princ(context->context, &d, NULL);
+ if (d.count >= INT_MAX)
+ *count = INT_MAX;
+ else
+ *count = d.count - 1;
+ if (ret == 0)
+ *princs = d.princs;
+ else
+ kadm5_free_name_list(context, d.princs, count);
+ free(d.exp2);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return _kadm5_error_code(ret);
+}
+
+struct foreach_online_data {
+ const char *exp;
+ char *exp2;
+ int (*cb)(void *, const char *);
+ void *cbdata;
+};
+
+static krb5_error_code
+foreach_online(krb5_context context, HDB *db, hdb_entry *ent, void *data)
+{
+ struct foreach_online_data *d = data;
+ krb5_error_code ret;
+ char *princ = NULL;
+
+ ret = krb5_unparse_name(context, ent->principal, &princ);
+ if (ret == 0) {
+ if (!d->exp ||
+ fnmatch(d->exp, princ, 0) == 0 || fnmatch(d->exp2, princ, 0) == 0)
+ ret = d->cb(d->cbdata, princ);
+ free(princ);
+ }
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_iter_principals(void *server_handle,
+ const char *expression,
+ int (*cb)(void *, const char *),
+ void *cbdata)
+{
+ struct foreach_online_data d;
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret = 0;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
+ if (ret) {
+ krb5_warn(context->context, ret, "opening database");
+ return ret;
+ }
+ }
+ d.exp = expression;
+ d.exp2 = NULL;
+ d.cb = cb;
+ d.cbdata = cbdata;
+ if (expression) {
+ krb5_realm r;
+ int aret;
+
+ ret = krb5_get_default_realm(context->context, &r);
+ if (ret == 0) {
+ aret = asprintf(&d.exp2, "%s@%s", expression, r);
+ free(r);
+ if (aret == -1 || d.exp2 == NULL)
+ ret = krb5_enomem(context->context);
+ }
+ }
+ if (ret == 0)
+ ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA,
+ foreach_online, &d);
+ free(d.exp2);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return _kadm5_error_code(ret);
+}
diff --git a/third_party/heimdal/lib/kadm5/get_s.c b/third_party/heimdal/lib/kadm5/get_s.c
new file mode 100644
index 0000000..c231366
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/get_s.c
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <krb5_locl.h>
+#include "kadm5_locl.h"
+#include <assert.h>
+
+RCSID("$Id$");
+
+static kadm5_ret_t
+add_tl_data(kadm5_principal_ent_t ent, int16_t type,
+ const void *data, size_t size)
+{
+ krb5_tl_data *tl;
+
+ tl = calloc(1, sizeof(*tl));
+ if (tl == NULL)
+ return _kadm5_error_code(ENOMEM);
+
+ tl->tl_data_type = type;
+ tl->tl_data_length = size;
+ tl->tl_data_contents = malloc(size);
+ if (tl->tl_data_contents == NULL && size != 0) {
+ free(tl);
+ return _kadm5_error_code(ENOMEM);
+ }
+ memcpy(tl->tl_data_contents, data, size);
+
+ tl->tl_data_next = ent->tl_data;
+ ent->tl_data = tl;
+ ent->n_tl_data++;
+
+ return 0;
+}
+
+static
+krb5_error_code
+copy_keyset_to_kadm5(kadm5_server_context *context, krb5_kvno kvno,
+ size_t n_keys, Key *keys, krb5_salt *salt,
+ kadm5_principal_ent_t out)
+{
+ size_t i;
+ Key *key;
+ krb5_key_data *kd;
+ krb5_data *sp;
+ krb5_error_code ret = 0;
+
+ for (i = 0; i < n_keys; i++) {
+ key = &keys[i];
+ kd = &out->key_data[out->n_key_data];
+ kd->key_data_ver = 2;
+ kd->key_data_kvno = kvno;
+ kd->key_data_type[0] = key->key.keytype;
+ if(key->salt)
+ kd->key_data_type[1] = key->salt->type;
+ else
+ kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
+ /* setup key */
+ kd->key_data_length[0] = key->key.keyvalue.length;
+ kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
+ if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
+ ret = krb5_enomem(context->context);
+ break;
+ }
+ memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
+ kd->key_data_length[0]);
+ /* setup salt */
+ if(key->salt)
+ sp = &key->salt->salt;
+ else
+ sp = &salt->saltvalue;
+ kd->key_data_length[1] = sp->length;
+ kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
+ if(kd->key_data_length[1] != 0
+ && kd->key_data_contents[1] == NULL) {
+ memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
+ ret = krb5_enomem(context->context);
+ break;
+ }
+ memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
+ out->n_key_data++;
+ }
+
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_get_principal(void *server_handle,
+ krb5_principal princ,
+ kadm5_principal_ent_t out,
+ uint32_t mask)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+ hdb_entry ent;
+ unsigned int flags = HDB_F_GET_ANY | HDB_F_ADMIN_DATA;
+
+ if ((mask & KADM5_KEY_DATA) || (mask & KADM5_KVNO))
+ flags |= HDB_F_ALL_KVNOS | HDB_F_DECRYPT;
+
+ memset(&ent, 0, sizeof(ent));
+ memset(out, 0, sizeof(*out));
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * We may want to attempt to recover the log on read operations, but we
+ * because the HDB/log lock order is reversed on slaves, in order to avoid
+ * lock contention from kadm5srv apps we need to make sure that the the HDB
+ * open for read-write is optimistic and attempts only a non-blocking lock,
+ * and if it doesn't get it then it should fallback to read-only. But we
+ * don't have that option in the hdb_open() interface at this time.
+ *
+ * For now we won't attempt to recover the log.
+ */
+
+ ret = hdb_fetch_kvno(context->context, context->db, princ, flags,
+ 0 /*timestamp*/, 0/*etype*/, 0/*kvno*/, &ent);
+
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ if(ret)
+ return _kadm5_error_code(ret);
+
+ if(mask & KADM5_PRINCIPAL)
+ ret = krb5_copy_principal(context->context, ent.principal,
+ &out->principal);
+ if(ret)
+ goto out;
+ if(mask & KADM5_PRINC_EXPIRE_TIME && ent.valid_end)
+ out->princ_expire_time = *ent.valid_end;
+ if(mask & KADM5_PW_EXPIRATION && ent.pw_end)
+ out->pw_expiration = *ent.pw_end;
+ if(mask & KADM5_LAST_PWD_CHANGE)
+ hdb_entry_get_pw_change_time(&ent, &out->last_pwd_change);
+ if(mask & KADM5_ATTRIBUTES){
+ out->attributes |= ent.flags.postdate ? 0 : KRB5_KDB_DISALLOW_POSTDATED;
+ out->attributes |= ent.flags.forwardable ? 0 : KRB5_KDB_DISALLOW_FORWARDABLE;
+ out->attributes |= ent.flags.initial ? KRB5_KDB_DISALLOW_TGT_BASED : 0;
+ out->attributes |= ent.flags.renewable ? 0 : KRB5_KDB_DISALLOW_RENEWABLE;
+ out->attributes |= ent.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE;
+ out->attributes |= ent.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0;
+ out->attributes |= ent.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0;
+ out->attributes |= ent.flags.require_pwchange ? KRB5_KDB_REQUIRES_PWCHANGE : 0;
+ out->attributes |= ent.flags.client ? 0 : KRB5_KDB_DISALLOW_CLIENT;
+ out->attributes |= ent.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR;
+ out->attributes |= ent.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0;
+ out->attributes |= ent.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0;
+ out->attributes |= ent.flags.trusted_for_delegation ? KRB5_KDB_TRUSTED_FOR_DELEGATION : 0;
+ out->attributes |= ent.flags.allow_kerberos4 ? KRB5_KDB_ALLOW_KERBEROS4 : 0;
+ out->attributes |= ent.flags.allow_digest ? KRB5_KDB_ALLOW_DIGEST : 0;
+ out->attributes |= ent.flags.virtual_keys ? KRB5_KDB_VIRTUAL_KEYS : 0;
+ out->attributes |= ent.flags.virtual ? KRB5_KDB_VIRTUAL : 0;
+ out->attributes |= ent.flags.no_auth_data_reqd ? KRB5_KDB_NO_AUTH_DATA_REQUIRED : 0;
+ out->attributes |= ent.flags.auth_data_reqd ? KRB5_KDB_AUTH_DATA_REQUIRED : 0;
+ }
+ if(mask & KADM5_MAX_LIFE) {
+ if(ent.max_life)
+ out->max_life = *ent.max_life;
+ else
+ out->max_life = INT_MAX;
+ }
+ if(mask & KADM5_MOD_TIME) {
+ if(ent.modified_by)
+ out->mod_date = ent.modified_by->time;
+ else
+ out->mod_date = ent.created_by.time;
+ }
+ if(mask & KADM5_MOD_NAME) {
+ if(ent.modified_by) {
+ if (ent.modified_by->principal != NULL)
+ ret = krb5_copy_principal(context->context,
+ ent.modified_by->principal,
+ &out->mod_name);
+ } else if(ent.created_by.principal != NULL)
+ ret = krb5_copy_principal(context->context,
+ ent.created_by.principal,
+ &out->mod_name);
+ else
+ out->mod_name = NULL;
+ }
+ if(ret)
+ goto out;
+
+ if(mask & KADM5_KVNO)
+ out->kvno = ent.kvno;
+ if(mask & KADM5_MKVNO) {
+ size_t n;
+ out->mkvno = 0; /* XXX */
+ for(n = 0; n < ent.keys.len; n++)
+ if(ent.keys.val[n].mkvno) {
+ out->mkvno = *ent.keys.val[n].mkvno; /* XXX this isn't right */
+ break;
+ }
+ }
+#if 0 /* XXX implement */
+ if(mask & KADM5_AUX_ATTRIBUTES)
+ ;
+ if(mask & KADM5_LAST_SUCCESS)
+ ;
+ if(mask & KADM5_LAST_FAILED)
+ ;
+ if(mask & KADM5_FAIL_AUTH_COUNT)
+ ;
+#endif
+ if(mask & KADM5_POLICY) {
+ HDB_extension *ext;
+
+ ext = hdb_find_extension(&ent, choice_HDB_extension_data_policy);
+ if (ext == NULL) {
+ out->policy = strdup("default");
+ /* It's OK if we retun NULL instead of "default" */
+ } else {
+ out->policy = strdup(ext->data.u.policy);
+ if (out->policy == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+ }
+ if(mask & KADM5_MAX_RLIFE) {
+ if(ent.max_renew)
+ out->max_renewable_life = *ent.max_renew;
+ else
+ out->max_renewable_life = INT_MAX;
+ }
+ if(mask & KADM5_KEY_DATA){
+ size_t i;
+ size_t n_keys = ent.keys.len;
+ krb5_salt salt;
+ HDB_extension *ext;
+ HDB_Ext_KeySet *hist_keys = NULL;
+
+ /* Don't return stale keys to kadm5 clients */
+ ret = hdb_prune_keys(context->context, &ent);
+ if (ret)
+ goto out;
+ ext = hdb_find_extension(&ent, choice_HDB_extension_data_hist_keys);
+ if (ext != NULL)
+ hist_keys = &ext->data.u.hist_keys;
+
+ krb5_get_pw_salt(context->context, ent.principal, &salt);
+ for (i = 0; hist_keys != NULL && i < hist_keys->len; i++)
+ n_keys += hist_keys->val[i].keys.len;
+ out->key_data = malloc(n_keys * sizeof(*out->key_data));
+ if (out->key_data == NULL && n_keys != 0) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ out->n_key_data = 0;
+ ret = copy_keyset_to_kadm5(context, ent.kvno, ent.keys.len,
+ ent.keys.val, &salt, out);
+ if (ret)
+ goto out;
+ for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) {
+ ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno,
+ hist_keys->val[i].keys.len,
+ hist_keys->val[i].keys.val,
+ &salt, out);
+ if (ret)
+ goto out;
+ }
+ krb5_free_salt(context->context, salt);
+ assert( out->n_key_data == n_keys );
+ }
+ assert(ret == 0);
+ if(mask & KADM5_TL_DATA) {
+ time_t last_pw_expire;
+ const HDB_Ext_PKINIT_acl *acl;
+ const HDB_Ext_Aliases *aliases;
+ const HDB_Ext_KeyRotation *kr;
+ heim_octet_string krb5_config;
+
+ if (ent.etypes) {
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
+ ent.etypes, &len, ret);
+ if (ret == 0) {
+ ret = add_tl_data(out, KRB5_TL_ETYPES, buf.data, buf.length);
+ free(buf.data);
+ }
+ if (ret)
+ goto out;
+ }
+
+ ret = hdb_entry_get_pw_change_time(&ent, &last_pw_expire);
+ if (ret == 0 && last_pw_expire) {
+ unsigned char buf[4];
+ _krb5_put_int(buf, last_pw_expire, sizeof(buf));
+ ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf));
+ if (ret)
+ goto out;
+ }
+
+ ret = hdb_entry_get_krb5_config(&ent, &krb5_config);
+ if (ret == 0 && krb5_config.length) {
+ ret = add_tl_data(out, KRB5_TL_KRB5_CONFIG, krb5_config.data,
+ krb5_config.length);
+ if (ret)
+ goto out;
+ }
+ /*
+ * If the client was allowed to get key data, let it have the
+ * password too.
+ */
+ if (mask & KADM5_KEY_DATA) {
+ heim_utf8_string pw;
+
+ /* XXX But not if the client doesn't have ext-keys */
+ ret = hdb_entry_get_password(context->context,
+ context->db, &ent, &pw);
+ if (ret == 0) {
+ ret = add_tl_data(out, KRB5_TL_PASSWORD, pw, strlen(pw) + 1);
+ free(pw);
+ if (ret)
+ goto out;
+ }
+ krb5_clear_error_message(context->context);
+ }
+
+ ret = hdb_entry_get_pkinit_acl(&ent, &acl);
+ if (ret == 0 && acl) {
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length,
+ acl, &len, ret);
+ if (ret)
+ goto out;
+ if (len != buf.length)
+ krb5_abortx(context->context,
+ "internal ASN.1 encoder error");
+ ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length);
+ free(buf.data);
+ if (ret)
+ goto out;
+ }
+
+ ret = hdb_entry_get_aliases(&ent, &aliases);
+ if (ret == 0 && aliases) {
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length,
+ aliases, &len, ret);
+ if (ret)
+ goto out;
+ if (len != buf.length)
+ krb5_abortx(context->context,
+ "internal ASN.1 encoder error");
+ ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length);
+ free(buf.data);
+ if (ret)
+ goto out;
+ }
+
+ ret = hdb_entry_get_key_rotation(context->context, &ent, &kr);
+ if (ret == 0 && kr) {
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(HDB_Ext_KeyRotation, buf.data, buf.length,
+ kr, &len, ret);
+ if (ret == 0)
+ ret = add_tl_data(out, KRB5_TL_KEY_ROTATION, buf.data, buf.length);
+ free(buf.data);
+ }
+ }
+
+ out:
+ if (ret)
+ kadm5_free_principal_ent(context, out);
+ hdb_free_entry(context->context, context->db, &ent);
+
+ return _kadm5_error_code(ret);
+}
diff --git a/third_party/heimdal/lib/kadm5/init_c.c b/third_party/heimdal/lib/kadm5/init_c.c
new file mode 100644
index 0000000..ffbb639
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/init_c.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+#include <sys/types.h>
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+
+RCSID("$Id$");
+
+static kadm5_ret_t
+kadm5_c_lock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
+static kadm5_ret_t
+kadm5_c_unlock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
+static void
+set_funcs(kadm5_client_context *c)
+{
+#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
+#define SETNOTIMP(C, F) (C)->funcs.F = 0
+ SET(c, chpass_principal);
+ SET(c, chpass_principal_with_key);
+ SET(c, create_principal);
+ SET(c, delete_principal);
+ SET(c, destroy);
+ SET(c, flush);
+ SET(c, get_principal);
+ SET(c, get_principals);
+ SET(c, get_privs);
+ SET(c, modify_principal);
+ SET(c, prune_principal);
+ SET(c, randkey_principal);
+ SET(c, rename_principal);
+ SET(c, lock);
+ SET(c, unlock);
+ SETNOTIMP(c, setkey_principal_3);
+ SET(c, iter_principals);
+ SET(c, dup_context);
+}
+
+kadm5_ret_t
+_kadm5_c_init_context(kadm5_client_context **ctx,
+ kadm5_config_params *params,
+ krb5_context context)
+{
+ krb5_error_code ret;
+ char *colon;
+
+ *ctx = malloc(sizeof(**ctx));
+ if (*ctx == NULL)
+ return krb5_enomem(context);
+ memset(*ctx, 0, sizeof(**ctx));
+ krb5_add_et_list(context, initialize_kadm5_error_table_r);
+ set_funcs(*ctx);
+ (*ctx)->context = context;
+ if (params->mask & KADM5_CONFIG_REALM) {
+ ret = 0;
+ (*ctx)->realm = strdup(params->realm);
+ if ((*ctx)->realm == NULL)
+ ret = krb5_enomem(context);
+ } else
+ ret = krb5_get_default_realm((*ctx)->context, &(*ctx)->realm);
+ if (ret) {
+ free(*ctx);
+ return ret;
+ }
+
+ /*
+ * FIXME: If we have a hostlist, we should use the hostlist so that if we
+ * can't reach one server we try another.
+ */
+ if (params->mask & KADM5_CONFIG_ADMIN_SERVER)
+ (*ctx)->admin_server = strdup(params->admin_server);
+ else {
+ char **hostlist;
+
+ ret = krb5_get_krb_admin_hst(context, &(*ctx)->realm, &hostlist);
+ if (ret) {
+ free((*ctx)->realm);
+ free(*ctx);
+ return ret;
+ }
+ (*ctx)->admin_server = strdup(*hostlist);
+ krb5_free_krbhst(context, hostlist);
+ }
+
+ if ((*ctx)->admin_server == NULL) {
+ free((*ctx)->realm);
+ free(*ctx);
+ return krb5_enomem(context);
+ }
+ colon = strchr((*ctx)->admin_server, ':');
+ if (colon != NULL)
+ *colon++ = '\0';
+
+ (*ctx)->kadmind_port = 0;
+
+ if (params->mask & KADM5_CONFIG_KADMIND_PORT)
+ (*ctx)->kadmind_port = params->kadmind_port;
+ else if (colon != NULL) {
+ char *end;
+
+ (*ctx)->kadmind_port = htons(strtol(colon, &end, 0));
+ }
+ if ((*ctx)->kadmind_port == 0)
+ (*ctx)->kadmind_port = krb5_getportbyname(context, "kerberos-adm",
+ "tcp", 749);
+
+ if (params->mask & KADM5_CONFIG_READONLY_ADMIN_SERVER) {
+ (*ctx)->readonly_admin_server = strdup(params->readonly_admin_server);
+ if ((*ctx)->readonly_admin_server == NULL) {
+ free((*ctx)->realm);
+ free(*ctx);
+ return krb5_enomem(context);
+ }
+ } else {
+ char **hostlist;
+
+ ret = krb5_get_krb_readonly_admin_hst(context, &(*ctx)->realm,
+ &hostlist);
+ if (ret == 0) {
+ (*ctx)->readonly_admin_server = strdup(*hostlist);
+ krb5_free_krbhst(context, hostlist);
+ if ((*ctx)->readonly_admin_server == NULL) {
+ free((*ctx)->realm);
+ free(*ctx);
+ return krb5_enomem(context);
+ }
+ }
+ }
+ if ((*ctx)->readonly_admin_server) {
+ colon = strchr((*ctx)->readonly_admin_server, ':');
+ if (colon != NULL)
+ *colon++ = '\0';
+
+ } else {
+ colon = NULL;
+ }
+
+ (*ctx)->readonly_kadmind_port = 0;
+ if (params->mask & KADM5_CONFIG_READONLY_KADMIN_PORT)
+ (*ctx)->readonly_kadmind_port = params->readonly_kadmind_port;
+ else if (colon != NULL) {
+ char *end;
+
+ (*ctx)->readonly_kadmind_port = htons(strtol(colon, &end, 0));
+ }
+ if ((*ctx)->readonly_kadmind_port == 0)
+ (*ctx)->readonly_kadmind_port = (*ctx)->kadmind_port;
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_c_dup_context(void *vin, void **out)
+{
+ krb5_error_code ret;
+ kadm5_client_context *in = vin;
+ krb5_context context = in->context;
+ kadm5_client_context *ctx;
+
+ *out = NULL;
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return krb5_enomem(in->context);
+
+
+ memset(ctx, 0, sizeof(*ctx));
+ set_funcs(ctx);
+ ctx->readonly_kadmind_port = in->readonly_kadmind_port;
+ ctx->kadmind_port = in->kadmind_port;
+
+ ret = krb5_copy_context(context, &(ctx->context));
+ if (ret == 0) {
+ ctx->my_context = TRUE;
+ ret = krb5_add_et_list(ctx->context, initialize_kadm5_error_table_r);
+ }
+ if (ret == 0 && (ctx->realm = strdup(in->realm)) == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0 &&
+ (ctx->admin_server = strdup(in->admin_server)) == NULL)
+ ret = krb5_enomem(context);
+ if (in->readonly_admin_server &&
+ (ctx->readonly_admin_server = strdup(in->readonly_admin_server)) == NULL)
+ ret = krb5_enomem(context);
+ if (in->keytab && (ctx->keytab = strdup(in->keytab)) == NULL)
+ ret = krb5_enomem(context);
+ if (in->ccache) {
+ char *fullname = NULL;
+
+ ret = krb5_cc_get_full_name(context, in->ccache, &fullname);
+ if (ret == 0)
+ ret = krb5_cc_resolve(context, fullname, &ctx->ccache);
+ free(fullname);
+ }
+ ctx->sock = -1;
+ if (ret == 0)
+ *out = ctx;
+ else
+ kadm5_c_destroy(ctx);
+ return ret;
+}
+
+static krb5_error_code
+get_kadm_ticket(krb5_context context,
+ krb5_ccache id,
+ krb5_principal client,
+ const char *server_name)
+{
+ krb5_error_code ret;
+ krb5_creds in, *out;
+
+ memset(&in, 0, sizeof(in));
+ in.client = client;
+ ret = krb5_parse_name(context, server_name, &in.server);
+ if(ret)
+ return ret;
+ ret = krb5_get_credentials(context, 0, id, &in, &out);
+ if(ret == 0)
+ krb5_free_creds(context, out);
+ krb5_free_principal(context, in.server);
+ return ret;
+}
+
+static krb5_error_code
+get_new_cache(krb5_context context,
+ krb5_principal client,
+ const char *password,
+ krb5_prompter_fct prompter,
+ const char *keytab,
+ const char *server_name,
+ krb5_ccache *ret_cache)
+{
+ krb5_error_code ret;
+ krb5_creds cred;
+ krb5_get_init_creds_opt *opt;
+ krb5_ccache id;
+
+ ret = krb5_get_init_creds_opt_alloc (context, &opt);
+ if (ret)
+ return ret;
+
+ krb5_get_init_creds_opt_set_default_flags(context, "kadmin",
+ krb5_principal_get_realm(context,
+ client),
+ opt);
+
+
+ krb5_get_init_creds_opt_set_forwardable (opt, FALSE);
+ krb5_get_init_creds_opt_set_proxiable (opt, FALSE);
+
+ if(password == NULL && prompter == NULL) {
+ krb5_keytab kt;
+ if(keytab == NULL)
+ ret = krb5_kt_default(context, &kt);
+ else
+ ret = krb5_kt_resolve(context, keytab, &kt);
+ if(ret) {
+ krb5_get_init_creds_opt_free(context, opt);
+ return ret;
+ }
+ ret = krb5_get_init_creds_keytab (context,
+ &cred,
+ client,
+ kt,
+ 0,
+ server_name,
+ opt);
+ krb5_kt_close(context, kt);
+ } else {
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ client,
+ password,
+ prompter,
+ NULL,
+ 0,
+ server_name,
+ opt);
+ }
+ krb5_get_init_creds_opt_free(context, opt);
+ switch(ret){
+ case 0:
+ break;
+ case KRB5_LIBOS_PWDINTR: /* don't print anything if it was just C-c:ed */
+ case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+ case KRB5KRB_AP_ERR_MODIFIED:
+ return KADM5_BAD_PASSWORD;
+ default:
+ return ret;
+ }
+ ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
+ if(ret)
+ return ret;
+ ret = krb5_cc_initialize (context, id, cred.client);
+ if (ret)
+ return ret;
+ ret = krb5_cc_store_cred (context, id, &cred);
+ if (ret)
+ return ret;
+ krb5_free_cred_contents (context, &cred);
+ *ret_cache = id;
+ return 0;
+}
+
+/*
+ * Check the credential cache `id´ to figure out what principal to use
+ * when talking to the kadmind. If there is a initial kadmin/admin@
+ * credential in the cache, use that client principal. Otherwise, use
+ * the client principals first component and add /admin to the
+ * principal.
+ */
+
+static krb5_error_code
+get_cache_principal(krb5_context context,
+ krb5_ccache *id,
+ krb5_principal *client)
+{
+ krb5_error_code ret;
+ const char *name, *inst;
+ krb5_principal p1, p2;
+
+ ret = krb5_cc_default(context, id);
+ if(ret) {
+ *id = NULL;
+ return ret;
+ }
+
+ ret = krb5_cc_get_principal(context, *id, &p1);
+ if(ret) {
+ krb5_cc_close(context, *id);
+ *id = NULL;
+ return ret;
+ }
+
+ ret = krb5_make_principal(context, &p2, NULL,
+ "kadmin", "admin", NULL);
+ if (ret) {
+ krb5_cc_close(context, *id);
+ *id = NULL;
+ krb5_free_principal(context, p1);
+ return ret;
+ }
+
+ {
+ krb5_creds in, *out;
+ krb5_kdc_flags flags;
+
+ flags.i = 0;
+ memset(&in, 0, sizeof(in));
+
+ in.client = p1;
+ in.server = p2;
+
+ /* check for initial ticket kadmin/admin */
+ ret = krb5_get_credentials_with_flags(context, KRB5_GC_CACHED, flags,
+ *id, &in, &out);
+ krb5_free_principal(context, p2);
+ if (ret == 0) {
+ if (out->flags.b.initial) {
+ *client = p1;
+ krb5_free_creds(context, out);
+ return 0;
+ }
+ krb5_free_creds(context, out);
+ }
+ }
+ krb5_cc_close(context, *id);
+ *id = NULL;
+
+ name = krb5_principal_get_comp_string(context, p1, 0);
+ inst = krb5_principal_get_comp_string(context, p1, 1);
+ if(inst == NULL || strcmp(inst, "admin") != 0) {
+ ret = krb5_make_principal(context, &p2, NULL, name, "admin", NULL);
+ krb5_free_principal(context, p1);
+ if(ret != 0)
+ return ret;
+
+ *client = p2;
+ return 0;
+ }
+
+ *client = p1;
+
+ return 0;
+}
+
+krb5_error_code
+_kadm5_c_get_cred_cache(krb5_context context,
+ const char *client_name,
+ const char *server_name,
+ const char *password,
+ krb5_prompter_fct prompter,
+ const char *keytab,
+ krb5_ccache ccache,
+ krb5_ccache *ret_cache)
+{
+ krb5_error_code ret;
+ krb5_ccache id = NULL;
+ krb5_principal default_client = NULL, client = NULL;
+
+ /* treat empty password as NULL */
+ if(password && *password == '\0')
+ password = NULL;
+ if(server_name == NULL)
+ server_name = KADM5_ADMIN_SERVICE;
+
+ if(client_name != NULL) {
+ ret = krb5_parse_name(context, client_name, &client);
+ if(ret)
+ return ret;
+ }
+
+ if(ccache != NULL) {
+ id = ccache;
+ ret = krb5_cc_get_principal(context, id, &client);
+ if(ret)
+ return ret;
+ } else {
+ /* get principal from default cache, ok if this doesn't work */
+
+ ret = get_cache_principal(context, &id, &default_client);
+ if (ret) {
+ /*
+ * No client was specified by the caller and we cannot
+ * determine the client from a credentials cache.
+ */
+ char userbuf[128];
+ const char *user = NULL;
+
+#ifndef WIN32
+ if (geteuid() == 0)
+ user = roken_get_loginname(userbuf, sizeof(userbuf));
+#endif
+ if (user == NULL)
+ user = roken_get_username(userbuf, sizeof(userbuf));
+ if (user == NULL) {
+ krb5_set_error_message(context, KADM5_FAILURE, "Unable to find local user name");
+ krb5_free_principal(context, client);
+ return KADM5_FAILURE;
+ }
+ ret = krb5_make_principal(context, &default_client,
+ NULL, user, "admin", NULL);
+ if (ret) {
+ krb5_free_principal(context, client);
+ return ret;
+ }
+ }
+ }
+
+
+ /*
+ * No client was specified by the caller, but we have a client
+ * from the default credentials cache.
+ */
+ if (client == NULL && default_client != NULL)
+ client = default_client;
+
+
+ if(id && client && (default_client == NULL ||
+ krb5_principal_compare(context, client, default_client) != 0)) {
+ ret = get_kadm_ticket(context, id, client, server_name);
+ if(ret == 0) {
+ *ret_cache = id;
+ krb5_free_principal(context, default_client);
+ if (default_client != client)
+ krb5_free_principal(context, client);
+ return 0;
+ }
+ if(ccache != NULL)
+ /* couldn't get ticket from cache */
+ return -1;
+ }
+ /* get creds via AS request */
+ if(id && (id != ccache))
+ krb5_cc_close(context, id);
+ if (client != default_client)
+ krb5_free_principal(context, default_client);
+
+ ret = get_new_cache(context, client, password, prompter, keytab,
+ server_name, ret_cache);
+ krb5_free_principal(context, client);
+ return ret;
+}
+
+static kadm5_ret_t
+kadm_connect(kadm5_client_context *ctx)
+{
+ kadm5_ret_t ret;
+ krb5_principal server = NULL;
+ krb5_ccache cc = NULL;
+ rk_socket_t s = rk_INVALID_SOCKET;
+ struct addrinfo *ai, *a;
+ struct addrinfo hints;
+ int free_ai = 0;
+ int error;
+ int kadmin_port = 0;
+ const char *admin_server = NULL;
+ char portstr[NI_MAXSERV];
+ const char *hostname, *slash;
+ char *service_name = NULL;
+ krb5_context context = ctx->context;
+ int writable = 0;
+
+ if (ctx->ac)
+ krb5_auth_con_free(context, ctx->ac);
+ ctx->ac = NULL;
+
+ if (!ctx->want_write) {
+ admin_server = ctx->readonly_admin_server;
+ kadmin_port = ctx->readonly_kadmind_port;
+ }
+ if (admin_server == NULL) {
+ admin_server = ctx->admin_server;
+ writable = 1;
+ }
+ if (kadmin_port < 1)
+ kadmin_port = ctx->kadmind_port;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ snprintf(portstr, sizeof(portstr), "%u", ntohs(kadmin_port));
+
+ hostname = admin_server;
+ slash = strchr(hostname, '/');
+ if (slash != NULL)
+ hostname = slash + 1;
+
+ error = getaddrinfo(hostname, portstr, &hints, &ai);
+ if (error) {
+ ret = KADM5_BAD_SERVER_NAME;
+ goto out;
+ }
+
+ free_ai = 1;
+ for (a = ai; a != NULL; a = a->ai_next) {
+ s = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ if (s < 0)
+ continue;
+ if (connect(s, a->ai_addr, a->ai_addrlen) < 0) {
+ krb5_warn(context, errno, "connect(%s)", hostname);
+ rk_closesocket(s);
+ s = rk_INVALID_SOCKET;
+ continue;
+ }
+ break;
+ }
+ if (a == NULL) {
+ krb5_set_error_message(context, ret = KADM5_FAILURE,
+ "failed to contact %s", hostname);
+ goto out;
+ }
+ ret = _kadm5_c_get_cred_cache(context,
+ ctx->client_name,
+ ctx->service_name,
+ NULL, ctx->prompter, ctx->keytab,
+ ctx->ccache, &cc);
+ if (ret)
+ goto out;
+
+ if (ctx->realm)
+ error = asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE,
+ ctx->realm);
+ else
+ error = asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
+
+ if (error == -1 || service_name == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ ret = krb5_parse_name(context, service_name, &server);
+ if (ret)
+ goto out;
+
+ ret = krb5_sendauth(context, &ctx->ac, &s,
+ KADMIN_APPL_VERSION, NULL,
+ server, AP_OPTS_MUTUAL_REQUIRED,
+ NULL, NULL, cc, NULL, NULL, NULL);
+ if (ret == 0) {
+ krb5_data params;
+ kadm5_config_params p;
+ memset(&p, 0, sizeof(p));
+ if(ctx->realm) {
+ p.mask |= KADM5_CONFIG_REALM;
+ p.realm = ctx->realm;
+ }
+ ret = _kadm5_marshal_params(context, &p, &params);
+ if (ret == 0) {
+ ret = krb5_write_priv_message(context, ctx->ac, &s, &params);
+ krb5_data_free(&params);
+ }
+ }
+
+ if (ret == 0) {
+ ctx->sock = s;
+ ctx->connected_to_writable = !!writable;
+ }
+
+out:
+ free(service_name);
+ krb5_cc_close(context, cc);
+ krb5_free_principal(context, server);
+ if (free_ai)
+ freeaddrinfo(ai);
+ if (ret) {
+ if (s != rk_INVALID_SOCKET)
+ rk_closesocket(s);
+ krb5_auth_con_free(context, ctx->ac);
+ ctx->ac = NULL;
+ }
+ return ret;
+}
+
+kadm5_ret_t
+_kadm5_connect(void *handle, int want_write)
+{
+ kadm5_client_context *ctx = handle;
+
+ /*
+ * Reconnect? Note that we don't reconnect to read-only kadmin servers if
+ * we're already connected to a writable kadmin server because we sometimes
+ * get a principal record after writing it. We really need the application
+ * upstairs to tell us when to stop hogging writable kadmin servers.
+ *
+ * FIXME: Add an API for marking a kadm5_client_context as not needing to
+ * connect to writable kadmin servers.
+ */
+ ctx->want_write = !!want_write;
+ if (ctx->sock != rk_INVALID_SOCKET && want_write &&
+ !ctx->connected_to_writable) {
+ rk_closesocket(ctx->sock);
+ ctx->sock = rk_INVALID_SOCKET;
+ }
+ if (ctx->sock == rk_INVALID_SOCKET)
+ return kadm_connect(ctx);
+ return 0;
+}
+
+static kadm5_ret_t
+kadm5_c_init_with_context(krb5_context context,
+ const char *client_name,
+ const char *password,
+ krb5_prompter_fct prompter,
+ const char *keytab,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ kadm5_ret_t ret;
+ kadm5_client_context *ctx = NULL;
+ krb5_ccache cc;
+
+ ret = _kadm5_c_init_context(&ctx, realm_params, context);
+ if (ret)
+ return ret;
+
+ if (password != NULL && *password != '\0') {
+ ret = _kadm5_c_get_cred_cache(context,
+ client_name,
+ service_name,
+ password, prompter, keytab, ccache, &cc);
+ if (ret) {
+ kadm5_c_destroy(ctx);
+ return ret;
+ }
+ ccache = cc;
+ }
+
+
+ if (client_name != NULL)
+ ctx->client_name = strdup(client_name);
+ else
+ ctx->client_name = NULL;
+ if (service_name != NULL)
+ ctx->service_name = strdup(service_name);
+ else
+ ctx->service_name = NULL;
+ ctx->prompter = prompter;
+ ctx->keytab = keytab;
+ ctx->ccache = ccache;
+ /* maybe we should copy the params here */
+ ctx->sock = -1;
+
+ *server_handle = ctx;
+ return 0;
+}
+
+static kadm5_ret_t
+init_context(const char *client_name,
+ const char *password,
+ krb5_prompter_fct prompter,
+ const char *keytab,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ krb5_context context;
+ kadm5_ret_t ret;
+ kadm5_server_context *ctx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return ret;
+ ret = kadm5_c_init_with_context(context,
+ client_name,
+ password,
+ prompter,
+ keytab,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+ if(ret){
+ krb5_free_context(context);
+ return ret;
+ }
+ ctx = *server_handle;
+ ctx->my_context = 1;
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_c_init_with_password_ctx(krb5_context context,
+ const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_context(context,
+ client_name,
+ password,
+ krb5_prompter_posix,
+ NULL,
+ NULL,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_c_init_with_password(const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return init_context(client_name,
+ password,
+ krb5_prompter_posix,
+ NULL,
+ NULL,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_c_init_with_skey_ctx(krb5_context context,
+ const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_context(context,
+ client_name,
+ NULL,
+ NULL,
+ keytab,
+ NULL,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+
+kadm5_ret_t
+kadm5_c_init_with_skey(const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return init_context(client_name,
+ NULL,
+ NULL,
+ keytab,
+ NULL,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_c_init_with_creds_ctx(krb5_context context,
+ const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_c_init_with_context(context,
+ client_name,
+ NULL,
+ NULL,
+ NULL,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_c_init_with_creds(const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return init_context(client_name,
+ NULL,
+ NULL,
+ NULL,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+#if 0
+kadm5_ret_t
+kadm5_init(char *client_name, char *pass,
+ char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+}
+#endif
+
diff --git a/third_party/heimdal/lib/kadm5/init_s.c b/third_party/heimdal/lib/kadm5/init_s.c
new file mode 100644
index 0000000..35402d8
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/init_s.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+#include <fcntl.h>
+
+
+static kadm5_ret_t
+kadm5_s_init_with_context(krb5_context context,
+ const char *client_name,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ kadm5_ret_t ret;
+ kadm5_server_context *ctx = NULL;
+ char *dbname;
+ char *stash_file;
+
+ *server_handle = NULL;
+ ret = _kadm5_s_init_context(&ctx, realm_params, context);
+ if (ret) {
+ kadm5_s_destroy(ctx);
+ return ret;
+ }
+
+ if (realm_params->mask & KADM5_CONFIG_DBNAME)
+ dbname = realm_params->dbname;
+ else
+ dbname = ctx->config.dbname;
+
+ if (realm_params->mask & KADM5_CONFIG_STASH_FILE)
+ stash_file = realm_params->stash_file;
+ else
+ stash_file = ctx->config.stash_file;
+
+ assert(dbname != NULL);
+ assert(stash_file != NULL);
+ assert(ctx->config.acl_file != NULL);
+ assert(ctx->log_context.log_file != NULL);
+#ifndef NO_UNIX_SOCKETS
+ assert(ctx->log_context.socket_name.sun_path[0] != '\0');
+#else
+ assert(ctx->log_context.socket_info != NULL);
+#endif
+
+ ret = hdb_create(ctx->context, &ctx->db, dbname);
+ if (ret == 0)
+ ret = hdb_set_master_keyfile(ctx->context,
+ ctx->db, stash_file);
+ if (ret) {
+ kadm5_s_destroy(ctx);
+ return ret;
+ }
+
+ ctx->log_context.log_fd = -1;
+
+#ifndef NO_UNIX_SOCKETS
+ ctx->log_context.socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+#else
+ ctx->log_context.socket_fd = socket(ctx->log_context.socket_info->ai_family,
+ ctx->log_context.socket_info->ai_socktype,
+ ctx->log_context.socket_info->ai_protocol);
+#endif
+
+ if (ctx->log_context.socket_fd != rk_INVALID_SOCKET)
+ socket_set_nonblocking(ctx->log_context.socket_fd, 1);
+
+ ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
+ if (ret == 0)
+ ret = _kadm5_acl_init(ctx);
+ if (ret)
+ kadm5_s_destroy(ctx);
+ else
+ *server_handle = ctx;
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_dup_context(void *vin, void **out)
+{
+ kadm5_server_context *in = vin;
+ kadm5_ret_t ret;
+ char *p = NULL;
+
+ ret = krb5_unparse_name(in->context, in->caller, &p);
+ if (ret == 0)
+ ret = kadm5_s_init_with_context(in->context, p, NULL,
+ &in->config, 0, 0, out);
+ free(p);
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_init_with_password_ctx(krb5_context context,
+ const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_context(context,
+ client_name,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_s_init_with_password(const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ krb5_context context;
+ kadm5_ret_t ret;
+ kadm5_server_context *ctx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return ret;
+ ret = kadm5_s_init_with_password_ctx(context,
+ client_name,
+ password,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+ if(ret){
+ krb5_free_context(context);
+ return ret;
+ }
+ ctx = *server_handle;
+ ctx->my_context = 1;
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_s_init_with_skey_ctx(krb5_context context,
+ const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_context(context,
+ client_name,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_s_init_with_skey(const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ krb5_context context;
+ kadm5_ret_t ret;
+ kadm5_server_context *ctx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return ret;
+ ret = kadm5_s_init_with_skey_ctx(context,
+ client_name,
+ keytab,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+ if(ret){
+ krb5_free_context(context);
+ return ret;
+ }
+ ctx = *server_handle;
+ ctx->my_context = 1;
+ return 0;
+}
+
+kadm5_ret_t
+kadm5_s_init_with_creds_ctx(krb5_context context,
+ const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_context(context,
+ client_name,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_s_init_with_creds(const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ krb5_context context;
+ kadm5_ret_t ret;
+ kadm5_server_context *ctx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ return ret;
+ ret = kadm5_s_init_with_creds_ctx(context,
+ client_name,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+ if(ret){
+ krb5_free_context(context);
+ return ret;
+ }
+ ctx = *server_handle;
+ ctx->my_context = 1;
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/iprop-commands.in b/third_party/heimdal/lib/kadm5/iprop-commands.in
new file mode 100644
index 0000000..5b59e5a
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/iprop-commands.in
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+/* $Id$ */
+
+command = {
+ name = "dump"
+ option = {
+ long = "config-file"
+ short = "c"
+ type = "string"
+ help = "configuration file"
+ argument = "file"
+ }
+ option = {
+ long = "no-lock"
+ short = "n"
+ type = "flag"
+ help = "don't lock iprop log"
+ }
+ option = {
+ long = "reverse"
+ short = "R"
+ type = "flag"
+ help = "dump the log in reverse order"
+ }
+ option = {
+ long = "realm"
+ short = "r"
+ type = "string"
+ help = "realm"
+ }
+ function = "iprop_dump"
+ help = "Prints the iprop transaction log in text."
+ max_args = "1"
+}
+command = {
+ name = "truncate"
+ option = {
+ long = "config-file"
+ short = "c"
+ type = "string"
+ help = "configuration file"
+ argument = "file"
+ }
+ option = {
+ long = "realm"
+ short = "r"
+ type = "string"
+ help = "realm"
+ }
+ option = {
+ long = "keep-entries"
+ short = "K"
+ type = "integer"
+ help = "number of entries to keep"
+ default = "-1"
+ }
+ option = {
+ long = "max-bytes"
+ short = "B"
+ type = "integer"
+ help = "keep entries that fit in the given number of bytes"
+ default = "-1"
+ }
+ option = {
+ long = "reset"
+ short = "R"
+ type = "flag"
+ help = "reset the log to version 1; forces full propagation"
+ default = "0";
+ }
+ function = "iprop_truncate"
+ help = "Truncate the log, preserve the version number. Keeps 100 entries by default."
+ max_args = "1"
+}
+command = {
+ name = "replay"
+ option = {
+ long = "start-version"
+ type = "integer"
+ help = "start replay with this version"
+ argument = "version-number"
+ default = "-1"
+ }
+ option = {
+ long = "end-version"
+ type = "integer"
+ help = "end replay with this version"
+ argument = "version-number"
+ default = "-1"
+ }
+ option = {
+ long = "config-file"
+ short = "c"
+ type = "string"
+ help = "configuration file"
+ argument = "file"
+ }
+ option = {
+ long = "realm"
+ short = "r"
+ type = "string"
+ help = "realm"
+ }
+ function = "iprop_replay"
+ help = "Replay the log on the database."
+ max_args = "1"
+}
+command = {
+ name = "last-version"
+ option = {
+ long = "config-file"
+ short = "c"
+ type = "string"
+ help = "configuration file"
+ argument = "file"
+ }
+ option = {
+ long = "no-lock"
+ short = "n"
+ type = "flag"
+ help = "don't lock iprop log"
+ }
+ option = {
+ long = "realm"
+ short = "r"
+ type = "string"
+ help = "realm"
+ }
+ function = "last_version"
+ help = "Print the last version of the log-file."
+}
+command = {
+ name = "signal"
+ option = {
+ long = "config-file"
+ short = "c"
+ type = "string"
+ help = "configuration file"
+ argument = "file"
+ }
+ option = {
+ long = "realm"
+ short = "r"
+ type = "string"
+ help = "realm"
+ }
+ function = "signal_master"
+ help = "Print the last version of the log-file."
+ max_args = "0"
+}
+command = {
+ name = "help"
+ argument = "command"
+ max_args = "1"
+ function = "help"
+}
diff --git a/third_party/heimdal/lib/kadm5/iprop-log-version.rc b/third_party/heimdal/lib/kadm5/iprop-log-version.rc
new file mode 100644
index 0000000..b8a2295
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/iprop-log-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "IProp Log Tool"
+#define RC_FILE_ORIG_0409 "iprop-log.exe"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/kadm5/iprop-log.8 b/third_party/heimdal/lib/kadm5/iprop-log.8
new file mode 100644
index 0000000..f34d800
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/iprop-log.8
@@ -0,0 +1,254 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 2005 - 2007 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd February 18, 2007
+.Dt IPROP-LOG 8
+.Os
+.Sh NAME
+.Nm iprop-log
+.Nd examine and maintain the iprop log file
+.Sh SYNOPSIS
+.Nm
+.Op Fl Fl version
+.Op Fl h | Fl Fl help
+.Ar command
+.Pp
+.Nm iprop-log truncate
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Oo Fl K Ar integer \*(Ba Xo
+.Fl Fl keep-entries= Ns Ar integer
+.Xc
+.Oc
+.Oo Fl B Ar integer \*(Ba Xo
+.Fl Fl max-bytes= Ns Ar integer
+.Xc
+.Oc
+.Oo Fl R \*(Ba Xo
+.Fl Fl reset
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Op Ar log-file
+.Pp
+.Nm iprop-log dump
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Oo Fl n \*(Ba Xo
+.Fl Fl no-lock
+.Xc
+.Oc
+.Oo Fl R \*(Ba Xo
+.Fl Fl reverse
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Op Ar log-file
+.Pp
+.Nm iprop-log replay
+.Op Fl Fl start-version= Ns Ar version-number
+.Op Fl Fl end-version= Ns Ar version-number
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Op Ar log-file
+.Pp
+.Nm iprop-log last-version
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Oo Fl n \*(Ba Xo
+.Fl Fl no-lock
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Op Ar log-files
+.Pp
+.Nm iprop-log signal
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Pp
+.Sh DESCRIPTION
+Supported options:
+.Bl -tag -width Ds
+.It Fl Fl version
+.It Fl h , Fl Fl help
+.El
+.Pp
+command can be one of the following:
+.Bl -tag -width truncate
+.It truncate
+.Bl -tag -width Ds
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+configuration file
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+realm
+.It Xo
+.Fl K Ar integer ,
+.Fl Fl keep-entries= Ns Ar integer
+.Xc
+.It Xo
+.Fl B Ar integer ,
+.Fl Fl max-bytes= Ns Ar integer
+.Xc
+.It Xo
+.Fl R ,
+.Fl Fl reset
+.Xc
+.El
+.Pp
+If
+.Fl Fl reset
+is given, then the given, configured, or default log file will be
+truncated and will start at version 1. This forces full propagations to
+all slave KDCs.
+.Pp
+Otherwise the log will be truncated but some entries will be preserved,
+as specified by the
+.Fl Fl keep-entries
+and/or
+.Fl Fl max-bytes
+options. The largest number of
+.Fl Fl keep-entries
+entries that are available and fit in the given
+.Fl Fl max-bytes
+option will be used. The
+.Fl Fl keep-entries option defaults to 100, and the
+.Fl Fl max-bytes
+option defaults to the
+.Ar log-max-size
+parameter in the configuration.
+.Pp
+.It dump
+.Bl -tag -width Ds
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+configuration file
+.It Xo
+.Fl r Ar string ,
+.Fl Fl realm= Ns Ar string
+.Xc
+realm
+.It Xo
+.Fl n Ar string ,
+.Fl Fl no-lock
+.Xc
+.It Xo
+.Fl R Ar string ,
+.Fl Fl reverse
+.Xc
+.El
+.Pp
+Print out all entries in the given, configured, or default log file to
+standard output. If the
+.Fl n
+option is used then don't lock the iprop log file. If the
+.Fl R
+option is used, then print the entries in reverse order
+(this can be useful when the log is very large).
+.It replay
+.Bl -tag -width Ds
+.It Fl Fl start-version= Ns Ar version-number
+start replay with this version
+.It Xo
+.Fl Fl end-version= Ns Ar version-number
+.Xc
+end replay with this version
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+configuration file
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+realm
+.El
+.Pp
+Replay the changes from specified entries (or all if none is specified)
+in the given, configured, or default transaction log file to the
+database.
+.It last-version
+.Bl -tag -width Ds
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+configuration file
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+realm
+.It Xo
+.Fl n Ar string ,
+.Fl Fl no-lock
+.Xc
+.El
+.Pp
+prints the version of the last record in each of the given log files, or
+the configured, or the default log file if none is given.
+.It signal
+.Bl -tag -width Ds
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+configuration file
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+realm
+.El
+.Pp
+Signals the ipropd-master daemon to send updates to slaves. Normally
+kadmin does this every time it writes to the database, so this should
+rarely be needed.
+.El
+.Sh SEE ALSO
+.Xr iprop 8
diff --git a/third_party/heimdal/lib/kadm5/iprop-log.c b/third_party/heimdal/lib/kadm5/iprop-log.c
new file mode 100644
index 0000000..a2ad51e
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/iprop-log.c
@@ -0,0 +1,653 @@
+/*
+ * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "iprop.h"
+#include <sl.h>
+#include <parse_time.h>
+#include "iprop-commands.h"
+
+RCSID("$Id$");
+
+static krb5_context context;
+
+static kadm5_server_context *
+get_kadmin_context(const char *config_file, char *realm)
+{
+ kadm5_config_params conf;
+ krb5_error_code ret;
+ void *kadm_handle;
+ char *file = NULL;
+ char **files;
+ int aret;
+
+ if (config_file == NULL) {
+ aret = asprintf(&file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1 || file == NULL)
+ errx(1, "out of memory");
+ config_file = file;
+ }
+
+ ret = krb5_prepend_config_files_default(config_file, &files);
+ free(file);
+ if (ret)
+ krb5_err(context, 1, ret, "getting configuration files");
+
+ ret = krb5_set_config_files(context, files);
+ krb5_free_config_files(files);
+ if (ret)
+ krb5_err(context, 1, ret, "reading configuration files");
+
+ memset(&conf, 0, sizeof(conf));
+ if(realm) {
+ conf.mask |= KADM5_CONFIG_REALM;
+ conf.realm = realm;
+ }
+
+ ret = kadm5_init_with_password_ctx (context,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+ if (ret)
+ krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
+
+ return (kadm5_server_context *)kadm_handle;
+}
+
+/*
+ * dump log
+ */
+
+static const char *op_names[] = {
+ "get",
+ "delete",
+ "create",
+ "rename",
+ "chpass",
+ "modify",
+ "randkey",
+ "get_privs",
+ "get_princs",
+ "chpass_with_key",
+ "nop"
+};
+
+static kadm5_ret_t
+print_entry(kadm5_server_context *server_context,
+ uint32_t ver,
+ time_t timestamp,
+ enum kadm_ops op,
+ uint32_t len,
+ krb5_storage *sp,
+ void *ctx)
+{
+ char t[256];
+ const char *entry_kind = ctx;
+ int32_t mask;
+ int32_t nop_time;
+ uint32_t nop_ver;
+ hdb_entry ent;
+ krb5_principal source;
+ char *name1, *name2;
+ krb5_data data;
+ krb5_context scontext = server_context->context;
+ krb5_error_code ret;
+
+ krb5_data_zero(&data);
+
+ strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
+
+ if((int)op < (int)kadm_get || (int)op > (int)kadm_nop) {
+ printf("unknown op: %d\n", op);
+ return 0;
+ }
+
+ printf ("%s%s: ver = %u, timestamp = %s, len = %u\n",
+ entry_kind, op_names[op], ver, t, len);
+ switch(op) {
+ case kadm_delete:
+ ret = krb5_ret_principal(sp, &source);
+ if (ret == 0)
+ ret = krb5_unparse_name(scontext, source, &name1);
+ if (ret)
+ krb5_err(scontext, 1, ret, "Failed to read a delete record");
+ printf(" %s\n", name1);
+ free(name1);
+ krb5_free_principal(scontext, source);
+ break;
+ case kadm_rename:
+ ret = krb5_data_alloc(&data, len);
+ if (ret == 0)
+ ret = krb5_ret_principal(sp, &source);
+ if (ret == 0 && krb5_storage_read(sp, data.data, data.length) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = hdb_value2entry(scontext, &data, &ent);
+ if (ret == 0)
+ ret = krb5_unparse_name(scontext, source, &name1);
+ if (ret == 0)
+ ret = krb5_unparse_name(scontext, ent.principal, &name2);
+ if (ret)
+ krb5_err(scontext, 1, ret, "Failed to read a rename record");
+ printf(" %s -> %s\n", name1, name2);
+ free(name1);
+ free(name2);
+ krb5_free_principal(scontext, source);
+ free_hdb_entry(&ent);
+ break;
+ case kadm_create:
+ ret = krb5_data_alloc(&data, len);
+ if (ret == 0 && krb5_storage_read(sp, data.data, data.length) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = hdb_value2entry(scontext, &data, &ent);
+ if (ret)
+ krb5_err(scontext, 1, ret, "Failed to read a create record");
+ mask = ~0;
+ goto foo;
+ case kadm_modify:
+ ret = krb5_data_alloc(&data, len);
+ if (ret == 0)
+ ret = krb5_ret_int32(sp, &mask);
+ if (ret == 0 && krb5_storage_read(sp, data.data, data.length) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = hdb_value2entry(scontext, &data, &ent);
+ if (ret)
+ krb5_err(scontext, 1, ret, "Failed to read a modify record");
+ foo:
+ if(ent.principal /* mask & KADM5_PRINCIPAL */) {
+ ret = krb5_unparse_name(scontext, ent.principal, &name1);
+ if (ret)
+ krb5_err(scontext, 1, ret,
+ "Failed to process a create or modify record");
+ printf(" principal = %s\n", name1);
+ free(name1);
+ }
+ if(mask & KADM5_PRINC_EXPIRE_TIME) {
+ if(ent.valid_end == NULL) {
+ strlcpy(t, "never", sizeof(t));
+ } else {
+ strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
+ localtime(ent.valid_end));
+ }
+ printf(" expires = %s\n", t);
+ }
+ if(mask & KADM5_PW_EXPIRATION) {
+ if(ent.pw_end == NULL) {
+ strlcpy(t, "never", sizeof(t));
+ } else {
+ strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S",
+ localtime(ent.pw_end));
+ }
+ printf(" password exp = %s\n", t);
+ }
+ if(mask & KADM5_LAST_PWD_CHANGE) {
+ }
+ if(mask & KADM5_ATTRIBUTES) {
+ unparse_flags(HDBFlags2int(ent.flags),
+ asn1_HDBFlags_units(), t, sizeof(t));
+ printf(" attributes = %s\n", t);
+ }
+ if(mask & KADM5_MAX_LIFE) {
+ if(ent.max_life == NULL)
+ strlcpy(t, "for ever", sizeof(t));
+ else
+ unparse_time(*ent.max_life, t, sizeof(t));
+ printf(" max life = %s\n", t);
+ }
+ if(mask & KADM5_MAX_RLIFE) {
+ if(ent.max_renew == NULL)
+ strlcpy(t, "for ever", sizeof(t));
+ else
+ unparse_time(*ent.max_renew, t, sizeof(t));
+ printf(" max rlife = %s\n", t);
+ }
+ if(mask & KADM5_MOD_TIME) {
+ printf(" mod time\n");
+ }
+ if(mask & KADM5_MOD_NAME) {
+ printf(" mod name\n");
+ }
+ if(mask & KADM5_KVNO) {
+ printf(" kvno = %d\n", ent.kvno);
+ }
+ if(mask & KADM5_MKVNO) {
+ printf(" mkvno\n");
+ }
+ if(mask & KADM5_AUX_ATTRIBUTES) {
+ printf(" aux attributes\n");
+ }
+ if(mask & KADM5_POLICY) {
+ printf(" policy\n");
+ }
+ if(mask & KADM5_POLICY_CLR) {
+ printf(" mod time\n");
+ }
+ if(mask & KADM5_LAST_SUCCESS) {
+ printf(" last success\n");
+ }
+ if(mask & KADM5_LAST_FAILED) {
+ printf(" last failed\n");
+ }
+ if(mask & KADM5_FAIL_AUTH_COUNT) {
+ printf(" fail auth count\n");
+ }
+ if(mask & KADM5_KEY_DATA) {
+ printf(" key data\n");
+ }
+ if(mask & KADM5_TL_DATA) {
+ printf(" tl data\n");
+ }
+ free_hdb_entry(&ent);
+ break;
+ case kadm_nop :
+ if (len == 16) {
+ uint64_t off;
+ ret = krb5_ret_uint64(sp, &off);
+ if (ret)
+ krb5_err(scontext, 1, ret, "Failed to read a no-op record");
+ printf("uberblock offset %llu ", (unsigned long long)off);
+ } else {
+ printf("nop");
+ }
+ if (len == 16 || len == 8) {
+ ret = krb5_ret_int32(sp, &nop_time);
+ if (ret == 0)
+ ret = krb5_ret_uint32(sp, &nop_ver);
+ if (ret)
+ krb5_err(scontext, 1, ret, "Failed to read a no-op record");
+
+ timestamp = nop_time;
+ strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
+ printf("timestamp %s version %u", t, nop_ver);
+ }
+ printf("\n");
+ break;
+ default:
+ krb5_errx(scontext, 1, "Unknown record type");
+ }
+ krb5_data_free(&data);
+
+ return 0;
+}
+
+int
+iprop_dump(struct dump_options *opt, int argc, char **argv)
+{
+ kadm5_server_context *server_context;
+ krb5_error_code ret;
+ enum kadm_iter_opts iter_opts_1st = 0;
+ enum kadm_iter_opts iter_opts_2nd = 0;
+ char *desc_1st = "";
+ char *desc_2nd = "";
+
+ server_context = get_kadmin_context(opt->config_file_string,
+ opt->realm_string);
+
+ if (argc > 0) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[0]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ }
+
+ if (opt->reverse_flag) {
+ iter_opts_1st = kadm_backward | kadm_unconfirmed;
+ iter_opts_2nd = kadm_backward | kadm_confirmed;
+ desc_1st = "unconfirmed ";
+ } else {
+ iter_opts_1st = kadm_forward | kadm_confirmed;
+ iter_opts_2nd = kadm_forward | kadm_unconfirmed;
+ desc_2nd = "unconfirmed";
+ }
+
+ if (opt->no_lock_flag) {
+ ret = kadm5_log_init_sharedlock(server_context, LOCK_NB);
+ if (ret == EAGAIN || ret == EWOULDBLOCK) {
+ warnx("Not locking the iprop log");
+ ret = kadm5_log_init_nolock(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_nolock");
+ }
+ } else {
+ warnx("If this command appears to block, try the --no-lock option");
+ ret = kadm5_log_init_sharedlock(server_context, 0);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_sharedlock");
+ }
+
+ ret = kadm5_log_foreach(server_context, iter_opts_1st,
+ NULL, print_entry, desc_1st);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_log_foreach");
+
+ ret = kadm5_log_foreach(server_context, iter_opts_2nd,
+ NULL, print_entry, desc_2nd);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_log_foreach");
+
+ ret = kadm5_log_end (server_context);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_log_end");
+
+ kadm5_destroy(server_context);
+ return 0;
+}
+
+int
+iprop_truncate(struct truncate_options *opt, int argc, char **argv)
+{
+ kadm5_server_context *server_context;
+ krb5_error_code ret;
+
+ server_context = get_kadmin_context(opt->config_file_string,
+ opt->realm_string);
+
+ if (argc > 0) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[0]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ }
+
+ if (opt->keep_entries_integer < 0 &&
+ opt->max_bytes_integer < 0) {
+ opt->keep_entries_integer = 100;
+ opt->max_bytes_integer = 0;
+ }
+ if (opt->keep_entries_integer < 0)
+ opt->keep_entries_integer = 0;
+ if (opt->max_bytes_integer < 0)
+ opt->max_bytes_integer = 0;
+
+ if (opt->reset_flag) {
+ /* First recover unconfirmed records */
+ ret = kadm5_log_init(server_context);
+ if (ret == 0)
+ ret = kadm5_log_reinit(server_context, 0);
+ } else {
+ ret = kadm5_log_init(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init");
+ ret = kadm5_log_truncate(server_context, opt->keep_entries_integer,
+ opt->max_bytes_integer);
+ }
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_truncate");
+
+ kadm5_log_signal_master(server_context);
+
+ kadm5_destroy(server_context);
+ return 0;
+}
+
+int
+last_version(struct last_version_options *opt, int argc, char **argv)
+{
+ kadm5_server_context *server_context;
+ char *alt_argv[2] = { NULL, NULL };
+ krb5_error_code ret;
+ uint32_t version;
+ size_t i;
+
+ server_context = get_kadmin_context(opt->config_file_string,
+ opt->realm_string);
+
+ if (argc == 0) {
+ alt_argv[0] = strdup(server_context->log_context.log_file);
+ if (alt_argv[0] == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ argv = alt_argv;
+ argc = 1;
+ }
+
+ for (i = 0; i < argc; i++) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[i]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+
+ if (opt->no_lock_flag) {
+ ret = kadm5_log_init_sharedlock(server_context, LOCK_NB);
+ if (ret == EAGAIN || ret == EWOULDBLOCK) {
+ warnx("Not locking the iprop log");
+ ret = kadm5_log_init_nolock(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_nolock");
+ }
+ } else {
+ warnx("If this command appears to block, try the "
+ "--no-lock option");
+ ret = kadm5_log_init_sharedlock(server_context, 0);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_sharedlock");
+ }
+
+ ret = kadm5_log_get_version (server_context, &version);
+ if (ret)
+ krb5_err (context, 1, ret, "kadm5_log_get_version");
+
+ ret = kadm5_log_end (server_context);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_log_end");
+
+ printf("version: %lu\n", (unsigned long)version);
+ }
+
+ kadm5_destroy(server_context);
+ free(alt_argv[0]);
+ return 0;
+}
+
+int
+signal_master(struct signal_options *opt, int argc, char **argv)
+{
+ kadm5_server_context *server_context;
+
+ server_context = get_kadmin_context(opt->config_file_string,
+ opt->realm_string);
+
+ kadm5_log_signal_master(server_context);
+
+ kadm5_destroy(server_context);
+ return 0;
+}
+
+/*
+ * Replay log
+ */
+
+int start_version = -1;
+int end_version = -1;
+
+static kadm5_ret_t
+apply_entry(kadm5_server_context *server_context,
+ uint32_t ver,
+ time_t timestamp,
+ enum kadm_ops op,
+ uint32_t len,
+ krb5_storage *sp,
+ void *ctx)
+{
+ struct replay_options *opt = ctx;
+ krb5_error_code ret;
+
+ if((opt->start_version_integer != -1 && ver < (uint32_t)opt->start_version_integer) ||
+ (opt->end_version_integer != -1 && ver > (uint32_t)opt->end_version_integer)) {
+ /* XXX skip this entry */
+ return 0;
+ }
+ printf ("ver %u... ", ver);
+ fflush (stdout);
+
+ ret = kadm5_log_replay(server_context, op, ver, len, sp);
+ if (ret)
+ krb5_warn (server_context->context, ret, "kadm5_log_replay");
+
+ printf ("done\n");
+
+ return 0;
+}
+
+int
+iprop_replay(struct replay_options *opt, int argc, char **argv)
+{
+ kadm5_server_context *server_context;
+ krb5_error_code ret;
+
+ server_context = get_kadmin_context(opt->config_file_string,
+ opt->realm_string);
+
+ if (argc > 0) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[0]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ }
+
+ ret = server_context->db->hdb_open(context,
+ server_context->db,
+ O_RDWR | O_CREAT, 0600);
+ if (ret)
+ krb5_err (context, 1, ret, "db->open");
+
+ ret = kadm5_log_init (server_context);
+ if (ret)
+ krb5_err (context, 1, ret, "kadm5_log_init");
+
+ ret = kadm5_log_foreach(server_context,
+ kadm_forward | kadm_confirmed | kadm_unconfirmed,
+ NULL, apply_entry, opt);
+ if(ret)
+ krb5_warn(context, ret, "kadm5_log_foreach");
+ ret = kadm5_log_end (server_context);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_log_end");
+ ret = server_context->db->hdb_close (context, server_context->db);
+ if (ret)
+ krb5_err (context, 1, ret, "db->close");
+
+ kadm5_destroy(server_context);
+ return 0;
+}
+
+static int help_flag;
+static int version_flag;
+
+static struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag,
+ NULL, NULL
+ },
+ { "help", 'h', arg_flag, &help_flag,
+ NULL, NULL
+ }
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+int
+help(void *opt, int argc, char **argv)
+{
+ if(argc == 0) {
+ sl_help(commands, 1, argv - 1 /* XXX */);
+ } else {
+ SL_cmd *c = sl_match (commands, argv[0], 0);
+ if(c == NULL) {
+ fprintf (stderr, "No such command: %s. "
+ "Try \"help\" for a list of commands\n",
+ argv[0]);
+ } else {
+ if(c->func) {
+ static char shelp[] = "--help";
+ char *fake[3];
+ fake[0] = argv[0];
+ fake[1] = shelp;
+ fake[2] = NULL;
+ (*c->func)(2, fake);
+ fprintf(stderr, "\n");
+ }
+ if(c->help && *c->help)
+ fprintf (stderr, "%s\n", c->help);
+ if((++c)->name && c->func == NULL) {
+ int f = 0;
+ fprintf (stderr, "Synonyms:");
+ while (c->name && c->func == NULL) {
+ fprintf (stderr, "%s%s", f ? ", " : " ", (c++)->name);
+ f = 1;
+ }
+ fprintf (stderr, "\n");
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+usage(int status)
+{
+ arg_printusage(args, num_args, NULL, "command");
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ krb5_error_code ret;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+ if(help_flag)
+ usage(0);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+ argc -= optidx;
+ argv += optidx;
+ if(argc == 0)
+ usage(1);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed with: %d\n", ret);
+
+ ret = sl_command(commands, argc, argv);
+ if(ret == -1)
+ warnx ("unrecognized command: %s", argv[0]);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/iprop.8 b/third_party/heimdal/lib/kadm5/iprop.8
new file mode 100644
index 0000000..41aac06
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/iprop.8
@@ -0,0 +1,246 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 2005 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd May 24, 2005
+.Dt IPROP 8
+.Os
+.Sh NAME
+.Nm iprop ,
+.Nm ipropd-master ,
+.Nm ipropd-slave
+.Nd propagate transactions from a Heimdal Kerberos master KDC to slave KDCs
+.Sh SYNOPSIS
+.Nm ipropd-master
+.Oo Fl c Ar string \*(Ba Xo
+.Fl Fl config-file= Ns Ar string
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Oo Fl k Ar kspec \*(Ba Xo
+.Fl Fl keytab= Ns Ar kspec
+.Xc
+.Oc
+.Oo Fl d Ar file \*(Ba Xo
+.Fl Fl database= Ns Ar file
+.Xc
+.Oc
+.Op Fl Fl slave-stats-file= Ns Ar file
+.Op Fl Fl time-missing= Ns Ar time
+.Op Fl Fl time-gone= Ns Ar time
+.Op Fl Fl detach
+.Op Fl Fl version
+.Op Fl Fl help
+.Nm ipropd-slave
+.Oo Fl c Ar string \*(Ba Xo Fl Fl config-file= Ns Ar string Xc Oc
+.Oo Fl r Ar string \*(Ba Xo Fl Fl realm= Ns Ar string Xc Oc
+.Oo Fl d Ar file \*(Ba Xo Fl Fl database= Ns Ar file Xc Oc
+.Oo Fl k Ar kspec \*(Ba Xo Fl Fl keytab= Ns Ar kspec Xc Oc
+.Oo Xo Fl Fl no-keytab Xc Oc
+.Oo Xo Fl Fl cache= Ns Ar cspec Xc Oc
+.Op Fl Fl statusfile= Ns Ar file
+.Op Fl Fl hostname= Ns Ar hostname
+.Op Fl Fl port= Ns Ar port
+.Op Fl Fl time-lost= Ns Ar time
+.Op Fl Fl async-hdb
+.Op Fl Fl detach
+.Op Fl Fl version
+.Op Fl Fl help
+.Ar master
+.Sh DESCRIPTION
+.Nm ipropd-master
+is used to propagate changes to a Heimdal Kerberos database from the
+master Kerberos server on which it runs to slave Kerberos servers
+running
+.Nm ipropd-slave .
+.Pp
+The slaves are specified by the contents of the
+.Pa slaves
+file in the KDC's database directory, e.g.\&
+.Pa /var/heimdal/slaves .
+This has principals one per-line of the form
+.Dl iprop/ Ns Ar slave Ns @ Ns Ar REALM
+where
+.Ar slave
+is the hostname of the slave server in the given
+.Ar REALM ,
+e.g.\&
+.Dl iprop/kerberos-1.example.com@EXAMPLE.COM
+On a slave, the argument
+.Fa master
+specifies the hostname of the master server from which to receive updates.
+.Pp
+In contrast to
+.Xr hprop 8 ,
+which sends the whole database to the slaves regularly,
+.Nm
+normally sends only the changes as they happen on the master.
+The master keeps track of all the changes by assigning a version
+number to every transaction to the database.
+The slaves know which was the latest version they saw, and in this
+way it can be determined if they are in sync or not.
+A log of all the transactions is kept on the master.
+When a slave is at an older version than the oldest one in the log,
+the whole database has to be sent.
+.Pp
+The log of transactions is also used to implement a two-phase commit
+(with roll-forward for recovery) method of updating the HDB.
+Transactions are first recorded in the log, then in the HDB, then
+the log is updated to mark the transaction as committed.
+.Pp
+The changes are propagated over a secure channel (on port 2121 by
+default).
+This should normally be defined as
+.Dq iprop/tcp
+in
+.Pa /etc/services
+or another source of the services database.
+.Pp
+The
+.Nm ipropd-master
+and
+.Nm ipropd-slave
+programs require acceptor and initiator credentials,
+respectively, for host-based principals for the
+.Ar iprop
+service and the fully-qualified hostnames of the hosts on which
+they run.
+.Pp
+The
+.Nm ipropd-master
+program uses, by default, the HDB-backed keytab
+.Ar HDBGET: ,
+though a file-based keytab can also be specified.
+.Pp
+The
+.Nm ipropd-slave
+program uses the default keytab, which is specified by the
+.Ev KRB5_KTNAME
+environment variable, or the value of the
+.Ar default_keytab_name
+configuration parameter in the
+.Ar [libdefaults]
+section.
+However, if the
+.Fl Fl no-keytab
+option is given, then
+.Nm ipropd-slave
+will use the given or default credentials cache, and it will
+expect that cache to be kept fresh externally (such as by the
+.Nm kinit
+program).
+.Pp
+There is a keep-alive feature logged in the master's
+.Pa slave-stats
+file (e.g.\&
+.Pa /var/heimdal/slave-stats ) .
+.Pp
+Note that hierarchical replication is supported by running
+an
+.Nm ipropd-master
+on the same KDC as an
+.Nm ipropd-slave .
+.Pp
+Supported options for
+.Nm ipropd-master :
+.Bl -tag -width Ds
+.It Fl c Ar string , Fl Fl config-file= Ns Ar string
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+.It Fl k Ar kspec , Fl Fl keytab= Ns Ar kspec
+Keytab for authenticating
+.Nm ipropd-slave
+clients.
+.It Fl Fl cache= Ns Ar cspec
+If the keytab given is the empty string then credentials will be
+used from the default credentials cache, or from the
+.Ar cspec
+if given.
+.It Fl d Ar file , Fl Fl database= Ns Ar file
+Database (default per KDC)
+.It Fl Fl slave-stats-file= Ns Ar file
+File for slave status information.
+.It Fl Fl time-missing= Ns Ar time
+Time before slave is polled for presence (default 2 min).
+.It Fl Fl time-gone= Ns Ar time
+Time of inactivity after which a slave is considered gone (default 5 min).
+.It Fl Fl detach
+Detach from console.
+.It Fl Fl version
+.It Fl Fl help
+.El
+.Pp
+Supported options for
+.Nm ipropd-slave :
+.Bl -tag -width Ds
+.It Fl c Ar string , Fl Fl config-file= Ns Ar string
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+.It Fl d Ar file , Fl Fl database= Ns Ar file
+Database (default per KDC)
+.It Fl k Ar kspec , Fl Fl keytab= Ns Ar kspec
+Keytab with client credentials for authenticating to
+.Nm ipropd-master .
+.It Fl Fl status-file= Ns Ar file
+.It Fl Fl hostname= Ns Ar hostname
+Hostname for client principal if different from actual hostname.
+.It Fl Fl port= Ns Ar port
+.It Fl Fl time-lost= Ns Ar time
+time before server is considered lost (default 5 min)
+.It Fl Fl async-hdb
+Use asynchronous writes.
+This is very useful for very busy sites or sites with very large
+HDBs.
+.It Fl Fl detach
+Detach from console.
+.It Fl Fl version
+.It Fl Fl help
+.El
+Time arguments for the relevant options above may be specified in forms
+like 5 min, 300 s, or simply a number of seconds.
+.Sh FILES
+.Pa slaves ,
+.Pa slave-stats
+in the database directory.
+.Pa ipropd-master.pid ,
+.Pa ipropd-slave.pid
+in the database directory, or in the directory named by the
+.Ev HEIM_PIDFILE_DIR
+environment variable.
+.Sh SEE ALSO
+.Xr kinit 1 ,
+.Xr krb5.conf 5 ,
+.Xr hprop 8 ,
+.Xr hpropd 8 ,
+.Xr iprop-log 8 ,
+.Xr kdc 8 .
diff --git a/third_party/heimdal/lib/kadm5/iprop.h b/third_party/heimdal/lib/kadm5/iprop.h
new file mode 100644
index 0000000..945fb90
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/iprop.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1998-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __IPROP_H__
+#define __IPROP_H__
+
+#include "kadm5_locl.h"
+#include <getarg.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
+#include <parse_time.h>
+
+#define IPROP_VERSION "iprop-0.0"
+
+#define IPROP_NAME "iprop"
+
+#define IPROP_SERVICE "iprop"
+
+#define IPROP_PORT 2121
+
+enum iprop_cmd { I_HAVE = 1,
+ FOR_YOU = 2,
+ TELL_YOU_EVERYTHING = 3,
+ ONE_PRINC = 4,
+ NOW_YOU_HAVE = 5,
+ ARE_YOU_THERE = 6,
+ I_AM_HERE = 7,
+ YOU_HAVE_LAST_VERSION = 8
+};
+
+extern sig_atomic_t exit_flag;
+void setup_signal(void);
+
+enum ipropd_exit_code {
+ IPROPD_DONE = 0,
+ IPROPD_RESTART = 1,
+ IPROPD_RESTART_SLOW = 2,
+ IPROPD_FATAL = 3,
+};
+
+int restarter(krb5_context, size_t *);
+
+#endif /* __IPROP_H__ */
diff --git a/third_party/heimdal/lib/kadm5/ipropd-master-version.rc b/third_party/heimdal/lib/kadm5/ipropd-master-version.rc
new file mode 100644
index 0000000..f51a891
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ipropd-master-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "IProp Master"
+#define RC_FILE_ORIG_0409 "ipropd-master.exe"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/kadm5/ipropd-slave-version.rc b/third_party/heimdal/lib/kadm5/ipropd-slave-version.rc
new file mode 100644
index 0000000..a1cee87
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ipropd-slave-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "IProp Slave"
+#define RC_FILE_ORIG_0409 "ipropd-slave.exe"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/kadm5/ipropd_common.c b/third_party/heimdal/lib/kadm5/ipropd_common.c
new file mode 100644
index 0000000..1decfe3
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ipropd_common.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "iprop.h"
+
+#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+sig_atomic_t exit_flag;
+
+static RETSIGTYPE
+sigterm(int sig)
+{
+ exit_flag = sig;
+}
+
+void
+setup_signal(void)
+{
+#ifdef HAVE_SIGACTION
+ {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = 0;
+ sa.sa_handler = sigterm;
+ sigemptyset(&sa.sa_mask);
+
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGXCPU, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, NULL);
+ }
+#else
+ signal(SIGINT, sigterm);
+ signal(SIGTERM, sigterm);
+#ifndef NO_SIGXCPU
+ signal(SIGXCPU, sigterm);
+#endif
+#ifndef NO_SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+#endif
+}
+
+/*
+ * Fork a child to run the service, and restart it if it dies.
+ *
+ * Returns -1 if not supported, else a file descriptor that the service
+ * should select() for. Any events on that file descriptor should cause
+ * the caller to exit immediately, as that means that the restarter
+ * exited.
+ *
+ * The service's normal exit status values should be should be taken
+ * from enum ipropd_exit_code. IPROPD_FATAL causes the restarter to
+ * stop restarting the service and to exit.
+ *
+ * A count of restarts is output via the `countp' argument, if it is
+ * non-NULL. This is useful for testing this function (e.g., kill the
+ * restarter after N restarts and check that the child gets the signal
+ * sent to it).
+ *
+ * This requires fork() and waitpid() (otherwise returns -1). Ignoring
+ * SIGCHLD, of course, would be bad.
+ *
+ * We could support this on Windows by spawning a child with mostly the
+ * same arguments as the restarter process.
+ */
+int
+restarter(krb5_context context, size_t *countp)
+{
+#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ struct timeval tmout;
+ pid_t pid = -1;
+ pid_t wpid = -1;
+ int status;
+ int fds[2];
+ int fds2[2];
+ size_t count = 0;
+ fd_set readset;
+
+ fds[0] = -1;
+ fds[1] = -1;
+ fds2[0] = -1;
+ fds2[1] = -1;
+
+ signal(SIGCHLD, SIG_DFL);
+
+ while (!exit_flag) {
+ /* Close the pipe ends we keep open */
+ if (fds[1] != -1)
+ (void) close(fds[1]);
+ if (fds2[0] != -1)
+ (void) close(fds2[1]);
+
+ /* A pipe so the child can detect the parent's death */
+ if (pipe(fds) == -1) {
+ krb5_err(context, 1, errno,
+ "Could not setup pipes in service restarter");
+ }
+
+ /* A pipe so the parent can detect the child's death */
+ if (pipe(fds2) == -1) {
+ krb5_err(context, 1, errno,
+ "Could not setup pipes in service restarter");
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ pid = fork();
+ if (pid == -1)
+ krb5_err(context, 1, errno, "Could not fork in service restarter");
+ if (pid == 0) {
+ if (countp != NULL)
+ *countp = count;
+ (void) close(fds[1]);
+ (void) close(fds2[0]);
+ return fds[0];
+ }
+
+ count++;
+
+ (void) close(fds[0]);
+ (void) close(fds2[1]);
+
+ do {
+ wpid = waitpid(pid, &status, 0);
+ } while (wpid == -1 && errno == EINTR && !exit_flag);
+ if (wpid == -1 && errno == EINTR)
+ break; /* We were signaled; gotta kill the child and exit */
+ if (wpid == -1) {
+ if (errno != ECHILD) {
+ warn("waitpid() failed; killing restarter's child process");
+ kill(pid, SIGTERM);
+ }
+ krb5_err(context, 1, errno, "restarter failed waiting for child");
+ }
+
+ assert(wpid == pid);
+ wpid = -1;
+ pid = -1;
+ if (WIFEXITED(status)) {
+ switch (WEXITSTATUS(status)) {
+ case IPROPD_DONE:
+ exit(0);
+ case IPROPD_RESTART_SLOW:
+ if (exit_flag)
+ exit(1);
+ krb5_warnx(context, "Waiting 2 minutes to restart");
+ sleep(120);
+ continue;
+ case IPROPD_FATAL:
+ krb5_errx(context, WEXITSTATUS(status),
+ "Sockets and pipes not supported for "
+ "iprop log files");
+ case IPROPD_RESTART:
+ default:
+ if (exit_flag)
+ exit(1);
+ /* Add exponential backoff (with max backoff)? */
+ krb5_warnx(context, "Waiting 30 seconds to restart");
+ sleep(30);
+ continue;
+ }
+ }
+ /* else */
+ krb5_warnx(context, "Child was killed; waiting 30 seconds to restart");
+ sleep(30);
+ }
+
+ if (pid == -1)
+ exit(0); /* No dead child to reap; done */
+
+ assert(pid > 0);
+ if (wpid != pid) {
+ warnx("Interrupted; killing child (pid %ld) with %d",
+ (long)pid, exit_flag);
+ krb5_warnx(context, "Interrupted; killing child (pid %ld) with %d",
+ (long)pid, exit_flag);
+ kill(pid, exit_flag);
+
+ /* Wait up to one second for the child */
+ tmout.tv_sec = 1;
+ tmout.tv_usec = 0;
+ FD_ZERO(&readset);
+ FD_SET(fds2[0], &readset);
+ /* We don't care why select() returns */
+ (void) select(fds2[0] + 1, &readset, NULL, NULL, &tmout);
+ /*
+ * We haven't reaped the child yet; if it's a zombie, then
+ * SIGKILLing it won't hurt. If it's not a zombie yet, well,
+ * we're out of patience.
+ */
+ kill(pid, SIGKILL);
+ do {
+ wpid = waitpid(pid, &status, 0);
+ } while (wpid != pid && errno == EINTR);
+ if (wpid == -1)
+ krb5_err(context, 1, errno, "restarter failed waiting for child");
+ }
+
+ /* Finally, the child is dead and reaped */
+ if (WIFEXITED(status))
+ exit(WEXITSTATUS(status));
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGTERM:
+ case SIGXCPU:
+ case SIGINT:
+ exit(0);
+ default:
+ /*
+ * Attempt to set the same exit status for the parent as for
+ * the child.
+ */
+ kill(getpid(), WTERMSIG(status));
+ /*
+ * We can get past the self-kill if we inherited a SIG_IGN
+ * disposition that the child reset to SIG_DFL.
+ */
+ }
+ }
+ exit(1);
+#else
+ if (countp != NULL)
+ *countp = 0;
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
diff --git a/third_party/heimdal/lib/kadm5/ipropd_master.c b/third_party/heimdal/lib/kadm5/ipropd_master.c
new file mode 100644
index 0000000..3d693bd
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ipropd_master.c
@@ -0,0 +1,1890 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "iprop.h"
+#include <rtbl.h>
+
+static krb5_log_facility *log_facility;
+
+static int verbose;
+
+static const char *slave_stats_file;
+static const char *slave_stats_temp_file;
+static const char *slave_time_missing = "2 min";
+static const char *slave_time_gone = "5 min";
+
+static int time_before_missing;
+static int time_before_gone;
+
+const char *master_hostname;
+const char *pidfile_basename;
+static char hostname[128];
+
+static krb5_socket_t
+make_signal_socket (krb5_context context)
+{
+#ifndef NO_UNIX_SOCKETS
+ struct sockaddr_un addr;
+ const char *fn;
+ krb5_socket_t fd;
+
+ fn = kadm5_log_signal_socket(context);
+
+ fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+ if (fd < 0)
+ krb5_err (context, 1, errno, "socket AF_UNIX");
+ memset (&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ strlcpy (addr.sun_path, fn, sizeof(addr.sun_path));
+ unlink (addr.sun_path);
+ if (bind (fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ krb5_err (context, 1, errno, "bind %s", addr.sun_path);
+ return fd;
+#else
+ struct addrinfo *ai = NULL;
+ krb5_socket_t fd;
+
+ kadm5_log_signal_socket_info(context, 1, &ai);
+
+ fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (rk_IS_BAD_SOCKET(fd))
+ krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF=%d", ai->ai_family);
+
+ if (rk_IS_SOCKET_ERROR( bind (fd, ai->ai_addr, ai->ai_addrlen) ))
+ krb5_err (context, 1, rk_SOCK_ERRNO, "bind");
+ return fd;
+#endif
+}
+
+static krb5_socket_t
+make_listen_socket (krb5_context context, const char *port_str)
+{
+ krb5_socket_t fd;
+ int one = 1;
+ struct sockaddr_in addr;
+
+ fd = socket (AF_INET, SOCK_STREAM, 0);
+ if (rk_IS_BAD_SOCKET(fd))
+ krb5_err (context, 1, rk_SOCK_ERRNO, "socket AF_INET");
+ (void) setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
+ memset (&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+
+ if (port_str) {
+ addr.sin_port = krb5_getportbyname (context,
+ port_str, "tcp",
+ 0);
+ if (addr.sin_port == 0) {
+ char *ptr;
+ long port;
+
+ port = strtol (port_str, &ptr, 10);
+ if (port == 0 && ptr == port_str)
+ krb5_errx (context, 1, "bad port `%s'", port_str);
+ addr.sin_port = htons(port);
+ }
+ } else {
+ addr.sin_port = krb5_getportbyname (context, IPROP_SERVICE,
+ "tcp", IPROP_PORT);
+ }
+ if(bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ krb5_err (context, 1, errno, "bind");
+ if (listen(fd, SOMAXCONN) < 0)
+ krb5_err (context, 1, errno, "listen");
+ return fd;
+}
+
+
+struct slave {
+ krb5_socket_t fd;
+ struct sockaddr_in addr;
+ char *name;
+ krb5_auth_context ac;
+ uint32_t version;
+ uint32_t version_tstamp;
+ uint32_t version_ack;
+ time_t seen;
+ unsigned long flags;
+#define SLAVE_F_DEAD 0x1
+#define SLAVE_F_AYT 0x2
+#define SLAVE_F_READY 0x4
+ /*
+ * We'll use non-blocking I/O so no slave can hold us back.
+ *
+ * We call the state left over from a partial write a "tail".
+ *
+ * The krb5_data holding an KRB-PRIV will be the write buffer.
+ */
+ struct {
+ /* Every message we send is a KRB-PRIV with a 4-byte length prefixed */
+ uint8_t header_buf[4];
+ krb5_data header;
+ krb5_data packet;
+ size_t packet_off;
+ /* For send_complete() we need an sp as part of the tail */
+ krb5_storage *dump;
+ uint32_t vno;
+ } tail;
+ struct {
+ uint8_t header_buf[4];
+ krb5_data packet;
+ size_t offset;
+ int hlen;
+ } input;
+ /*
+ * Continuation for fair diff sending we send N entries at a time.
+ */
+ struct {
+ off_t off_next_version; /* offset in log of next diff */
+ uint32_t initial_version; /* at time of previous diff */
+ uint32_t initial_tstamp; /* at time of previous diff */
+ uint32_t last_version_sent;
+ int more; /* need to send more diffs */
+ } next_diff;
+ struct slave *next;
+};
+
+typedef struct slave slave;
+
+static int
+check_acl (krb5_context context, const char *name)
+{
+ const char *fn;
+ FILE *fp;
+ char buf[256];
+ int ret = 1;
+ char *slavefile = NULL;
+
+ if (asprintf(&slavefile, "%s/slaves", hdb_db_dir(context)) == -1
+ || slavefile == NULL)
+ errx(1, "out of memory");
+
+ fn = krb5_config_get_string_default(context,
+ NULL,
+ slavefile,
+ "kdc",
+ "iprop-acl",
+ NULL);
+
+ fp = fopen (fn, "r");
+ free(slavefile);
+ if (fp == NULL)
+ return 1;
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ buf[strcspn(buf, "\r\n")] = '\0';
+ if (strcmp (buf, name) == 0) {
+ ret = 0;
+ break;
+ }
+ }
+ fclose (fp);
+ return ret;
+}
+
+static void
+slave_seen(slave *s)
+{
+ s->flags &= ~SLAVE_F_AYT;
+ s->seen = time(NULL);
+}
+
+static int
+slave_missing_p (slave *s)
+{
+ if (time(NULL) > s->seen + time_before_missing)
+ return 1;
+ return 0;
+}
+
+static int
+slave_gone_p (slave *s)
+{
+ if (time(NULL) > s->seen + time_before_gone)
+ return 1;
+ return 0;
+}
+
+static void
+slave_dead(krb5_context context, slave *s)
+{
+ krb5_warnx(context, "slave %s dead", s->name);
+
+ if (!rk_IS_BAD_SOCKET(s->fd)) {
+ rk_closesocket (s->fd);
+ s->fd = rk_INVALID_SOCKET;
+ }
+ s->flags |= SLAVE_F_DEAD;
+ slave_seen(s);
+}
+
+static void
+remove_slave (krb5_context context, slave *s, slave **root)
+{
+ slave **p;
+
+ if (!rk_IS_BAD_SOCKET(s->fd))
+ rk_closesocket (s->fd);
+ if (s->name)
+ free (s->name);
+ if (s->ac)
+ krb5_auth_con_free (context, s->ac);
+
+ /* Free any pending input/output state */
+ krb5_data_free(&s->input.packet);
+ krb5_data_free(&s->tail.packet);
+ krb5_storage_free(s->tail.dump);
+
+ for (p = root; *p; p = &(*p)->next)
+ if (*p == s) {
+ *p = s->next;
+ break;
+ }
+ free (s);
+}
+
+static void
+add_slave (krb5_context context, krb5_keytab keytab, slave **root,
+ krb5_socket_t fd)
+{
+ krb5_principal server = NULL;
+ krb5_error_code ret;
+ slave *s;
+ socklen_t addr_len;
+ krb5_ticket *ticket = NULL;
+
+ s = calloc(1, sizeof(*s));
+ if (s == NULL) {
+ krb5_warnx (context, "add_slave: no memory");
+ return;
+ }
+ s->name = NULL;
+ s->ac = NULL;
+ s->input.packet.data = NULL;
+ s->tail.header.data = NULL;
+ s->tail.packet.data = NULL;
+ s->tail.dump = NULL;
+
+ addr_len = sizeof(s->addr);
+ s->fd = accept (fd, (struct sockaddr *)&s->addr, &addr_len);
+ if (rk_IS_BAD_SOCKET(s->fd)) {
+ krb5_warn (context, rk_SOCK_ERRNO, "accept");
+ goto error;
+ }
+
+ /*
+ * We write message lengths separately from the payload, and may do
+ * back-to-back small writes when flushing pending input and then a new
+ * update. Avoid Nagle delays.
+ */
+#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
+ {
+ int nodelay = 1;
+ (void) setsockopt(s->fd, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&nodelay, sizeof(nodelay));
+ }
+#endif
+
+ ret = krb5_sname_to_principal (context, hostname, IPROP_NAME,
+ KRB5_NT_SRV_HST, &server);
+ if (ret) {
+ krb5_warn (context, ret, "krb5_sname_to_principal");
+ goto error;
+ }
+
+ ret = krb5_recvauth (context, &s->ac, &s->fd,
+ IPROP_VERSION, server, 0, keytab, &ticket);
+
+ /*
+ * We'll be doing non-blocking I/O only after authentication. We don't
+ * want to get stuck talking to any one slave.
+ *
+ * If we get a partial write, we'll finish writing when the socket becomes
+ * writable.
+ *
+ * Partial reads will be treated as EOF, causing the slave to be marked
+ * dead.
+ *
+ * To do non-blocking I/O for authentication we'll have to implement our
+ * own krb5_recvauth().
+ */
+ socket_set_nonblocking(s->fd, 1);
+
+ if (ret) {
+ krb5_warn (context, ret, "krb5_recvauth");
+ goto error;
+ }
+ ret = krb5_unparse_name (context, ticket->client, &s->name);
+ if (ret) {
+ krb5_warn (context, ret, "krb5_unparse_name");
+ goto error;
+ }
+ if (check_acl (context, s->name)) {
+ krb5_warnx (context, "%s not in acl", s->name);
+ goto error;
+ }
+
+ {
+ slave *l = *root;
+
+ while (l) {
+ if (strcmp(l->name, s->name) == 0)
+ break;
+ l = l->next;
+ }
+ if (l) {
+ if (l->flags & SLAVE_F_DEAD) {
+ remove_slave(context, l, root);
+ } else {
+ krb5_warnx (context, "second connection from %s", s->name);
+ goto error;
+ }
+ }
+ }
+
+ krb5_free_principal(context, server);
+ krb5_free_ticket(context, ticket);
+ krb5_warnx (context, "connection from %s", s->name);
+
+ s->version = 0;
+ s->version_ack = 0;
+ s->flags = 0;
+ slave_seen(s);
+ s->next = *root;
+ *root = s;
+ return;
+error:
+ remove_slave(context, s, root);
+ krb5_free_principal(context, server);
+ if (ticket)
+ krb5_free_ticket(context, ticket);
+}
+
+static int
+dump_one (krb5_context context, HDB *db, hdb_entry *entry, void *v)
+{
+ krb5_error_code ret;
+ krb5_storage *dump = (krb5_storage *)v;
+ krb5_storage *sp;
+ krb5_data data;
+
+ ret = hdb_entry2value (context, entry, &data);
+ if (ret)
+ return ret;
+ ret = krb5_data_realloc (&data, data.length + 4);
+ if (ret)
+ goto done;
+ memmove ((char *)data.data + 4, data.data, data.length - 4);
+ sp = krb5_storage_from_data(&data);
+ if (sp == NULL) {
+ ret = krb5_enomem(context);
+ goto done;
+ }
+ ret = krb5_store_uint32(sp, ONE_PRINC);
+ krb5_storage_free(sp);
+
+ if (ret == 0)
+ ret = krb5_store_data(dump, data);
+
+done:
+ krb5_data_free (&data);
+ return ret;
+}
+
+static int
+write_dump (krb5_context context, krb5_storage *dump,
+ const char *database, uint32_t current_version)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ HDB *db;
+ krb5_data data;
+ char buf[8];
+
+ /* we assume that the caller has obtained an exclusive lock */
+
+ ret = krb5_storage_truncate(dump, 0);
+ if (ret)
+ return ret;
+
+ if (krb5_storage_seek(dump, 0, SEEK_SET) != 0)
+ return errno;
+
+ /*
+ * First we store zero as the HDB version, this will indicate to a
+ * later reader that the dumpfile is invalid. We later write the
+ * correct version in the file after we have written all of the
+ * messages. A dump with a zero version will not be considered
+ * to be valid.
+ */
+
+ ret = krb5_store_uint32(dump, 0);
+ if (ret)
+ return ret;
+
+ ret = hdb_create (context, &db, database);
+ if (ret)
+ krb5_err (context, IPROPD_RESTART, ret, "hdb_create: %s", database);
+ ret = db->hdb_open (context, db, O_RDONLY, 0);
+ if (ret)
+ krb5_err (context, IPROPD_RESTART, ret, "db->open");
+
+ sp = krb5_storage_from_mem (buf, 4);
+ if (sp == NULL)
+ krb5_errx (context, IPROPD_RESTART, "krb5_storage_from_mem");
+ krb5_store_uint32 (sp, TELL_YOU_EVERYTHING);
+ krb5_storage_free (sp);
+
+ data.data = buf;
+ data.length = 4;
+
+ ret = krb5_store_data(dump, data);
+ if (ret) {
+ krb5_warn (context, ret, "write_dump");
+ return ret;
+ }
+
+ ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, dump_one, dump);
+ if (ret) {
+ krb5_warn (context, ret, "write_dump: hdb_foreach");
+ return ret;
+ }
+
+ (*db->hdb_close)(context, db);
+ (*db->hdb_destroy)(context, db);
+
+ sp = krb5_storage_from_mem (buf, 8);
+ if (sp == NULL)
+ krb5_errx (context, IPROPD_RESTART, "krb5_storage_from_mem");
+ ret = krb5_store_uint32(sp, NOW_YOU_HAVE);
+ if (ret == 0)
+ krb5_store_uint32(sp, current_version);
+ krb5_storage_free (sp);
+
+ data.length = 8;
+
+ if (ret == 0)
+ ret = krb5_store_data(dump, data);
+
+ /*
+ * We must ensure that the entire valid dump is written to disk
+ * before we write the current version at the front thus making
+ * it a valid dump file. If we crash around here, this can be
+ * important upon reboot.
+ */
+
+ if (ret == 0)
+ ret = krb5_storage_fsync(dump);
+
+ if (ret == 0 && krb5_storage_seek(dump, 0, SEEK_SET) == -1)
+ ret = errno;
+
+ /* Write current version at the front making the dump valid */
+
+ if (ret == 0)
+ ret = krb5_store_uint32(dump, current_version);
+
+ /*
+ * We don't need to fsync(2) after the real version is written as
+ * it is not a disaster if it doesn't make it to disk if we crash.
+ * After all, we'll just create a new dumpfile.
+ */
+
+ if (ret == 0)
+ krb5_warnx(context, "wrote new dumpfile (version %u)",
+ current_version);
+ else
+ krb5_warn(context, ret, "failed to write new dumpfile (version %u)",
+ current_version);
+
+ return ret;
+}
+
+static int
+mk_priv_tail(krb5_context context, slave *s, krb5_data *data)
+{
+ uint32_t len;
+ int ret;
+
+ ret = krb5_mk_priv(context, s->ac, data, &s->tail.packet, NULL);
+ if (ret)
+ return ret;
+
+ len = s->tail.packet.length;
+ _krb5_put_int(s->tail.header_buf, len, sizeof(s->tail.header_buf));
+ s->tail.header.length = sizeof(s->tail.header_buf);
+ s->tail.header.data = s->tail.header_buf;
+ return 0;
+}
+
+static int
+have_tail(slave *s)
+{
+ return s->tail.header.length || s->tail.packet.length || s->tail.dump;
+}
+
+static int
+more_diffs(slave *s)
+{
+ return s->next_diff.more;
+}
+
+#define SEND_COMPLETE_MAX_RECORDS 50
+#define SEND_DIFFS_MAX_RECORDS 50
+
+static int
+send_tail(krb5_context context, slave *s)
+{
+ krb5_data data;
+ ssize_t bytes = 0;
+ size_t rem = 0;
+ size_t n;
+ int ret;
+
+ if (! have_tail(s))
+ return 0;
+
+ /*
+ * For the case where we're continuing a send_complete() send up to
+ * SEND_COMPLETE_MAX_RECORDS records now, and the rest asynchronously
+ * later. This ensures that sending a complete dump to a slow-to-drain
+ * client does not prevent others from getting serviced.
+ */
+ for (n = 0; n < SEND_COMPLETE_MAX_RECORDS; n++) {
+ if (! have_tail(s))
+ return 0;
+
+ if (s->tail.header.length) {
+ bytes = krb5_net_write(context, &s->fd,
+ s->tail.header.data,
+ s->tail.header.length);
+ if (bytes < 0)
+ goto err;
+
+ s->tail.header.length -= bytes;
+ s->tail.header.data = (char *)s->tail.header.data + bytes;
+ rem = s->tail.header.length;
+ if (rem)
+ goto ewouldblock;
+ }
+
+ if (s->tail.packet.length) {
+ bytes = krb5_net_write(context, &s->fd,
+ (char *)s->tail.packet.data + s->tail.packet_off,
+ s->tail.packet.length - s->tail.packet_off);
+ if (bytes < 0)
+ goto err;
+ s->tail.packet_off += bytes;
+ if (bytes)
+ slave_seen(s);
+ rem = s->tail.packet.length - s->tail.packet_off;
+ if (rem)
+ goto ewouldblock;
+
+ krb5_data_free(&s->tail.packet);
+ s->tail.packet_off = 0;
+ }
+
+ if (s->tail.dump == NULL)
+ return 0;
+
+ /*
+ * We're in the middle of a send_complete() that was interrupted by
+ * EWOULDBLOCK. Continue the sending of the dump.
+ */
+ ret = krb5_ret_data(s->tail.dump, &data);
+ if (ret == HEIM_ERR_EOF) {
+ krb5_storage_free(s->tail.dump);
+ s->tail.dump = NULL;
+ s->version = s->tail.vno;
+ return 0;
+ }
+
+ if (ret) {
+ krb5_warn(context, ret, "failed to read entry from dump!");
+ } else {
+ ret = mk_priv_tail(context, s, &data);
+ krb5_data_free(&data);
+ if (ret == 0)
+ continue;
+ krb5_warn(context, ret, "failed to make and send a KRB-PRIV to %s",
+ s->name);
+ }
+
+ slave_dead(context, s);
+ return ret;
+ }
+
+ if (ret == 0 && s->tail.dump != NULL)
+ return EWOULDBLOCK;
+
+err:
+ if (errno != EAGAIN && errno != EWOULDBLOCK) {
+ krb5_warn(context, ret = errno,
+ "error sending diffs to now-dead slave %s", s->name);
+ slave_dead(context, s);
+ return ret;
+ }
+
+ewouldblock:
+ if (verbose)
+ krb5_warnx(context, "would block writing %llu bytes to slave %s",
+ (unsigned long long)rem, s->name);
+ return EWOULDBLOCK;
+}
+
+static int
+send_complete(krb5_context context, slave *s, const char *database,
+ uint32_t current_version, uint32_t oldest_version,
+ uint32_t initial_log_tstamp)
+{
+ krb5_error_code ret;
+ krb5_storage *dump = NULL;
+ uint32_t vno = 0;
+ int fd = -1;
+ struct stat st;
+ char *dfn;
+
+ ret = asprintf(&dfn, "%s/ipropd.dumpfile", hdb_db_dir(context));
+ if (ret == -1 || !dfn)
+ return krb5_enomem(context);
+
+ fd = open(dfn, O_CREAT|O_RDWR, 0600);
+ if (fd == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "Cannot open/create iprop dumpfile %s", dfn);
+ free(dfn);
+ return ret;
+ }
+ free(dfn);
+
+ dump = krb5_storage_from_fd(fd);
+ if (!dump) {
+ ret = errno;
+ krb5_warn(context, ret, "krb5_storage_from_fd");
+ goto done;
+ }
+
+ for (;;) {
+ ret = flock(fd, LOCK_SH);
+ if (ret == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "flock(fd, LOCK_SH)");
+ goto done;
+ }
+
+ if (krb5_storage_seek(dump, 0, SEEK_SET) == (off_t)-1) {
+ ret = errno;
+ krb5_warn(context, ret, "krb5_storage_seek(dump, 0, SEEK_SET)");
+ goto done;
+ }
+
+ vno = 0;
+ ret = krb5_ret_uint32(dump, &vno);
+ if (ret && ret != HEIM_ERR_EOF) {
+ krb5_warn(context, ret, "krb5_ret_uint32(dump, &vno)");
+ goto done;
+ }
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "send_complete: could not stat dump file");
+ goto done;
+ }
+
+ /*
+ * If the current dump has an appropriate version, then we can
+ * break out of the loop and send the file below.
+ */
+ if (ret == 0 && vno != 0 && st.st_mtime > initial_log_tstamp &&
+ vno >= oldest_version && vno <= current_version)
+ break;
+
+ if (verbose)
+ krb5_warnx(context, "send_complete: dumping HDB");
+
+ /*
+ * Otherwise, we may need to write a new dump file. We
+ * obtain an exclusive lock on the fd. Because this is
+ * not guaranteed to be an upgrade of our existing shared
+ * lock, someone else may have written a new dumpfile while
+ * we were waiting and so we must first check the vno of
+ * the dump to see if that happened. If it did, we need
+ * to go back to the top of the loop so that we can downgrade
+ * our lock to a shared one.
+ */
+
+ ret = flock(fd, LOCK_EX);
+ if (ret == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "flock(fd, LOCK_EX)");
+ goto done;
+ }
+
+ ret = krb5_storage_seek(dump, 0, SEEK_SET);
+ if (ret == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "krb5_storage_seek(dump, 0, SEEK_SET)");
+ goto done;
+ }
+
+ vno = 0;
+ ret = krb5_ret_uint32(dump, &vno);
+ if (ret && ret != HEIM_ERR_EOF) {
+ krb5_warn(context, ret, "krb5_ret_uint32(dump, &vno)");
+ goto done;
+ }
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "send_complete: could not stat dump file");
+ goto done;
+ }
+
+ /* check if someone wrote a better version for us */
+ if (ret == 0 && vno != 0 && st.st_mtime > initial_log_tstamp &&
+ vno >= oldest_version && vno <= current_version)
+ continue;
+
+ /* Now, we know that we must write a new dump file. */
+
+ ret = write_dump(context, dump, database, current_version);
+ if (ret)
+ goto done;
+
+ /*
+ * And we must continue to the top of the loop so that we can
+ * downgrade to a shared lock.
+ */
+ }
+
+ /*
+ * Leaving the above loop, dump should have a ptr right after the initial
+ * 4 byte DB version number and we should have a shared lock on the file
+ * (which we may have just created), so we are reading to start sending
+ * the data down the wire.
+ *
+ * Note: (krb5_storage_from_fd() dup()'s the fd)
+ */
+
+ s->tail.dump = dump;
+ s->tail.vno = vno;
+ dump = NULL;
+ ret = send_tail(context, s);
+
+done:
+ if (fd != -1)
+ close(fd);
+ if (dump)
+ krb5_storage_free(dump);
+ return ret;
+}
+
+static int
+send_are_you_there (krb5_context context, slave *s)
+{
+ krb5_storage *sp;
+ krb5_data data;
+ char buf[4];
+ int ret;
+
+ if (s->flags & (SLAVE_F_DEAD|SLAVE_F_AYT))
+ return 0;
+
+ /*
+ * Write any remainder of previous write, if we can. If we'd block we'll
+ * return EWOULDBLOCK.
+ */
+ ret = send_tail(context, s);
+ if (ret)
+ return ret;
+
+ krb5_warnx(context, "slave %s missing, sending AYT", s->name);
+
+ s->flags |= SLAVE_F_AYT;
+
+ data.data = buf;
+ data.length = 4;
+
+ sp = krb5_storage_from_mem (buf, 4);
+ if (sp == NULL) {
+ krb5_warnx (context, "are_you_there: krb5_data_alloc");
+ slave_dead(context, s);
+ return ENOMEM;
+ }
+ ret = krb5_store_uint32(sp, ARE_YOU_THERE);
+ krb5_storage_free (sp);
+
+ if (ret == 0)
+ ret = mk_priv_tail(context, s, &data);
+ if (ret == 0)
+ ret = send_tail(context, s);
+ if (ret && ret != EWOULDBLOCK) {
+ krb5_warn(context, ret, "are_you_there");
+ slave_dead(context, s);
+ }
+ return ret;
+}
+
+static int
+diffready(krb5_context context, slave *s)
+{
+ /*
+ * Don't send any diffs until slave has sent an I_HAVE telling us the
+ * initial version number!
+ */
+ if ((s->flags & SLAVE_F_READY) == 0)
+ return 0;
+
+ if (s->flags & SLAVE_F_DEAD) {
+ if (verbose)
+ krb5_warnx(context, "not sending diffs to dead slave %s", s->name);
+ return 0;
+ }
+
+ /* Write any remainder of previous write, if we can. */
+ if (send_tail(context, s) != 0)
+ return 0;
+
+ return 1;
+}
+
+static int
+nodiffs(krb5_context context, slave *s, uint32_t current_version)
+{
+ krb5_storage *sp;
+ krb5_data data;
+ int ret;
+
+ if (s->version < current_version)
+ return 0;
+
+ /*
+ * If we had sent a partial diff, and now they're caught up, then there's
+ * no more.
+ */
+ s->next_diff.more = 0;
+
+ if (verbose)
+ krb5_warnx(context, "slave %s version %ld already sent", s->name,
+ (long)s->version);
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem");
+
+ ret = krb5_store_uint32(sp, YOU_HAVE_LAST_VERSION);
+ if (ret == 0) {
+ krb5_data_zero(&data);
+ ret = krb5_storage_to_data(sp, &data);
+ }
+ krb5_storage_free(sp);
+ if (ret == 0) {
+ ret = mk_priv_tail(context, s, &data);
+ krb5_data_free(&data);
+ }
+ if (ret == 0)
+ send_tail(context, s);
+
+ return 1;
+}
+
+/*
+ * Lock the log and return initial version and timestamp
+ */
+static int
+get_first(kadm5_server_context *server_context, int log_fd,
+ uint32_t *initial_verp, uint32_t *initial_timep)
+{
+ krb5_context context = server_context->context;
+ int ret;
+
+ /*
+ * We don't want to perform tight retry loops on log access errors, so on
+ * error mark the slave dead. The slave reconnect after a delay...
+ */
+ if (flock(log_fd, LOCK_SH) == -1) {
+ krb5_warn(context, errno, "could not obtain shared lock on log file");
+ return -1;
+ }
+
+ ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST,
+ initial_verp, initial_timep);
+ if (ret == HEIM_ERR_EOF)
+ ret = kadm5_log_get_version_fd(server_context, log_fd,
+ LOG_VERSION_UBER, initial_verp,
+ initial_timep);
+ if (ret != 0) {
+ flock(log_fd, LOCK_UN);
+ krb5_warn(context, ret, "could not read initial log entry");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*-
+ * Find the left end of the diffs in the log we want to send.
+ *
+ * - On success, return a positive offset to the first new entry, retaining
+ * a read lock on the log file.
+ * - On error, return a negative offset, with the lock released.
+ * - If we simply find no successor entry in the log, return zero
+ * with the lock released, which indicates that fallback to send_complete()
+ * is needed.
+ */
+static off_t
+get_left(kadm5_server_context *server_context, slave *s, krb5_storage *sp,
+ int log_fd, uint32_t current_version,
+ uint32_t *initial_verp, uint32_t *initial_timep)
+{
+ krb5_context context = server_context->context;
+ off_t pos;
+ off_t left;
+ int ret;
+
+ for (;;) {
+ uint32_t ver = s->version;
+
+ /* This acquires a read lock on success */
+ ret = get_first(server_context, log_fd,
+ initial_verp, initial_timep);
+ if (ret != 0)
+ return -1;
+
+ /* When the slave version is out of range, send the whole database. */
+ if (ver == 0 || ver < *initial_verp || ver > current_version) {
+ flock(log_fd, LOCK_UN);
+ return 0;
+ }
+
+ /* Avoid seeking past the last committed record */
+ if (kadm5_log_goto_end(server_context, sp) != 0 ||
+ (pos = krb5_storage_seek(sp, 0, SEEK_CUR)) < 0)
+ goto err;
+
+ /*
+ * First try to see if we can find it quickly by seeking to the right
+ * end of the previous diff sent.
+ */
+ if (s->next_diff.last_version_sent > 0 &&
+ s->next_diff.off_next_version > 0 &&
+ s->next_diff.off_next_version < pos &&
+ s->next_diff.initial_version == *initial_verp &&
+ s->next_diff.initial_tstamp == *initial_timep) {
+ /*
+ * Sanity check that the left version matches what we wanted, the
+ * log may have been truncated since.
+ */
+ left = s->next_diff.off_next_version;
+ if (krb5_storage_seek(sp, left, SEEK_SET) != left)
+ goto err;
+ if (kadm5_log_next(context, sp, &ver, NULL, NULL, NULL) == 0 &&
+ ver == s->next_diff.last_version_sent + 1)
+ return left;
+ }
+
+ if (krb5_storage_seek(sp, pos, SEEK_SET) != pos)
+ goto err;
+
+ /*
+ * Drop the lock and try to find the left entry by seeking backward
+ * from the end of the end of the log. If we succeed, re-acquire the
+ * lock, update "next_diff", and retry the fast-path.
+ */
+ flock(log_fd, LOCK_UN);
+
+ /* Slow path: seek backwards, entry by entry, from the end */
+ for (;;) {
+ enum kadm_ops op;
+ uint32_t len;
+
+ ret = kadm5_log_previous(context, sp, &ver, NULL, &op, &len);
+ if (ret)
+ return -1;
+ left = krb5_storage_seek(sp, -16, SEEK_CUR);
+ if (left < 0)
+ return left;
+ if (ver == s->version + 1)
+ break;
+
+ /*
+ * We don't expect to reach the slave's version, unless the log
+ * has been modified after we released the lock.
+ */
+ if (ver == s->version) {
+ krb5_warnx(context, "iprop log truncated while sending diffs "
+ "to slave?? ver = %lu", (unsigned long)ver);
+ return -1;
+ }
+
+ /* If we've reached the uber record, send the complete database */
+ if (left == 0 || (ver == 0 && op == kadm_nop))
+ return 0;
+ }
+ assert(ver == s->version + 1);
+
+ /* Set up the fast-path pre-conditions */
+ s->next_diff.last_version_sent = s->version;
+ s->next_diff.off_next_version = left;
+ s->next_diff.initial_version = *initial_verp;
+ s->next_diff.initial_tstamp = *initial_timep;
+
+ /*
+ * If we loop then we're hoping to hit the fast path so we can return a
+ * non-zero, positive left offset with the lock held.
+ *
+ * We just updated the fast path pre-conditions, so unless a log
+ * truncation event happens between the point where we dropped the lock
+ * and the point where we rearcuire it above, we will hit the fast
+ * path.
+ */
+ }
+
+ err:
+ flock(log_fd, LOCK_UN);
+ return -1;
+}
+
+static off_t
+get_right(krb5_context context, int log_fd, krb5_storage *sp,
+ int lastver, slave *s, off_t left, uint32_t *verp)
+{
+ int ret = 0;
+ int i = 0;
+ uint32_t ver = s->version;
+ off_t right = krb5_storage_seek(sp, left, SEEK_SET);
+
+ if (right <= 0) {
+ flock(log_fd, LOCK_UN);
+ return -1;
+ }
+
+ /* The "lastver" bound should preclude us reaching EOF */
+ for (; ret == 0 && i < SEND_DIFFS_MAX_RECORDS && ver < lastver; ++i) {
+ uint32_t logver;
+
+ ret = kadm5_log_next(context, sp, &logver, NULL, NULL, NULL);
+ if (logver != ++ver)
+ ret = KADM5_LOG_CORRUPT;
+ }
+
+ if (ret == 0)
+ right = krb5_storage_seek(sp, 0, SEEK_CUR);
+ else
+ right = -1;
+ if (right <= 0) {
+ flock(log_fd, LOCK_UN);
+ return -1;
+ }
+ *verp = ver;
+ return right;
+}
+
+static void
+send_diffs(kadm5_server_context *server_context, slave *s, int log_fd,
+ const char *database, uint32_t current_version)
+{
+ krb5_context context = server_context->context;
+ krb5_storage *sp;
+ uint32_t initial_version;
+ uint32_t initial_tstamp;
+ uint32_t ver = 0;
+ off_t left = 0;
+ off_t right = 0;
+ krb5_ssize_t bytes;
+ krb5_data data;
+ int ret = 0;
+
+ if (!diffready(context, s) || nodiffs(context, s, current_version))
+ return;
+
+ if (verbose)
+ krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name);
+
+ sp = krb5_storage_from_fd(log_fd);
+ if (sp == NULL)
+ krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM,
+ "send_diffs: out of memory");
+
+ left = get_left(server_context, s, sp, log_fd, current_version,
+ &initial_version, &initial_tstamp);
+ if (left < 0) {
+ krb5_storage_free(sp);
+ slave_dead(context, s);
+ return;
+ }
+
+ if (left == 0) {
+ /* Slave's version is not in the log, fall back on send_complete() */
+ krb5_storage_free(sp);
+ send_complete(context, s, database, current_version,
+ initial_version, initial_tstamp);
+ return;
+ }
+
+ /* We still hold the read lock, if right > 0 */
+ right = get_right(server_context->context, log_fd, sp, current_version,
+ s, left, &ver);
+ if (right == left) {
+ flock(log_fd, LOCK_UN);
+ krb5_storage_free(sp);
+ return;
+ }
+ if (right < left) {
+ assert(right < 0);
+ krb5_storage_free(sp);
+ slave_dead(context, s);
+ return;
+ }
+
+ if (krb5_storage_seek(sp, left, SEEK_SET) != left) {
+ ret = errno ? errno : EIO;
+ flock(log_fd, LOCK_UN);
+ krb5_warn(context, ret, "send_diffs: krb5_storage_seek");
+ krb5_storage_free(sp);
+ slave_dead(context, s);
+ return;
+ }
+
+ ret = krb5_data_alloc(&data, right - left + 4);
+ if (ret) {
+ flock(log_fd, LOCK_UN);
+ krb5_warn(context, ret, "send_diffs: krb5_data_alloc");
+ krb5_storage_free(sp);
+ slave_dead(context, s);
+ return;
+ }
+
+ bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4);
+ flock(log_fd, LOCK_UN);
+ krb5_storage_free(sp);
+ if (bytes != data.length - 4)
+ krb5_errx(context, IPROPD_RESTART, "locked log truncated???");
+
+ sp = krb5_storage_from_data(&data);
+ if (sp == NULL) {
+ krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "out of memory");
+ return;
+ }
+ ret = krb5_store_uint32(sp, FOR_YOU);
+ krb5_storage_free(sp);
+
+ if (ret == 0)
+ ret = mk_priv_tail(context, s, &data);
+ krb5_data_free(&data);
+ if (ret == 0) {
+ /* Save the fast-path continuation */
+ s->next_diff.last_version_sent = ver;
+ s->next_diff.off_next_version = right;
+ s->next_diff.initial_version = initial_version;
+ s->next_diff.initial_tstamp = initial_tstamp;
+ s->next_diff.more = ver < current_version;
+ ret = send_tail(context, s);
+
+ krb5_warnx(context,
+ "syncing slave %s from version %lu to version %lu",
+ s->name, (unsigned long)s->version,
+ (unsigned long)ver);
+ s->version = ver;
+ }
+
+ if (ret && ret != EWOULDBLOCK) {
+ krb5_warn(context, ret, "send_diffs: making or sending "
+ "KRB-PRIV message");
+ slave_dead(context, s);
+ return;
+ }
+ slave_seen(s);
+ return;
+}
+
+/* Sensible bound on slave message size */
+#define SLAVE_MSG_MAX 65536
+
+static int
+fill_input(krb5_context context, slave *s)
+{
+ krb5_error_code ret;
+
+ if (s->input.hlen < 4) {
+ uint8_t *buf = s->input.header_buf + s->input.hlen;
+ size_t len = 4 - s->input.hlen;
+ krb5_ssize_t bytes = krb5_net_read(context, &s->fd, buf, len);
+
+ if (bytes == 0)
+ return HEIM_ERR_EOF;
+ if (bytes < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return EWOULDBLOCK;
+ return errno ? errno : EIO;
+ }
+ s->input.hlen += bytes;
+ if (bytes < len)
+ return EWOULDBLOCK;
+
+ buf = s->input.header_buf;
+ len = ((unsigned long)buf[0] << 24) | (buf[1] << 16)
+ | (buf[2] << 8) | buf[3];
+ if (len > SLAVE_MSG_MAX)
+ return EINVAL;
+ ret = krb5_data_alloc(&s->input.packet, len);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (s->input.offset < s->input.packet.length) {
+ u_char *buf = (u_char *)s->input.packet.data + s->input.offset;
+ size_t len = s->input.packet.length - s->input.offset;
+ krb5_ssize_t bytes = krb5_net_read(context, &s->fd, buf, len);
+
+ if (bytes == 0)
+ return HEIM_ERR_EOF;
+ if (bytes < 0) {
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
+ return EWOULDBLOCK;
+ return errno ? errno : EIO;
+ }
+ s->input.offset += bytes;
+ if (bytes != len)
+ return EWOULDBLOCK;
+ }
+ return 0;
+}
+
+static int
+read_msg(krb5_context context, slave *s, krb5_data *out)
+{
+ int ret = fill_input(context, s);
+
+ if (ret != 0)
+ return ret;
+
+ ret = krb5_rd_priv(context, s->ac, &s->input.packet, out, NULL);
+
+ /* Prepare for next packet */
+ krb5_data_free(&s->input.packet);
+ s->input.offset = 0;
+ s->input.hlen = 0;
+
+ return ret;
+}
+
+static int
+process_msg(kadm5_server_context *server_context, slave *s, int log_fd,
+ const char *database, uint32_t current_version)
+{
+ krb5_context context = server_context->context;
+ int ret = 0;
+ krb5_data out;
+ krb5_storage *sp;
+ uint32_t tmp;
+
+ ret = read_msg(context, s, &out);
+ if (ret) {
+ if (ret != EWOULDBLOCK)
+ krb5_warn(context, ret, "error reading message from %s", s->name);
+ return ret;
+ }
+
+ sp = krb5_storage_from_mem(out.data, out.length);
+ if (sp == NULL) {
+ krb5_warnx(context, "process_msg: no memory");
+ krb5_data_free(&out);
+ return 1;
+ }
+ if (krb5_ret_uint32(sp, &tmp) != 0) {
+ krb5_warnx(context, "process_msg: client send too short command");
+ krb5_data_free(&out);
+ return 1;
+ }
+ switch (tmp) {
+ case I_HAVE :
+ ret = krb5_ret_uint32(sp, &tmp);
+ if (ret != 0) {
+ krb5_warnx(context, "process_msg: client send too little I_HAVE data");
+ break;
+ }
+ /*
+ * XXX Make the slave send the timestamp as well, and try to get it
+ * here, and pass it to send_diffs().
+ */
+ /*
+ * New slave whose version number we've not yet seen. If the version
+ * number is zero, the slave has no data, and we'll send a complete
+ * database (that happens in send_diffs()). Otherwise, we'll record a
+ * non-zero initial version and attempt an incremental update.
+ *
+ * NOTE!: Once the slave is "ready" (its first I_HAVE has conveyed its
+ * initial version), we MUST NOT update s->version to the slave's
+ * I_HAVE version, since we may already have sent later updates, and
+ * MUST NOT send them again, otherwise we can get further and further
+ * out of sync resending larger and larger diffs. The "not yet ready"
+ * is an essential precondition for setting s->version to the value
+ * in the I_HAVE message. This happens only once when the slave
+ * first connects.
+ */
+ if (!(s->flags & SLAVE_F_READY)) {
+ if (current_version < tmp) {
+ krb5_warnx(context, "Slave %s (version %u) has later version "
+ "than the master (version %u) OUT OF SYNC",
+ s->name, tmp, current_version);
+ /* Force send_complete() */
+ tmp = 0;
+ }
+ /*
+ * Mark the slave as ready for updates based on incoming signals.
+ * Prior to the initial I_HAVE, we don't know the slave's version
+ * number, and MUST not send it anything, since we'll needlessly
+ * attempt to send the whole database!
+ */
+ s->version = tmp;
+ s->flags |= SLAVE_F_READY;
+ if (verbose)
+ krb5_warnx(context, "slave %s ready for updates from version %u",
+ s->name, tmp);
+ }
+ if ((s->version_ack = tmp) < s->version)
+ break;
+ send_diffs(server_context, s, log_fd, database, current_version);
+ break;
+ case I_AM_HERE :
+ if (verbose)
+ krb5_warnx(context, "slave %s is there", s->name);
+ break;
+ case ARE_YOU_THERE:
+ case FOR_YOU :
+ default :
+ krb5_warnx(context, "Ignoring command %d", tmp);
+ break;
+ }
+
+ krb5_data_free(&out);
+ krb5_storage_free(sp);
+
+ slave_seen(s);
+
+ return ret;
+}
+
+#define SLAVE_NAME "Name"
+#define SLAVE_ADDRESS "Address"
+#define SLAVE_VERSION "Version"
+#define SLAVE_STATUS "Status"
+#define SLAVE_SEEN "Last Seen"
+
+static void
+init_stats_names(krb5_context context)
+{
+ const char *fn = NULL;
+ char *buf = NULL;
+
+ if (slave_stats_file)
+ fn = slave_stats_file;
+ else if ((fn = krb5_config_get_string(context, NULL, "kdc",
+ "iprop-stats", NULL)) == NULL) {
+ if (asprintf(&buf, "%s/slaves-stats", hdb_db_dir(context)) != -1
+ && buf != NULL)
+ fn = buf;
+ buf = NULL;
+ }
+ if (fn != NULL) {
+ slave_stats_file = fn;
+ if (asprintf(&buf, "%s.tmp", fn) != -1 && buf != NULL)
+ slave_stats_temp_file = buf;
+ }
+}
+
+static void
+write_master_down(krb5_context context)
+{
+ char str[100];
+ time_t t = time(NULL);
+ FILE *fp = NULL;
+
+ if (slave_stats_temp_file != NULL)
+ fp = fopen(slave_stats_temp_file, "w");
+ if (fp == NULL)
+ return;
+ if (krb5_format_time(context, t, str, sizeof(str), TRUE) == 0)
+ fprintf(fp, "master down at %s\n", str);
+ else
+ fprintf(fp, "master down\n");
+
+ if (fclose(fp) != EOF)
+ (void) rk_rename(slave_stats_temp_file, slave_stats_file);
+}
+
+static void
+write_stats(krb5_context context, slave *slaves, uint32_t current_version)
+{
+ char str[100];
+ rtbl_t tbl;
+ time_t t = time(NULL);
+ FILE *fp = NULL;
+
+ if (slave_stats_temp_file != NULL)
+ fp = fopen(slave_stats_temp_file, "w");
+ if (fp == NULL)
+ return;
+
+ if (krb5_format_time(context, t, str, sizeof(str), TRUE))
+ snprintf(str, sizeof(str), "<unknown-time>");
+ fprintf(fp, "Status for slaves, last updated: %s\n\n", str);
+
+ fprintf(fp, "Master version: %lu\n\n", (unsigned long)current_version);
+
+ tbl = rtbl_create();
+ if (tbl == NULL) {
+ fclose(fp);
+ return;
+ }
+
+ rtbl_add_column(tbl, SLAVE_NAME, 0);
+ rtbl_add_column(tbl, SLAVE_ADDRESS, 0);
+ rtbl_add_column(tbl, SLAVE_VERSION, RTBL_ALIGN_RIGHT);
+ rtbl_add_column(tbl, SLAVE_STATUS, 0);
+ rtbl_add_column(tbl, SLAVE_SEEN, 0);
+
+ rtbl_set_prefix(tbl, " ");
+ rtbl_set_column_prefix(tbl, SLAVE_NAME, "");
+
+ while (slaves) {
+ krb5_address addr;
+ krb5_error_code ret;
+ rtbl_add_column_entry(tbl, SLAVE_NAME, slaves->name);
+ ret = krb5_sockaddr2address (context,
+ (struct sockaddr*)&slaves->addr, &addr);
+ if(ret == 0) {
+ krb5_print_address(&addr, str, sizeof(str), NULL);
+ krb5_free_address(context, &addr);
+ rtbl_add_column_entry(tbl, SLAVE_ADDRESS, str);
+ } else
+ rtbl_add_column_entry(tbl, SLAVE_ADDRESS, "<unknown>");
+
+ snprintf(str, sizeof(str), "%u", (unsigned)slaves->version_ack);
+ rtbl_add_column_entry(tbl, SLAVE_VERSION, str);
+
+ if (slaves->flags & SLAVE_F_DEAD)
+ rtbl_add_column_entry(tbl, SLAVE_STATUS, "Down");
+ else
+ rtbl_add_column_entry(tbl, SLAVE_STATUS, "Up");
+
+ ret = krb5_format_time(context, slaves->seen, str, sizeof(str), TRUE);
+ if (ret)
+ rtbl_add_column_entry(tbl, SLAVE_SEEN, "<error-formatting-time>");
+ else
+ rtbl_add_column_entry(tbl, SLAVE_SEEN, str);
+
+ slaves = slaves->next;
+ }
+
+ rtbl_format(tbl, fp);
+ rtbl_destroy(tbl);
+
+ if (fclose(fp) != EOF)
+ (void) rk_rename(slave_stats_temp_file, slave_stats_file);
+}
+
+
+static char sHDB[] = "HDBGET:";
+static char *realm;
+static int version_flag;
+static int help_flag;
+static char *keytab_str = sHDB;
+static char *database;
+static char *config_file;
+static char *port_str;
+static int detach_from_console;
+static int daemon_child = -1;
+
+static struct getargs args[] = {
+ { "config-file", 'c', arg_string, &config_file, NULL, NULL },
+ { "realm", 'r', arg_string, &realm, NULL, NULL },
+ { "keytab", 'k', arg_string, &keytab_str,
+ "keytab to get authentication from", "kspec" },
+ { "database", 'd', arg_string, &database, "database", "file"},
+ { "slave-stats-file", 0, arg_string, rk_UNCONST(&slave_stats_file),
+ "file for slave status information", "file"},
+ { "time-missing", 0, arg_string, rk_UNCONST(&slave_time_missing),
+ "time before slave is polled for presence", "time"},
+ { "time-gone", 0, arg_string, rk_UNCONST(&slave_time_gone),
+ "time of inactivity after which a slave is considered gone", "time"},
+ { "port", 0, arg_string, &port_str,
+ "port ipropd will listen to", "port"},
+ { "detach", 0, arg_flag, &detach_from_console,
+ "detach from console", NULL },
+ { "daemon-child", 0, arg_integer, &daemon_child,
+ "private argument, do not use", NULL },
+ { "pidfile-basename", 0, arg_string, &pidfile_basename,
+ "basename of pidfile; private argument for testing", "NAME" },
+ { "hostname", 0, arg_string, rk_UNCONST(&master_hostname),
+ "hostname of master (if not same as hostname)", "hostname" },
+ { "verbose", 0, arg_flag, &verbose, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ void *kadm_handle;
+ kadm5_server_context *server_context;
+ kadm5_config_params conf;
+ krb5_socket_t signal_fd, listen_fd;
+ int log_fd;
+ slave *slaves = NULL;
+ uint32_t current_version = 0, old_version = 0;
+ krb5_keytab keytab;
+ char **files;
+ int aret;
+ int optidx = 0;
+ int restarter_fd = -1;
+ struct stat st;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, num_args, argc, argv, &optidx))
+ krb5_std_usage(1, args, num_args);
+
+ if (help_flag)
+ krb5_std_usage(0, args, num_args);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ memset(hostname, 0, sizeof(hostname));
+
+ if (master_hostname &&
+ strlcpy(hostname, master_hostname,
+ sizeof(hostname)) >= sizeof(hostname)) {
+ errx(1, "Hostname too long: %s", master_hostname);
+ } else if (master_hostname == NULL) {
+ if (gethostname(hostname, sizeof(hostname)) == -1)
+ err(1, "Could not get hostname");
+ if (hostname[sizeof(hostname) - 1] != '\0')
+ errx(1, "Hostname too long %.*s...",
+ (int)sizeof(hostname), hostname);
+ }
+
+ if (detach_from_console && daemon_child == -1)
+ daemon_child = roken_detach_prep(argc, argv, "--daemon-child");
+ rk_pidfile(pidfile_basename);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
+ setup_signal();
+
+ if (config_file == NULL) {
+ aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1 || config_file == NULL)
+ errx(1, "out of memory");
+ }
+
+ ret = krb5_prepend_config_files_default(config_file, &files);
+ if (ret)
+ krb5_err(context, 1, ret, "getting configuration files");
+
+ ret = krb5_set_config_files(context, files);
+ krb5_free_config_files(files);
+ if (ret)
+ krb5_err(context, 1, ret, "reading configuration files");
+
+ init_stats_names(context);
+
+ time_before_gone = parse_time (slave_time_gone, "s");
+ if (time_before_gone < 0)
+ krb5_errx (context, 1, "couldn't parse time: %s", slave_time_gone);
+ time_before_missing = parse_time (slave_time_missing, "s");
+ if (time_before_missing < 0)
+ krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing);
+
+ krb5_openlog(context, "ipropd-master", &log_facility);
+ krb5_set_warn_dest(context, log_facility);
+
+ ret = krb5_kt_register(context, &hdb_get_kt_ops);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_kt_register");
+
+ ret = krb5_kt_resolve(context, keytab_str, &keytab);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve: %s", keytab_str);
+
+ memset(&conf, 0, sizeof(conf));
+ if(realm) {
+ conf.mask |= KADM5_CONFIG_REALM;
+ conf.realm = realm;
+ }
+ ret = kadm5_init_with_skey_ctx (context,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+ if (ret)
+ krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
+
+ server_context = (kadm5_server_context *)kadm_handle;
+
+ log_fd = open (server_context->log_context.log_file, O_RDONLY, 0);
+ if (log_fd < 0)
+ krb5_err (context, 1, errno, "open %s",
+ server_context->log_context.log_file);
+
+ if (fstat(log_fd, &st) == -1)
+ krb5_err(context, 1, errno, "stat %s",
+ server_context->log_context.log_file);
+
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, 1, errno, "shared flock %s",
+ server_context->log_context.log_file);
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, NULL);
+ flock(log_fd, LOCK_UN);
+
+ signal_fd = make_signal_socket (context);
+ listen_fd = make_listen_socket (context, port_str);
+
+ krb5_warnx(context, "ipropd-master started at version: %lu",
+ (unsigned long)current_version);
+
+ roken_detach_finish(NULL, daemon_child);
+ restarter_fd = restarter(context, NULL);
+
+ while (exit_flag == 0){
+ slave *p;
+ fd_set readset, writeset;
+ int max_fd = 0;
+ struct timeval to = {30, 0};
+ uint32_t vers;
+ struct stat st2;;
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE ||
+ restarter_fd >= FD_SETSIZE)
+ krb5_errx (context, IPROPD_RESTART, "fd too large");
+#endif
+
+ FD_ZERO(&readset);
+ FD_ZERO(&writeset);
+ FD_SET(signal_fd, &readset);
+ max_fd = max(max_fd, signal_fd);
+ FD_SET(listen_fd, &readset);
+ max_fd = max(max_fd, listen_fd);
+ if (restarter_fd > -1) {
+ FD_SET(restarter_fd, &readset);
+ max_fd = max(max_fd, restarter_fd);
+ }
+
+ for (p = slaves; p != NULL; p = p->next) {
+ if (p->flags & SLAVE_F_DEAD)
+ continue;
+ FD_SET(p->fd, &readset);
+ if (have_tail(p) || more_diffs(p))
+ FD_SET(p->fd, &writeset);
+ max_fd = max(max_fd, p->fd);
+ }
+
+ ret = select(max_fd + 1, &readset, &writeset, NULL, &to);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ krb5_err (context, IPROPD_RESTART, errno, "select");
+ }
+
+ if (stat(server_context->log_context.log_file, &st2) == -1) {
+ krb5_warn(context, errno, "could not stat log file by path");
+ st2 = st;
+ }
+
+ if (st2.st_dev != st.st_dev || st2.st_ino != st.st_ino) {
+ (void) close(log_fd);
+
+ log_fd = open(server_context->log_context.log_file, O_RDONLY, 0);
+ if (log_fd < 0)
+ krb5_err(context, IPROPD_RESTART_SLOW, errno, "open %s",
+ server_context->log_context.log_file);
+
+ if (fstat(log_fd, &st) == -1)
+ krb5_err(context, IPROPD_RESTART_SLOW, errno, "stat %s",
+ server_context->log_context.log_file);
+
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, IPROPD_RESTART, errno, "shared flock %s",
+ server_context->log_context.log_file);
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, NULL);
+ flock(log_fd, LOCK_UN);
+ }
+
+ if (ret == 0) {
+ /* Recover from failed transactions */
+ if (kadm5_log_init_nb(server_context) == 0)
+ kadm5_log_end(server_context);
+
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, IPROPD_RESTART, errno,
+ "could not lock log file");
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, NULL);
+ flock(log_fd, LOCK_UN);
+
+ if (current_version > old_version) {
+ if (verbose)
+ krb5_warnx(context,
+ "Missed a signal, updating slaves %lu to %lu",
+ (unsigned long)old_version,
+ (unsigned long)current_version);
+ for (p = slaves; p != NULL; p = p->next) {
+ if (p->flags & SLAVE_F_DEAD)
+ continue;
+ send_diffs(server_context, p, log_fd, database,
+ current_version);
+ }
+ old_version = current_version;
+ }
+ }
+
+ if (ret && FD_ISSET(restarter_fd, &readset)) {
+ exit_flag = SIGTERM;
+ break;
+ }
+
+ if (ret && FD_ISSET(signal_fd, &readset)) {
+#ifndef NO_UNIX_SOCKETS
+ struct sockaddr_un peer_addr;
+#else
+ struct sockaddr_storage peer_addr;
+#endif
+ socklen_t peer_len = sizeof(peer_addr);
+
+ if(recvfrom(signal_fd, (void *)&vers, sizeof(vers), 0,
+ (struct sockaddr *)&peer_addr, &peer_len) < 0) {
+ krb5_warn (context, errno, "recvfrom");
+ continue;
+ }
+ --ret;
+ assert(ret >= 0);
+ old_version = current_version;
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, IPROPD_RESTART, errno, "shared flock %s",
+ server_context->log_context.log_file);
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, NULL);
+ flock(log_fd, LOCK_UN);
+ if (current_version != old_version) {
+ /*
+ * If current_version < old_version then the log got
+ * truncated and we'll end up doing full propagations.
+ *
+ * Truncating the log when the current version is
+ * numerically small can lead to race conditions.
+ * Ideally we should identify log versions as
+ * {init_or_trunc_time, vno}, then we could not have any
+ * such race conditions, but this would either require
+ * breaking backwards compatibility for the protocol or
+ * adding new messages to it.
+ */
+ if (verbose)
+ krb5_warnx(context,
+ "Got a signal, updating slaves %lu to %lu",
+ (unsigned long)old_version,
+ (unsigned long)current_version);
+ for (p = slaves; p != NULL; p = p->next) {
+ if (p->flags & SLAVE_F_DEAD)
+ continue;
+ send_diffs(server_context, p, log_fd, database,
+ current_version);
+ }
+ } else {
+ if (verbose)
+ krb5_warnx(context,
+ "Got a signal, but no update in log version %lu",
+ (unsigned long)current_version);
+ }
+ }
+
+ for (p = slaves; p != NULL; p = p->next) {
+ if (!(p->flags & SLAVE_F_DEAD) &&
+ FD_ISSET(p->fd, &writeset) &&
+ ((have_tail(p) && send_tail(context, p) == 0) ||
+ (!have_tail(p) && more_diffs(p)))) {
+ send_diffs(server_context, p, log_fd, database,
+ current_version);
+ }
+ }
+
+ for(p = slaves; p != NULL; p = p->next) {
+ if (p->flags & SLAVE_F_DEAD)
+ continue;
+ if (ret && FD_ISSET(p->fd, &readset)) {
+ --ret;
+ assert(ret >= 0);
+ ret = process_msg(server_context, p, log_fd, database,
+ current_version);
+ if (ret && ret != EWOULDBLOCK)
+ slave_dead(context, p);
+ } else if (slave_gone_p (p))
+ slave_dead(context, p);
+ else if (slave_missing_p (p))
+ send_are_you_there (context, p);
+ }
+
+ if (ret && FD_ISSET(listen_fd, &readset)) {
+ add_slave (context, keytab, &slaves, listen_fd);
+ --ret;
+ assert(ret >= 0);
+ }
+ write_stats(context, slaves, current_version);
+ }
+
+ if(exit_flag == SIGINT || exit_flag == SIGTERM)
+ krb5_warnx(context, "%s terminated", getprogname());
+#ifdef SIGXCPU
+ else if(exit_flag == SIGXCPU)
+ krb5_warnx(context, "%s CPU time limit exceeded", getprogname());
+#endif
+ else
+ krb5_warnx(context, "%s unexpected exit reason: %ld",
+ getprogname(), (long)exit_flag);
+
+ write_master_down(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/ipropd_slave.c b/third_party/heimdal/lib/kadm5/ipropd_slave.c
new file mode 100644
index 0000000..e572bff
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/ipropd_slave.c
@@ -0,0 +1,1186 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "iprop.h"
+
+RCSID("$Id$");
+
+static const char *config_name = "ipropd-slave";
+
+static int verbose;
+static int async_hdb = 0;
+static int no_keytab_flag;
+static char *ccache_str;
+static char *keytab_str;
+
+static krb5_log_facility *log_facility;
+static char five_min[] = "5 min";
+static char *server_time_lost = five_min;
+static int time_before_lost;
+static const char *slave_str;
+static const char *pidfile_basename;
+static char *realm;
+
+static int
+connect_to_master (krb5_context context, const char *master,
+ const char *port_str)
+{
+ char port[NI_MAXSERV];
+ struct addrinfo *ai, *a;
+ struct addrinfo hints;
+ int error;
+ int one = 1;
+ int s = -1;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+
+ if (port_str == NULL) {
+ snprintf(port, sizeof(port), "%u", IPROP_PORT);
+ port_str = port;
+ }
+
+ error = getaddrinfo(master, port_str, &hints, &ai);
+ if (error) {
+ krb5_warnx(context, "Failed to get address of to %s: %s",
+ master, gai_strerror(error));
+ return -1;
+ }
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ char node[NI_MAXHOST];
+ error = getnameinfo(a->ai_addr, a->ai_addrlen,
+ node, sizeof(node), NULL, 0, NI_NUMERICHOST);
+ if (error)
+ strlcpy(node, "[unknown-addr]", sizeof(node));
+
+ s = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ if (s < 0)
+ continue;
+ if (connect(s, a->ai_addr, a->ai_addrlen) < 0) {
+ krb5_warn(context, errno, "connection failed to %s[%s]",
+ master, node);
+ close(s);
+ continue;
+ }
+ krb5_warnx(context, "connection successful "
+ "to master: %s[%s]", master, node);
+ break;
+ }
+ freeaddrinfo(ai);
+
+ if (a == NULL)
+ return -1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0)
+ krb5_warn(context, errno, "setsockopt(SO_KEEPALIVE) failed");
+
+ /*
+ * We write message lengths separately from the payload, avoid Nagle
+ * delays.
+ */
+#if defined(IPPROTO_TCP) && defined(TCP_NODELAY)
+ (void) setsockopt(s, IPPROTO_TCP, TCP_NODELAY,
+ (void *)&one, sizeof(one));
+#endif
+
+ return s;
+}
+
+static void
+get_creds(krb5_context context, krb5_ccache *cache, const char *serverhost)
+{
+ krb5_keytab keytab;
+ krb5_principal client;
+ krb5_error_code ret;
+ krb5_get_init_creds_opt *init_opts;
+ krb5_creds creds;
+ char *server;
+ char keytab_buf[256];
+ int aret;
+
+ if (no_keytab_flag) {
+ /* We're using an externally refreshed ccache */
+ if (*cache == NULL) {
+ if (ccache_str == NULL)
+ ret = krb5_cc_default(context, cache);
+ else
+ ret = krb5_cc_resolve(context, ccache_str, cache);
+ if (ret)
+ krb5_err(context, 1, ret, "Could not resolve the default cache");
+ }
+ return;
+ }
+
+ if (keytab_str == NULL) {
+ ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf));
+ if (ret == 0) {
+ keytab_str = keytab_buf;
+ } else {
+ krb5_warn(context, ret, "Using HDBGET: as the default keytab");
+ keytab_str = "HDBGET:";
+ }
+ }
+
+ if (*cache)
+ krb5_cc_destroy(context, *cache);
+ *cache = NULL;
+
+ ret = krb5_kt_resolve(context, keytab_str, &keytab);
+ if(ret)
+ krb5_err(context, 1, ret, "%s", keytab_str);
+
+ ret = krb5_sname_to_principal(context, slave_str, IPROP_NAME,
+ KRB5_NT_SRV_HST, &client);
+ if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal");
+ if (realm)
+ ret = krb5_principal_set_realm(context, client, realm);
+ if (ret) krb5_err(context, 1, ret, "krb5_principal_set_realm");
+
+ ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
+ if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+
+ aret = asprintf (&server, "%s/%s", IPROP_NAME, serverhost);
+ if (aret == -1 || server == NULL)
+ krb5_errx (context, 1, "malloc: no memory");
+
+ ret = krb5_get_init_creds_keytab(context, &creds, client, keytab,
+ 0, server, init_opts);
+ free (server);
+ krb5_get_init_creds_opt_free(context, init_opts);
+ if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
+
+ ret = krb5_kt_close(context, keytab);
+ if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
+
+ ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache);
+ if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique");
+
+ ret = krb5_cc_initialize(context, *cache, creds.client);
+ if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_store_cred(context, *cache, &creds);
+ if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+ krb5_free_cred_contents(context, &creds);
+ krb5_free_principal(context, client);
+}
+
+static krb5_error_code
+ihave(krb5_context context, krb5_auth_context auth_context,
+ int fd, uint32_t version)
+{
+ int ret;
+ u_char buf[8];
+ krb5_storage *sp;
+ krb5_data data;
+
+ sp = krb5_storage_from_mem(buf, 8);
+ if (sp == NULL)
+ krb5_err(context, IPROPD_RESTART_SLOW, ENOMEM, "Out of memory");
+ ret = krb5_store_uint32(sp, I_HAVE);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, version);
+ krb5_storage_free(sp);
+ data.length = 8;
+ data.data = buf;
+
+ if (ret == 0) {
+ if (verbose)
+ krb5_warnx(context, "telling master we are at %u", version);
+
+ ret = krb5_write_priv_message(context, auth_context, &fd, &data);
+ if (ret)
+ krb5_warn(context, ret, "krb5_write_message");
+ }
+ return ret;
+}
+
+#ifndef EDQUOT
+/* There's no EDQUOT on WIN32, for example */
+#define EDQUOT ENOSPC
+#endif
+
+static int
+append_to_log_file(krb5_context context,
+ kadm5_server_context *server_context,
+ krb5_storage *sp, off_t start, ssize_t slen)
+{
+ size_t len;
+ ssize_t sret;
+ off_t log_off;
+ int ret, ret2;
+ void *buf;
+
+ if (verbose)
+ krb5_warnx(context, "appending diffs to log");
+
+ if (slen == 0)
+ return 0;
+ if (slen < 0)
+ return EINVAL;
+ len = slen;
+ if (len != slen)
+ return EOVERFLOW;
+
+ buf = malloc(len);
+ if (buf == NULL && len != 0)
+ return krb5_enomem(context);
+
+ if (krb5_storage_seek(sp, start, SEEK_SET) != start) {
+ krb5_errx(context, IPROPD_RESTART,
+ "krb5_storage_seek() failed"); /* can't happen */
+ }
+ sret = krb5_storage_read(sp, buf, len);
+ if (sret < 0)
+ return errno;
+ if (len != (size_t)sret) {
+ /* Can't happen */
+ krb5_errx(context, IPROPD_RESTART,
+ "short krb5_storage_read() from memory buffer");
+ }
+ log_off = lseek(server_context->log_context.log_fd, 0, SEEK_CUR);
+ if (log_off == -1)
+ return errno;
+
+ /*
+ * Use net_write() so we get an errno if less that len bytes were
+ * written.
+ */
+ sret = net_write(server_context->log_context.log_fd, buf, len);
+ free(buf);
+ if (sret != slen)
+ ret = errno;
+ else
+ ret = fsync(server_context->log_context.log_fd);
+ if (ret == 0)
+ return 0;
+ krb5_warn(context, ret,
+ "Failed to write iprop log fd %d %llu bytes at offset %lld: %d",
+ server_context->log_context.log_fd, (unsigned long long)len,
+ (long long)log_off, ret);
+
+ /*
+ * Attempt to recover from this. First, truncate the log file
+ * and reset the fd offset. Failure to do this -> unlink the
+ * log file and re-create it. Since we're the slave, we ought to be
+ * able to recover from the log being unlinked...
+ */
+ if (ftruncate(server_context->log_context.log_fd, log_off) == -1 ||
+ lseek(server_context->log_context.log_fd, log_off, SEEK_SET) == -1) {
+ (void) kadm5_log_end(server_context);
+ if (unlink(server_context->log_context.log_file) == -1) {
+ krb5_err(context, IPROPD_FATAL, errno,
+ "Failed to recover from failure to write log "
+ "entries from master to disk");
+ }
+ ret2 = kadm5_log_init(server_context);
+ if (ret2) {
+ krb5_err(context, IPROPD_RESTART_SLOW, ret2,
+ "Failed to initialize log to recover from "
+ "failure to write log entries from master to disk");
+ }
+ }
+ if (ret == ENOSPC || ret == EDQUOT || ret == EFBIG) {
+ /* Unlink the file in these cases. */
+ krb5_warn(context, IPROPD_RESTART_SLOW,
+ "Failed to write log entries from master to disk");
+ (void) kadm5_log_end(server_context);
+ if (unlink(server_context->log_context.log_file) == -1) {
+ krb5_err(context, IPROPD_FATAL, errno,
+ "Failed to recover from failure to write log "
+ "entries from master to disk");
+ }
+ ret2 = kadm5_log_init(server_context);
+ if (ret2) {
+ krb5_err(context, IPROPD_RESTART_SLOW, ret2,
+ "Failed to initialize log to recover from "
+ "failure to write log entries from master to disk");
+ }
+ return ret;
+ }
+ /*
+ * All other errors we treat as fatal here. This includes, for
+ * example, EIO and EPIPE (sorry, can't log to pipes nor sockets).
+ */
+ krb5_err(context, IPROPD_FATAL, ret,
+ "Failed to write log entries from master to disk");
+}
+
+static int
+receive_loop(krb5_context context,
+ krb5_storage *sp,
+ kadm5_server_context *server_context)
+{
+ int ret;
+ off_t left, right, off;
+ uint32_t len, vers;
+
+ if (verbose)
+ krb5_warnx(context, "receiving diffs");
+
+ ret = kadm5_log_exclusivelock(server_context);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret,
+ "Failed to lock iprop log for writes");
+
+ /*
+ * Seek to the first entry in the message from the master that is
+ * past the current version of the local database.
+ */
+ do {
+ uint32_t timestamp;
+ uint32_t op;
+
+ if ((ret = krb5_ret_uint32(sp, &vers)) == HEIM_ERR_EOF) {
+ krb5_warnx(context, "master sent no new iprop entries");
+ return 0;
+ }
+
+ /*
+ * TODO We could do more to validate the entries from the master
+ * here. And we could use/reuse more kadm5_log_*() code here.
+ *
+ * Alternatively we should trust that the master sent us exactly
+ * what we needed and just write this to the log file and let
+ * kadm5_log_recover() do the rest.
+ */
+ if (ret || krb5_ret_uint32(sp, &timestamp) != 0 ||
+ krb5_ret_uint32(sp, &op) != 0 ||
+ krb5_ret_uint32(sp, &len) != 0) {
+
+ /*
+ * This shouldn't happen. Reconnecting probably won't help
+ * if it does happen, but by reconnecting we get a chance to
+ * connect to a new master if a new one is configured.
+ */
+ krb5_warnx(context, "iprop entries from master were truncated");
+ return EINVAL;
+ }
+ if (vers > server_context->log_context.version) {
+ break;
+ }
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (krb5_storage_seek(sp, len + 8, SEEK_CUR) != off + len + 8) {
+ krb5_warnx(context, "iprop entries from master were truncated");
+ return EINVAL;
+ }
+ if (verbose) {
+ krb5_warnx(context, "diff contains old log record version "
+ "%u %lld %u length %u",
+ vers, (long long)timestamp, op, len);
+ }
+ } while(vers <= server_context->log_context.version);
+
+ /*
+ * Read the remaining entries into memory...
+ */
+ /* SEEK_CUR is a header into the first entry we care about */
+ left = krb5_storage_seek(sp, -16, SEEK_CUR);
+ right = krb5_storage_seek(sp, 0, SEEK_END);
+ if (right - left < 24 + len) {
+ krb5_warnx(context, "iprop entries from master were truncated");
+ return EINVAL;
+ }
+
+ /*
+ * ...and then write them out to the on-disk log.
+ */
+
+ ret = append_to_log_file(context, server_context, sp, left, right - left);
+ if (ret)
+ return ret;
+
+ /*
+ * Replay the new entries.
+ */
+ if (verbose)
+ krb5_warnx(context, "replaying entries from master");
+ ret = kadm5_log_recover(server_context, kadm_recover_replay);
+ if (ret) {
+ krb5_warn(context, ret, "replay failed");
+ return ret;
+ }
+
+ ret = kadm5_log_get_version(server_context, &vers);
+ if (ret) {
+ krb5_warn(context, ret,
+ "could not get log version after applying diffs!");
+ return ret;
+ }
+ if (verbose)
+ krb5_warnx(context, "slave at version %u", vers);
+
+ if (vers != server_context->log_context.version) {
+ krb5_warnx(context, "slave's log_context version (%u) is "
+ "inconsistent with log's version (%u)",
+ server_context->log_context.version, vers);
+ }
+
+ return 0;
+}
+
+static int
+receive(krb5_context context,
+ krb5_storage *sp,
+ kadm5_server_context *server_context)
+{
+ krb5_error_code ret, ret2;
+ HDB *mydb = server_context->db;
+
+ ret = mydb->hdb_open(context, server_context->db, O_RDWR | O_CREAT, 0600);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->open");
+
+ (void) mydb->hdb_set_sync(context, mydb, !async_hdb);
+ ret2 = receive_loop(context, sp, server_context);
+ if (ret2)
+ krb5_warn(context, ret2, "receive from ipropd-master had errors");
+
+ ret = mydb->hdb_close(context, server_context->db);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
+
+ (void) kadm5_log_sharedlock(server_context);
+ if (verbose)
+ krb5_warnx(context, "downgraded iprop log lock to shared");
+ kadm5_log_signal_master(server_context);
+ if (verbose)
+ krb5_warnx(context, "signaled master for hierarchical iprop");
+ return ret2;
+}
+
+static void
+send_im_here(krb5_context context, int fd,
+ krb5_auth_context auth_context)
+{
+ krb5_storage *sp;
+ krb5_data data;
+ krb5_error_code ret;
+
+ ret = krb5_data_alloc(&data, 4);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret, "send_im_here");
+
+ sp = krb5_storage_from_data (&data);
+ if (sp == NULL)
+ krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_data");
+ ret = krb5_store_uint32(sp, I_AM_HERE);
+ krb5_storage_free(sp);
+
+ if (ret == 0) {
+ ret = krb5_write_priv_message(context, auth_context, &fd, &data);
+ krb5_data_free(&data);
+
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret, "krb5_write_priv_message");
+
+ if (verbose)
+ krb5_warnx(context, "pinged master");
+ }
+
+ return;
+}
+
+static void
+reinit_log(krb5_context context,
+ kadm5_server_context *server_context,
+ uint32_t vno)
+{
+ krb5_error_code ret;
+
+ if (verbose)
+ krb5_warnx(context, "truncating log on slave");
+
+ ret = kadm5_log_reinit(server_context, vno);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "kadm5_log_reinit");
+ (void) kadm5_log_sharedlock(server_context);
+ if (verbose)
+ krb5_warnx(context, "downgraded iprop log lock to shared");
+}
+
+
+static krb5_error_code
+receive_everything(krb5_context context, int fd,
+ kadm5_server_context *server_context,
+ krb5_auth_context auth_context)
+{
+ int ret;
+ krb5_data data;
+ uint32_t vno = 0;
+ uint32_t opcode;
+ krb5_storage *sp;
+
+ char *dbname;
+ HDB *mydb;
+
+ krb5_warnx(context, "receive complete database");
+
+ ret = kadm5_log_exclusivelock(server_context);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret,
+ "Failed to lock iprop log for writes");
+ if (server_context->db->hdb_method_name) {
+ ret = asprintf(&dbname, "%.*s:%s-NEW",
+ (int) strlen(server_context->db->hdb_method_name) - 1,
+ server_context->db->hdb_method_name,
+ server_context->db->hdb_name);
+ } else {
+ ret = asprintf(&dbname, "%s-NEW", server_context->db->hdb_name);
+ }
+ if (ret == -1)
+ krb5_err(context, IPROPD_RESTART, ENOMEM, "asprintf");
+ ret = hdb_create(context, &mydb, dbname);
+ if(ret)
+ krb5_err(context, IPROPD_RESTART, ret, "hdb_create");
+ free(dbname);
+
+ ret = hdb_set_master_keyfile(context,
+ mydb, server_context->config.stash_file);
+ if(ret)
+ krb5_err(context, IPROPD_RESTART, ret, "hdb_set_master_keyfile");
+
+ /* I really want to use O_EXCL here, but given that I can't easily clean
+ up on error, I won't */
+ ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret, "db->open");
+
+ (void) mydb->hdb_set_sync(context, mydb, 0);
+
+ sp = NULL;
+ krb5_data_zero(&data);
+ do {
+ ret = krb5_read_priv_message(context, auth_context, &fd, &data);
+
+ if (ret) {
+ krb5_warn(context, ret, "krb5_read_priv_message");
+ goto cleanup;
+ }
+
+ sp = krb5_storage_from_data(&data);
+ if (sp == NULL)
+ krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_data");
+ krb5_ret_uint32(sp, &opcode);
+ if (opcode == ONE_PRINC) {
+ krb5_data fake_data;
+ hdb_entry entry;
+
+ krb5_storage_free(sp);
+
+ fake_data.data = (char *)data.data + 4;
+ fake_data.length = data.length - 4;
+
+ memset(&entry, 0, sizeof(entry));
+
+ ret = hdb_value2entry(context, &fake_data, &entry);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret, "hdb_value2entry");
+ ret = mydb->hdb_store(server_context->context,
+ mydb,
+ 0, &entry);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "hdb_store");
+
+ hdb_free_entry(context, mydb, &entry);
+ krb5_data_free(&data);
+ } else if (opcode == NOW_YOU_HAVE)
+ ;
+ else
+ krb5_errx(context, 1, "strange opcode %d", opcode);
+ } while (opcode == ONE_PRINC);
+
+ if (opcode != NOW_YOU_HAVE)
+ krb5_errx(context, IPROPD_RESTART_SLOW,
+ "receive_everything: strange %d", opcode);
+
+ krb5_ret_uint32(sp, &vno);
+ krb5_storage_free(sp);
+
+ reinit_log(context, server_context, vno);
+
+ ret = mydb->hdb_set_sync(context, mydb, !async_hdb);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "failed to sync the received HDB");
+ ret = mydb->hdb_close(context, mydb);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
+
+ ret = mydb->hdb_rename(context, mydb, server_context->db->hdb_name);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->rename");
+
+
+ return 0;
+
+ cleanup:
+ krb5_data_free(&data);
+
+ if (ret)
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
+
+ ret = mydb->hdb_destroy(context, mydb);
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret, "db->destroy");
+
+ krb5_warnx(context, "receive complete database, version %ld", (long)vno);
+ return ret;
+}
+
+static void
+slave_status(krb5_context context,
+ const char *file,
+ const char *status, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+
+static void
+slave_status(krb5_context context,
+ const char *file,
+ const char *fmt, ...)
+{
+ char *status;
+ char *fmt2;
+ va_list args;
+ int len;
+
+ if (asprintf(&fmt2, "%s\n", fmt) == -1 || fmt2 == NULL) {
+ (void) unlink(file);
+ return;
+ }
+ va_start(args, fmt);
+ len = vasprintf(&status, fmt2, args);
+ free(fmt2);
+ va_end(args);
+ if (len < 0 || status == NULL) {
+ (void) unlink(file);
+ return;
+ }
+ rk_dumpdata(file, status, len);
+ krb5_warnx(context, "slave status change: %s", status);
+ free(status);
+}
+
+static void
+is_up_to_date(krb5_context context, const char *file,
+ kadm5_server_context *server_context)
+{
+ krb5_error_code ret;
+ char buf[80];
+ ret = krb5_format_time(context, time(NULL), buf, sizeof(buf), 1);
+ if (ret) {
+ unlink(file);
+ return;
+ }
+ slave_status(context, file, "up-to-date with version: %lu at %s",
+ (unsigned long)server_context->log_context.version, buf);
+}
+
+static char *database;
+static char *status_file;
+static char *config_file;
+static int version_flag;
+static int help_flag;
+static char *port_str;
+static int detach_from_console;
+static int daemon_child = -1;
+
+static struct getargs args[] = {
+ { "config-file", 'c', arg_string, &config_file, NULL, NULL },
+ { "realm", 'r', arg_string, &realm, NULL, NULL },
+ { "database", 'd', arg_string, &database, "database", "file"},
+ { "no-keytab", 0, arg_flag, &no_keytab_flag,
+ "use externally refreshed cache", NULL },
+ { "ccache", 0, arg_string, &ccache_str,
+ "client credentials", "CCACHE" },
+ { "keytab", 'k', arg_string, &keytab_str,
+ "client credentials keytab", "KEYTAB" },
+ { "time-lost", 0, arg_string, &server_time_lost,
+ "time before server is considered lost", "time" },
+ { "status-file", 0, arg_string, &status_file,
+ "file to write out status into", "file" },
+ { "port", 0, arg_string, &port_str,
+ "port ipropd-slave will connect to", "port"},
+ { "detach", 0, arg_flag, &detach_from_console,
+ "detach from console", NULL },
+ { "daemon-child", 0, arg_integer, &daemon_child,
+ "private argument, do not use", NULL },
+ { "pidfile-basename", 0, arg_string, &pidfile_basename,
+ "basename of pidfile; private argument for testing", "NAME" },
+ { "async-hdb", 'a', arg_flag, &async_hdb, NULL, NULL },
+ { "hostname", 0, arg_string, rk_UNCONST(&slave_str),
+ "hostname of slave (if not same as hostname)", "hostname" },
+ { "verbose", 0, arg_flag, &verbose, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+static void
+usage(int status)
+{
+ arg_printusage(args, num_args, NULL, "master");
+ exit(status);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret, ret2;
+ krb5_context context;
+ krb5_auth_context auth_context;
+ void *kadm_handle;
+ kadm5_server_context *server_context;
+ kadm5_config_params conf;
+ int master_fd;
+ krb5_ccache ccache = NULL;
+ krb5_principal server;
+ char **files;
+ int optidx = 0;
+ time_t reconnect_min;
+ time_t backoff;
+ time_t reconnect_max;
+ time_t reconnect;
+ time_t before = 0;
+ int restarter_fd = -1;
+
+ const char *master;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, num_args, argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage(0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (detach_from_console && daemon_child == -1)
+ daemon_child = roken_detach_prep(argc, argv, "--daemon-child");
+ rk_pidfile(pidfile_basename);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ setup_signal();
+
+ if (config_file == NULL) {
+ if (asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context)) == -1
+ || config_file == NULL)
+ errx(1, "out of memory");
+ }
+
+ ret = krb5_prepend_config_files_default(config_file, &files);
+ if (ret)
+ krb5_err(context, 1, ret, "getting configuration files");
+
+ ret = krb5_set_config_files(context, files);
+ krb5_free_config_files(files);
+ if (ret)
+ krb5_err(context, 1, ret, "reading configuration files");
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 1)
+ usage(1);
+
+ master = argv[0];
+
+ if (status_file == NULL) {
+ if (asprintf(&status_file, "%s/ipropd-slave-status", hdb_db_dir(context)) < 0 || status_file == NULL)
+ krb5_errx(context, 1, "can't allocate status file buffer");
+ }
+
+ krb5_openlog(context, "ipropd-slave", &log_facility);
+ krb5_set_warn_dest(context, log_facility);
+
+ slave_status(context, status_file, "bootstrapping");
+
+ ret = krb5_kt_register(context, &hdb_get_kt_ops);
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_kt_register");
+
+ time_before_lost = parse_time (server_time_lost, "s");
+ if (time_before_lost < 0)
+ krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost);
+
+ slave_status(context, status_file, "getting credentials from keytab/database");
+
+ memset(&conf, 0, sizeof(conf));
+ if(realm) {
+ conf.mask |= KADM5_CONFIG_REALM;
+ conf.realm = realm;
+ }
+ if (database) {
+ conf.mask |= KADM5_CONFIG_DBNAME;
+ conf.dbname = database;
+ }
+ ret = kadm5_init_with_password_ctx (context,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+ if (ret)
+ krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
+
+ server_context = (kadm5_server_context *)kadm_handle;
+
+ slave_status(context, status_file, "creating log file");
+
+ ret = server_context->db->hdb_open(context,
+ server_context->db,
+ O_RDWR | O_CREAT, 0600);
+ if (ret)
+ krb5_err (context, 1, ret, "db->open");
+
+ ret = kadm5_log_init(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init");
+ (void) kadm5_log_sharedlock(server_context);
+ if (verbose)
+ krb5_warnx(context, "downgraded iprop log lock to shared");
+
+ ret = server_context->db->hdb_close(context, server_context->db);
+ if (ret)
+ krb5_err(context, 1, ret, "db->close");
+
+ get_creds(context, &ccache, master);
+
+ ret = krb5_sname_to_principal (context, master, IPROP_NAME,
+ KRB5_NT_SRV_HST, &server);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_sname_to_principal");
+
+ auth_context = NULL;
+ master_fd = -1;
+
+ krb5_appdefault_time(context, config_name, NULL, "reconnect-min",
+ 10, &reconnect_min);
+ krb5_appdefault_time(context, config_name, NULL, "reconnect-max",
+ 300, &reconnect_max);
+ krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff",
+ 10, &backoff);
+ reconnect = reconnect_min;
+
+ slave_status(context, status_file, "ipropd-slave started");
+
+ roken_detach_finish(NULL, daemon_child);
+ restarter_fd = restarter(context, NULL);
+
+ while (!exit_flag) {
+ struct timeval to;
+ time_t now, elapsed;
+ fd_set readset;
+ int connected = FALSE;
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ if (restarter_fd >= FD_SETSIZE)
+ krb5_errx(context, IPROPD_RESTART, "fd too large");
+#endif
+
+ FD_ZERO(&readset);
+ if (restarter_fd > -1)
+ FD_SET(restarter_fd, &readset);
+
+ now = time(NULL);
+ elapsed = now - before;
+
+ if (elapsed < reconnect) {
+ time_t left = reconnect - elapsed;
+ krb5_warnx(context, "sleeping %d seconds before "
+ "retrying to connect", (int)left);
+ to.tv_sec = left;
+ to.tv_usec = 0;
+ if (select(restarter_fd + 1, &readset, NULL, NULL, &to) == 1) {
+ exit_flag = SIGTERM;
+ continue;
+ }
+ }
+ before = now;
+
+ slave_status(context, status_file, "connecting to master: %s\n", master);
+
+ master_fd = connect_to_master (context, master, port_str);
+ if (master_fd < 0)
+ goto retry;
+
+ reconnect = reconnect_min;
+
+ if (auth_context) {
+ krb5_auth_con_free(context, auth_context);
+ auth_context = NULL;
+ }
+ get_creds(context, &ccache, master);
+ if (verbose)
+ krb5_warnx(context, "authenticating to master");
+ ret = krb5_sendauth (context, &auth_context, &master_fd,
+ IPROP_VERSION, NULL, server,
+ AP_OPTS_MUTUAL_REQUIRED, NULL, NULL,
+ ccache, NULL, NULL, NULL);
+ if (ret) {
+ krb5_warn (context, ret, "krb5_sendauth");
+ goto retry;
+ }
+
+ krb5_warnx(context, "ipropd-slave started at version: %ld",
+ (long)server_context->log_context.version);
+
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
+ if (ret)
+ goto retry;
+
+ connected = TRUE;
+
+ if (verbose)
+ krb5_warnx(context, "connected to master");
+
+ slave_status(context, status_file, "connected to master, waiting instructions");
+
+ while (connected && !exit_flag) {
+ krb5_data out;
+ krb5_storage *sp;
+ uint32_t tmp;
+ int max_fd;
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ if (master_fd >= FD_SETSIZE)
+ krb5_errx(context, IPROPD_RESTART, "fd too large");
+ if (restarter_fd >= FD_SETSIZE)
+ krb5_errx(context, IPROPD_RESTART, "fd too large");
+ max_fd = max(restarter_fd, master_fd);
+#endif
+
+ FD_ZERO(&readset);
+ FD_SET(master_fd, &readset);
+ if (restarter_fd != -1)
+ FD_SET(restarter_fd, &readset);
+
+ to.tv_sec = time_before_lost;
+ to.tv_usec = 0;
+
+ ret = select (max_fd + 1,
+ &readset, NULL, NULL, &to);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ krb5_err (context, 1, errno, "select");
+ }
+ if (ret == 0) {
+ krb5_warnx(context, "server didn't send a message "
+ "in %d seconds", time_before_lost);
+ connected = FALSE;
+ continue;
+ }
+
+ if (restarter_fd > -1 && FD_ISSET(restarter_fd, &readset)) {
+ if (verbose)
+ krb5_warnx(context, "slave restarter exited");
+ exit_flag = SIGTERM;
+ }
+
+ if (!FD_ISSET(master_fd, &readset))
+ continue;
+
+ if (verbose)
+ krb5_warnx(context, "message from master");
+
+ ret = krb5_read_priv_message(context, auth_context, &master_fd, &out);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_read_priv_message");
+ connected = FALSE;
+ continue;
+ }
+
+ sp = krb5_storage_from_mem (out.data, out.length);
+ if (sp == NULL)
+ krb5_err(context, IPROPD_RESTART, errno, "krb5_storage_from_mem");
+ ret = krb5_ret_uint32(sp, &tmp);
+ if (ret == HEIM_ERR_EOF) {
+ krb5_warn(context, ret, "master sent zero-length message");
+ connected = FALSE;
+ continue;
+ }
+ if (ret != 0) {
+ krb5_warn(context, ret, "couldn't read master's message");
+ connected = FALSE;
+ continue;
+ }
+
+ /*
+ * It's unclear why we open th HDB and call kadm5_log_init() here.
+ *
+ * We don't need it to process the log entries we receive in the
+ * FOR_YOU case: we already call kadm5_log_recover() in receive() /
+ * receive_loop(). Maybe it's just just in case, though at the
+ * cost of synchronization with ipropd-master if we're running one
+ * for hierarchical iprop.
+ */
+ ret = server_context->db->hdb_open(context,
+ server_context->db,
+ O_RDWR | O_CREAT, 0600);
+ if (ret)
+ krb5_err (context, 1, ret, "db->open while handling a "
+ "message from the master");
+ ret = kadm5_log_init(server_context);
+ if (ret) {
+ krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while "
+ "handling a message from the master");
+ }
+ (void) kadm5_log_sharedlock(server_context);
+ if (verbose)
+ krb5_warnx(context, "downgraded iprop log lock to shared");
+
+ ret = server_context->db->hdb_close (context, server_context->db);
+ if (ret)
+ krb5_err (context, 1, ret, "db->close while handling a "
+ "message from the master");
+
+ switch (tmp) {
+ case FOR_YOU :
+ if (verbose)
+ krb5_warnx(context, "master sent us diffs");
+ ret2 = receive(context, sp, server_context);
+ if (ret2)
+ krb5_warn(context, ret2,
+ "receive from ipropd-master had errors");
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
+ if (ret || ret2)
+ connected = FALSE;
+
+ /*
+ * If it returns an error, receive() may nonetheless
+ * have committed some entries successfully, so we must
+ * update the slave_status even if there were errors.
+ */
+ is_up_to_date(context, status_file, server_context);
+ break;
+ case TELL_YOU_EVERYTHING :
+ if (verbose)
+ krb5_warnx(context, "master sent us a full dump");
+ ret = receive_everything(context, master_fd, server_context,
+ auth_context);
+ (void) kadm5_log_sharedlock(server_context);
+ if (ret == 0) {
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
+ }
+ if (ret)
+ connected = FALSE;
+ else
+ is_up_to_date(context, status_file, server_context);
+ if (verbose)
+ krb5_warnx(context, "downgraded iprop log lock to shared");
+ kadm5_log_signal_master(server_context);
+ if (verbose)
+ krb5_warnx(context, "signaled master for hierarchical iprop");
+ break;
+ case ARE_YOU_THERE :
+ if (verbose)
+ krb5_warnx(context, "master sent us a ping");
+ is_up_to_date(context, status_file, server_context);
+ /*
+ * We used to send an I_HAVE here. But the master may send
+ * ARE_YOU_THERE messages in response to local, possibly-
+ * transient errors, and if that happens and we respond with an
+ * I_HAVE then we'll loop hard if the error was not transient.
+ *
+ * So we don't ihave() here.
+ */
+ send_im_here(context, master_fd, auth_context);
+ break;
+ case YOU_HAVE_LAST_VERSION:
+ if (verbose)
+ krb5_warnx(context, "master tells us we are up to date");
+ is_up_to_date(context, status_file, server_context);
+ break;
+ case NOW_YOU_HAVE :
+ case I_HAVE :
+ case ONE_PRINC :
+ case I_AM_HERE :
+ default :
+ krb5_warnx (context, "Ignoring command %d", tmp);
+ break;
+ }
+ krb5_storage_free (sp);
+ krb5_data_free (&out);
+
+ }
+
+ slave_status(context, status_file, "disconnected from master");
+ retry:
+ if (connected == FALSE)
+ krb5_warnx (context, "disconnected for server");
+
+ if (exit_flag)
+ krb5_warnx (context, "got an exit signal");
+
+ if (master_fd >= 0)
+ close(master_fd);
+
+ reconnect += backoff;
+ if (reconnect > reconnect_max) {
+ slave_status(context, status_file, "disconnected from master for a long time");
+ reconnect = reconnect_max;
+ }
+ }
+
+ if (status_file) {
+ /* XXX It'd be better to leave it saying we're not here */
+ unlink(status_file);
+ }
+
+ if (0);
+#ifndef NO_SIGXCPU
+ else if(exit_flag == SIGXCPU)
+ krb5_warnx(context, "%s CPU time limit exceeded", getprogname());
+#endif
+ else if(exit_flag == SIGINT || exit_flag == SIGTERM)
+ krb5_warnx(context, "%s terminated", getprogname());
+ else
+ krb5_warnx(context, "%s unexpected exit reason: %ld",
+ getprogname(), (long)exit_flag);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/kadm5-hook.h b/third_party/heimdal/lib/kadm5/kadm5-hook.h
new file mode 100644
index 0000000..7d47f55
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/kadm5-hook.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2010
+ * The Board of Trustees of the Leland Stanford Junior University
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef KADM5_HOOK_H
+#define KADM5_HOOK_H 1
+
+#define KADM5_HOOK_VERSION_V1 1
+
+#include <heimbase-svc.h>
+
+/*
+ * Each hook is called before the operation using KADM5_STAGE_PRECOMMIT and
+ * then after the operation using KADM5_STAGE_POSTCOMMIT. If the hook returns
+ * failure during precommit, the operation is aborted without changes to the
+ * database. All post-commit hook are invoked if the operation was attempted.
+ *
+ * Note that unlike libkrb5 plugins, returning success does not prevent other
+ * plugins being called (i.e. it is equivalent to KRB5_PLUGIN_NO_HANDLE).
+ */
+enum kadm5_hook_stage {
+ KADM5_HOOK_STAGE_PRECOMMIT,
+ KADM5_HOOK_STAGE_POSTCOMMIT
+};
+
+#define KADM5_HOOK_FLAG_KEEPOLD 0x1 /* keep old password */
+#define KADM5_HOOK_FLAG_CONDITIONAL 0x2 /* only change password if different */
+
+typedef struct kadm5_hook_ftable {
+ HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context);
+
+ const char *name;
+ const char *vendor;
+
+ /*
+ * Hook functions; NULL functions are ignored. code is only valid on
+ * post-commit hooks and represents the result of the commit. Post-
+ * commit hooks are not called if a pre-commit hook aborted the call.
+ */
+ krb5_error_code (KRB5_CALLCONV *chpass)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password);
+
+ krb5_error_code (KRB5_CALLCONV *chpass_with_key)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_key_data,
+ krb5_key_data *key_data);
+
+ krb5_error_code (KRB5_CALLCONV *create)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ kadm5_principal_ent_t ent,
+ uint32_t mask,
+ const char *password);
+
+ krb5_error_code (KRB5_CALLCONV *modify)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ kadm5_principal_ent_t ent,
+ uint32_t mask);
+
+ krb5_error_code (KRB5_CALLCONV *delete)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ);
+
+ krb5_error_code (KRB5_CALLCONV *randkey)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ);
+
+ krb5_error_code (KRB5_CALLCONV *rename)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal source,
+ krb5_const_principal target);
+
+ krb5_error_code (KRB5_CALLCONV *set_keys)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ size_t n_keys,
+ krb5_keyblock *keyblocks);
+
+ krb5_error_code (KRB5_CALLCONV *prune)(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ int kvno);
+
+} kadm5_hook_ftable;
+
+/*
+ * libkadm5srv expects a symbol named kadm5_hook_plugin_load that must be a
+ * function of type kadm5_hook_plugin_load_t.
+ */
+typedef krb5_error_code
+(KRB5_CALLCONV *kadm5_hook_plugin_load_t)(krb5_context context,
+ krb5_get_instance_func_t *func,
+ size_t *n_hooks,
+ const kadm5_hook_ftable *const **hooks);
+
+#endif /* !KADM5_HOOK_H */
diff --git a/third_party/heimdal/lib/kadm5/kadm5-pwcheck.h b/third_party/heimdal/lib/kadm5/kadm5-pwcheck.h
new file mode 100644
index 0000000..70cbae5
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/kadm5-pwcheck.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef KADM5_PWCHECK_H
+#define KADM5_PWCHECK_H 1
+
+
+#define KADM5_PASSWD_VERSION_V0 0
+#define KADM5_PASSWD_VERSION_V1 1
+
+typedef const char* (*kadm5_passwd_quality_check_func_v0)(krb5_context,
+ krb5_principal,
+ krb5_data*);
+
+/*
+ * The 4th argument, is a tuning parameter for the quality check
+ * function, the lib/caller will providing it for the password quality
+ * module.
+ */
+
+typedef int
+(*kadm5_passwd_quality_check_func)(krb5_context context,
+ krb5_principal principal,
+ krb5_data *password,
+ const char *tuning,
+ char *message,
+ size_t length);
+
+struct kadm5_pw_policy_check_func {
+ const char *name;
+ kadm5_passwd_quality_check_func func;
+};
+
+struct kadm5_pw_policy_verifier {
+ const char *name;
+ int version;
+ const char *vendor;
+ const struct kadm5_pw_policy_check_func *funcs;
+};
+
+#endif /* KADM5_PWCHECK_H */
diff --git a/third_party/heimdal/lib/kadm5/kadm5_err.et b/third_party/heimdal/lib/kadm5/kadm5_err.et
new file mode 100644
index 0000000..e9a3a0b
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/kadm5_err.et
@@ -0,0 +1,114 @@
+#
+# Error messages for the kadm5 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id$"
+
+error_table ovk kadm5
+
+prefix KADM5
+error_code FAILURE, "Operation failed for unspecified reason"
+error_code AUTH_GET, "Operation requires `get' privilege"
+error_code AUTH_ADD, "Operation requires `add' privilege"
+error_code AUTH_MODIFY, "Operation requires `modify' privilege"
+error_code AUTH_DELETE, "Operation requires `delete' privilege"
+error_code AUTH_INSUFFICIENT, "Insufficient authorization for operation"
+error_code BAD_DB, "Database inconsistency detected"
+error_code DUP, "Principal or policy already exists"
+error_code RPC_ERROR, "Communication failure with server"
+error_code NO_SRV, "No administration server found for realm"
+error_code BAD_HIST_KEY, "Password history principal key version mismatch"
+error_code NOT_INIT, "Connection to server not initialized"
+error_code UNK_PRINC, "Principal does not exist"
+error_code UNK_POLICY, "Policy does not exist"
+error_code BAD_MASK, "Invalid field mask for operation"
+error_code BAD_CLASS, "Invalid number of character classes"
+error_code BAD_LENGTH, "Invalid password length"
+error_code BAD_POLICY, "Invalid policy name"
+error_code BAD_PRINCIPAL, "Invalid principal name."
+error_code BAD_AUX_ATTR, "Invalid auxillary attributes"
+error_code BAD_HISTORY, "Invalid password history count"
+error_code BAD_MIN_PASS_LIFE, "Password minimum life is greater than password maximum life"
+error_code PASS_Q_TOOSHORT, "Password is too short"
+error_code PASS_Q_CLASS, "Password does not contain enough character classes"
+error_code PASS_Q_DICT, "Password is in the password dictionary"
+error_code PASS_REUSE, "Can't reuse password"
+error_code PASS_TOOSOON, "Current password's minimum life has not expired"
+error_code POLICY_REF, "Policy is in use"
+error_code INIT, "Connection to server already initialized"
+error_code BAD_PASSWORD, "Incorrect password"
+error_code PROTECT_PRINCIPAL, "Can't change protected principal"
+error_code BAD_SERVER_HANDLE, "Programmer error! Bad Admin server handle"
+error_code BAD_STRUCT_VERSION, "Programmer error! Bad API structure version"
+error_code OLD_STRUCT_VERSION, "API structure version specified by application is no longer supported"
+error_code NEW_STRUCT_VERSION, "API structure version specified by application is unknown to libraries"
+error_code BAD_API_VERSION, "Programmer error! Bad API version"
+error_code OLD_LIB_API_VERSION, "API version specified by application is no longer supported by libraries"
+error_code OLD_SERVER_API_VERSION,"API version specified by application is no longer supported by server"
+error_code NEW_LIB_API_VERSION, "API version specified by application is unknown to libraries"
+error_code NEW_SERVER_API_VERSION,"API version specified by application is unknown to server"
+error_code SECURE_PRINC_MISSING,"Database error! Required principal missing"
+error_code NO_RENAME_SALT, "The salt type of the specified principal does not support renaming"
+error_code BAD_CLIENT_PARAMS, "Invalid configuration parameter for remote KADM5 client"
+error_code BAD_SERVER_PARAMS, "Invalid configuration parameter for local KADM5 client."
+error_code AUTH_LIST, "Operation requires `list' privilege"
+error_code AUTH_CHANGEPW, "Operation requires `change-password' privilege"
+error_code BAD_TL_TYPE, "Invalid tagged data list element type"
+error_code MISSING_CONF_PARAMS, "Required parameters in kdc.conf missing"
+error_code BAD_SERVER_NAME, "Bad krb5 admin server hostname"
+error_code KS_TUPLE_NOSUPP, "Key/salt tuples not supported by this function"
+error_code SETKEY3_ETYPE_MISMATCH, "Key/salt tuples don't match keys"
+error_code DECRYPT_USAGE_NOSUPP, "Given usage of kadm5_decrypt() not supported"
+error_code POLICY_OP_NOSUPP, "Policy operations not supported"
+error_code KEEPOLD_NOSUPP, "Keep old keys option not supported"
+error_code AUTH_GET_KEYS, "Operation requires `get-keys' privilege"
+error_code ALREADY_LOCKED, "Database already locked"
+error_code NOT_LOCKED, "Database not locked"
+error_code LOG_CORRUPT, "Incremental propagation log got corrupted"
+error_code LOG_NEEDS_UPGRADE, "Incremental propagation log must be upgraded"
+error_code BAD_SERVER_HOOK, "Bad KADM5 server hook"
+error_code SERVER_HOOK_NOT_FOUND, "Cannot find KADM5 server hook"
+error_code OLD_SERVER_HOOK_VERSION, "KADM5 server hook is too old for this version of Heimdal"
+error_code NEW_SERVER_HOOK_VERSION, "KADM5 server hook is too new for this version of Heimdal"
+error_code READ_ONLY, "Database is read-only; try primary server"
+error_code PASS_Q_GENERIC, "Unspecified password quality failure"
+
+
+# MIT has:
+#
+# - GSS_ERROR sandwiched by AUTH_CHANGEPW and BAD_TL_TYPE
+# error_code GSS_ERROR, "GSS-API (or Kerberos) error"
+
+# - AUTH_SETKEY, SETKEY_DUP_ENCTYPES, and SETV4KEY_INVAL_ENCTYPE, sandwiched by
+# BAD_SERVER_NAME and SETKEY3_ETYPE_MISMATCH
+# error_code AUTH_SETKEY, "Operation requires ``set-key'' privilege"
+# error_code SETKEY_DUP_ENCTYPES, "Multiple values for single or folded enctype"
+# error_code SETV4KEY_INVAL_ENCTYPE, "Invalid enctype for setv4key"
+
+# - all of the following after SETKEY3_ETYPE_MISMATCH
+# error_code MISSING_KRB5_CONF_PARAMS, "Missing parameters in krb5.conf required for kadmin client"
+# error_code XDR_FAILURE, "XDR encoding error"
+# error_code CANT_RESOLVE, "Cannot resolve network address for admin server in requested realm"
+# error_code BAD_KEYSALTS, "Invalid key/salt tuples"
+# error_code SETKEY_BAD_KVNO, "Invalid multiple or duplicate kvnos in setkey operation"
+# error_code AUTH_EXTRACT, "Operation requires ``extract-keys'' privilege"
+# error_code PROTECT_KEYS, "Principal keys are locked down"
+# error_code AUTH_INITIAL, "Operation requires initial ticket"
+
+# AUTH_EXTRACT is the same as our AUTH_GET_KEYS
+# MISSING_KRB5_CONF_PARAMS is the same as our MISSING_CONF_PARAMS
+
+# We have a number of errors not in MIT:
+# - KS_TUPLE_NOSUPP (no longer relevant)
+# - DECRYPT_USAGE_NOSUPP (could be replaced with some other, no?)
+# - POLICY_OP_NOSUPP (could be made irrelevant)
+# - ALREADY_LOCKED (in MIT KDB locks are recursive)
+# - NOT_LOCKED (KRB5_KDB_NOTLOCKED in MIT)
+# - LOG_CORRUPT (unique to Heimdal)
+# - LOG_NEEDS_UPGRADE (unique to Heimdal)
+# - BAD_SERVER_HOOK (unique to Heimdal, not used in-tree)
+# - SERVER_HOOK_NOT_FOUND (unique to Heimdal, not used in-tree)
+# - OLD_SERVER_HOOK_VERSION (unique to Heimdal, not used in-tree)
+# - NEW_SERVER_HOOK_VERSION (unique to Heimdal, not used in-tree)
+# - READ_ONLY (should not be unique to Heimdal, but is)
diff --git a/third_party/heimdal/lib/kadm5/kadm5_locl.h b/third_party/heimdal/lib/kadm5/kadm5_locl.h
new file mode 100644
index 0000000..cb0b1b4
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/kadm5_locl.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1997-2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __KADM5_LOCL_H__
+#define __KADM5_LOCL_H__
+
+#include <config.h>
+#include <roken.h>
+#include <heimbase.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <limits.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_TCP_H
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <fnmatch.h>
+#include <krb5_locl.h>
+#include "admin.h"
+#include "kadm5_err.h"
+#include <hdb.h>
+#include <der.h>
+#include <parse_units.h>
+#include "private.h"
+
+#endif /* __KADM5_LOCL_H__ */
diff --git a/third_party/heimdal/lib/kadm5/kadm5_pwcheck.3 b/third_party/heimdal/lib/kadm5/kadm5_pwcheck.3
new file mode 100644
index 0000000..5174d9b
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/kadm5_pwcheck.3
@@ -0,0 +1,159 @@
+.\" Copyright (c) 2003 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd February 29, 2004
+.Dt KADM5_PWCHECK 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_pwcheck ,
+.Nm kadm5_setup_passwd_quality_check ,
+.Nm kadm5_add_passwd_quality_verifier ,
+.Nm kadm5_check_password_quality
+.Nd Heimdal warning and error functions
+.Sh LIBRARY
+Kerberos 5 Library (libkadm5srv, -lkadm5srv)
+.Sh SYNOPSIS
+.In kadm5-protos.h
+.In kadm5-pwcheck.h
+.Ft void
+.Fo kadm5_setup_passwd_quality_check
+.Fa "krb5_context context"
+.Fa "const char *check_library"
+.Fa "const char *check_function"
+.Fc
+.Ft "krb5_error_code"
+.Fo kadm5_add_passwd_quality_verifier
+.Fa "krb5_context context"
+.Fa "const char *check_library"
+.Fc
+.Ft "const char *"
+.Fo kadm5_check_password_quality
+.Fa "krb5_context context"
+.Fa "krb5_principal principal"
+.Fa "krb5_data *pwd_data"
+.Fc
+.Ft int
+.Fo "(*kadm5_passwd_quality_check_func)"
+.Fa "krb5_context context"
+.Fa "krb5_principal principal"
+.Fa "krb5_data *password"
+.Fa "const char *tuning"
+.Fa "char *message"
+.Fa "size_t length"
+.Fc
+.Sh DESCRIPTION
+These functions perform the quality check for the heimdal database
+library.
+.Pp
+There are two versions of the shared object API; the old version (0)
+is deprecated, but still supported. The new version (1) supports
+multiple password quality checking policies in the same shared object.
+See below for details.
+.Pp
+The password quality checker will run all policies that are
+configured by the user. If any policy rejects the password, the password
+will be rejected.
+.Pp
+Policy names are of the form
+.Ql module-name:policy-name
+or, if the the policy name is unique enough, just
+.Ql policy-name .
+.Sh IMPLEMENTING A PASSWORD QUALITY CHECKING SHARED OBJECT
+(This refers to the version 1 API only.)
+.Pp
+Module shared objects may conveniently be compiled and linked with
+.Xr libtool 1 .
+An object needs to export a symbol called
+.Ql kadm5_password_verifier
+of the type
+.Ft "struct kadm5_pw_policy_verifier" .
+.Pp
+Its
+.Ft name
+and
+.Ft vendor
+fields should contain the obvious information.
+.Ft name
+must match the
+.Ql module-name
+portion of the policy name (the part before the colon), if the policy name
+contains a colon, or the policy will not be run.
+.Ft version
+should be
+.Dv KADM5_PASSWD_VERSION_V1 .
+.Pp
+.Ft funcs
+contains an array of
+.Ft "struct kadm5_pw_policy_check_func"
+structures that is terminated with an entry whose
+.Ft name
+component is
+.Dv NULL .
+The
+.Ft name
+field of the array must match the
+.Ql policy-name
+portion of a policy name (the part after the colon, or the complete policy
+name if there is no colon) specified by the user or the policy will not be
+run. The
+.Ft func
+fields of the array elements are functions that are exported by the
+module to be called to check the password. They get the following
+arguments: the Kerberos context, principal, password, a tuning parameter, and
+a pointer to a message buffer and its length. The tuning parameter
+for the quality check function is currently always
+.Dv NULL .
+If the password is acceptable, the function returns zero. Otherwise
+it returns non-zero and fills in the message buffer with an
+appropriate explanation.
+.Sh RUNNING THE CHECKS
+.Nm kadm5_setup_passwd_quality_check
+sets up type 0 checks. It sets up all type 0 checks defined in
+.Xr krb5.conf 5
+if called with the last two arguments null.
+.Pp
+.Nm kadm5_add_passwd_quality_verifier
+sets up type 1 checks. It sets up all type 1 tests defined in
+.Xr krb5.conf 5
+if called with a null second argument.
+.Nm kadm5_check_password_quality
+runs the checks in the order in which they are defined in
+.Xr krb5.conf 5
+and the order in which they occur in a
+module's
+.Ft funcs
+array until one returns non-zero.
+.Sh SEE ALSO
+.Xr libtool 1 ,
+.Xr krb5 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/kadm5/keys.c b/third_party/heimdal/lib/kadm5/keys.c
new file mode 100644
index 0000000..ed7cc83
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/keys.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * free all the memory used by (len, keys)
+ */
+
+void
+_kadm5_free_keys (krb5_context context,
+ int len, Key *keys)
+{
+ hdb_free_keys(context, len, keys);
+}
+
+/*
+ * null-ify `len', `keys'
+ */
+
+void
+_kadm5_init_keys (Key *keys, int len)
+{
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ keys[i].mkvno = NULL;
+ keys[i].salt = NULL;
+ keys[i].key.keyvalue.length = 0;
+ keys[i].key.keyvalue.data = NULL;
+ }
+}
+
+
+/*
+ * return 1 if any key in `keys1, len1' exists in `keys2, len2'
+ */
+static int
+_kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
+{
+ size_t i, j;
+ size_t optimize;
+
+ for (i = 0; i < len1; ++i) {
+ optimize = 0;
+ for (j = 0; j < len2; j++) {
+ if ((keys1[i].salt != NULL && keys2[j].salt == NULL)
+ || (keys1[i].salt == NULL && keys2[j].salt != NULL))
+ continue;
+
+ if (keys1[i].salt != NULL) {
+ if (keys1[i].salt->type != keys2[j].salt->type)
+ continue;
+ if (keys1[i].salt->salt.length != keys2[j].salt->salt.length)
+ continue;
+ if (memcmp (keys1[i].salt->salt.data, keys2[j].salt->salt.data,
+ keys1[i].salt->salt.length) != 0)
+ continue;
+ }
+ if (keys1[i].key.keytype != keys2[j].key.keytype)
+ continue;
+ optimize = 1;
+ if (keys1[i].key.keyvalue.length != keys2[j].key.keyvalue.length)
+ continue;
+ if (memcmp (keys1[i].key.keyvalue.data, keys2[j].key.keyvalue.data,
+ keys1[i].key.keyvalue.length) != 0)
+ continue;
+
+ return 1;
+ }
+
+ /*
+ * Optimization: no need to check all of keys1[] if one there
+ * was one key in keys2[] with matching enctype and salt but not
+ * matching key. Assumption: all keys in keys1[] and keys2[]
+ * are output by string2key.
+ */
+ if (optimize)
+ return 0;
+ }
+ return 0;
+}
+
+/*
+ * return 1 if any key in `keys1, len1' exists in hist_keys
+ */
+int
+_kadm5_exists_keys_hist(Key *keys1, int len1, HDB_Ext_KeySet *hist_keys)
+{
+ size_t i;
+
+ for (i = 0; i < hist_keys->len; i++) {
+ if (_kadm5_exists_keys(keys1, len1,
+ hist_keys->val[i].keys.val,
+ hist_keys->val[i].keys.len))
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def b/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def
new file mode 100644
index 0000000..56366ae
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/libkadm5srv-exports.def
@@ -0,0 +1,93 @@
+EXPORTS
+;! kadm5_ad_init_with_password
+;! kadm5_ad_init_with_password_ctx
+ kadm5_add_passwd_quality_verifier
+ kadm5_all_keys_are_bogus
+ kadm5_check_password_quality
+ kadm5_chpass_principal
+ kadm5_chpass_principal_3
+ kadm5_chpass_principal_with_key
+ kadm5_chpass_principal_with_key_3
+ kadm5_create_policy
+ kadm5_create_principal
+ kadm5_create_principal_3
+ kadm5_decrypt_key
+ kadm5_delete_policy
+ kadm5_delete_principal
+ kadm5_destroy
+ kadm5_dup_context
+ kadm5_flush
+ kadm5_free_key_data
+ kadm5_free_name_list
+ kadm5_free_policy_ent
+ kadm5_free_principal_ent
+ kadm5_get_instance
+ kadm5_get_policies
+ kadm5_get_policy
+ kadm5_get_principal
+ kadm5_get_principals
+ kadm5_get_privs
+ kadm5_init_with_creds
+ kadm5_init_with_creds_ctx
+ kadm5_init_with_password
+ kadm5_init_with_password_ctx
+ kadm5_init_with_skey
+ kadm5_init_with_skey_ctx
+ kadm5_iter_principals
+ kadm5_lock
+ kadm5_modify_policy
+ kadm5_modify_principal
+ kadm5_prune_principal
+ kadm5_randkey_principal
+ kadm5_randkey_principal_3
+ kadm5_rename_principal
+ kadm5_ret_key_data
+ kadm5_ret_principal_ent
+ kadm5_ret_principal_ent_mask
+ kadm5_ret_tl_data
+ kadm5_setkey_principal
+ kadm5_setkey_principal_3
+ kadm5_setup_passwd_quality_check
+ kadm5_some_keys_are_bogus
+ kadm5_store_key_data
+ kadm5_store_principal_ent
+ kadm5_store_principal_ent_mask
+ kadm5_store_principal_ent_nokeys
+ kadm5_store_tl_data
+ kadm5_unlock
+ kadm5_s_init_with_password_ctx
+ kadm5_s_init_with_password
+ kadm5_s_init_with_skey_ctx
+ kadm5_s_init_with_skey
+ kadm5_s_init_with_creds_ctx
+ kadm5_s_init_with_creds
+ kadm5_s_chpass_principal_cond
+ kadm5_s_create_principal_with_key
+ kadm5_log_exclusivelock
+ kadm5_log_set_version
+ kadm5_log_sharedlock
+ kadm5_log_signal_master
+;! kadm5_log_signal_socket
+ kadm5_log_signal_socket_info ;!
+ kadm5_log_previous
+ kadm5_log_goto_first
+ kadm5_log_goto_end
+ kadm5_log_foreach
+ kadm5_log_get_version_fd
+ kadm5_log_get_version
+ kadm5_log_recover
+ kadm5_log_replay
+ kadm5_log_end
+ kadm5_log_reinit
+ kadm5_log_init
+ kadm5_log_init_nb
+ kadm5_log_init_nolock
+ kadm5_log_init_sharedlock
+ kadm5_log_next
+ kadm5_log_nop
+ kadm5_log_truncate
+ kadm5_log_modify
+ _kadm5_acl_check_permission
+ _kadm5_unmarshal_params
+ _kadm5_s_get_db
+ _kadm5_privs_to_string
diff --git a/third_party/heimdal/lib/kadm5/libkadm5srv-version.rc b/third_party/heimdal/lib/kadm5/libkadm5srv-version.rc
new file mode 100644
index 0000000..065c18f
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/libkadm5srv-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_DLL
+#define RC_FILE_DESC_0409 "Heimdal Kerberos v5 Administration Library"
+#define RC_FILE_ORIG_0409 "libkadm5srv.dll"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/kadm5/log.c b/third_party/heimdal/lib/kadm5/log.c
new file mode 100644
index 0000000..96d063e
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/log.c
@@ -0,0 +1,2756 @@
+/*
+ * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+#include "heim_threads.h"
+
+RCSID("$Id$");
+
+/*
+ * A log consists of a sequence of records of this form:
+ *
+ * version number 4 bytes -\
+ * time in seconds 4 bytes +> preamble --+> header
+ * operation (enum kadm_ops) 4 bytes -/ /
+ * n, length of payload 4 bytes --------------+
+ * PAYLOAD DATA... n bytes
+ * n, length of payload 4 bytes ----------------+> trailer
+ * version number 4 bytes ->postamble ---/
+ *
+ * I.e., records have a header and a trailer so that knowing the offset
+ * of an record's start or end one can traverse the log forwards and
+ * backwards.
+ *
+ * The log always starts with a nop record (uber record) that contains the
+ * offset (8 bytes) of the first unconfirmed record (typically EOF), and the
+ * version number and timestamp of the preceding last confirmed record:
+ *
+ * offset of next new record 8 bytes
+ * last record time 4 bytes
+ * last record version number 4 bytes
+ *
+ * When an iprop slave receives a complete database, it saves that version as
+ * the last confirmed version, without writing any other records to the log. We
+ * use that version as the basis for further updates.
+ *
+ * kadm5 write operations are done in this order:
+ *
+ * - replay unconfirmed log records
+ * - write (append) and fsync() the log record for the kadm5 update
+ * - update the HDB (which includes fsync() or moral equivalent)
+ * - update the log uber record to mark the log record written as
+ * confirmed (not fsync()ed)
+ *
+ * This makes it possible and safe to seek to the logical end of the log
+ * (that is, the end of the last confirmed record) without traversing
+ * the whole log forward from offset zero. Unconfirmed records (which
+ * -currently- should never be more than one) can then be found (and
+ * rolled forward) by traversing forward from the logical end of the
+ * log. The trailers make it possible to traverse the log backwards
+ * from the logical end.
+ *
+ * This also makes the log + the HDB a two-phase commit with
+ * roll-forward system.
+ *
+ * HDB entry exists and HDB entry does not exist errors occurring during
+ * replay of unconfirmed records are ignored. This is because the
+ * corresponding HDB update might have completed. But also because a
+ * change to add aliases to a principal can fail because we don't check
+ * for alias conflicts before going ahead with the write operation.
+ *
+ * Non-sensical and incomplete log records found during roll-forward are
+ * truncated. A log record is non-sensical if its header and trailer
+ * don't match.
+ *
+ * Recovery (by rolling forward) occurs at the next read or write by a
+ * kadm5 API reader (e.g., kadmin), but not by an hdb API reader (e.g.,
+ * the KDC). This means that, e.g., a principal rename could fail in
+ * between the store and the delete, and recovery might not take place
+ * until the next write operation.
+ *
+ * The log record payload format for create is:
+ *
+ * DER-encoded HDB_entry n bytes
+ *
+ * The log record payload format for update is:
+ *
+ * mask 4 bytes
+ * DER-encoded HDB_entry n-4 bytes
+ *
+ * The log record payload format for delete is:
+ *
+ * krb5_store_principal n bytes
+ *
+ * The log record payload format for rename is:
+ *
+ * krb5_store_principal m bytes (old principal name)
+ * DER-encoded HDB_entry n-m bytes (new record)
+ *
+ * The log record payload format for nop varies:
+ *
+ * - The zeroth record in new logs is a nop with a 16 byte payload:
+ *
+ * offset of end of last confirmed record 8 bytes
+ * timestamp of last confirmed record 4 bytes
+ * version number of last confirmed record 4 bytes
+ *
+ * - New non-zeroth nop records:
+ *
+ * nop type 4 bytes
+ *
+ * - Old nop records:
+ *
+ * version number 4 bytes
+ * timestamp 4 bytes
+ *
+ * Upon initialization, the log's uber record will have version 1, and
+ * will be followed by a nop record with version 2. The version numbers
+ * of additional records will be monotonically increasing.
+ *
+ * Truncation (kadm5_log_truncate()) takes some N > 0 records from the
+ * tail of the log and writes them to the beginning of the log after an
+ * uber record whose version will then be one less than the first of
+ * those records.
+ *
+ * On masters the log should never have more than one unconfirmed
+ * record, but slaves append all of a master's "diffs" and then call
+ * kadm5_log_recover() to recover.
+ */
+
+/*
+ * HDB and log lock order on the master:
+ *
+ * 1) open and lock the HDB
+ * 2) open and lock the log
+ * 3) do something
+ * 4) unlock and close the log
+ * 5) repeat (2)..(4) if desired
+ * 6) unlock and close the HDB
+ *
+ * The kadmin -l lock command can be used to hold the HDB open and
+ * locked for multiple operations.
+ *
+ * HDB and log lock order on the slave:
+ *
+ * 1) open and lock the log
+ * 2) open and lock the HDB
+ * 3) replay entries
+ * 4) unlock and close the HDB
+ * 5) repeat (2)..(4) until signaled
+ * 6) unlock and close the HDB
+ *
+ * The slave doesn't want to allow other local writers, after all, thus
+ * the order is reversed. This means that using "kadmin -l" on a slave
+ * will deadlock with ipropd-slave -- don't do that.
+ */
+
+#define LOG_HEADER_SZ ((off_t)(sizeof(uint32_t) * 4))
+#define LOG_TRAILER_SZ ((off_t)(sizeof(uint32_t) * 2))
+#define LOG_WRAPPER_SZ ((off_t)(LOG_HEADER_SZ + LOG_TRAILER_SZ))
+#define LOG_UBER_LEN ((off_t)(sizeof(uint64_t) + sizeof(uint32_t) * 2))
+#define LOG_UBER_SZ ((off_t)(LOG_WRAPPER_SZ + LOG_UBER_LEN))
+
+#define LOG_NOPEEK 0
+#define LOG_DOPEEK 1
+
+/*
+ * Read the header of the record starting at the current offset into sp.
+ *
+ * Preserves sp's offset on success if `peek', else skips the header.
+ *
+ * Preserves sp's offset on failure where possible.
+ */
+static kadm5_ret_t
+get_header(krb5_storage *sp, int peek, uint32_t *verp, uint32_t *tstampp,
+ enum kadm_ops *opp, uint32_t *lenp)
+{
+ krb5_error_code ret;
+ uint32_t tstamp, op, len;
+ off_t off, new_off;
+
+ if (tstampp == NULL)
+ tstampp = &tstamp;
+ if (lenp == NULL)
+ lenp = &len;
+
+ *verp = 0;
+ *tstampp = 0;
+ if (opp != NULL)
+ *opp = kadm_nop;
+ *lenp = 0;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return errno;
+ ret = krb5_ret_uint32(sp, verp);
+ if (ret == HEIM_ERR_EOF) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return HEIM_ERR_EOF;
+ }
+ if (ret)
+ goto log_corrupt;
+ ret = krb5_ret_uint32(sp, tstampp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Note: sizeof(*opp) might not == sizeof(op) */
+ ret = krb5_ret_uint32(sp, &op);
+ if (ret)
+ goto log_corrupt;
+ if (opp != NULL)
+ *opp = op;
+
+ ret = krb5_ret_uint32(sp, lenp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Restore offset if requested */
+ if (peek == LOG_DOPEEK) {
+ new_off = krb5_storage_seek(sp, off, SEEK_SET);
+ if (new_off == -1)
+ return errno;
+ if (new_off != off)
+ return EIO;
+ }
+
+ return 0;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return KADM5_LOG_CORRUPT;
+}
+
+/*
+ * Seek to the start of the preceding record's header and returns its
+ * offset. If sp is at offset zero this sets *verp = 0 and returns 0.
+ *
+ * Does not verify the header of the previous entry.
+ *
+ * On error returns -1, setting errno (possibly to a kadm5_ret_t or
+ * krb5_error_code value) and preserves sp's offset where possible.
+ */
+static off_t
+seek_prev(krb5_storage *sp, uint32_t *verp, uint32_t *lenp)
+{
+ krb5_error_code ret;
+ uint32_t len, ver;
+ off_t off_len;
+ off_t off, new_off;
+
+ if (lenp == NULL)
+ lenp = &len;
+ if (verp == NULL)
+ verp = &ver;
+
+ *verp = 0;
+ *lenp = 0;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return off;
+ if (off == 0)
+ return 0;
+
+ /* Check that `off' allows for the record's header and trailer */
+ if (off < LOG_WRAPPER_SZ)
+ goto log_corrupt;
+
+ /* Get the previous entry's length and version from its trailer */
+ new_off = krb5_storage_seek(sp, -8, SEEK_CUR);
+ if (new_off == -1)
+ return -1;
+ if (new_off != off - 8) {
+ errno = EIO;
+ return -1;
+ }
+ ret = krb5_ret_uint32(sp, lenp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Check for overflow/sign extension */
+ off_len = (off_t)*lenp;
+ if (off_len < 0 || *lenp != (uint32_t)off_len)
+ goto log_corrupt;
+
+ ret = krb5_ret_uint32(sp, verp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Check that `off' allows for the record */
+ if (off < LOG_WRAPPER_SZ + off_len)
+ goto log_corrupt;
+
+ /* Seek backwards to the entry's start */
+ new_off = krb5_storage_seek(sp, -(LOG_WRAPPER_SZ + off_len), SEEK_CUR);
+ if (new_off == -1)
+ return -1;
+ if (new_off != off - (LOG_WRAPPER_SZ + off_len)) {
+ errno = EIO;
+ return -1;
+ }
+ return new_off;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ errno = KADM5_LOG_CORRUPT;
+ return -1;
+}
+
+/*
+ * Seek to the start of the next entry's header.
+ *
+ * On error returns -1 and preserves sp's offset.
+ */
+static off_t
+seek_next(krb5_storage *sp)
+{
+ krb5_error_code ret;
+ uint32_t ver, ver2, len, len2;
+ enum kadm_ops op;
+ uint32_t tstamp;
+ off_t off, off_len, new_off;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return off;
+
+ errno = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+ if (errno)
+ return -1;
+
+ /* Check for overflow */
+ off_len = len;
+ if (off_len < 0)
+ goto log_corrupt;
+
+ new_off = krb5_storage_seek(sp, off_len, SEEK_CUR);
+ if (new_off == -1) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return -1;
+ }
+ if (new_off != off + LOG_HEADER_SZ + off_len)
+ goto log_corrupt;
+ ret = krb5_ret_uint32(sp, &len2);
+ if (ret || len2 != len)
+ goto log_corrupt;
+ ret = krb5_ret_uint32(sp, &ver2);
+ if (ret || ver2 != ver)
+ goto log_corrupt;
+ new_off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (new_off == -1) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return -1;
+ }
+ if (new_off != off + off_len + LOG_WRAPPER_SZ)
+ goto log_corrupt;
+
+ return off + off_len + LOG_WRAPPER_SZ;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ errno = KADM5_LOG_CORRUPT;
+ return -1;
+}
+
+/*
+ * Get the version of the entry ending at the current offset into sp.
+ * If it is the uber record, return its nominal version instead.
+ *
+ * Returns HEIM_ERR_EOF if sp is at offset zero.
+ *
+ * Preserves sp's offset.
+ */
+static kadm5_ret_t
+get_version_prev(krb5_storage *sp, uint32_t *verp, uint32_t *tstampp)
+{
+ krb5_error_code ret;
+ uint32_t ver, ver2, len, len2;
+ off_t off, prev_off, new_off;
+
+ *verp = 0;
+ if (tstampp != NULL)
+ *tstampp = 0;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return errno;
+ if (off == 0)
+ return HEIM_ERR_EOF;
+
+ /* Read the trailer and seek back */
+ prev_off = seek_prev(sp, &ver, &len);
+ if (prev_off == -1)
+ return errno;
+
+ /* Uber record? Return nominal version. */
+ if (prev_off == 0 && len == LOG_UBER_LEN && ver == 0) {
+ /* Skip 8 byte offset and 4 byte time */
+ if (krb5_storage_seek(sp, LOG_HEADER_SZ + 12, SEEK_SET)
+ != LOG_HEADER_SZ + 12)
+ return errno;
+ ret = krb5_ret_uint32(sp, verp);
+ if (krb5_storage_seek(sp, 0, SEEK_SET) != 0)
+ return errno;
+ if (ret != 0)
+ return ret;
+ } else {
+ *verp = ver;
+ }
+
+ /* Verify that the trailer matches header */
+ ret = get_header(sp, LOG_NOPEEK, &ver2, tstampp, NULL, &len2);
+ if (ret || ver != ver2 || len != len2)
+ goto log_corrupt;
+
+ /* Preserve offset */
+ new_off = krb5_storage_seek(sp, off, SEEK_SET);
+ if (new_off == -1)
+ return errno;
+ if (new_off != off) {
+ errno = EIO;
+ return errno;
+ }
+ return 0;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return KADM5_LOG_CORRUPT;
+}
+
+static size_t
+get_max_log_size(krb5_context context)
+{
+ off_t n;
+
+ /* Use database-label-specific lookup? No, ETOOHARD. */
+ /* Default to 50MB max log size */
+ n = krb5_config_get_int_default(context, NULL, 52428800,
+ "kdc",
+ "log-max-size",
+ NULL);
+ if (n >= 4 * (LOG_UBER_LEN + LOG_WRAPPER_SZ) && n == (size_t)n)
+ return (size_t)n;
+ return 0;
+}
+
+static kadm5_ret_t truncate_if_needed(kadm5_server_context *);
+
+/*
+ * Get the version and timestamp metadata of either the first, or last
+ * confirmed entry in the log.
+ *
+ * If `which' is LOG_VERSION_UBER, then this gets the version number of the uber
+ * uber record which must be 0, or else we need to upgrade the log.
+ *
+ * If `which' is LOG_VERSION_FIRST, then this gets the metadata for the
+ * logically first entry past the uberblock, or returns HEIM_ERR_EOF if
+ * only the uber record is present.
+ *
+ * If `which' is LOG_VERSION_LAST, then this gets metadata for the last
+ * confirmed entry's version and timestamp. If only the uber record is present,
+ * then the version will be its "nominal" version, which may differ from its
+ * actual version (0).
+ *
+ * The `fd''s offset will be set to the start of the header of the entry
+ * identified by `which'.
+ */
+kadm5_ret_t
+kadm5_log_get_version_fd(kadm5_server_context *server_context, int fd,
+ int which, uint32_t *ver, uint32_t *tstamp)
+{
+ kadm5_ret_t ret = 0;
+ krb5_storage *sp;
+ enum kadm_ops op = kadm_get;
+ uint32_t len = 0;
+ uint32_t tmp;
+
+ if (fd == -1)
+ return 0; /* /dev/null */
+
+ if (tstamp == NULL)
+ tstamp = &tmp;
+
+ *ver = 0;
+ *tstamp = 0;
+
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ return errno ? errno : ENOMEM;
+
+ switch (which) {
+ case LOG_VERSION_LAST:
+ ret = kadm5_log_goto_end(server_context, sp);
+ if (ret == 0)
+ ret = get_version_prev(sp, ver, tstamp);
+ break;
+ case LOG_VERSION_FIRST:
+ ret = kadm5_log_goto_first(server_context, sp);
+ if (ret == 0)
+ ret = get_header(sp, LOG_DOPEEK, ver, tstamp, NULL, NULL);
+ break;
+ case LOG_VERSION_UBER:
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == 0)
+ ret = get_header(sp, LOG_DOPEEK, ver, tstamp, &op, &len);
+ else
+ ret = errno;
+ if (ret == 0 && (op != kadm_nop || len != LOG_UBER_LEN || *ver != 0))
+ ret = KADM5_LOG_NEEDS_UPGRADE;
+ break;
+ default:
+ ret = ENOTSUP;
+ break;
+ }
+
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/* Get the version of the last confirmed entry in the log */
+kadm5_ret_t
+kadm5_log_get_version(kadm5_server_context *server_context, uint32_t *ver)
+{
+ return kadm5_log_get_version_fd(server_context,
+ server_context->log_context.log_fd,
+ LOG_VERSION_LAST, ver, NULL);
+}
+
+/* Sets the version in the context, but NOT in the log */
+kadm5_ret_t
+kadm5_log_set_version(kadm5_server_context *context, uint32_t vno)
+{
+ kadm5_log_context *log_context = &context->log_context;
+
+ log_context->version = vno;
+ return 0;
+}
+
+/*
+ * Open the log and setup server_context->log_context
+ */
+static kadm5_ret_t
+log_open(kadm5_server_context *server_context, int lock_mode)
+{
+ int fd = -1;
+ int lock_it = 0;
+ int lock_nb = 0;
+ int oflags = O_RDWR;
+ kadm5_ret_t ret;
+ kadm5_log_context *log_context = &server_context->log_context;
+
+ if (lock_mode & LOCK_NB) {
+ lock_mode &= ~LOCK_NB;
+ lock_nb = LOCK_NB;
+ }
+
+ if (lock_mode == log_context->lock_mode && log_context->log_fd != -1)
+ return 0;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0) {
+ /* log_context->log_fd should be -1 here */
+ return 0;
+ }
+
+ if (log_context->log_fd != -1) {
+ /* Lock or change lock */
+ fd = log_context->log_fd;
+ if (lseek(fd, 0, SEEK_SET) == -1)
+ return errno;
+ lock_it = (lock_mode != log_context->lock_mode);
+ } else {
+ /* Open and lock */
+ if (lock_mode != LOCK_UN)
+ oflags |= O_CREAT;
+ fd = open(log_context->log_file, oflags, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(server_context->context, ret,
+ "log_open: open %s", log_context->log_file);
+ return ret;
+ }
+ lock_it = (lock_mode != LOCK_UN);
+ }
+ if (lock_it && flock(fd, lock_mode | lock_nb) < 0) {
+ ret = errno;
+ krb5_set_error_message(server_context->context, ret,
+ "log_open: flock %s", log_context->log_file);
+ if (fd != log_context->log_fd)
+ (void) close(fd);
+ return ret;
+ }
+
+ log_context->log_fd = fd;
+ log_context->lock_mode = lock_mode;
+ log_context->read_only = (lock_mode != LOCK_EX);
+
+ return 0;
+}
+
+/*
+ * Open the log and setup server_context->log_context
+ */
+static kadm5_ret_t
+log_init(kadm5_server_context *server_context, int lock_mode)
+{
+ int fd;
+ struct stat st;
+ uint32_t vno;
+ size_t maxbytes = get_max_log_size(server_context->context);
+ kadm5_ret_t ret;
+ kadm5_log_context *log_context = &server_context->log_context;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0) {
+ /* log_context->log_fd should be -1 here */
+ return 0;
+ }
+
+ ret = log_open(server_context, lock_mode);
+ if (ret)
+ return ret;
+
+ fd = log_context->log_fd;
+ if (!log_context->read_only) {
+ if (fstat(fd, &st) == -1)
+ ret = errno;
+ if (ret == 0 && st.st_size == 0) {
+ /* Write first entry */
+ log_context->version = 0;
+ ret = kadm5_log_nop(server_context, kadm_nop_plain);
+ if (ret == 0)
+ return 0; /* no need to truncate_if_needed(): it's not */
+ }
+ if (ret == 0) {
+ ret = kadm5_log_get_version_fd(server_context, fd,
+ LOG_VERSION_UBER, &vno, NULL);
+
+ /* Upgrade the log if it was an old-style log */
+ if (ret == KADM5_LOG_NEEDS_UPGRADE)
+ ret = kadm5_log_truncate(server_context, 0, maxbytes / 4);
+ }
+ if (ret == 0)
+ ret = kadm5_log_recover(server_context, kadm_recover_replay);
+ }
+
+ if (ret == 0) {
+ ret = kadm5_log_get_version_fd(server_context, fd, LOG_VERSION_LAST,
+ &log_context->version, NULL);
+ if (ret == HEIM_ERR_EOF)
+ ret = 0;
+ }
+
+ if (ret == 0)
+ ret = truncate_if_needed(server_context);
+
+ if (ret != 0)
+ (void) kadm5_log_end(server_context);
+ return ret;
+}
+
+/* Open the log with an exclusive lock */
+kadm5_ret_t
+kadm5_log_init(kadm5_server_context *server_context)
+{
+ return log_init(server_context, LOCK_EX);
+}
+
+/* Upgrade log lock to exclusive */
+kadm5_ret_t
+kadm5_log_exclusivelock(kadm5_server_context *server_context)
+{
+ kadm5_log_context *log_context = &server_context->log_context;
+
+ if (log_context->lock_mode == LOCK_EX)
+ return 0;
+ if (log_context->log_fd == -1)
+ return EINVAL;
+ if (flock(log_context->log_fd, LOCK_EX) < 0)
+ return errno;
+ log_context->read_only = 0;
+ log_context->lock_mode = LOCK_EX;
+ return 0;
+}
+
+/* Downgrade log lock to shared */
+kadm5_ret_t
+kadm5_log_sharedlock(kadm5_server_context *server_context)
+{
+ kadm5_log_context *log_context = &server_context->log_context;
+
+ if (log_context->lock_mode == LOCK_SH)
+ return 0;
+ if (log_context->log_fd == -1)
+ return EINVAL;
+ if (flock(log_context->log_fd, LOCK_SH) < 0)
+ return errno;
+ log_context->read_only = 1;
+ log_context->lock_mode = LOCK_SH;
+ return 0;
+}
+
+/* Open the log with an exclusive non-blocking lock */
+kadm5_ret_t
+kadm5_log_init_nb(kadm5_server_context *server_context)
+{
+ return log_init(server_context, LOCK_EX | LOCK_NB);
+}
+
+/* Open the log with no locks */
+kadm5_ret_t
+kadm5_log_init_nolock(kadm5_server_context *server_context)
+{
+ return log_init(server_context, LOCK_UN);
+}
+
+/* Open the log with a shared lock */
+kadm5_ret_t
+kadm5_log_init_sharedlock(kadm5_server_context *server_context, int lock_flags)
+{
+ return log_init(server_context, LOCK_SH | lock_flags);
+}
+
+/*
+ * Reinitialize the log and open it
+ */
+kadm5_ret_t
+kadm5_log_reinit(kadm5_server_context *server_context, uint32_t vno)
+{
+ int ret;
+ kadm5_log_context *log_context = &server_context->log_context;
+
+ ret = log_open(server_context, LOCK_EX);
+ if (ret)
+ return ret;
+ if (log_context->log_fd != -1) {
+ if (ftruncate(log_context->log_fd, 0) < 0) {
+ ret = errno;
+ return ret;
+ }
+ if (lseek(log_context->log_fd, 0, SEEK_SET) < 0) {
+ ret = errno;
+ return ret;
+ }
+ }
+
+ /* Write uber entry and truncation nop with version `vno` */
+ log_context->version = vno;
+ return kadm5_log_nop(server_context, kadm_nop_plain);
+}
+
+/* Close the server_context->log_context. */
+kadm5_ret_t
+kadm5_log_end(kadm5_server_context *server_context)
+{
+ kadm5_log_context *log_context = &server_context->log_context;
+ kadm5_ret_t ret = 0;
+ int fd = log_context->log_fd;
+
+ if (fd != -1) {
+ if (log_context->lock_mode != LOCK_UN) {
+ if (flock(fd, LOCK_UN) == -1 && errno == EBADF)
+ ret = errno;
+ }
+ if (ret != EBADF && close(fd) == -1)
+ ret = errno;
+ }
+ log_context->log_fd = -1;
+ log_context->lock_mode = LOCK_UN;
+ return ret;
+}
+
+/*
+ * Write the version, timestamp, and op for a new entry.
+ *
+ * Note that the sp should be a krb5_storage_emem(), not a file.
+ *
+ * On success the sp's offset will be where the length of the payload
+ * should be written.
+ */
+static kadm5_ret_t
+kadm5_log_preamble(kadm5_server_context *context,
+ krb5_storage *sp,
+ enum kadm_ops op,
+ uint32_t vno)
+{
+ kadm5_log_context *log_context = &context->log_context;
+ time_t now = time(NULL);
+ kadm5_ret_t ret;
+
+ ret = krb5_store_uint32(sp, vno);
+ if (ret)
+ return ret;
+ ret = krb5_store_uint32(sp, now);
+ if (ret)
+ return ret;
+ log_context->last_time = now;
+
+ if (op < kadm_first || op > kadm_last)
+ return ERANGE;
+ return krb5_store_uint32(sp, op);
+}
+
+/* Writes the version part of the trailer */
+static kadm5_ret_t
+kadm5_log_postamble(kadm5_log_context *context,
+ krb5_storage *sp,
+ uint32_t vno)
+{
+ return krb5_store_uint32(sp, vno);
+}
+
+/*
+ * Signal the ipropd-master about changes to the log.
+ */
+/*
+ * XXX Get rid of the ifdef by having a sockaddr in log_context in both
+ * cases.
+ *
+ * XXX Better yet, just connect to the master's socket that slaves
+ * connect to, and then disconnect. The master should then check the
+ * log on every connection accepted. Then we wouldn't need IPC to
+ * signal the master.
+ */
+void
+kadm5_log_signal_master(kadm5_server_context *context)
+{
+ kadm5_log_context *log_context = &context->log_context;
+#ifndef NO_UNIX_SOCKETS
+ (void) sendto(log_context->socket_fd,
+ (void *)&log_context->version,
+ sizeof(log_context->version),
+ 0,
+ (struct sockaddr *)&log_context->socket_name,
+ sizeof(log_context->socket_name));
+#else
+ (void) sendto(log_context->socket_fd,
+ (void *)&log_context->version,
+ sizeof(log_context->version),
+ 0,
+ log_context->socket_info->ai_addr,
+ log_context->socket_info->ai_addrlen);
+#endif
+}
+
+/*
+ * Write sp's contents (which must be a fully formed record, complete
+ * with header, payload, and trailer) to the log and fsync the log.
+ *
+ * Does not free sp.
+ */
+
+static kadm5_ret_t
+kadm5_log_flush(kadm5_server_context *context, krb5_storage *sp)
+{
+ kadm5_log_context *log_context = &context->log_context;
+ kadm5_ret_t ret;
+ krb5_data data;
+ size_t len;
+ krb5_ssize_t bytes;
+ uint32_t new_ver, prev_ver;
+ off_t off, end;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return 0;
+
+ if (log_context->read_only)
+ return EROFS;
+
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
+ return errno;
+
+ ret = get_header(sp, LOG_DOPEEK, &new_ver, NULL, NULL, NULL);
+ if (ret)
+ return ret;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret)
+ return ret;
+
+ /* Abandon the emem storage reference */
+ sp = krb5_storage_from_fd(log_context->log_fd);
+ if (sp == NULL) {
+ krb5_data_free(&data);
+ return krb5_enomem(context->context);
+ }
+
+ /* Check that we are at the end of the log and fail if not */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off == -1) {
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return errno;
+ }
+ end = krb5_storage_seek(sp, 0, SEEK_END);
+ if (end == -1) {
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return errno;
+ }
+ if (end != off) {
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return KADM5_LOG_CORRUPT;
+ }
+
+ /* Enforce monotonically incremented versioning of records */
+ if (seek_prev(sp, &prev_ver, NULL) == -1 ||
+ krb5_storage_seek(sp, end, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ if (prev_ver != 0 && prev_ver != log_context->version)
+ return EINVAL; /* Internal error, really; just a consistency check */
+
+ if (prev_ver != 0 && new_ver != prev_ver + 1) {
+ krb5_warnx(context->context, "refusing to write a log record "
+ "with non-monotonic version (new: %u, old: %u)",
+ new_ver, prev_ver);
+ return KADM5_LOG_CORRUPT;
+ }
+
+ len = data.length;
+ bytes = krb5_storage_write(sp, data.data, len);
+ krb5_data_free(&data);
+ if (bytes != len) {
+ krb5_storage_free(sp);
+ ret = bytes == -1 ? errno : KADM5_LOG_CORRUPT;
+ krb5_warn(context->context, ret, "short write to iprop log file");
+ return ret;
+ }
+ if (bytes != (krb5_ssize_t)len) {
+ krb5_storage_free(sp);
+ return EIO;
+ }
+
+ ret = krb5_storage_fsync(sp);
+ krb5_storage_free(sp);
+ if (ret)
+ return ret;
+
+ /* Retain the nominal database version when flushing the uber record */
+ if (new_ver != 0)
+ log_context->version = new_ver;
+ return 0;
+}
+
+/*
+ * Add a `create' operation to the log and perform the create against the HDB.
+ */
+kadm5_ret_t
+kadm5_log_create(kadm5_server_context *context, hdb_entry *entry)
+{
+ krb5_storage *sp;
+ krb5_ssize_t bytes;
+ kadm5_ret_t ret;
+ krb5_data value;
+ hdb_entry ent, existing;
+ kadm5_log_context *log_context = &context->log_context;
+
+ memset(&existing, 0, sizeof(existing));
+ memset(&ent, 0, sizeof(ent));
+ ent = *entry;
+
+ /*
+ * Do not allow creation of concrete entries within namespaces unless
+ * explicitly requested.
+ */
+ ret = hdb_fetch_kvno(context->context, context->db, entry->principal, 0,
+ 0, 0, 0, &existing);
+ if (ret != 0 && ret != HDB_ERR_NOENTRY)
+ return ret;
+ if (ret == 0 && !ent.flags.materialize &&
+ (existing.flags.virtual || existing.flags.virtual_keys)) {
+ hdb_free_entry(context->context, context->db, &existing);
+ return HDB_ERR_EXISTS;
+ }
+ if (ret == 0)
+ hdb_free_entry(context->context, context->db, &existing);
+ ent.flags.materialize = 0; /* Clear in stored entry */
+
+ /*
+ * If we're not logging then we can't recover-to-perform, so just
+ * perform.
+ */
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return context->db->hdb_store(context->context, context->db, 0, &ent);
+
+ /*
+ * Test for any conflicting entries before writing the log. If we commit
+ * to the log we'll end-up rolling forward on recovery, but that would be
+ * wrong if the initial create is rejected.
+ */
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_PRECHECK, &ent);
+ if (ret == 0)
+ ret = hdb_entry2value(context->context, entry, &value);
+ if (ret)
+ return ret;
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_create,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, value.length);
+ if (ret == 0) {
+ bytes = krb5_storage_write(sp, value.data, value.length);
+ if (bytes != (krb5_ssize_t)value.length)
+ ret = bytes == -1 ? errno : krb5_enomem(context->context);
+ }
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, value.length);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ krb5_storage_free(sp);
+ krb5_data_free(&value);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+ return ret;
+}
+
+/*
+ * Read the data of a create log record from `sp' and change the
+ * database.
+ */
+static kadm5_ret_t
+kadm5_log_replay_create(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ hdb_entry ent;
+
+ memset(&ent, 0, sizeof(ent));
+
+ ret = krb5_data_alloc(&data, len);
+ if (ret) {
+ krb5_set_error_message(context->context, ret, "out of memory");
+ return ret;
+ }
+ krb5_storage_read(sp, data.data, len);
+ ret = hdb_value2entry(context->context, &data, &ent);
+ krb5_data_free(&data);
+ if (ret) {
+ krb5_set_error_message(context->context, ret,
+ "Unmarshaling hdb entry in log failed, "
+ "version: %ld", (long)ver);
+ return ret;
+ }
+ ret = context->db->hdb_store(context->context, context->db, 0, &ent);
+ hdb_free_entry(context->context, context->db, &ent);
+ return ret;
+}
+
+/*
+ * Add a `delete' operation to the log.
+ */
+kadm5_ret_t
+kadm5_log_delete(kadm5_server_context *context,
+ krb5_principal princ)
+{
+ kadm5_ret_t ret;
+ kadm5_log_context *log_context = &context->log_context;
+ krb5_storage *sp;
+ uint32_t len = 0; /* So dumb compilers don't warn */
+ off_t end_off = 0; /* Ditto; this allows de-indentation by two levels */
+ off_t off;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return context->db->hdb_remove(context->context, context->db, 0,
+ princ);
+ ret = context->db->hdb_remove(context->context, context->db,
+ HDB_F_PRECHECK, princ);
+ if (ret)
+ return ret;
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_delete,
+ log_context->version + 1);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ /*
+ * Write a 0 length which we overwrite once we know the length of
+ * the principal name payload.
+ */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, princ);
+ if (ret == 0) {
+ end_off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (end_off == -1)
+ ret = errno;
+ else if (end_off < off)
+ ret = KADM5_LOG_CORRUPT;
+ }
+ if (ret == 0) {
+ /* We wrote sizeof(uint32_t) + payload length bytes */
+ len = (uint32_t)(end_off - off);
+ if (end_off - off != len || len < sizeof(len))
+ ret = KADM5_LOG_CORRUPT;
+ else
+ len -= sizeof(len);
+ }
+ if (ret == 0 && krb5_storage_seek(sp, off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0 && krb5_storage_seek(sp, end_off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Read a `delete' log operation from `sp' and apply it.
+ */
+static kadm5_ret_t
+kadm5_log_replay_delete(kadm5_server_context *context,
+ uint32_t ver, uint32_t len, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ krb5_principal principal;
+
+ ret = krb5_ret_principal(sp, &principal);
+ if (ret) {
+ krb5_set_error_message(context->context, ret, "Failed to read deleted "
+ "principal from log version: %ld", (long)ver);
+ return ret;
+ }
+
+ ret = context->db->hdb_remove(context->context, context->db, 0, principal);
+ krb5_free_principal(context->context, principal);
+ return ret;
+}
+
+static kadm5_ret_t kadm5_log_replay_rename(kadm5_server_context *,
+ uint32_t, uint32_t,
+ krb5_storage *);
+
+/*
+ * Add a `rename' operation to the log.
+ */
+kadm5_ret_t
+kadm5_log_rename(kadm5_server_context *context,
+ krb5_principal source,
+ hdb_entry *entry)
+{
+ krb5_storage *sp;
+ krb5_ssize_t bytes;
+ kadm5_ret_t ret;
+ uint32_t len = 0; /* So dumb compilers don't warn */
+ off_t end_off = 0; /* Ditto; this allows de-indentation by two levels */
+ off_t off;
+ krb5_data value;
+ hdb_entry ent;
+ kadm5_log_context *log_context = &context->log_context;
+
+ memset(&ent, 0, sizeof(ent));
+ ent = *entry;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0) {
+ ret = context->db->hdb_store(context->context, context->db, 0, &ent);
+ if (ret == 0)
+ return context->db->hdb_remove(context->context, context->db, 0,
+ source);
+ return ret;
+ }
+
+ /*
+ * Pre-check that the transaction will succeed.
+ *
+ * Note that rename doesn't work to swap a principal's canonical
+ * name with one of its aliases. To make that work would require
+ * adding an hdb_rename() method for renaming principals (there's an
+ * hdb_rename() method already, but for renaming the HDB), which is
+ * ETOOMUCHWORK for the time being.
+ */
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_PRECHECK, &ent);
+ if (ret == 0)
+ ret = context->db->hdb_remove(context->context, context->db,
+ HDB_F_PRECHECK, source);
+ if (ret)
+ return ret;
+
+ sp = krb5_storage_emem();
+ krb5_data_zero(&value);
+ if (sp == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_rename,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = hdb_entry2value(context->context, entry, &value);
+ if (ret) {
+ krb5_data_free(&value);
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ /*
+ * Write a zero length which we'll overwrite once we know the length of the
+ * payload.
+ */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, source);
+ if (ret == 0) {
+ errno = 0;
+ bytes = krb5_storage_write(sp, value.data, value.length);
+ if (bytes != (krb5_ssize_t)value.length)
+ ret = bytes == -1 ? errno : krb5_enomem(context->context);
+ }
+ if (ret == 0) {
+ end_off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (end_off == -1)
+ ret = errno;
+ else if (end_off < off)
+ ret = KADM5_LOG_CORRUPT;
+ }
+ if (ret == 0) {
+ /* We wrote sizeof(uint32_t) + payload length bytes */
+ len = (uint32_t)(end_off - off);
+ if (end_off - off != len || len < sizeof(len))
+ ret = KADM5_LOG_CORRUPT;
+ else
+ len -= sizeof(len);
+ if (ret == 0 && krb5_storage_seek(sp, off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0 && krb5_storage_seek(sp, end_off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+ }
+ krb5_data_free(&value);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Read a `rename' log operation from `sp' and apply it.
+ */
+
+static kadm5_ret_t
+kadm5_log_replay_rename(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+ krb5_principal source;
+ hdb_entry target_ent;
+ krb5_data value;
+ off_t off;
+ size_t princ_len, data_len;
+
+ memset(&target_ent, 0, sizeof(target_ent));
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ ret = krb5_ret_principal(sp, &source);
+ if (ret) {
+ krb5_set_error_message(context->context, ret, "Failed to read renamed "
+ "principal in log, version: %ld", (long)ver);
+ return ret;
+ }
+ princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
+ data_len = len - princ_len;
+ ret = krb5_data_alloc(&value, data_len);
+ if (ret) {
+ krb5_free_principal (context->context, source);
+ return ret;
+ }
+ krb5_storage_read(sp, value.data, data_len);
+ ret = hdb_value2entry(context->context, &value, &target_ent);
+ krb5_data_free(&value);
+ if (ret) {
+ krb5_free_principal(context->context, source);
+ return ret;
+ }
+ ret = context->db->hdb_store(context->context, context->db,
+ 0, &target_ent);
+ hdb_free_entry(context->context, context->db, &target_ent);
+ if (ret) {
+ krb5_free_principal(context->context, source);
+ return ret;
+ }
+ ret = context->db->hdb_remove(context->context, context->db, 0, source);
+ krb5_free_principal(context->context, source);
+
+ return ret;
+}
+
+/*
+ * Add a `modify' operation to the log.
+ */
+kadm5_ret_t
+kadm5_log_modify(kadm5_server_context *context,
+ hdb_entry *entry,
+ uint32_t mask)
+{
+ krb5_storage *sp;
+ krb5_ssize_t bytes;
+ kadm5_ret_t ret;
+ krb5_data value;
+ uint32_t len;
+ hdb_entry ent;
+ kadm5_log_context *log_context = &context->log_context;
+
+ memset(&ent, 0, sizeof(ent));
+ ent = *entry;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return context->db->hdb_store(context->context, context->db,
+ HDB_F_REPLACE, &ent);
+
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_PRECHECK | HDB_F_REPLACE, &ent);
+ if (ret)
+ return ret;
+
+ sp = krb5_storage_emem();
+ krb5_data_zero(&value);
+ if (sp == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret == 0)
+ ret = hdb_entry2value(context->context, entry, &value);
+ if (ret) {
+ krb5_data_free(&value);
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ len = value.length + sizeof(len);
+ if (value.length > len || len > INT32_MAX)
+ ret = E2BIG;
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_modify,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, mask);
+ if (ret == 0) {
+ bytes = krb5_storage_write(sp, value.data, value.length);
+ if (bytes != (krb5_ssize_t)value.length)
+ ret = bytes == -1 ? errno : krb5_enomem(context->context);
+ }
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+ krb5_data_free(&value);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Read a `modify' log operation from `sp' and apply it.
+ */
+static kadm5_ret_t
+kadm5_log_replay_modify(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
+{
+ krb5_error_code ret;
+ uint32_t mask;
+ krb5_data value;
+ hdb_entry ent, log_ent;
+
+ memset(&log_ent, 0, sizeof(log_ent));
+
+ ret = krb5_ret_uint32(sp, &mask);
+ if (ret)
+ return ret;
+ len -= 4;
+ ret = krb5_data_alloc (&value, len);
+ if (ret) {
+ krb5_set_error_message(context->context, ret, "out of memory");
+ return ret;
+ }
+ errno = 0;
+ if (krb5_storage_read (sp, value.data, len) != (krb5_ssize_t)len) {
+ ret = errno ? errno : EIO;
+ return ret;
+ }
+ ret = hdb_value2entry (context->context, &value, &log_ent);
+ krb5_data_free(&value);
+ if (ret)
+ return ret;
+
+ memset(&ent, 0, sizeof(ent));
+ /* NOTE: We do not use hdb_fetch_kvno() here */
+ ret = context->db->hdb_fetch_kvno(context->context, context->db,
+ log_ent.principal,
+ HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
+ HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
+ if (ret)
+ goto out;
+ if (mask & KADM5_PRINC_EXPIRE_TIME) {
+ if (log_ent.valid_end == NULL) {
+ ent.valid_end = NULL;
+ } else {
+ if (ent.valid_end == NULL) {
+ ent.valid_end = malloc(sizeof(*ent.valid_end));
+ if (ent.valid_end == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+ *ent.valid_end = *log_ent.valid_end;
+ }
+ }
+ if (mask & KADM5_PW_EXPIRATION) {
+ if (log_ent.pw_end == NULL) {
+ ent.pw_end = NULL;
+ } else {
+ if (ent.pw_end == NULL) {
+ ent.pw_end = malloc(sizeof(*ent.pw_end));
+ if (ent.pw_end == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+ *ent.pw_end = *log_ent.pw_end;
+ }
+ }
+ if (mask & KADM5_LAST_PWD_CHANGE) {
+ krb5_warnx (context->context,
+ "Unimplemented mask KADM5_LAST_PWD_CHANGE");
+ }
+ if (mask & KADM5_ATTRIBUTES) {
+ ent.flags = log_ent.flags;
+ }
+ if (mask & KADM5_MAX_LIFE) {
+ if (log_ent.max_life == NULL) {
+ ent.max_life = NULL;
+ } else {
+ if (ent.max_life == NULL) {
+ ent.max_life = malloc (sizeof(*ent.max_life));
+ if (ent.max_life == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+ *ent.max_life = *log_ent.max_life;
+ }
+ }
+ if ((mask & KADM5_MOD_TIME) && (mask & KADM5_MOD_NAME)) {
+ if (ent.modified_by == NULL) {
+ ent.modified_by = malloc(sizeof(*ent.modified_by));
+ if (ent.modified_by == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ } else
+ free_Event(ent.modified_by);
+ ret = copy_Event(log_ent.modified_by, ent.modified_by);
+ if (ret) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+ if (mask & KADM5_KVNO) {
+ ent.kvno = log_ent.kvno;
+ }
+ if (mask & KADM5_MKVNO) {
+ krb5_warnx(context->context, "Unimplemented mask KADM5_KVNO");
+ }
+ if (mask & KADM5_AUX_ATTRIBUTES) {
+ krb5_warnx(context->context,
+ "Unimplemented mask KADM5_AUX_ATTRIBUTES");
+ }
+ if (mask & KADM5_POLICY_CLR) {
+ krb5_warnx(context->context, "Unimplemented mask KADM5_POLICY_CLR");
+ }
+ if (mask & KADM5_MAX_RLIFE) {
+ if (log_ent.max_renew == NULL) {
+ ent.max_renew = NULL;
+ } else {
+ if (ent.max_renew == NULL) {
+ ent.max_renew = malloc (sizeof(*ent.max_renew));
+ if (ent.max_renew == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out;
+ }
+ }
+ *ent.max_renew = *log_ent.max_renew;
+ }
+ }
+ if (mask & KADM5_LAST_SUCCESS) {
+ krb5_warnx(context->context, "Unimplemented mask KADM5_LAST_SUCCESS");
+ }
+ if (mask & KADM5_LAST_FAILED) {
+ krb5_warnx(context->context, "Unimplemented mask KADM5_LAST_FAILED");
+ }
+ if (mask & KADM5_FAIL_AUTH_COUNT) {
+ krb5_warnx(context->context,
+ "Unimplemented mask KADM5_FAIL_AUTH_COUNT");
+ }
+ if (mask & KADM5_KEY_DATA) {
+ size_t num;
+ size_t i;
+
+ /*
+ * We don't need to do anything about key history here because
+ * the log entry contains a complete entry, including hdb
+ * extensions. We do need to make sure that KADM5_TL_DATA is in
+ * the mask though, since that's what it takes to update the
+ * extensions (see below).
+ */
+ mask |= KADM5_TL_DATA;
+
+ for (i = 0; i < ent.keys.len; ++i)
+ free_Key(&ent.keys.val[i]);
+ free (ent.keys.val);
+
+ num = log_ent.keys.len;
+
+ ent.keys.len = num;
+ ent.keys.val = malloc(len * sizeof(*ent.keys.val));
+ if (ent.keys.val == NULL) {
+ krb5_enomem(context->context);
+ goto out;
+ }
+ for (i = 0; i < ent.keys.len; ++i) {
+ ret = copy_Key(&log_ent.keys.val[i],
+ &ent.keys.val[i]);
+ if (ret) {
+ krb5_set_error_message(context->context, ret, "out of memory");
+ goto out;
+ }
+ }
+ }
+ if ((mask & KADM5_TL_DATA) && log_ent.etypes) {
+ if (ent.etypes)
+ free_HDB_EncTypeList(ent.etypes);
+ free(ent.etypes);
+ ent.etypes = calloc(1, sizeof(*ent.etypes));
+ if (ent.etypes == NULL)
+ ret = ENOMEM;
+ if (ret == 0)
+ ret = copy_HDB_EncTypeList(log_ent.etypes, ent.etypes);
+ if (ret) {
+ ret = krb5_enomem(context->context);
+ free(ent.etypes);
+ ent.etypes = NULL;
+ goto out;
+ }
+ }
+
+ if ((mask & KADM5_TL_DATA) && log_ent.extensions) {
+ if (ent.extensions) {
+ free_HDB_extensions(ent.extensions);
+ free(ent.extensions);
+ ent.extensions = NULL;
+ }
+
+ ent.extensions = calloc(1, sizeof(*ent.extensions));
+ if (ent.extensions == NULL)
+ ret = ENOMEM;
+
+ if (ret == 0)
+ ret = copy_HDB_extensions(log_ent.extensions,
+ ent.extensions);
+ if (ret) {
+ ret = krb5_enomem(context->context);
+ free(ent.extensions);
+ ent.extensions = NULL;
+ goto out;
+ }
+ }
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_REPLACE, &ent);
+ out:
+ hdb_free_entry(context->context, context->db, &ent);
+ hdb_free_entry(context->context, context->db, &log_ent);
+ return ret;
+}
+
+/*
+ * Update the first entry (which should be a `nop'), the "uber-entry".
+ */
+static kadm5_ret_t
+log_update_uber(kadm5_server_context *context, off_t off)
+{
+ kadm5_log_context *log_context = &context->log_context;
+ kadm5_ret_t ret = 0;
+ krb5_storage *sp, *mem_sp;
+ krb5_data data;
+ uint32_t op, len;
+ ssize_t bytes;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return 0;
+
+ if (log_context->read_only)
+ return EROFS;
+
+ krb5_data_zero(&data);
+
+ mem_sp = krb5_storage_emem();
+ if (mem_sp == NULL)
+ return krb5_enomem(context->context);
+
+ sp = krb5_storage_from_fd(log_context->log_fd);
+ if (sp == NULL) {
+ krb5_storage_free(mem_sp);
+ return krb5_enomem(context->context);
+ }
+
+ /* Skip first entry's version and timestamp */
+ if (krb5_storage_seek(sp, 8, SEEK_SET) == -1) {
+ ret = errno;
+ goto out;
+ }
+
+ /* If the first entry is not a nop, there's nothing we can do here */
+ ret = krb5_ret_uint32(sp, &op);
+ if (ret || op != kadm_nop)
+ goto out;
+
+ /* If the first entry is not a 16-byte nop, ditto */
+ ret = krb5_ret_uint32(sp, &len);
+ if (ret || len != LOG_UBER_LEN)
+ goto out;
+
+ /*
+ * Try to make the writes here as close to atomic as possible: a
+ * single write() call.
+ */
+ ret = krb5_store_uint64(mem_sp, off);
+ if (ret)
+ goto out;
+ ret = krb5_store_uint32(mem_sp, log_context->last_time);
+ if (ret)
+ goto out;
+ ret = krb5_store_uint32(mem_sp, log_context->version);
+ if (ret)
+ goto out;
+
+ krb5_storage_to_data(mem_sp, &data);
+ bytes = krb5_storage_write(sp, data.data, data.length);
+ if (bytes < 0)
+ ret = errno;
+ else if (bytes != data.length)
+ ret = EIO;
+
+ /*
+ * We don't fsync() this write because we can recover if the write
+ * doesn't complete, though for now we don't have code for properly
+ * dealing with the offset not getting written completely.
+ *
+ * We should probably have two copies of the offset so we can use
+ * one copy to verify the other, and when they don't match we could
+ * traverse the whole log forwards, replaying just the last entry.
+ */
+
+out:
+ if (ret == 0)
+ kadm5_log_signal_master(context);
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ krb5_storage_free(mem_sp);
+ if (lseek(log_context->log_fd, off, SEEK_SET) == -1)
+ ret = ret ? ret : errno;
+
+ return ret;
+}
+
+/*
+ * Add a `nop' operation to the log. Does not close the log.
+ */
+kadm5_ret_t
+kadm5_log_nop(kadm5_server_context *context, enum kadm_nop_type nop_type)
+{
+ krb5_storage *sp;
+ kadm5_ret_t ret;
+ kadm5_log_context *log_context = &context->log_context;
+ off_t off;
+ uint32_t vno = log_context->version;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return 0;
+
+ off = lseek(log_context->log_fd, 0, SEEK_CUR);
+ if (off == -1)
+ return errno;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return krb5_enomem(context->context);
+
+ ret = kadm5_log_preamble(context, sp, kadm_nop, off == 0 ? 0 : vno + 1);
+ if (ret)
+ goto out;
+
+ if (off == 0) {
+ /*
+ * First entry (uber-entry) gets room for offset of next new
+ * entry and time and version of last entry.
+ */
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN);
+ /* These get overwritten with the same values below */
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, LOG_UBER_SZ);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, log_context->last_time);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, vno);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN);
+ } else if (nop_type == kadm_nop_plain) {
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0);
+ } else {
+ ret = krb5_store_uint32(sp, sizeof(uint32_t));
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, nop_type);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, sizeof(uint32_t));
+ }
+
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp, off == 0 ? 0 : vno + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+
+ if (ret == 0 && off == 0 && nop_type != kadm_nop_plain)
+ ret = kadm5_log_nop(context, nop_type);
+
+ if (ret == 0 && off != 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+
+out:
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Read a `nop' log operation from `sp' and "apply" it (there's nothing
+ * to do).
+ *
+ * FIXME Actually, if the nop payload is 4 bytes and contains an enum
+ * kadm_nop_type value of kadm_nop_trunc then we should truncate the
+ * log, and if it contains a kadm_nop_close then we should rename a new
+ * log into place. However, this is not implemented yet.
+ */
+static kadm5_ret_t
+kadm5_log_replay_nop(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
+{
+ return 0;
+}
+
+struct replay_cb_data {
+ size_t count;
+ uint32_t ver;
+ enum kadm_recover_mode mode;
+};
+
+
+/*
+ * Recover or perform the initial commit of an unconfirmed log entry
+ */
+static kadm5_ret_t
+recover_replay(kadm5_server_context *context,
+ uint32_t ver, time_t timestamp, enum kadm_ops op,
+ uint32_t len, krb5_storage *sp, void *ctx)
+{
+ struct replay_cb_data *data = ctx;
+ kadm5_ret_t ret;
+ off_t off;
+
+ /* On initial commit there must be just one pending unconfirmed entry */
+ if (data->count > 0 && data->mode == kadm_recover_commit)
+ return KADM5_LOG_CORRUPT;
+
+ /* We're at the start of the payload; compute end of entry offset */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR) + len + LOG_TRAILER_SZ;
+
+ /* We cannot perform log recovery on LDAP and such backends */
+ if (data->mode == kadm_recover_replay &&
+ (context->db->hdb_capability_flags & HDB_CAP_F_SHARED_DIRECTORY))
+ ret = 0;
+ else
+ ret = kadm5_log_replay(context, op, ver, len, sp);
+ switch (ret) {
+ case HDB_ERR_NOENTRY:
+ case HDB_ERR_EXISTS:
+ if (data->mode != kadm_recover_replay)
+ return ret;
+ case 0:
+ break;
+ case KADM5_LOG_CORRUPT:
+ return -1;
+ default:
+ krb5_warn(context->context, ret, "unexpected error while replaying");
+ return -1;
+ }
+ data->count++;
+ data->ver = ver;
+
+ /*
+ * With replay we may be making multiple HDB changes. We must sync the
+ * confirmation of each one before moving on to the next. Otherwise, we
+ * might attempt to replay multiple already applied updates, and this may
+ * introduce unintended intermediate states or fail to yield the same final
+ * result.
+ */
+ kadm5_log_set_version(context, ver);
+ ret = log_update_uber(context, off);
+ if (ret == 0 && data->mode != kadm_recover_commit)
+ ret = krb5_storage_fsync(sp);
+ return ret;
+}
+
+
+kadm5_ret_t
+kadm5_log_recover(kadm5_server_context *context, enum kadm_recover_mode mode)
+{
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ struct replay_cb_data replay_data;
+
+ replay_data.count = 0;
+ replay_data.ver = 0;
+ replay_data.mode = mode;
+
+ sp = krb5_storage_from_fd(context->log_context.log_fd);
+ if (sp == NULL)
+ return errno ? errno : EIO;
+ ret = kadm5_log_goto_end(context, sp);
+
+ if (ret == 0)
+ ret = kadm5_log_foreach(context, kadm_forward | kadm_unconfirmed,
+ NULL, recover_replay, &replay_data);
+ if (ret == 0 && mode == kadm_recover_commit && replay_data.count != 1)
+ ret = KADM5_LOG_CORRUPT;
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Call `func' for each log record in the log in `context'.
+ *
+ * `func' is optional.
+ *
+ * If `func' returns -1 then log traversal terminates and this returns 0.
+ * Otherwise `func''s return is returned if there are no other errors.
+ */
+kadm5_ret_t
+kadm5_log_foreach(kadm5_server_context *context,
+ enum kadm_iter_opts iter_opts,
+ off_t *off_lastp,
+ kadm5_ret_t (*func)(kadm5_server_context *server_context,
+ uint32_t ver, time_t timestamp,
+ enum kadm_ops op, uint32_t len,
+ krb5_storage *sp, void *ctx),
+ void *ctx)
+{
+ kadm5_ret_t ret = 0;
+ int fd = context->log_context.log_fd;
+ krb5_storage *sp;
+ off_t off_last;
+ off_t this_entry = 0;
+ off_t log_end = 0;
+
+ if (strcmp(context->log_context.log_file, "/dev/null") == 0)
+ return 0;
+
+ if (off_lastp == NULL)
+ off_lastp = &off_last;
+ *off_lastp = -1;
+
+ if (((iter_opts & kadm_forward) && (iter_opts & kadm_backward)) ||
+ (!(iter_opts & kadm_confirmed) && !(iter_opts & kadm_unconfirmed)))
+ return EINVAL;
+
+ if ((iter_opts & kadm_forward) && (iter_opts & kadm_confirmed) &&
+ (iter_opts & kadm_unconfirmed)) {
+ /*
+ * We want to traverse all log entries, confirmed or not, from
+ * the start, then there's no need to kadm5_log_goto_end()
+ * -- no reason to try to find the end.
+ */
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ return errno ? errno : ENOMEM;
+
+ log_end = krb5_storage_seek(sp, 0, SEEK_END);
+ if (log_end == -1 ||
+ krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_storage_free(sp);
+ return ret;
+ }
+ } else {
+ /* Get the end of the log based on the uber entry */
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ return errno ? errno : ENOMEM;
+ ret = kadm5_log_goto_end(context, sp);
+ if (ret != 0)
+ return ret;
+ log_end = krb5_storage_seek(sp, 0, SEEK_CUR);
+ }
+
+ *off_lastp = log_end;
+
+ if ((iter_opts & kadm_forward) && (iter_opts & kadm_confirmed)) {
+ /* Start at the beginning */
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_storage_free(sp);
+ return ret;
+ }
+ } else if ((iter_opts & kadm_backward) && (iter_opts & kadm_unconfirmed)) {
+ /*
+ * We're at the confirmed end but need to be at the unconfirmed
+ * end. Skip forward to the real end, re-entering to do it.
+ */
+ ret = kadm5_log_foreach(context, kadm_forward | kadm_unconfirmed,
+ &log_end, NULL, NULL);
+ if (ret)
+ return ret;
+ if (krb5_storage_seek(sp, log_end, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_storage_free(sp);
+ return ret;
+ }
+ }
+
+ for (;;) {
+ uint32_t ver, ver2, len, len2;
+ uint32_t tstamp;
+ time_t timestamp;
+ enum kadm_ops op;
+
+ if ((iter_opts & kadm_backward)) {
+ off_t o;
+
+ o = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (o == 0 ||
+ ((iter_opts & kadm_unconfirmed) && o <= *off_lastp))
+ break;
+ ret = kadm5_log_previous(context->context, sp, &ver,
+ &timestamp, &op, &len);
+ if (ret)
+ break;
+
+ /* Offset is now at payload of current entry */
+
+ o = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (o == -1) {
+ ret = errno;
+ break;
+ }
+ this_entry = o - LOG_HEADER_SZ;
+ if (this_entry < 0) {
+ ret = KADM5_LOG_CORRUPT;
+ break;
+ }
+ } else {
+ /* Offset is now at start of current entry, read header */
+ this_entry = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (!(iter_opts & kadm_unconfirmed) && this_entry == log_end)
+ break;
+ ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+ if (ret == HEIM_ERR_EOF) {
+ ret = 0;
+ break;
+ }
+ timestamp = tstamp;
+ if (ret)
+ break;
+ /* Offset is now at payload of current entry */
+ }
+
+ /* Validate trailer before calling the callback */
+ if (krb5_storage_seek(sp, len, SEEK_CUR) == -1) {
+ ret = errno;
+ break;
+ }
+
+ ret = krb5_ret_uint32(sp, &len2);
+ if (ret)
+ break;
+ ret = krb5_ret_uint32(sp, &ver2);
+ if (ret)
+ break;
+ if (len != len2 || ver != ver2) {
+ ret = KADM5_LOG_CORRUPT;
+ break;
+ }
+
+ /* Rewind to start of payload and call callback if we have one */
+ if (krb5_storage_seek(sp, this_entry + LOG_HEADER_SZ,
+ SEEK_SET) == -1) {
+ ret = errno;
+ break;
+ }
+
+ if (func != NULL) {
+ ret = (*func)(context, ver, timestamp, op, len, sp, ctx);
+ if (ret) {
+ /* Callback signals desire to stop by returning -1 */
+ if (ret == -1)
+ ret = 0;
+ break;
+ }
+ }
+ if ((iter_opts & kadm_forward)) {
+ off_t o;
+
+ o = krb5_storage_seek(sp, this_entry+LOG_WRAPPER_SZ+len, SEEK_SET);
+ if (o == -1) {
+ ret = errno;
+ break;
+ }
+ if (o > log_end)
+ *off_lastp = o;
+ } else if ((iter_opts & kadm_backward)) {
+ /*
+ * Rewind to the start of this entry so kadm5_log_previous()
+ * can find the previous one.
+ */
+ if (krb5_storage_seek(sp, this_entry, SEEK_SET) == -1) {
+ ret = errno;
+ break;
+ }
+ }
+ }
+ if ((ret == HEIM_ERR_EOF || ret == KADM5_LOG_CORRUPT) &&
+ (iter_opts & kadm_forward) &&
+ context->log_context.lock_mode == LOCK_EX) {
+ /*
+ * Truncate partially written last log entry so we can write
+ * again.
+ */
+ ret = krb5_storage_truncate(sp, this_entry);
+ if (ret == 0 &&
+ krb5_storage_seek(sp, this_entry, SEEK_SET) == -1)
+ ret = errno;
+ krb5_warnx(context->context, "Truncating log at partial or "
+ "corrupt %s entry",
+ this_entry > log_end ? "unconfirmed" : "confirmed");
+ }
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Go to the first record, which, if we have an uber record, will be
+ * the second record.
+ */
+kadm5_ret_t
+kadm5_log_goto_first(kadm5_server_context *server_context, krb5_storage *sp)
+{
+ enum kadm_ops op;
+ uint32_t ver, len;
+ kadm5_ret_t ret;
+
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
+ return KADM5_LOG_CORRUPT;
+
+ ret = get_header(sp, LOG_DOPEEK, &ver, NULL, &op, &len);
+ if (ret)
+ return ret;
+ if (op == kadm_nop && len == LOG_UBER_LEN && seek_next(sp) == -1)
+ return KADM5_LOG_CORRUPT;
+ return 0;
+}
+
+/*
+ * Go to end of log.
+ */
+kadm5_ret_t
+kadm5_log_goto_end(kadm5_server_context *server_context, krb5_storage *sp)
+{
+ krb5_error_code ret = 0;
+ enum kadm_ops op;
+ uint32_t ver, len;
+ uint32_t tstamp;
+ uint64_t off;
+
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
+ return errno;
+ ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+ if (ret == HEIM_ERR_EOF) {
+ (void) krb5_storage_seek(sp, 0, SEEK_SET);
+ return 0;
+ }
+ if (ret == KADM5_LOG_CORRUPT)
+ goto truncate;
+ if (ret)
+ return ret;
+
+ if (op == kadm_nop && len == LOG_UBER_LEN) {
+ /* New style log */
+ ret = krb5_ret_uint64(sp, &off);
+ if (ret)
+ goto truncate;
+
+ if (krb5_storage_seek(sp, off, SEEK_SET) == -1)
+ return ret;
+
+ if (off >= LOG_UBER_SZ) {
+ ret = get_version_prev(sp, &ver, NULL);
+ if (ret == 0)
+ return 0;
+ }
+ /* Invalid offset in uber entry */
+ goto truncate;
+ }
+
+ /* Old log with no uber entry */
+ if (krb5_storage_seek(sp, 0, SEEK_END) == -1) {
+ static int warned = 0;
+ if (!warned) {
+ warned = 1;
+ krb5_warnx(server_context->context,
+ "Old log found; truncate it to upgrade");
+ }
+ }
+ ret = get_version_prev(sp, &ver, NULL);
+ if (ret)
+ goto truncate;
+ return 0;
+
+truncate:
+ /* If we can, truncate */
+ if (server_context->log_context.lock_mode == LOCK_EX) {
+ ret = kadm5_log_reinit(server_context, 0);
+ if (ret == 0) {
+ krb5_warn(server_context->context, ret,
+ "Invalid log; truncating to recover");
+ if (krb5_storage_seek(sp, 0, SEEK_END) >= 0)
+ return 0;
+ }
+ }
+ ret = KADM5_LOG_CORRUPT;
+ krb5_warn(server_context->context, ret,
+ "Invalid log; truncate to recover");
+ return ret;
+}
+
+/*
+ * Return the next log entry.
+ *
+ * The pointer in `sp' is assumed to be at the end of an entry. On success,
+ * the `sp' pointer is set to the next entry (not the data portion). In case
+ * of error, it's not changed at all.
+ */
+kadm5_ret_t
+kadm5_log_next(krb5_context context,
+ krb5_storage *sp,
+ uint32_t *verp,
+ time_t *tstampp,
+ enum kadm_ops *opp,
+ uint32_t *lenp)
+{
+ uint32_t len = 0;
+ uint32_t len2 = 0;
+ uint32_t ver = verp ? *verp : 0;
+ uint32_t ver2;
+ uint32_t tstamp = tstampp ? *tstampp : 0;
+ enum kadm_ops op = kadm_nop;
+ off_t off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ kadm5_ret_t ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+
+ /* Validate the trailer */
+ if (ret == 0 && krb5_storage_seek(sp, len, SEEK_CUR) == -1)
+ ret = errno;
+
+ if (ret == 0)
+ ret = krb5_ret_uint32(sp, &len2);
+ if (ret == 0)
+ ret = krb5_ret_uint32(sp, &ver2);
+ if (ret == 0 && (len != len2 || ver != ver2))
+ ret = KADM5_LOG_CORRUPT;
+ if (ret != 0) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return ret;
+ }
+
+ if (verp)
+ *verp = ver;
+ if (tstampp)
+ *tstampp = tstamp;
+ if (opp)
+ *opp = op;
+ if (lenp)
+ *lenp = len;
+ return 0;
+}
+
+/*
+ * Return previous log entry.
+ *
+ * The pointer in `sp' is assumed to be at the top of the entry after
+ * previous entry (e.g., at EOF). On success, the `sp' pointer is set to
+ * data portion of previous entry. In case of error, it's not changed
+ * at all.
+ */
+kadm5_ret_t
+kadm5_log_previous(krb5_context context,
+ krb5_storage *sp,
+ uint32_t *verp,
+ time_t *tstampp,
+ enum kadm_ops *opp,
+ uint32_t *lenp)
+{
+ krb5_error_code ret;
+ off_t oldoff;
+ uint32_t ver2, len2;
+ uint32_t tstamp;
+
+ oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (oldoff == -1)
+ goto log_corrupt;
+
+ /* This reads the physical version of the uber record */
+ if (seek_prev(sp, verp, lenp) == -1)
+ goto log_corrupt;
+
+ ret = get_header(sp, LOG_NOPEEK, &ver2, &tstamp, opp, &len2);
+ if (ret) {
+ (void) krb5_storage_seek(sp, oldoff, SEEK_SET);
+ return ret;
+ }
+ if (tstampp)
+ *tstampp = tstamp;
+ if (ver2 != *verp || len2 != *lenp)
+ goto log_corrupt;
+
+ return 0;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, oldoff, SEEK_SET);
+ return KADM5_LOG_CORRUPT;
+}
+
+/*
+ * Replay a record from the log
+ */
+
+kadm5_ret_t
+kadm5_log_replay(kadm5_server_context *context,
+ enum kadm_ops op,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
+{
+ switch (op) {
+ case kadm_create :
+ return kadm5_log_replay_create(context, ver, len, sp);
+ case kadm_delete :
+ return kadm5_log_replay_delete(context, ver, len, sp);
+ case kadm_rename :
+ return kadm5_log_replay_rename(context, ver, len, sp);
+ case kadm_modify :
+ return kadm5_log_replay_modify(context, ver, len, sp);
+ case kadm_nop :
+ return kadm5_log_replay_nop(context, ver, len, sp);
+ default :
+ /*
+ * FIXME This default arm makes it difficult to add new kadm_ops
+ * values.
+ */
+ krb5_set_error_message(context->context, KADM5_FAILURE,
+ "Unsupported replay op %d", (int)op);
+ (void) krb5_storage_seek(sp, len, SEEK_CUR);
+ return KADM5_FAILURE;
+ }
+}
+
+struct load_entries_data {
+ krb5_data *entries;
+ unsigned char *p;
+ uint32_t first;
+ uint32_t last;
+ size_t bytes;
+ size_t nentries;
+ size_t maxbytes;
+ size_t maxentries;
+};
+
+
+/*
+ * Prepend one entry with header and trailer to the entry buffer, stopping when
+ * we've reached either of the byte or entry-count limits (if non-zero).
+ *
+ * This is a two-pass algorithm:
+ *
+ * In the first pass, when entries->entries == NULL, we compute the space
+ * required, and count the entries that fit up from zero.
+ *
+ * In the second pass we fill the buffer, and count the entries back down to
+ * zero. The space used must be an exact fit, and the number of entries must
+ * reach zero at that point or an error is returned.
+ *
+ * The caller MUST check that entries->nentries == 0 at the end of the second
+ * pass.
+ */
+static kadm5_ret_t
+load_entries_cb(kadm5_server_context *server_context,
+ uint32_t ver,
+ time_t timestamp,
+ enum kadm_ops op,
+ uint32_t len,
+ krb5_storage *sp,
+ void *ctx)
+{
+ struct load_entries_data *entries = ctx;
+ kadm5_ret_t ret;
+ ssize_t bytes;
+ size_t entry_len = len + LOG_WRAPPER_SZ;
+ unsigned char *base;
+
+ if (entries->entries == NULL) {
+ size_t total = entries->bytes + entry_len;
+
+ /*
+ * First run: find the size of krb5_data buffer needed.
+ *
+ * If the log was huge we'd have to perhaps open a temp file for this.
+ * For now KISS.
+ */
+ if ((op == kadm_nop && entry_len == LOG_UBER_SZ) ||
+ entry_len < len /*overflow?*/ ||
+ (entries->maxbytes > 0 && total > entries->maxbytes) ||
+ total < entries->bytes /*overflow?*/ ||
+ (entries->maxentries > 0 && entries->nentries == entries->maxentries))
+ return -1; /* stop iteration */
+ entries->bytes = total;
+ entries->first = ver;
+ if (entries->nentries++ == 0)
+ entries->last = ver;
+ return 0;
+ }
+
+ /* Second run: load the data into memory */
+ base = (unsigned char *)entries->entries->data;
+ if (entries->p - base < entry_len && entries->p != base) {
+ /*
+ * This can't happen normally: we stop the log record iteration
+ * above before we get here. This could happen if someone wrote
+ * garbage to the log while we were traversing it. We return an
+ * error instead of asserting.
+ */
+ return KADM5_LOG_CORRUPT;
+ }
+
+ /*
+ * sp here is a krb5_storage_from_fd() of the log file, and the
+ * offset pointer points at the current log record payload.
+ *
+ * Seek back to the start of the record poayload so we can read the
+ * whole record.
+ */
+ if (krb5_storage_seek(sp, -LOG_HEADER_SZ, SEEK_CUR) == -1)
+ return errno;
+
+ /*
+ * We read the header, payload, and trailer into the buffer we have, that
+ * many bytes before the previous record we read.
+ */
+ errno = 0;
+ bytes = krb5_storage_read(sp, entries->p - entry_len, entry_len);
+ ret = errno;
+ if (bytes < 0 || bytes != entry_len)
+ return ret ? ret : EIO;
+
+ entries->first = ver;
+ --entries->nentries;
+ entries->p -= entry_len;
+ return (entries->p == base) ? -1 : 0;
+}
+
+
+/*
+ * Serialize a tail fragment of the log as a krb5_data, this is constrained to
+ * at most `maxbytes' bytes and to at most `maxentries' entries if not zero.
+ */
+static kadm5_ret_t
+load_entries(kadm5_server_context *context, krb5_data *p,
+ size_t maxentries, size_t maxbytes,
+ uint32_t *first, uint32_t *last)
+{
+ struct load_entries_data entries;
+ kadm5_ret_t ret;
+ unsigned char *base;
+
+ krb5_data_zero(p);
+
+ *first = 0;
+
+ memset(&entries, 0, sizeof(entries));
+ entries.entries = NULL;
+ entries.p = NULL;
+ entries.maxentries = maxentries;
+ entries.maxbytes = maxbytes;
+
+ /* Figure out how many bytes it will take */
+ ret = kadm5_log_foreach(context, kadm_backward | kadm_confirmed,
+ NULL, load_entries_cb, &entries);
+ if (ret)
+ return ret;
+
+ /*
+ * If no entries fit our limits, we do not truncate, instead the caller can
+ * call kadm5_log_reinit() if desired.
+ */
+ if (entries.bytes == 0)
+ return 0;
+
+ ret = krb5_data_alloc(p, entries.bytes);
+ if (ret)
+ return ret;
+
+ *first = entries.first;
+ *last = entries.last;
+ entries.entries = p;
+ base = (unsigned char *)entries.entries->data;
+ entries.p = base + entries.bytes;
+
+ ret = kadm5_log_foreach(context, kadm_backward | kadm_confirmed,
+ NULL, load_entries_cb, &entries);
+ if (ret == 0 &&
+ (entries.nentries || entries.p != base || entries.first != *first))
+ ret = KADM5_LOG_CORRUPT;
+ if (ret)
+ krb5_data_free(p);
+ return ret;
+}
+
+/*
+ * Truncate the log, retaining at most `keep' entries and at most `maxbytes'.
+ * If `maxbytes' is zero, keep at most the default log size limit.
+ */
+kadm5_ret_t
+kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
+{
+ kadm5_ret_t ret;
+ uint32_t first, last, last_tstamp;
+ time_t now = time(NULL);
+ krb5_data entries;
+ krb5_storage *sp;
+ ssize_t bytes;
+ uint64_t sz;
+ off_t off;
+
+ if (maxbytes == 0)
+ maxbytes = get_max_log_size(context->context);
+
+ if (strcmp(context->log_context.log_file, "/dev/null") == 0)
+ return 0;
+
+ if (context->log_context.read_only)
+ return EROFS;
+
+ /* Get the desired records. */
+ krb5_data_zero(&entries);
+ ret = load_entries(context, &entries, keep, maxbytes, &first, &last);
+ if (ret)
+ return ret;
+
+ if (first == 0) {
+ /*
+ * No records found/fit within resource limits. The caller should call
+ * kadm5_log_reinit(context) to truly truncate and reset the log to
+ * version 0, else call again with better limits.
+ */
+ krb5_data_free(&entries);
+ return EINVAL;
+ }
+
+ /* Check that entries.length won't overflow off_t */
+ sz = LOG_UBER_SZ + entries.length;
+ off = (off_t)sz;
+ if (off < 0 || off != sz || sz < entries.length) {
+ krb5_data_free(&entries);
+ return EOVERFLOW; /* caller should ask for fewer entries */
+ }
+
+ /* Truncate to zero size and seek to zero offset */
+ if (ftruncate(context->log_context.log_fd, 0) < 0 ||
+ lseek(context->log_context.log_fd, 0, SEEK_SET) < 0) {
+ krb5_data_free(&entries);
+ return errno;
+ }
+
+ /*
+ * Write the uber record and then the records loaded. Confirm the entries
+ * after writing them.
+ *
+ * If we crash then the log may not have all the entries we want, and
+ * replaying only some of the entries will leave us in a bad state.
+ * Additionally, we don't have mathematical proof that replaying the last
+ * N>1 entries is always idempotent. And though we believe we can make
+ * such replays idempotent, they would still leave the HDB with
+ * intermediate states that would not have occurred on the master.
+ *
+ * By initially setting the offset in the uber record to 0, the log will be
+ * seen as invalid should we crash here, thus the only
+ * harm will be that we'll reinitialize the log and force full props.
+ *
+ * We can't use the normal kadm5_log_*() machinery for this because
+ * we must set specific version numbers and timestamps. To keep
+ * things simple we don't try to do a single atomic write here as we
+ * do in kadm5_log_flush().
+ *
+ * We really do want to keep the new first entry's version and
+ * timestamp so we don't trip up iprop.
+ *
+ * Keep this in sync with kadm5_log_nop().
+ */
+ sp = krb5_storage_from_fd(context->log_context.log_fd);
+ if (sp == NULL) {
+ ret = errno;
+ krb5_warn(context->context, ret, "Unable to keep entries");
+ krb5_data_free(&entries);
+ return errno;
+ }
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, now);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, kadm_nop); /* end of preamble */
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN); /* end of header */
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, LOG_UBER_SZ);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, now);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, last);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0); /* end of trailer */
+ if (ret == 0) {
+ bytes = krb5_storage_write(sp, entries.data, entries.length);
+ if (bytes != entries.length)
+ ret = bytes == -1 ? errno : EIO;
+ }
+ if (ret == 0)
+ ret = krb5_storage_fsync(sp);
+ /* Confirm all the records now */
+ if (ret == 0) {
+ if (krb5_storage_seek(sp, LOG_HEADER_SZ, SEEK_SET) == -1)
+ ret = errno;
+ }
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, off);
+ krb5_data_free(&entries);
+ krb5_storage_free(sp);
+
+ if (ret) {
+ krb5_warn(context->context, ret, "Unable to keep entries");
+ (void) ftruncate(context->log_context.log_fd, LOG_UBER_SZ);
+ (void) lseek(context->log_context.log_fd, 0, SEEK_SET);
+ return ret;
+ }
+
+ /* Done. Now rebuild the log_context state. */
+ (void) lseek(context->log_context.log_fd, off, SEEK_SET);
+ sp = krb5_storage_from_fd(context->log_context.log_fd);
+ if (sp == NULL)
+ return errno ? errno : krb5_enomem(context->context);
+ ret = kadm5_log_goto_end(context, sp);
+ if (ret == 0) {
+ ret = get_version_prev(sp, &context->log_context.version, &last_tstamp);
+ if (ret == 0)
+ context->log_context.last_time = last_tstamp;
+ }
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * "Truncate" the log if not read only and over the desired maximum size. We
+ * attempt to retain 1/4 of the existing storage.
+ *
+ * Called after successful log recovery, so at this point we must have no
+ * unconfirmed entries in the log.
+ */
+static kadm5_ret_t
+truncate_if_needed(kadm5_server_context *context)
+{
+ kadm5_ret_t ret = 0;
+ kadm5_log_context *log_context = &context->log_context;
+ size_t maxbytes;
+ struct stat st;
+
+ if (log_context->log_fd == -1 || log_context->read_only)
+ return 0;
+ if (strcmp(context->log_context.log_file, "/dev/null") == 0)
+ return 0;
+
+ maxbytes = get_max_log_size(context->context);
+ if (maxbytes <= 0)
+ return 0;
+
+ if (fstat(log_context->log_fd, &st) == -1)
+ return errno;
+ if (st.st_size == (size_t)st.st_size && (size_t)st.st_size <= maxbytes)
+ return 0;
+
+ /* Shrink the log by a factor of 4 */
+ ret = kadm5_log_truncate(context, 0, maxbytes/4);
+ return ret == EINVAL ? 0 : ret;
+}
+
+#ifndef NO_UNIX_SOCKETS
+
+static char *default_signal = NULL;
+static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+const char *
+kadm5_log_signal_socket(krb5_context context)
+{
+ int ret = 0;
+
+ HEIMDAL_MUTEX_lock(&signal_mutex);
+ if (!default_signal)
+ ret = asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
+ if (ret == -1)
+ default_signal = NULL;
+ HEIMDAL_MUTEX_unlock(&signal_mutex);
+
+ return krb5_config_get_string_default(context,
+ NULL,
+ default_signal,
+ "kdc",
+ "signal_socket",
+ NULL);
+}
+
+#else /* NO_UNIX_SOCKETS */
+
+#define SIGNAL_SOCKET_HOST "127.0.0.1"
+#define SIGNAL_SOCKET_PORT "12701"
+
+kadm5_ret_t
+kadm5_log_signal_socket_info(krb5_context context,
+ int server_end,
+ struct addrinfo **ret_addrs)
+{
+ struct addrinfo hints;
+ struct addrinfo *addrs = NULL;
+ kadm5_ret_t ret = KADM5_FAILURE;
+ int wsret;
+
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_flags = AI_NUMERICHOST;
+ if (server_end)
+ hints.ai_flags |= AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ wsret = getaddrinfo(SIGNAL_SOCKET_HOST,
+ SIGNAL_SOCKET_PORT,
+ &hints, &addrs);
+
+ if (wsret != 0) {
+ krb5_set_error_message(context, KADM5_FAILURE,
+ "%s", gai_strerror(wsret));
+ goto done;
+ }
+
+ if (addrs == NULL) {
+ krb5_set_error_message(context, KADM5_FAILURE,
+ "getaddrinfo() failed to return address list");
+ goto done;
+ }
+
+ *ret_addrs = addrs;
+ addrs = NULL;
+ ret = 0;
+
+ done:
+ if (addrs)
+ freeaddrinfo(addrs);
+ return ret;
+}
+
+#endif
diff --git a/third_party/heimdal/lib/kadm5/marshall.c b/third_party/heimdal/lib/kadm5/marshall.c
new file mode 100644
index 0000000..849698f
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/marshall.c
@@ -0,0 +1,962 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+#define CHECK(e) do { if ((ret = e)) goto out; } while (0)
+
+int
+kadm5_some_keys_are_bogus(size_t n_keys, krb5_key_data *keys)
+{
+ size_t i;
+
+ for (i = 0; i < n_keys; i++) {
+ krb5_key_data *key = &keys[i];
+ if (key->key_data_length[0] == sizeof(KADM5_BOGUS_KEY_DATA) - 1 &&
+ ct_memcmp(key->key_data_contents[1], KADM5_BOGUS_KEY_DATA,
+ key->key_data_length[0]) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+int
+kadm5_all_keys_are_bogus(size_t n_keys, krb5_key_data *keys)
+{
+ size_t i;
+
+ if (n_keys == 0)
+ return 0;
+
+ for (i = 0; i < n_keys; i++) {
+ krb5_key_data *key = &keys[i];
+ if (key->key_data_length[0] != sizeof(KADM5_BOGUS_KEY_DATA) - 1 ||
+ ct_memcmp(key->key_data_contents[1], KADM5_BOGUS_KEY_DATA,
+ key->key_data_length[0]) != 0)
+ return 0;
+ }
+ return 1;
+}
+
+kadm5_ret_t
+kadm5_store_key_data(krb5_storage *sp,
+ krb5_key_data *key)
+{
+ kadm5_ret_t ret;
+ krb5_data c;
+
+ CHECK(krb5_store_int32(sp, key->key_data_ver));
+ CHECK(krb5_store_int32(sp, key->key_data_kvno));
+ CHECK(krb5_store_int32(sp, key->key_data_type[0]));
+ c.length = key->key_data_length[0];
+ c.data = key->key_data_contents[0];
+ CHECK(krb5_store_data(sp, c));
+ CHECK(krb5_store_int32(sp, key->key_data_type[1]));
+ c.length = key->key_data_length[1];
+ c.data = key->key_data_contents[1];
+ CHECK(krb5_store_data(sp, c));
+
+out:
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_store_fake_key_data(krb5_storage *sp,
+ krb5_key_data *key)
+{
+ kadm5_ret_t ret;
+ krb5_data c;
+
+ CHECK(krb5_store_int32(sp, key->key_data_ver));
+ CHECK(krb5_store_int32(sp, key->key_data_kvno));
+ CHECK(krb5_store_int32(sp, key->key_data_type[0]));
+
+ /*
+ * This is the key contents. We want it to be obvious to the client
+ * (if it really did want the keys) that the key won't work.
+ * 32-bit keys are no good for any enctype, so that should do.
+ * Clients that didn't need keys will ignore this, and clients that
+ * did want keys will either fail or they'll, say, create bogus
+ * keytab entries that will subsequently fail to be useful.
+ */
+ c.length = sizeof (KADM5_BOGUS_KEY_DATA) - 1;
+ c.data = KADM5_BOGUS_KEY_DATA;
+ CHECK(krb5_store_data(sp, c));
+
+ /* This is the salt -- no need to send garbage */
+ CHECK(krb5_store_int32(sp, key->key_data_type[1]));
+ c.length = key->key_data_length[1];
+ c.data = key->key_data_contents[1];
+ CHECK(krb5_store_data(sp, c));
+
+out:
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_ret_key_data(krb5_storage *sp,
+ krb5_key_data *key)
+{
+ kadm5_ret_t ret;
+ krb5_data c;
+ int32_t tmp;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0) {
+ key->key_data_ver = tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ }
+ if (ret == 0) {
+ key->key_data_kvno = tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ }
+ if (ret == 0) {
+ key->key_data_type[0] = tmp;
+ ret = krb5_ret_data(sp, &c);
+ }
+ if (ret == 0) {
+ key->key_data_length[0] = c.length;
+ key->key_data_contents[0] = c.data;
+ ret = krb5_ret_int32(sp, &tmp);
+ }
+ if (ret == 0) {
+ key->key_data_type[1] = tmp;
+ ret = krb5_ret_data(sp, &c);
+ }
+ if (ret == 0) {
+ key->key_data_length[1] = c.length;
+ key->key_data_contents[1] = c.data;
+ return 0;
+ }
+ return KADM5_FAILURE;
+}
+
+kadm5_ret_t
+kadm5_store_tl_data(krb5_storage *sp,
+ krb5_tl_data *tl)
+{
+ kadm5_ret_t ret;
+ krb5_data c;
+
+ CHECK(krb5_store_int32(sp, tl->tl_data_type));
+ c.length = tl->tl_data_length;
+ c.data = tl->tl_data_contents;
+ CHECK(krb5_store_data(sp, c));
+
+out:
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_ret_tl_data(krb5_storage *sp,
+ krb5_tl_data *tl)
+{
+ kadm5_ret_t ret;
+ krb5_data c;
+ int32_t tmp;
+
+ CHECK(krb5_ret_int32(sp, &tmp));
+ tl->tl_data_type = tmp;
+ CHECK(krb5_ret_data(sp, &c));
+ tl->tl_data_length = c.length;
+ tl->tl_data_contents = c.data;
+
+out:
+ return ret;
+}
+
+static kadm5_ret_t
+store_principal_ent(krb5_storage *sp,
+ kadm5_principal_ent_t princ,
+ uint32_t mask, int wkeys)
+{
+ kadm5_ret_t ret = 0;
+ int i;
+
+ if (mask & KADM5_PRINCIPAL)
+ CHECK(krb5_store_principal(sp, princ->principal));
+ if (mask & KADM5_PRINC_EXPIRE_TIME)
+ CHECK(krb5_store_int32(sp, princ->princ_expire_time));
+ if (mask & KADM5_PW_EXPIRATION)
+ CHECK(krb5_store_int32(sp, princ->pw_expiration));
+ if (mask & KADM5_LAST_PWD_CHANGE)
+ CHECK(krb5_store_int32(sp, princ->last_pwd_change));
+ if (mask & KADM5_MAX_LIFE)
+ CHECK(krb5_store_int32(sp, princ->max_life));
+ if (mask & KADM5_MOD_NAME) {
+ CHECK(krb5_store_int32(sp, princ->mod_name != NULL));
+ if(princ->mod_name)
+ CHECK(krb5_store_principal(sp, princ->mod_name));
+ }
+ if (mask & KADM5_MOD_TIME)
+ CHECK(krb5_store_int32(sp, princ->mod_date));
+ if (mask & KADM5_ATTRIBUTES)
+ CHECK(krb5_store_int32(sp, princ->attributes));
+ if (mask & KADM5_KVNO)
+ CHECK(krb5_store_int32(sp, princ->kvno));
+ if (mask & KADM5_MKVNO)
+ CHECK(krb5_store_int32(sp, princ->mkvno));
+ if (mask & KADM5_POLICY) {
+ CHECK(krb5_store_int32(sp, princ->policy != NULL));
+ if(princ->policy)
+ CHECK(krb5_store_string(sp, princ->policy));
+ }
+ if (mask & KADM5_AUX_ATTRIBUTES)
+ CHECK(krb5_store_int32(sp, princ->aux_attributes));
+ if (mask & KADM5_MAX_RLIFE)
+ CHECK(krb5_store_int32(sp, princ->max_renewable_life));
+ if (mask & KADM5_LAST_SUCCESS)
+ CHECK(krb5_store_int32(sp, princ->last_success));
+ if (mask & KADM5_LAST_FAILED)
+ CHECK(krb5_store_int32(sp, princ->last_failed));
+ if (mask & KADM5_FAIL_AUTH_COUNT)
+ CHECK(krb5_store_int32(sp, princ->fail_auth_count));
+ if (mask & KADM5_KEY_DATA) {
+ CHECK(krb5_store_int32(sp, princ->n_key_data));
+ for(i = 0; i < princ->n_key_data; i++) {
+ if (wkeys)
+ CHECK(kadm5_store_key_data(sp, &princ->key_data[i]));
+ else
+ CHECK(kadm5_store_fake_key_data(sp, &princ->key_data[i]));
+ }
+ }
+ if (mask & KADM5_TL_DATA) {
+ krb5_tl_data *tp;
+
+ CHECK(krb5_store_int32(sp, princ->n_tl_data));
+ for (tp = princ->tl_data; tp; tp = tp->tl_data_next)
+ CHECK(kadm5_store_tl_data(sp, tp));
+ }
+
+out:
+ return ret;
+}
+
+
+kadm5_ret_t
+kadm5_store_principal_ent(krb5_storage *sp,
+ kadm5_principal_ent_t princ)
+{
+ return store_principal_ent (sp, princ, ~0, 1);
+}
+
+kadm5_ret_t
+kadm5_store_principal_ent_nokeys(krb5_storage *sp,
+ kadm5_principal_ent_t princ)
+{
+ return store_principal_ent (sp, princ, ~0, 0);
+}
+
+kadm5_ret_t
+kadm5_store_principal_ent_mask(krb5_storage *sp,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ kadm5_ret_t ret;
+
+ ret = krb5_store_int32(sp, mask);
+ if (ret == 0)
+ ret = store_principal_ent(sp, princ, mask, 1);
+ return ret;
+}
+
+static kadm5_ret_t
+ret_principal_ent(krb5_storage *sp,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ kadm5_ret_t ret = 0;
+ int i;
+ int32_t tmp;
+
+ if (mask & KADM5_PRINCIPAL)
+ CHECK(krb5_ret_principal(sp, &princ->principal));
+
+ if (mask & KADM5_PRINC_EXPIRE_TIME) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->princ_expire_time = tmp;
+ }
+ if (mask & KADM5_PW_EXPIRATION) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->pw_expiration = tmp;
+ }
+ if (mask & KADM5_LAST_PWD_CHANGE) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->last_pwd_change = tmp;
+ }
+ if (mask & KADM5_MAX_LIFE) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->max_life = tmp;
+ }
+ if (mask & KADM5_MOD_NAME) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ if(tmp)
+ CHECK(krb5_ret_principal(sp, &princ->mod_name));
+ else
+ princ->mod_name = NULL;
+ }
+ if (mask & KADM5_MOD_TIME) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->mod_date = tmp;
+ }
+ if (mask & KADM5_ATTRIBUTES) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->attributes = tmp;
+ }
+ if (mask & KADM5_KVNO) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->kvno = tmp;
+ }
+ if (mask & KADM5_MKVNO) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->mkvno = tmp;
+ }
+ if (mask & KADM5_POLICY) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ if(tmp)
+ CHECK(krb5_ret_string(sp, &princ->policy));
+ else
+ princ->policy = NULL;
+ }
+ if (mask & KADM5_AUX_ATTRIBUTES) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->aux_attributes = tmp;
+ }
+ if (mask & KADM5_MAX_RLIFE) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->max_renewable_life = tmp;
+ }
+ if (mask & KADM5_LAST_SUCCESS) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->last_success = tmp;
+ }
+ if (mask & KADM5_LAST_FAILED) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->last_failed = tmp;
+ }
+ if (mask & KADM5_FAIL_AUTH_COUNT) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->fail_auth_count = tmp;
+ }
+ if (mask & KADM5_KEY_DATA) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->n_key_data = tmp;
+ princ->key_data = calloc(princ->n_key_data, sizeof(*princ->key_data));
+ if (princ->key_data == NULL && princ->n_key_data != 0)
+ return ENOMEM;
+ for(i = 0; i < princ->n_key_data; i++)
+ CHECK(kadm5_ret_key_data(sp, &princ->key_data[i]));
+ }
+ if (mask & KADM5_TL_DATA) {
+ CHECK(krb5_ret_int32(sp, &tmp));
+ princ->n_tl_data = tmp;
+ princ->tl_data = NULL;
+ for(i = 0; i < princ->n_tl_data; i++){
+ krb5_tl_data *tp = malloc(sizeof(*tp));
+ if (tp == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ret = kadm5_ret_tl_data(sp, tp);
+ if (ret == 0) {
+ tp->tl_data_next = princ->tl_data;
+ princ->tl_data = tp;
+ } else {
+ free(tp);
+ goto out;
+ }
+ }
+ }
+
+out:
+ /* Can't free princ here -- we don't have a context */
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_ret_principal_ent(krb5_storage *sp,
+ kadm5_principal_ent_t princ)
+{
+ return ret_principal_ent (sp, princ, ~0);
+}
+
+kadm5_ret_t
+kadm5_ret_principal_ent_mask(krb5_storage *sp,
+ kadm5_principal_ent_t princ,
+ uint32_t *mask)
+{
+ kadm5_ret_t ret;
+ int32_t tmp;
+
+ ret = krb5_ret_int32 (sp, &tmp);
+ if (ret) {
+ *mask = 0;
+ return ret;
+ }
+ *mask = tmp;
+ return ret_principal_ent (sp, princ, *mask);
+}
+
+kadm5_ret_t
+_kadm5_marshal_params(krb5_context context,
+ kadm5_config_params *params,
+ krb5_data *out)
+{
+ kadm5_ret_t ret;
+
+ krb5_storage *sp = krb5_storage_emem();
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ ret = krb5_store_int32(sp, params->mask & (KADM5_CONFIG_REALM));
+ if (ret == 0 && (params->mask & KADM5_CONFIG_REALM))
+ ret = krb5_store_string(sp, params->realm);
+ if (ret == 0)
+ ret = krb5_storage_to_data(sp, out);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+kadm5_ret_t
+_kadm5_unmarshal_params(krb5_context context,
+ krb5_data *in,
+ kadm5_config_params *params)
+{
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ int32_t mask;
+
+ sp = krb5_storage_from_data(in);
+ if (sp == NULL)
+ return ENOMEM;
+
+ ret = krb5_ret_int32(sp, &mask);
+ if (ret)
+ goto out;
+ params->mask = mask;
+
+ if(params->mask & KADM5_CONFIG_REALM)
+ ret = krb5_ret_string(sp, &params->realm);
+ out:
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+#ifdef TEST
+#include <getarg.h>
+#include <krb5-protos.h>
+#include <hex.h>
+
+static int version_flag;
+static int help_flag;
+static int verbose_flag;
+static int in_text_flag = 0;
+static int in_binary_flag = 0;
+static int out_hex_flag = 0;
+static int out_binary_flag = 0;
+static int must_round_trip_flag = 0;
+static char *byteorder_string_in_string;
+static char *byteorder_string_out_string;
+static struct getargs args[] = {
+ { "version", '\0', arg_flag, &version_flag,
+ "Version", NULL },
+ { "help", '\0', arg_flag, &help_flag,
+ "Show this message", NULL },
+ { "verbose", 'v', arg_flag, &verbose_flag, NULL, NULL },
+ { "in-text", '\0', arg_flag, &in_text_flag,
+ "Input is a text \"recipe\"", NULL },
+ { "in-binary", '\0', arg_flag, &in_binary_flag,
+ "Input is binary", NULL },
+ { "out-hex", '\0', arg_flag, &out_hex_flag,
+ "Output hex", NULL },
+ { "out-binary", '\0', arg_flag, &out_binary_flag,
+ "Output binary", NULL },
+ { "must-round-trip", '\0', arg_flag, &must_round_trip_flag,
+ "Check that encoding and decoding round-trip", NULL },
+ { "byte-order-out", '\0', arg_string, &byteorder_string_out_string,
+ "Output byte order", "host, network, be, or le" },
+ { "byte-order-in", '\0', arg_string, &byteorder_string_in_string,
+ "Input byte order", "host, network, packed, be, or le" },
+};
+
+#define DO_TYPE1(t, r, s) \
+ if (strcmp(type, #t) == 0) { \
+ t v; \
+ ret = r(in, &v); \
+ if (ret == 0) \
+ ret = s(out, v); \
+ return ret; \
+ }
+
+#define DO_TYPE2(t, r, s) \
+ if (strcmp(type, #t) == 0) { \
+ t v; \
+ ret = r(in, &v); \
+ if (ret == 0) \
+ ret = s(out, &v); \
+ return ret; \
+ }
+
+static krb5_error_code
+reencode(const char *type, krb5_storage *in, krb5_storage *out)
+{
+ krb5_error_code ret;
+
+ krb5_storage_seek(in, 0, SEEK_SET);
+
+ /*
+ * TODO: When --verbose print a visual representation of the value.
+ *
+ * We have functionality in lib/krb5 for that for krb5_principal and
+ * krb5_address, but not any of the others. Adding krb5_print_*()
+ * and kadm5_print_*() functions just for this program to use seems
+ * annoying.
+ */
+ DO_TYPE1(krb5_keyblock, krb5_ret_keyblock, krb5_store_keyblock);
+ DO_TYPE1(krb5_principal, krb5_ret_principal, krb5_store_principal);
+ DO_TYPE1(krb5_times, krb5_ret_times, krb5_store_times);
+ DO_TYPE1(krb5_address, krb5_ret_address, krb5_store_address);
+ DO_TYPE1(krb5_addresses, krb5_ret_addrs, krb5_store_addrs);
+ DO_TYPE1(krb5_authdata, krb5_ret_authdata, krb5_store_authdata);
+
+ DO_TYPE2(krb5_creds, krb5_ret_creds, krb5_store_creds);
+ DO_TYPE2(krb5_key_data, kadm5_ret_key_data, kadm5_store_key_data);
+ DO_TYPE2(krb5_tl_data, kadm5_ret_tl_data, kadm5_store_tl_data);
+ DO_TYPE2(kadm5_principal_ent_rec, kadm5_ret_principal_ent,
+ kadm5_store_principal_ent);
+
+ return ENOTSUP;
+}
+
+static krb5_error_code
+eval_recipe1(krb5_storage *sp, const char *typ, const char *val)
+{
+ krb5_error_code ret;
+ uint64_t vu = 0;
+ int64_t vi = 0;
+ int consumed = 0;
+
+ if (strncmp(typ, "int", sizeof("int") - 1) == 0) {
+ if (sscanf(val, "%"PRIi64"%n", &vi, &consumed) != 1)
+ return EINVAL;
+ if (consumed < 1)
+ return EINVAL;
+ while (isspace((unsigned char)val[consumed]))
+ consumed++;
+ if (val[consumed] != '\0')
+ return EINVAL;
+ } else if (strncmp(typ, "uint", sizeof("uint") - 1) == 0) {
+ /* There's no equally-useful equivalent of %i for unsigned */
+ if (val[0] == '0') {
+ if (val[1] == 'x') {
+ if (sscanf(val, "%"PRIx64"%n", &vu, &consumed) != 1)
+ return EINVAL;
+ } else {
+ if (sscanf(val, "%"PRIo64"%n", &vu, &consumed) != 1)
+ return EINVAL;
+ }
+ } else {
+ if (sscanf(val, "%"PRIu64"%n", &vu, &consumed) != 1)
+ return EINVAL;
+ }
+ if (consumed < 1)
+ return EINVAL;
+ while (isspace((unsigned char)val[consumed]))
+ consumed++;
+ if (val[consumed] != '\0')
+ return EINVAL;
+ vi = (int64_t)vu;
+ }
+#define DO_INTn(n) \
+ if (strcmp(typ, "int" #n) == 0) { \
+ if (n < 64 && vi < INT ## n ## _MIN) \
+ return EOVERFLOW; \
+ if (n < 64 && vi > INT ## n ## _MAX) \
+ return EOVERFLOW; \
+ return krb5_store_int ## n (sp, vi); \
+ }
+ DO_INTn(8);
+ DO_INTn(16);
+ DO_INTn(32);
+ DO_INTn(64);
+#define DO_UINTn(n) \
+ if (strcmp(typ, "uint" #n) == 0) { \
+ if (n < 64 && vu > INT ## n ## _MAX) \
+ return EOVERFLOW; \
+ return krb5_store_int ## n (sp, vi); \
+ }
+ DO_UINTn(8);
+ DO_UINTn(16);
+ DO_UINTn(32);
+ DO_UINTn(64);
+ if (strcmp(typ, "string") == 0)
+ return krb5_store_string(sp, val);
+ if (strcmp(typ, "stringz") == 0)
+ return krb5_store_stringz(sp, val);
+ if (strcmp(typ, "stringnl") == 0)
+ return krb5_store_stringnl(sp, val);
+ if (strcmp(typ, "data") == 0) {
+ ssize_t dsz = strlen(val);
+ krb5_data d;
+
+ /*
+ * 'data' as in 'krb5_data'.
+ *
+ * krb5_store_data() stores the length then the data.
+ */
+ if (krb5_data_alloc(&d, dsz))
+ return ENOMEM;
+ dsz = hex_decode(val, d.data, d.length);
+ if (dsz < 0)
+ return EINVAL;
+ d.length = dsz;
+ ret = krb5_store_data(sp, d);
+ krb5_data_free(&d);
+ return ret;
+ }
+ if (strcmp(typ, "rawdata") == 0) {
+ ssize_t dsz = strlen(val);
+ void *d;
+
+ /* Store the data w/o a length prefix */
+ d = malloc(dsz);
+ if (d == NULL)
+ return ENOMEM;
+ dsz = hex_decode(val, d, dsz);
+ if (dsz < 0)
+ return EINVAL;
+ ret = krb5_store_datalen(sp, d, dsz);
+ free(d);
+ return ret;
+ }
+ return ENOTSUP;
+}
+
+static krb5_storage *
+eval_recipe(char *r, int spflags)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ unsigned int lineno = 0;
+ char *nxt = NULL;
+ char *p;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ errx(1, "Out of memory");
+ krb5_storage_set_flags(sp, spflags);
+
+ for (p = r; p && *p; p = nxt) {
+ char *typ;
+ char *val;
+
+ lineno++;
+
+ /* Terminate p at \n */
+ nxt = p;
+ do {
+ nxt = strpbrk(nxt, "\r\n");
+ if (nxt && *nxt == '\r') {
+ if (*(++nxt) != '\n')
+ continue;
+ }
+ if (nxt && *nxt == '\n') {
+ *(nxt++) = '\0';
+ break;
+ }
+ } while (nxt);
+
+ while (isspace((unsigned char)*p))
+ p++;
+ if (*p == '#') {
+ p = nxt;
+ continue;
+ }
+ if (*p == '\0')
+ continue;
+ typ = p;
+ val = strpbrk(p, " \t");
+ if (val) {
+ *(val++) = '\0';
+ while (isspace((unsigned char)*val))
+ val++;
+ }
+ ret = eval_recipe1(sp, typ, val);
+ if (ret)
+ krb5_err(NULL, 1, ret, "Error at line %u", lineno);
+ }
+ return sp;
+}
+
+static void
+usage(int code)
+{
+ if (code)
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+
+ arg_printusage(args, sizeof(args) / sizeof(args[0]), "test_marshall",
+ "Usage: test_marshal [options] TYPE-NAME INPUT-FILE "
+ "[OUTPUT-FILE]\n"
+ "\tText inputs must be of the form:\n\n"
+ "\t\tsimpletype literalvalue\n\n"
+ "\twhere {simpletype} is one of:\n\n"
+ "\t\tint8\n"
+ "\t\tint16\n"
+ "\t\tint32\n"
+ "\t\tint64\n"
+ "\t\tuint8\n"
+ "\t\tuint16\n"
+ "\t\tuint32\n"
+ "\t\tuint64\n"
+ "\t\tstring\n"
+ "\t\tstringz\n"
+ "\t\tstringnl\n"
+ "\t\tdata\n"
+ "\t\trawdata\n\n"
+ "\tand {literalvalue} is as appropriate for the {simpletype}:\n\n"
+ "\t - For int types the value can be decimal, octal, or hexadecimal.\n"
+ "\t - For string types the string ends at the end of the line.\n"
+ "\t - For {data} the value is hex and will be encoded as a 32-bit\n"
+ "\t length then the raw binary data.\n"
+ "\t - For {rawdata} the value is hex and will be encoded as just the\n"
+ "\t raw binary data.\n\n"
+ "\tThe {TYPE} must be one of: krb5_keyblock, krb5_principal,\n"
+ "\tkrb5_times, krb5_address, krb5_addresses, krb5_authdata,\n"
+ "\tkrb5_creds, krb5_key_data, krb5_tl_data, or\n"
+ "\tkadm5_principal_ent_rec.\n\n"
+ "Options:\n");
+ exit(code);
+}
+
+static krb5_flags
+byteorder_flags(const char *s)
+{
+ if (s == NULL)
+ return KRB5_STORAGE_BYTEORDER_BE;
+ if (strcasecmp(s, "packed") == 0)
+ return KRB5_STORAGE_BYTEORDER_PACKED;
+ if (strcasecmp(s, "host") == 0)
+ return KRB5_STORAGE_BYTEORDER_HOST;
+ if (strcasecmp(s, "network") == 0)
+ return KRB5_STORAGE_BYTEORDER_BE;
+ if (strcasecmp(s, "be") == 0)
+ return KRB5_STORAGE_BYTEORDER_BE;
+ if (strcasecmp(s, "le") == 0)
+ return KRB5_STORAGE_BYTEORDER_LE;
+ return 0;
+}
+
+/*
+ * This program is intended to make fuzzing of krb5_ret_*() and kadm5_ret_*()
+ * possible.
+ *
+ * Inputs are either binary encodings or simplistic textual representations of
+ * XDR-ish data structures normally coded with {kadm5,krb5}_{ret,store}_*()
+ * functions.
+ *
+ * A textual representation of these structures looks like:
+ *
+ * type value
+ * ..
+ *
+ * where type is one of char, int32, etc., and where value is an appropriate
+ * literal for type.
+ */
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ krb5_storage *insp = NULL;
+ krb5_storage *insp2 = NULL;
+ krb5_storage *outsp = NULL;
+ krb5_flags spflags_in = 0;
+ krb5_flags spflags_out = 0;
+ krb5_data i, i2, o;
+ size_t insz = 0;
+ char *hexout = NULL;
+ char *hexin = NULL;
+ char *intxt = NULL;
+ void *inbin = NULL;
+ int optidx = 0;
+
+ if (getarg(args, sizeof(args)/sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage(0);
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 1)
+ errx(1, "Missing type name argument");
+ if (argc < 2)
+ errx(1, "Missing input file argument");
+ if (argc > 3)
+ errx(1, "Too many arguments");
+
+ if ((in_text_flag && in_binary_flag) ||
+ (!in_text_flag && !in_binary_flag))
+ errx(1, "One and only one of --in-text and --in-binary must be given");
+ if (out_hex_flag && out_binary_flag)
+ errx(1, "At most one of --out-text and --out-binary must be given");
+
+ if (!out_hex_flag && !out_binary_flag) {
+ if (isatty(STDOUT_FILENO)) {
+ warnx("Will output hex because stdout is a terminal");
+ out_hex_flag = 1;
+ } else {
+ warnx("Will output binary");
+ out_binary_flag = 1;
+ }
+ }
+
+ spflags_in |= byteorder_flags(byteorder_string_in_string);
+ spflags_out |= byteorder_flags(byteorder_string_out_string);
+
+ /* Read the input */
+ if (in_text_flag)
+ errno = rk_undumptext(argv[1], &intxt, NULL);
+ else
+ errno = rk_undumpdata(argv[1], &inbin, &insz);
+ if (errno)
+ err(1, "Could not read %s", argv[1]);
+
+ /* If the input is a recipe, evaluate it */
+ if (intxt)
+ insp = eval_recipe(intxt, spflags_in);
+ else
+ insp = krb5_storage_from_mem(inbin, insz);
+ if (insp == NULL)
+ errx(1, "Out of memory");
+ krb5_storage_set_flags(insp, spflags_in);
+
+ ret = krb5_storage_to_data(insp, &i);
+ if (ret)
+ krb5_err(NULL, 1, ret, "Could not check round-tripping");
+
+ if (out_hex_flag) {
+ char *hexstr = NULL;
+
+ if (hex_encode(i.data, i.length, &hexstr) == -1)
+ err(1, "Could not hex-encode output");
+ if (argv[2]) {
+ FILE *f;
+
+ f = fopen(argv[2], "w");
+ if (f == NULL)
+ err(1, "Could not open %s for writing", argv[2]);
+ if (fprintf(f, "%s\n", hexstr) < 0 || fclose(f))
+ err(1, "Could write to %s", argv[2]);
+ } else {
+ if (printf("%s\n", hexstr) < 0)
+ err(1, "Could not write to stdout");
+ }
+ free(hexstr);
+ } else {
+ if (argv[2]) {
+ rk_dumpdata(argv[2], i.data, i.length);
+ } else {
+ if (fwrite(i.data, i.length, 1, stdout) != 1 ||
+ fflush(stdout) != 0)
+ err(1, "Could not output encoding");
+ }
+ }
+
+ outsp = krb5_storage_emem();
+ if (outsp == NULL)
+ errx(1, "Out of memory");
+ krb5_storage_set_flags(outsp, spflags_out);
+
+ ret = reencode(argv[0], insp, outsp);
+ if (ret)
+ krb5_err(NULL, 1, ret, "Could not decode and re-encode");
+
+ if (i.length == o.length && memcmp(i.data, o.data, i.length) == 0) {
+ if (verbose_flag)
+ fprintf(stderr, "Encoding round-trips!\n");
+ goto out;
+ }
+
+ ret = krb5_storage_to_data(outsp, &o);
+ if (ret)
+ krb5_err(NULL, 1, ret, "Out of memory");
+
+ /*
+ * The encoding did not round trip. Sadly kadm5_ret_principal_ent()
+ * reverses the TL data list. So try to re-encode one more time.
+ */
+
+ if (strcmp(argv[0], "kadm5_principal_ent_rec") == 0) {
+ insp2 = krb5_storage_emem();
+ if (insp2 == NULL)
+ errx(1, "Out of memory");
+
+ krb5_storage_set_flags(insp2, spflags_in);
+ ret = reencode(argv[0], outsp, insp2);
+ if (ret == 0)
+ ret = krb5_storage_to_data(insp2, &i2);
+ if (ret)
+ krb5_err(NULL, 1, ret, "Could not decode and re-encode");
+ if (i.length == i2.length && memcmp(i.data, i2.data, i.length) == 0) {
+ if (verbose_flag)
+ fprintf(stderr, "Encoding round-trips!\n");
+ goto out;
+ }
+ }
+ if (hex_encode(i.data, i.length, &hexin) < 0)
+ errx(1, "Out of memory");
+ if (hex_encode(o.data, o.length, &hexout) < 0)
+ errx(1, "Out of memory");
+ if (must_round_trip_flag) {
+ errx(1, "Encoding does not round-trip\n(in: %s)\n(out: %s)", hexin,
+ hexout);
+ } else {
+ warnx("Encoding does not round-trip\n(in: %s)\n(out: %s)", hexin,
+ hexout);
+ }
+
+out:
+
+ free(hexin);
+ free(hexout);
+ krb5_data_free(&o);
+ krb5_data_free(&i);
+ krb5_data_free(&i2);
+ krb5_storage_free(insp);
+ krb5_storage_free(outsp);
+ krb5_storage_free(insp2);
+ return ret;
+}
+#endif
diff --git a/third_party/heimdal/lib/kadm5/modify_c.c b/third_party/heimdal/lib/kadm5/modify_c.c
new file mode 100644
index 0000000..a38cb33
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/modify_c.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_modify_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_modify);
+ if (ret)
+ goto out;
+ ret = kadm5_store_principal_ent(sp, princ);
+ if (ret)
+ goto out;
+ ret = krb5_store_int32(sp, mask);
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/modify_s.c b/third_party/heimdal/lib/kadm5/modify_s.c
new file mode 100644
index 0000000..2159caf
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/modify_s.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1997-2001, 2003, 2005-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct modify_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ kadm5_principal_ent_t princ;
+ uint32_t mask;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+modify_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct modify_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->modify(context, hookctx, ctx->stage,
+ ctx->code, ctx->princ, ctx->mask);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "modify",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+modify_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ krb5_error_code ret;
+ struct modify_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+ ctx.mask = mask;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, modify_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+static kadm5_ret_t
+modify_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ uint32_t forbidden_mask)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry ent;
+ kadm5_ret_t ret;
+
+ memset(&ent, 0, sizeof(ent));
+
+ if((mask & forbidden_mask))
+ return KADM5_BAD_MASK;
+ if((mask & KADM5_POLICY) && strcmp(princ->policy, "default") != 0)
+ return KADM5_UNK_POLICY;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ /*
+ * NOTE: We do not use hdb_fetch_kvno() here, which means we'll
+ * automatically reject modifications of would-be virtual principals.
+ */
+ ret = context->db->hdb_fetch_kvno(context->context, context->db,
+ princ->principal,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if (ret)
+ goto out2;
+
+ ret = modify_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, princ, mask);
+ if (ret)
+ goto out3;
+ /*
+ * XXX Make sure that _kadm5_setup_entry() checks that the time of last
+ * change in `ent' matches the one in `princ'.
+ */
+ ret = _kadm5_setup_entry(context, &ent, mask, princ, mask, NULL, 0);
+ if (ret)
+ goto out3;
+ ret = _kadm5_set_modifier(context, &ent);
+ if (ret)
+ goto out3;
+
+ /*
+ * If any keys are bogus, disallow the modify. If the keys were
+ * bogus as stored in the HDB we could allow those through, but
+ * distinguishing that case from a pre-1.6 client using add_enctype
+ * without the get-keys privilege requires more work (mainly: checking that
+ * the bogus keys in princ->key_data[] have corresponding bogus keys in ent
+ * before calling _kadm5_setup_entry()).
+ */
+ if ((mask & KADM5_KEY_DATA) &&
+ kadm5_some_keys_are_bogus(princ->n_key_data, princ->key_data)) {
+ ret = KADM5_AUTH_GET_KEYS; /* Not quite appropriate, but it'll do */
+ goto out3;
+ }
+
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out3;
+
+ if ((mask & KADM5_POLICY)) {
+ HDB_extension ext;
+
+ memset(&ext, 0, sizeof(ext));
+ /* XXX should be TRUE, but we don't yet support policies */
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_policy;
+ ext.data.u.policy = strdup(princ->policy);
+ if (ext.data.u.policy == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out3;
+ }
+ /* This calls free_HDB_extension(), freeing ext.data.u.policy */
+ ret = hdb_replace_extension(context->context, &ent, &ext);
+ free(ext.data.u.policy);
+ if (ret)
+ goto out3;
+ }
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent,
+ mask | KADM5_MOD_NAME | KADM5_MOD_TIME);
+
+ (void) modify_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, princ, mask);
+
+ out3:
+ hdb_free_entry(context->context, context->db, &ent);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
+
+
+kadm5_ret_t
+kadm5_s_modify_principal(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask)
+{
+ return modify_principal(server_handle, princ, mask,
+ KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
+ | KADM5_MOD_NAME | KADM5_MKVNO
+ | KADM5_AUX_ATTRIBUTES | KADM5_LAST_SUCCESS
+ | KADM5_LAST_FAILED);
+}
diff --git a/third_party/heimdal/lib/kadm5/password_quality.c b/third_party/heimdal/lib/kadm5/password_quality.c
new file mode 100644
index 0000000..84c1d39
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/password_quality.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (c) 1997-2000, 2003-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+#include "kadm5-pwcheck.h"
+
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+static int
+min_length_passwd_quality (krb5_context context,
+ krb5_principal principal,
+ krb5_data *pwd,
+ const char *opaque,
+ char *message,
+ size_t length)
+{
+ uint32_t min_length = krb5_config_get_int_default(context, NULL, 6,
+ "password_quality",
+ "min_length",
+ NULL);
+
+ if (pwd->length < min_length) {
+ strlcpy(message, "Password too short", length);
+ return 1;
+ } else
+ return 0;
+}
+
+static const char *
+min_length_passwd_quality_v0 (krb5_context context,
+ krb5_principal principal,
+ krb5_data *pwd)
+{
+ static char message[1024];
+ int ret;
+
+ message[0] = '\0';
+
+ ret = min_length_passwd_quality(context, principal, pwd, NULL,
+ message, sizeof(message));
+ if (ret)
+ return message;
+ return NULL;
+}
+
+
+static int
+char_class_passwd_quality (krb5_context context,
+ krb5_principal principal,
+ krb5_data *pwd,
+ const char *opaque,
+ char *message,
+ size_t length)
+{
+ const char *classes[] = {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
+ "abcdefghijklmnopqrstuvwxyz",
+ "1234567890",
+ " !\"#$%&'()*+,-./:;<=>?@\\]^_`{|}~"
+ };
+ int counter = 0, req_classes;
+ size_t i, len;
+ char *pw;
+
+ req_classes = krb5_config_get_int_default(context, NULL, 3,
+ "password_quality",
+ "min_classes",
+ NULL);
+
+ len = pwd->length + 1;
+ pw = malloc(len);
+ if (pw == NULL) {
+ strlcpy(message, "out of memory", length);
+ return 1;
+ }
+ strlcpy(pw, pwd->data, len);
+ len = strlen(pw);
+
+ for (i = 0; i < sizeof(classes)/sizeof(classes[0]); i++) {
+ if (strcspn(pw, classes[i]) < len)
+ counter++;
+ }
+ memset(pw, 0, pwd->length + 1);
+ free(pw);
+ if (counter < req_classes) {
+ snprintf(message, length,
+ "Password doesn't meet complexity requirement.\n"
+ "Add more characters from at least %d of the\n"
+ "following classes:\n"
+ "1. English uppercase characters (A through Z)\n"
+ "2. English lowercase characters (a through z)\n"
+ "3. Base 10 digits (0 through 9)\n"
+ "4. Nonalphanumeric characters (e.g., !, $, #, %%)", req_classes);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+external_passwd_quality (krb5_context context,
+ krb5_principal principal,
+ krb5_data *pwd,
+ const char *opaque,
+ char *message,
+ size_t length)
+{
+ krb5_error_code ret;
+ const char *program;
+ char *p;
+ pid_t child;
+ int status;
+ char reply[1024];
+ FILE *in = NULL, *out = NULL, *error = NULL;
+
+ if (memchr(pwd->data, '\n', pwd->length) != NULL) {
+ snprintf(message, length, "password contains newline, "
+ "not valid for external test");
+ return 1;
+ }
+
+ program = krb5_config_get_string(context, NULL,
+ "password_quality",
+ "external_program",
+ NULL);
+ if (program == NULL) {
+ snprintf(message, length, "external password quality "
+ "program not configured");
+ return 1;
+ }
+
+ ret = krb5_unparse_name(context, principal, &p);
+ if (ret) {
+ strlcpy(message, "out of memory", length);
+ return 1;
+ }
+
+ child = pipe_execv(&in, &out, &error, program, program, p, NULL);
+ if (child < 0) {
+ snprintf(message, length, "external password quality "
+ "program failed to execute for principal %s", p);
+ free(p);
+ return 1;
+ }
+
+ fprintf(in, "principal: %s\n"
+ "new-password: %.*s\n"
+ "end\n",
+ p, (int)pwd->length, (char *)pwd->data);
+
+ fclose(in);
+
+ if (fgets(reply, sizeof(reply), out) == NULL) {
+
+ if (fgets(reply, sizeof(reply), error) == NULL) {
+ snprintf(message, length, "external password quality "
+ "program failed without error");
+
+ } else {
+ reply[strcspn(reply, "\n")] = '\0';
+ snprintf(message, length, "External password quality "
+ "program failed: %s", reply);
+ }
+
+ fclose(out);
+ fclose(error);
+ wait_for_process(child);
+ free(p);
+ return 1;
+ }
+ reply[strcspn(reply, "\n")] = '\0';
+
+ fclose(out);
+ fclose(error);
+
+ status = wait_for_process(child);
+
+ if (SE_IS_ERROR(status) || SE_PROCSTATUS(status) != 0) {
+ snprintf(message, length, "external program failed: %s", reply);
+ free(p);
+ return 1;
+ }
+
+ if (strcmp(reply, "APPROVED") != 0) {
+ snprintf(message, length, "%s", reply);
+ free(p);
+ return 1;
+ }
+
+ free(p);
+
+ return 0;
+}
+
+
+static kadm5_passwd_quality_check_func_v0 passwd_quality_check =
+ min_length_passwd_quality_v0;
+
+struct kadm5_pw_policy_check_func builtin_funcs[] = {
+ { "minimum-length", min_length_passwd_quality },
+ { "character-class", char_class_passwd_quality },
+ { "external-check", external_passwd_quality },
+ { NULL, NULL }
+};
+struct kadm5_pw_policy_verifier builtin_verifier = {
+ "builtin",
+ KADM5_PASSWD_VERSION_V1,
+ "Heimdal builtin",
+ builtin_funcs
+};
+
+static struct kadm5_pw_policy_verifier **verifiers;
+static int num_verifiers;
+
+/*
+ * setup the password quality hook
+ */
+
+void
+kadm5_setup_passwd_quality_check(krb5_context context,
+ const char *check_library,
+ const char *check_function)
+{
+#ifdef HAVE_DLOPEN
+ void *handle;
+ void *sym;
+ int *version;
+ const char *tmp;
+
+ if(check_library == NULL) {
+ tmp = krb5_config_get_string(context, NULL,
+ "password_quality",
+ "check_library",
+ NULL);
+ if(tmp != NULL)
+ check_library = tmp;
+ }
+ if(check_function == NULL) {
+ tmp = krb5_config_get_string(context, NULL,
+ "password_quality",
+ "check_function",
+ NULL);
+ if(tmp != NULL)
+ check_function = tmp;
+ }
+ if(check_library != NULL && check_function == NULL)
+ check_function = "passwd_check";
+
+ if(check_library == NULL)
+ return;
+ handle = dlopen(check_library, RTLD_NOW | RTLD_LOCAL | RTLD_GROUP);
+ if(handle == NULL) {
+ krb5_warnx(context, "failed to open `%s'", check_library);
+ return;
+ }
+ version = (int *) dlsym(handle, "version");
+ if(version == NULL) {
+ krb5_warnx(context,
+ "didn't find `version' symbol in `%s'", check_library);
+ dlclose(handle);
+ return;
+ }
+ if(*version != KADM5_PASSWD_VERSION_V0) {
+ krb5_warnx(context,
+ "version of loaded library is %d (expected %d)",
+ *version, KADM5_PASSWD_VERSION_V0);
+ dlclose(handle);
+ return;
+ }
+ sym = dlsym(handle, check_function);
+ if(sym == NULL) {
+ krb5_warnx(context,
+ "didn't find `%s' symbol in `%s'",
+ check_function, check_library);
+ dlclose(handle);
+ return;
+ }
+ passwd_quality_check = (kadm5_passwd_quality_check_func_v0) sym;
+#endif /* HAVE_DLOPEN */
+}
+
+#ifdef HAVE_DLOPEN
+
+static krb5_error_code
+add_verifier(krb5_context context, const char *check_library)
+{
+ struct kadm5_pw_policy_verifier *v, **tmp;
+ void *handle;
+ int i;
+
+ handle = dlopen(check_library, RTLD_NOW | RTLD_LOCAL | RTLD_GROUP);
+ if(handle == NULL) {
+ krb5_warnx(context, "failed to open `%s'", check_library);
+ return ENOENT;
+ }
+ v = (struct kadm5_pw_policy_verifier *) dlsym(handle, "kadm5_password_verifier");
+ if(v == NULL) {
+ krb5_warnx(context,
+ "didn't find `kadm5_password_verifier' symbol "
+ "in `%s'", check_library);
+ dlclose(handle);
+ return ENOENT;
+ }
+ if(v->version != KADM5_PASSWD_VERSION_V1) {
+ krb5_warnx(context,
+ "version of loaded library is %d (expected %d)",
+ v->version, KADM5_PASSWD_VERSION_V1);
+ dlclose(handle);
+ return EINVAL;
+ }
+ for (i = 0; i < num_verifiers; i++) {
+ if (strcmp(v->name, verifiers[i]->name) == 0)
+ break;
+ }
+ if (i < num_verifiers) {
+ krb5_warnx(context, "password verifier library `%s' is already loaded",
+ v->name);
+ dlclose(handle);
+ return 0;
+ }
+
+ tmp = realloc(verifiers, (num_verifiers + 1) * sizeof(*verifiers));
+ if (tmp == NULL) {
+ krb5_warnx(context, "out of memory");
+ dlclose(handle);
+ return 0;
+ }
+ verifiers = tmp;
+ verifiers[num_verifiers] = v;
+ num_verifiers++;
+
+ return 0;
+}
+
+#endif
+
+krb5_error_code
+kadm5_add_passwd_quality_verifier(krb5_context context,
+ const char *check_library)
+{
+#ifdef HAVE_DLOPEN
+
+ if(check_library == NULL) {
+ krb5_error_code ret = 0;
+ char **strs;
+ char **tmp;
+
+ strs = krb5_config_get_strings(context, NULL,
+ "password_quality",
+ "policy_libraries",
+ NULL);
+ if (strs == NULL)
+ return 0;
+
+ for (tmp = strs; *tmp; tmp++) {
+ ret = add_verifier(context, *tmp);
+ if (ret)
+ break;
+ }
+ krb5_config_free_strings(strs);
+ return ret;
+ } else {
+ return add_verifier(context, check_library);
+ }
+#else
+ return 0;
+#endif /* HAVE_DLOPEN */
+}
+
+/*
+ *
+ */
+
+static const struct kadm5_pw_policy_check_func *
+find_func(krb5_context context, const char *name)
+{
+ const struct kadm5_pw_policy_check_func *f;
+ char *module = NULL;
+ const char *p, *func;
+ int i;
+
+ p = strchr(name, ':');
+ if (p) {
+ size_t len = p - name + 1;
+ func = p + 1;
+ module = malloc(len);
+ if (module == NULL)
+ return NULL;
+ strlcpy(module, name, len);
+ } else
+ func = name;
+
+ /* Find module in loaded modules first */
+ for (i = 0; i < num_verifiers; i++) {
+ if (module && strcmp(module, verifiers[i]->name) != 0)
+ continue;
+ for (f = verifiers[i]->funcs; f->name ; f++)
+ if (strcmp(func, f->name) == 0) {
+ if (module)
+ free(module);
+ return f;
+ }
+ }
+ /* Lets try try the builtin modules */
+ if (module == NULL || strcmp(module, "builtin") == 0) {
+ for (f = builtin_verifier.funcs; f->name ; f++)
+ if (strcmp(func, f->name) == 0) {
+ if (module)
+ free(module);
+ return f;
+ }
+ }
+ if (module)
+ free(module);
+ return NULL;
+}
+
+const char *
+kadm5_check_password_quality (krb5_context context,
+ krb5_principal principal,
+ krb5_data *pwd_data)
+{
+ const struct kadm5_pw_policy_check_func *proc;
+ static char error_msg[1024];
+ const char *msg;
+ char **v, **vp;
+ int ret;
+
+ /*
+ * Check if we should use the old version of policy function.
+ */
+
+ v = krb5_config_get_strings(context, NULL,
+ "password_quality",
+ "policies",
+ NULL);
+ if (v == NULL) {
+ msg = (*passwd_quality_check) (context, principal, pwd_data);
+ if (msg)
+ krb5_set_error_message(context, 0, "password policy failed: %s", msg);
+ return msg;
+ }
+
+ error_msg[0] = '\0';
+
+ msg = NULL;
+ for(vp = v; *vp; vp++) {
+ proc = find_func(context, *vp);
+ if (proc == NULL) {
+ msg = "failed to find password verifier function";
+ krb5_set_error_message(context, 0, "Failed to find password policy "
+ "function: %s", *vp);
+ break;
+ }
+ ret = (proc->func)(context, principal, pwd_data, NULL,
+ error_msg, sizeof(error_msg));
+ if (ret) {
+ krb5_set_error_message(context, 0, "Password policy "
+ "%s failed with %s",
+ proc->name, error_msg);
+ msg = error_msg;
+ break;
+ }
+ }
+ krb5_config_free_strings(v);
+
+ /* If the default quality check isn't used, lets check that the
+ * old quality function the user have set too */
+ if (msg == NULL && passwd_quality_check != min_length_passwd_quality_v0) {
+ msg = (*passwd_quality_check) (context, principal, pwd_data);
+ if (msg)
+ krb5_set_error_message(context, 0, "(old) password policy "
+ "failed with %s", msg);
+
+ }
+ return msg;
+}
diff --git a/third_party/heimdal/lib/kadm5/private.h b/third_party/heimdal/lib/kadm5/private.h
new file mode 100644
index 0000000..1cb8e39
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/private.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 1997-2000 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __kadm5_privatex_h__
+#define __kadm5_privatex_h__
+
+#include "kadm5-hook.h"
+
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
+struct kadm_func {
+ kadm5_ret_t (*chpass_principal) (void *, krb5_principal, int,
+ int, krb5_key_salt_tuple*, const char*);
+ kadm5_ret_t (*create_principal) (void*, kadm5_principal_ent_t, uint32_t,
+ int, krb5_key_salt_tuple *,
+ const char*);
+ kadm5_ret_t (*delete_principal) (void*, krb5_principal);
+ kadm5_ret_t (*destroy) (void*);
+ kadm5_ret_t (*flush) (void*);
+ kadm5_ret_t (*get_principal) (void*, krb5_principal,
+ kadm5_principal_ent_t, uint32_t);
+ kadm5_ret_t (*get_principals) (void*, const char*, char***, int*);
+ kadm5_ret_t (*get_privs) (void*, uint32_t*);
+ kadm5_ret_t (*modify_principal) (void*, kadm5_principal_ent_t, uint32_t);
+ kadm5_ret_t (*randkey_principal) (void*, krb5_principal, krb5_boolean, int,
+ krb5_key_salt_tuple*, krb5_keyblock**,
+ int*);
+ kadm5_ret_t (*rename_principal) (void*, krb5_principal, krb5_principal);
+ kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal, int,
+ int, krb5_key_data *);
+ kadm5_ret_t (*lock) (void *);
+ kadm5_ret_t (*unlock) (void *);
+ kadm5_ret_t (*setkey_principal_3) (void *, krb5_principal, krb5_boolean,
+ int, krb5_key_salt_tuple *,
+ krb5_keyblock *, int);
+ kadm5_ret_t (*prune_principal) (void *, krb5_principal, int);
+ kadm5_ret_t (*iter_principals) (void*, const char*, int (*)(void *, const char *), void *);
+ kadm5_ret_t (*dup_context) (void*, void **);
+};
+
+typedef struct kadm5_hook_context {
+ void *dsohandle;
+ const kadm5_hook_ftable *hook;
+ void *data;
+} kadm5_hook_context;
+
+/* XXX should be integrated */
+typedef struct kadm5_common_context {
+ krb5_context context;
+ krb5_boolean my_context;
+ struct kadm_func funcs;
+ void *data;
+} kadm5_common_context;
+
+typedef struct kadm5_log_peer {
+ int fd;
+ char *name;
+ krb5_auth_context ac;
+ struct kadm5_log_peer *next;
+} kadm5_log_peer;
+
+typedef struct kadm5_log_context {
+ char *log_file;
+ int log_fd;
+ int read_only;
+ int lock_mode;
+ uint32_t version;
+ time_t last_time;
+#ifndef NO_UNIX_SOCKETS
+ struct sockaddr_un socket_name;
+#else
+ struct addrinfo *socket_info;
+#endif
+ krb5_socket_t socket_fd;
+} kadm5_log_context;
+
+typedef struct kadm5_server_context {
+ krb5_context context;
+ krb5_boolean my_context;
+ struct kadm_func funcs;
+ /* */
+ kadm5_config_params config;
+ HDB *db;
+ int keep_open;
+ krb5_principal caller;
+ unsigned acl_flags;
+ kadm5_log_context log_context;
+ size_t num_hooks;
+ kadm5_hook_context **hooks;
+} kadm5_server_context;
+
+typedef struct kadm5_client_context {
+ krb5_context context;
+ krb5_boolean my_context;
+ struct kadm_func funcs;
+ /* */
+ krb5_auth_context ac;
+ char *realm;
+ char *admin_server;
+ int kadmind_port;
+ krb5_socket_t sock;
+ char *client_name;
+ char *service_name;
+ krb5_prompter_fct prompter;
+ const char *keytab;
+ krb5_ccache ccache;
+ kadm5_config_params *realm_params;
+ char *readonly_admin_server;
+ int readonly_kadmind_port;
+ unsigned int want_write:1;
+ unsigned int connected_to_writable:1;
+} kadm5_client_context;
+
+typedef struct kadm5_ad_context {
+ krb5_context context;
+ krb5_boolean my_context;
+ struct kadm_func funcs;
+ /* */
+ kadm5_config_params config;
+ krb5_principal caller;
+ krb5_ccache ccache;
+ char *client_name;
+ char *realm;
+ void *ldap_conn;
+ char *base_dn;
+} kadm5_ad_context;
+
+/*
+ * This enum is used in the iprop log file and on the wire in the iprop
+ * protocol. DO NOT CHANGE, except to add new op types at the end, and
+ * look for places in lib/kadm5/log.c to update.
+ */
+enum kadm_ops {
+ kadm_get,
+ kadm_delete,
+ kadm_create,
+ kadm_rename,
+ kadm_chpass,
+ kadm_modify,
+ kadm_randkey,
+ kadm_get_privs,
+ kadm_get_princs,
+ kadm_chpass_with_key,
+ kadm_nop,
+ kadm_prune,
+ kadm_first = kadm_get,
+ kadm_last = kadm_prune
+};
+
+/* FIXME nop types are currently not implemented */
+enum kadm_nop_type {
+ kadm_nop_plain, /* plain nop, not relevance except as uberblock */
+ kadm_nop_trunc, /* indicates that the master truncated the log */
+ kadm_nop_close /* indicates that the master closed this log */
+};
+
+enum kadm_iter_opts {
+ kadm_forward = 1,
+ kadm_backward = 2,
+ kadm_confirmed = 4,
+ kadm_unconfirmed = 8
+};
+
+enum kadm_recover_mode {
+ kadm_recover_commit,
+ kadm_recover_replay
+};
+
+#define KADMIN_APPL_VERSION "KADM0.1"
+#define KADMIN_OLD_APPL_VERSION "KADM0.0"
+
+extern struct heim_plugin_data kadm5_hook_plugin_data;
+
+#include "kadm5-private.h"
+
+#endif /* __kadm5_privatex_h__ */
diff --git a/third_party/heimdal/lib/kadm5/privs_c.c b/third_party/heimdal/lib/kadm5/privs_c.c
new file mode 100644
index 0000000..b6879cf
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/privs_c.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_get_privs(void *server_handle, uint32_t *privs)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ *privs = 0;
+
+ ret = _kadm5_connect(server_handle, 0 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_store_int32(sp, kadm_get_privs);
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (ret == 0)
+ krb5_ret_uint32(sp, privs);
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free (&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/privs_s.c b/third_party/heimdal/lib/kadm5/privs_s.c
new file mode 100644
index 0000000..bfe298d
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/privs_s.c
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_s_get_privs(void *server_handle, uint32_t *privs)
+{
+ kadm5_server_context *context = server_handle;
+ *privs = context->acl_flags;
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/prune_c.c b/third_party/heimdal/lib/kadm5/prune_c.c
new file mode 100644
index 0000000..70a1dcb
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/prune_c.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018 Cesnet z.s.p.o.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_prune_principal(void *server_handle, krb5_principal princ, int kvno)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret, ret2;
+ krb5_storage *sp = NULL;
+ unsigned char buf[1024];
+ krb5_data reply;
+
+ krb5_data_zero(&reply);
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret == 0 && (sp = krb5_storage_from_mem(buf, sizeof(buf))) == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret == 0)
+ ret = krb5_store_int32(sp, kadm_prune);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, princ);
+ if (ret == 0)
+ ret = krb5_store_int32(sp, kvno);
+ if (ret == 0)
+ ret = _kadm5_client_send(context, sp);
+ if (ret == 0)
+ ret = _kadm5_client_recv(context, &reply);
+ krb5_storage_free(sp);
+ sp = NULL;
+ if (ret == 0 && (sp = krb5_storage_from_data(&reply)) == NULL)
+ ret = krb5_enomem(context->context);
+ if (ret == 0)
+ ret = krb5_ret_int32(sp, &ret2);
+ if (ret == 0) {
+ krb5_clear_error_message(context->context);
+ ret = ret2;
+ }
+ krb5_data_free(&reply);
+ krb5_storage_free(sp);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/prune_s.c b/third_party/heimdal/lib/kadm5/prune_s.c
new file mode 100644
index 0000000..96133f2
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/prune_s.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2018 Cesnet z.s.p.o.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct prune_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal princ;
+ int kvno;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+prune_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct prune_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->prune(context, hookctx,
+ ctx->stage, ctx->code, ctx->princ, ctx->kvno);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "prune",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+prune_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ int kvno)
+{
+ krb5_error_code ret;
+ struct prune_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+ ctx.kvno = kvno;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, prune_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_prune_principal(void *server_handle,
+ krb5_principal princ,
+ int kvno)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry ent;
+ kadm5_ret_t ret;
+
+ memset(&ent, 0, sizeof(ent));
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ /* NOTE: We do not use hdb_fetch_kvno() here */
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if (ret)
+ goto out2;
+
+ ret = prune_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, princ, kvno);
+ if (ret)
+ goto out3;
+
+ ret = hdb_prune_keys_kvno(context->context, &ent, kvno);
+ if (ret)
+ goto out3;
+
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out3;
+
+ ret = kadm5_log_modify(context, &ent, KADM5_KEY_DATA);
+
+ (void) prune_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, princ, kvno);
+
+out3:
+ hdb_free_entry(context->context, context->db, &ent);
+out2:
+ (void) kadm5_log_end(context);
+out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
diff --git a/third_party/heimdal/lib/kadm5/randkey_c.c b/third_party/heimdal/lib/kadm5/randkey_c.c
new file mode 100644
index 0000000..cb0ec86
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/randkey_c.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_randkey_principal(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **new_keys,
+ int *n_keys)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1536];
+ int32_t tmp;
+ size_t i;
+ krb5_data reply;
+ krb5_keyblock *k;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+
+ /*
+ * NOTE WELL: This message is extensible. It currently consists of:
+ *
+ * - opcode (kadm_randkey)
+ * - principal name (princ)
+ *
+ * followed by optional items, each of which must be present if
+ * there are any items following them that are also present:
+ *
+ * - keepold boolean (whether to delete old kvnos)
+ * - number of key/salt type tuples
+ * - array of {enctype, salttype}
+ *
+ * Eventually we may add:
+ *
+ * - opaque string2key parameters (salt, rounds, ...)
+ */
+ ret = krb5_store_int32(sp, kadm_randkey);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, princ);
+
+ if (ret == 0 && (keepold == TRUE || n_ks_tuple > 0))
+ ret = krb5_store_uint32(sp, keepold);
+ if (ret == 0 && n_ks_tuple > 0)
+ ret = krb5_store_uint32(sp, n_ks_tuple);
+ for (i = 0; ret == 0 && i < n_ks_tuple; i++) {
+ ret = krb5_store_int32(sp, ks_tuple[i].ks_enctype);
+ if (ret == 0)
+ ret = krb5_store_int32(sp, ks_tuple[i].ks_salttype);
+ }
+ /* Future extensions go here */
+ if (ret)
+ goto out;
+
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (ret)
+ goto out;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret)
+ goto out;
+ if (tmp < 0) {
+ ret = EOVERFLOW;
+ goto out;
+ }
+ k = calloc(tmp, sizeof(*k));
+ if (k == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ for (i = 0; ret == 0 && i < tmp; i++) {
+ ret = krb5_ret_keyblock(sp, &k[i]);
+ if (ret)
+ break;
+ }
+ if (ret == 0 && n_keys && new_keys) {
+ *n_keys = tmp;
+ *new_keys = k;
+ } else {
+ krb5_free_keyblock_contents(context->context, &k[i]);
+ for (; i > 0; i--)
+ krb5_free_keyblock_contents(context->context, &k[i - 1]);
+ free(k);
+ }
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/randkey_s.c b/third_party/heimdal/lib/kadm5/randkey_s.c
new file mode 100644
index 0000000..cb36967
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/randkey_s.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 1997-2001, 2003-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct randkey_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal princ;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+randkey_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct randkey_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->randkey(context, hookctx,
+ ctx->stage, ctx->code, ctx->princ);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "randkey",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+randkey_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ)
+{
+ krb5_error_code ret;
+ struct randkey_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, randkey_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+/*
+ * Set the keys of `princ' to random values, returning the random keys
+ * in `new_keys', `n_keys'.
+ */
+
+kadm5_ret_t
+kadm5_s_randkey_principal(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **new_keys,
+ int *n_keys)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry ent;
+ kadm5_ret_t ret;
+ size_t i;
+
+ memset(&ent, 0, sizeof(ent));
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ /* NOTE: We do not use hdb_fetch_kvno() here (maybe we should) */
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if(ret)
+ goto out2;
+
+ ret = randkey_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT, 0, princ);
+ if (ret)
+ goto out3;
+
+ if (keepold) {
+ ret = hdb_add_current_keys_to_history(context->context, &ent);
+ if (ret == 0 && keepold == 1)
+ ret = hdb_prune_keys_kvno(context->context, &ent, 0);
+ if (ret)
+ goto out3;
+ } else {
+ /* Remove all key history */
+ ret = hdb_clear_extension(context->context, &ent,
+ choice_HDB_extension_data_hist_keys);
+ if (ret)
+ goto out3;
+ }
+
+ ret = _kadm5_set_keys_randomly(context, &ent, n_ks_tuple, ks_tuple,
+ new_keys, n_keys);
+ if (ret)
+ goto out3;
+ ent.kvno++;
+
+ ent.flags.require_pwchange = 0;
+
+ ret = _kadm5_set_modifier(context, &ent);
+ if(ret)
+ goto out4;
+ ret = _kadm5_bump_pw_expire(context, &ent);
+ if (ret)
+ goto out4;
+
+ if (keepold) {
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out4;
+ } else {
+ HDB_extension ext;
+
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ ext.data.u.hist_keys.len = 0;
+ ext.data.u.hist_keys.val = NULL;
+ hdb_replace_extension(context->context, &ent, &ext);
+ }
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent,
+ KADM5_ATTRIBUTES | KADM5_PRINCIPAL |
+ KADM5_MOD_NAME | KADM5_MOD_TIME |
+ KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
+
+ (void) randkey_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT, ret, princ);
+
+ out4:
+ if (ret) {
+ for (i = 0; i < *n_keys; ++i)
+ krb5_free_keyblock_contents(context->context, &(*new_keys)[i]);
+ free (*new_keys);
+ *new_keys = NULL;
+ *n_keys = 0;
+ }
+ out3:
+ hdb_free_entry(context->context, context->db, &ent);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
diff --git a/third_party/heimdal/lib/kadm5/rename_c.c b/third_party/heimdal/lib/kadm5/rename_c.c
new file mode 100644
index 0000000..fb2cdc5
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/rename_c.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 1997 - 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_c_rename_principal(void *server_handle,
+ krb5_principal source,
+ krb5_principal target)
+{
+ kadm5_client_context *context = server_handle;
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ unsigned char buf[1024];
+ int32_t tmp;
+ krb5_data reply;
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ return ret;
+
+ krb5_data_zero(&reply);
+
+ sp = krb5_storage_from_mem(buf, sizeof(buf));
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+
+ ret = krb5_store_int32(sp, kadm_rename);
+ if (ret)
+ goto out;
+ ret = krb5_store_principal(sp, source);
+ if (ret)
+ goto out;
+ ret = krb5_store_principal(sp, target);
+ if (ret)
+ goto out;
+ ret = _kadm5_client_send(context, sp);
+ if (ret)
+ goto out_keep_error;
+ ret = _kadm5_client_recv(context, &reply);
+ if (ret)
+ goto out_keep_error;
+ krb5_storage_free(sp);
+ sp = krb5_storage_from_data(&reply);
+ if (sp == NULL) {
+ ret = krb5_enomem(context->context);
+ goto out_keep_error;
+ }
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+
+ out:
+ krb5_clear_error_message(context->context);
+
+ out_keep_error:
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/kadm5/rename_s.c b/third_party/heimdal/lib/kadm5/rename_s.c
new file mode 100644
index 0000000..9143318
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/rename_s.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003, 2005 - 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+struct rename_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal source, target;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+rename_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct rename_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->rename(context, hookctx,
+ ctx->stage, ctx->code,
+ ctx->source, ctx->target);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "rename",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+rename_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal source,
+ krb5_const_principal target)
+{
+ krb5_error_code ret;
+ struct rename_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.source = source;
+ ctx.target = target;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, rename_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+kadm5_ret_t
+kadm5_s_rename_principal(void *server_handle,
+ krb5_principal source,
+ krb5_principal target)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+ hdb_entry ent;
+ krb5_principal oldname;
+ size_t i;
+
+ memset(&ent, 0, sizeof(ent));
+ if (krb5_principal_compare(context->context, source, target))
+ return KADM5_DUP; /* XXX is this right? */
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
+ /* NOTE: We do not use hdb_fetch_kvno() here */
+ ret = context->db->hdb_fetch_kvno(context->context, context->db,
+ source,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if (ret)
+ goto out2;
+ oldname = ent.principal;
+
+ ret = rename_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT,
+ 0, source, target);
+ if (ret)
+ goto out3;
+
+ ret = _kadm5_set_modifier(context, &ent);
+ if (ret)
+ goto out3;
+ {
+ /* fix salt */
+ Salt salt;
+ krb5_salt salt2;
+ memset(&salt, 0, sizeof(salt));
+ ret = krb5_get_pw_salt(context->context, source, &salt2);
+ if (ret)
+ goto out3;
+ salt.type = hdb_pw_salt;
+ salt.salt = salt2.saltvalue;
+ for(i = 0; i < ent.keys.len; i++){
+ if(ent.keys.val[i].salt == NULL){
+ ent.keys.val[i].salt =
+ malloc(sizeof(*ent.keys.val[i].salt));
+ if (ent.keys.val[i].salt == NULL)
+ ret = krb5_enomem(context->context);
+ else
+ ret = copy_Salt(&salt, ent.keys.val[i].salt);
+ if (ret)
+ break;
+ }
+ }
+ krb5_free_salt(context->context, salt2);
+ }
+ if (ret)
+ goto out3;
+
+ /* Borrow target */
+ ent.principal = target;
+ ret = hdb_seal_keys(context->context, context->db, &ent);
+ if (ret)
+ goto out3;
+
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_rename(context, source, &ent);
+
+ (void) rename_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT,
+ ret, source, target);
+
+ out3:
+ ent.principal = oldname; /* Unborrow target */
+ hdb_free_entry(context->context, context->db, &ent);
+
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
+ return _kadm5_error_code(ret);
+}
+
diff --git a/third_party/heimdal/lib/kadm5/sample_hook.c b/third_party/heimdal/lib/kadm5/sample_hook.c
new file mode 100644
index 0000000..03ac32b
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/sample_hook.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2018-2019, AuriStor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <assert.h>
+
+#include <krb5.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "admin.h"
+#include "kadm5-hook.h"
+
+/*
+ * Sample kadm5 hook plugin that just logs when it is called. Install it
+ * somewhere and configure the path in the [kadmin] section of krb5.conf.
+ * e.g.
+ *
+ * [kadmin]
+ * plugin_dir = /usr/local/heimdal/lib/plugin/kadm5
+ *
+ */
+
+static char sample_data_1, sample_data_2;
+
+static krb5_error_code
+sample_log(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ const char *tag,
+ krb5_error_code code,
+ krb5_const_principal princ)
+{
+ char *p = NULL;
+ int which = 0;
+
+ /* verify we get called with the right contex tpointer */
+ if (data == &sample_data_1)
+ which = 1;
+ else if (data == &sample_data_2)
+ which = 2;
+
+ assert(which != 0);
+
+ /* code should always be zero on pre-commit */
+ assert(code == 0 || stage == KADM5_HOOK_STAGE_POSTCOMMIT);
+
+ if (princ)
+ (void) krb5_unparse_name(context, princ, &p);
+
+ krb5_warn(context, code, "sample_hook_%d: %s %s hook princ '%s'", which, tag,
+ stage == KADM5_HOOK_STAGE_PRECOMMIT ? "pre-commit" : "post-commit",
+ p != NULL ? p : "<unknown>");
+
+ krb5_xfree(p);
+
+ /* returning zero and KRB5_PLUGIN_NO_HANDLE are the same for hook plugins */
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_init_1(krb5_context context, void **data)
+{
+ *data = &sample_data_1;
+ krb5_warn(context, 0, "sample_hook_1: initializing");
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_init_2(krb5_context context, void **data)
+{
+ *data = &sample_data_2;
+ krb5_warn(context, 0, "sample_hook_2: initializing");
+ return 0;
+}
+
+static void KRB5_CALLCONV
+sample_fini(void *data)
+{
+ krb5_warn(NULL, 0, "sample_fini: finalizing");
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_chpass_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ return sample_log(context, data, stage, "chpass", code, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_chpass_with_key_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_key_data,
+ krb5_key_data *key_data)
+{
+ return sample_log(context, data, stage, "chpass_with_key", code, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_create_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ kadm5_principal_ent_t ent,
+ uint32_t mask,
+ const char *password)
+{
+ return sample_log(context, data, stage, "create", code, ent->principal);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_modify_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ kadm5_principal_ent_t ent,
+ uint32_t mask)
+{
+ return sample_log(context, data, stage, "modify", code, ent->principal);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_delete_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ)
+{
+ return sample_log(context, data, stage, "delete", code, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_randkey_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ)
+{
+ return sample_log(context, data, stage, "randkey", code, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_rename_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal source,
+ krb5_const_principal target)
+{
+ return sample_log(context, data, stage, "rename", code, source);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_set_keys_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ size_t n_keys,
+ krb5_keyblock *keyblocks)
+{
+ return sample_log(context, data, stage, "set_keys", code, princ);
+}
+
+static krb5_error_code KRB5_CALLCONV
+sample_prune_hook(krb5_context context,
+ void *data,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ int kvno)
+{
+ return sample_log(context, data, stage, "prune", code, princ);
+}
+
+
+static const kadm5_hook_ftable sample_hook_1 = {
+ KADM5_HOOK_VERSION_V1,
+ sample_init_1,
+ sample_fini,
+ "sample_hook_1",
+ "Heimdal",
+ sample_chpass_hook,
+ sample_chpass_with_key_hook,
+ sample_create_hook,
+ sample_modify_hook,
+ sample_delete_hook,
+ sample_randkey_hook,
+ sample_rename_hook,
+ sample_set_keys_hook,
+ sample_prune_hook,
+};
+
+static const kadm5_hook_ftable sample_hook_2 = {
+ KADM5_HOOK_VERSION_V1,
+ sample_init_2,
+ sample_fini,
+ "sample_hook_2",
+ "Heimdal",
+ sample_chpass_hook,
+ sample_chpass_with_key_hook,
+ sample_create_hook,
+ sample_modify_hook,
+ sample_delete_hook,
+ sample_randkey_hook,
+ sample_rename_hook,
+ sample_set_keys_hook,
+ sample_prune_hook,
+};
+
+/* Arrays of pointers, because hooks may be different versions/sizes */
+static const kadm5_hook_ftable *const sample_hooks[] = {
+ &sample_hook_1,
+ &sample_hook_2,
+};
+
+krb5_error_code
+kadm5_hook_plugin_load(krb5_context context,
+ krb5_get_instance_func_t *get_instance,
+ size_t *num_hooks,
+ const kadm5_hook_ftable *const **hooks);
+
+static uintptr_t KRB5_LIB_CALL
+sample_hook_get_instance(const char *libname)
+{
+ if (strcmp(libname, "kadm5") == 0)
+ return kadm5_get_instance(libname);
+ else if (strcmp(libname, "krb5") == 0)
+ return krb5_get_instance(libname);
+
+ return 0;
+}
+
+krb5_error_code
+kadm5_hook_plugin_load(krb5_context context,
+ krb5_get_instance_func_t *get_instance,
+ size_t *num_hooks,
+ const kadm5_hook_ftable *const **hooks)
+{
+ *get_instance = sample_hook_get_instance;
+ *num_hooks = sizeof(sample_hooks) / sizeof(sample_hooks[0]);
+ *hooks = sample_hooks;
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/sample_passwd_check.c b/third_party/heimdal/lib/kadm5/sample_passwd_check.c
new file mode 100644
index 0000000..6df9513
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/sample_passwd_check.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+/* $Id$ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <krb5.h>
+
+const char* check_length(krb5_context, krb5_principal, krb5_data *);
+
+/* specify the api-version this library conforms to */
+
+int version = 0;
+
+/* just check the length of the password, this is what the default
+ check does, but this lets you specify the minimum length in
+ krb5.conf */
+const char*
+check_length(krb5_context context,
+ krb5_principal prinipal,
+ krb5_data *password)
+{
+ int min_length = krb5_config_get_int_default(context, NULL, 6,
+ "password_quality",
+ "min_length",
+ NULL);
+ if(password->length < min_length)
+ return "Password too short";
+ return NULL;
+}
+
+#ifdef DICTPATH
+
+/* use cracklib to check password quality; this requires a patch for
+ cracklib that can be found at
+ ftp://ftp.pdc.kth.se/pub/krb/src/cracklib.patch */
+
+const char*
+check_cracklib(krb5_context context,
+ krb5_principal principal,
+ krb5_data *password)
+{
+ char *s = malloc(password->length + 1);
+ char *msg;
+ char *strings[2];
+ if(s == NULL)
+ return NULL; /* XXX */
+ strings[0] = principal->name.name_string.val[0]; /* XXX */
+ strings[1] = NULL;
+ memcpy(s, password->data, password->length);
+ s[password->length] = '\0';
+ msg = FascistCheck(s, DICTPATH, strings);
+ memset(s, 0, password->length);
+ free(s);
+ return msg;
+}
+#endif
diff --git a/third_party/heimdal/lib/kadm5/send_recv.c b/third_party/heimdal/lib/kadm5/send_recv.c
new file mode 100644
index 0000000..cf52fcd
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/send_recv.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1997-2003, 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+_kadm5_client_send(kadm5_client_context *context, krb5_storage *sp)
+{
+ krb5_data msg, out;
+ krb5_error_code ret;
+ size_t len;
+ krb5_storage *sock;
+
+ assert(context->sock != rk_INVALID_SOCKET);
+
+ len = krb5_storage_seek(sp, 0, SEEK_CUR);
+ ret = krb5_data_alloc(&msg, len);
+ if (ret) {
+ krb5_clear_error_message(context->context);
+ return ret;
+ }
+ krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_read(sp, msg.data, msg.length);
+
+ ret = krb5_mk_priv(context->context, context->ac, &msg, &out, NULL);
+ krb5_data_free(&msg);
+ if(ret)
+ return ret;
+
+ sock = krb5_storage_from_socket(context->sock);
+ if(sock == NULL) {
+ krb5_data_free(&out);
+ return krb5_enomem(context->context);
+ }
+
+ ret = krb5_store_data(sock, out);
+ if (ret)
+ krb5_clear_error_message(context->context);
+ krb5_storage_free(sock);
+ krb5_data_free(&out);
+ return ret;
+}
+
+kadm5_ret_t
+_kadm5_client_recv(kadm5_client_context *context, krb5_data *reply)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ krb5_storage *sock;
+
+ sock = krb5_storage_from_socket(context->sock);
+ if (sock == NULL)
+ return krb5_enomem(context->context);
+ ret = krb5_ret_data(sock, &data);
+
+ krb5_storage_free(sock);
+ krb5_clear_error_message(context->context);
+ if(ret == KRB5_CC_END)
+ return KADM5_RPC_ERROR;
+ else if(ret)
+ return ret;
+
+ ret = krb5_rd_priv(context->context, context->ac, &data, reply, NULL);
+ krb5_data_free(&data);
+ return ret;
+}
+
diff --git a/third_party/heimdal/lib/kadm5/server_glue.c b/third_party/heimdal/lib/kadm5/server_glue.c
new file mode 100644
index 0000000..4b430b6
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/server_glue.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+kadm5_init_with_password(const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_password(client_name,
+ password,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_password_ctx(krb5_context context,
+ const char *client_name,
+ const char *password,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_password_ctx(context,
+ client_name,
+ password,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_skey(const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_skey(client_name,
+ keytab,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_skey_ctx(krb5_context context,
+ const char *client_name,
+ const char *keytab,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_skey_ctx(context,
+ client_name,
+ keytab,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_creds(const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_creds(client_name,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
+
+kadm5_ret_t
+kadm5_init_with_creds_ctx(krb5_context context,
+ const char *client_name,
+ krb5_ccache ccache,
+ const char *service_name,
+ kadm5_config_params *realm_params,
+ unsigned long struct_version,
+ unsigned long api_version,
+ void **server_handle)
+{
+ return kadm5_s_init_with_creds_ctx(context,
+ client_name,
+ ccache,
+ service_name,
+ realm_params,
+ struct_version,
+ api_version,
+ server_handle);
+}
diff --git a/third_party/heimdal/lib/kadm5/server_hooks.c b/third_party/heimdal/lib/kadm5/server_hooks.c
new file mode 100644
index 0000000..0973382
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/server_hooks.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018, AuriStor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "kadm5_locl.h"
+
+static const char *kadm5_hook_plugin_deps[] = {
+ "kadm5",
+ "krb5",
+ NULL
+};
+
+struct heim_plugin_data kadm5_hook_plugin_data = {
+ "kadm5",
+ "kadm5_hook",
+ KADM5_HOOK_VERSION_V1,
+ kadm5_hook_plugin_deps,
+ kadm5_get_instance
+};
+
+void
+_kadm5_s_set_hook_error_message(kadm5_server_context *context,
+ krb5_error_code ret,
+ const char *op,
+ const struct kadm5_hook_ftable *hook,
+ enum kadm5_hook_stage stage)
+{
+ assert(ret != 0);
+
+ krb5_set_error_message(context->context, ret,
+ "%s hook `%s' failed %s-commit",
+ op, hook->name,
+ stage == KADM5_HOOK_STAGE_PRECOMMIT ? "pre" : "post");
+}
+
+kadm5_ret_t
+_kadm5_s_init_hooks(kadm5_server_context *ctx)
+{
+ krb5_context context = ctx->context;
+ char **dirs;
+
+ dirs = krb5_config_get_strings(context, NULL, "kadmin",
+ "plugin_dir", NULL);
+ if (dirs == NULL)
+ return 0;
+
+ _krb5_load_plugins(context, "kadm5", (const char **)dirs);
+ krb5_config_free_strings(dirs);
+
+ return 0;
+}
+
+void
+_kadm5_s_free_hooks(kadm5_server_context *ctx)
+{
+ _krb5_unload_plugins(ctx->context, "kadm5");
+}
+
+uintptr_t KRB5_LIB_CALL
+kadm5_get_instance(const char *libname)
+{
+ static const char *instance = "libkadm5";
+
+ if (strcmp(libname, "kadm5") == 0)
+ return (uintptr_t)instance;
+ else if (strcmp(libname, "krb5") == 0)
+ return krb5_get_instance(libname);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/set_keys.c b/third_party/heimdal/lib/kadm5/set_keys.c
new file mode 100644
index 0000000..c30c5d8
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/set_keys.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+/*
+ * Set the keys of `ent' to the string-to-key of `password'
+ */
+
+kadm5_ret_t
+_kadm5_set_keys(kadm5_server_context *context,
+ hdb_entry *ent,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ Key *keys;
+ size_t num_keys;
+ kadm5_ret_t ret;
+
+ ret = hdb_generate_key_set_password_with_ks_tuple(context->context,
+ ent->principal,
+ password,
+ ks_tuple, n_ks_tuple,
+ &keys, &num_keys);
+ if (ret)
+ return ret;
+
+ _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
+ ent->keys.val = keys;
+ ent->keys.len = num_keys;
+
+ hdb_entry_set_pw_change_time(context->context, ent, 0);
+
+ if (krb5_config_get_bool_default(context->context, NULL, FALSE,
+ "kadmin", "save-password", NULL))
+ {
+ ret = hdb_entry_set_password(context->context, context->db,
+ ent, password);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void
+setup_Key(Key *k, Salt *s, krb5_key_data *kd, size_t kd_offset)
+{
+ memset(k, 0, sizeof (*k)); /* sets mkvno and salt */
+ k->key.keytype = kd[kd_offset].key_data_type[0];
+ k->key.keyvalue.length = kd[kd_offset].key_data_length[0];
+ k->key.keyvalue.data = kd[kd_offset].key_data_contents[0];
+
+ if(kd[kd_offset].key_data_ver == 2) {
+ memset(s, 0, sizeof (*s));
+ s->type = kd[kd_offset].key_data_type[1];
+ s->salt.length = kd[kd_offset].key_data_length[1];
+ s->salt.data = kd[kd_offset].key_data_contents[1];
+ k->salt = s;
+ }
+}
+
+/*
+ * Set the keys of `ent' to (`n_key_data', `key_data')
+ */
+
+kadm5_ret_t
+_kadm5_set_keys2(kadm5_server_context *context,
+ hdb_entry *ent,
+ int16_t n_key_data,
+ krb5_key_data *key_data)
+{
+ krb5_error_code ret;
+ size_t i, k;
+ HDB_extension ext;
+ HDB_extension *extp = NULL;
+ HDB_Ext_KeySet *hist_keys = &ext.data.u.hist_keys;
+ Key key;
+ Salt salt;
+ Keys keys;
+ hdb_keyset hkset;
+ krb5_kvno kvno = -1;
+ int one_key_set = 1;
+ int replace_hist_keys = 0;
+
+ if (n_key_data == 0) {
+ /* Clear all keys! */
+ ret = hdb_clear_extension(context->context, ent,
+ choice_HDB_extension_data_hist_keys);
+ if (ret)
+ return ret;
+ free_Keys(&ent->keys);
+ return 0;
+ }
+
+ memset(&keys, 0, sizeof (keys));
+ memset(&hkset, 0, sizeof (hkset)); /* set set_time */
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ memset(hist_keys, 0, sizeof (*hist_keys));
+
+ for (i = 0; i < n_key_data; i++) {
+ if (kvno != -1 && kvno != key_data[i].key_data_kvno) {
+ one_key_set = 0;
+ break;
+ }
+ kvno = key_data[i].key_data_kvno;
+ }
+ if (one_key_set) {
+ /*
+ * If we're updating KADM5_KEY_DATA with a single keyset then we
+ * assume we must be setting the principal's kvno as well!
+ *
+ * Just have to be careful about old clients that might have
+ * sent 0 as the kvno... This may seem ugly, but it's the price
+ * of backwards compatibility with pre-multi-kvno kadmin clients
+ * (besides, who's to say that updating KADM5_KEY_DATA requires
+ * updating the entry's kvno?)
+ *
+ * Note that we do nothing special for the case where multiple
+ * keysets are given but the entry's kvno is not set and not in
+ * the given set of keysets. If this happens we'll just update
+ * the key history only and leave the current keyset alone.
+ */
+ if (kvno == 0) {
+ /* Force kvno to 1 if it was 0; (ank would do this anyways) */
+ if (ent->kvno == 0)
+ ent->kvno = 1;
+ /* Below we need key_data[*].kvno to be reasonable */
+ for (i = 0; i < n_key_data; i++)
+ key_data[i].key_data_kvno = ent->kvno;
+ } else {
+ /*
+ * Or force the entry's kvno to match the one from the new,
+ * singular keyset
+ */
+ ent->kvno = kvno;
+ }
+ }
+
+ for (i = 0; i < n_key_data; i++) {
+ if (key_data[i].key_data_kvno == ent->kvno) {
+ /* A current key; add to current key set */
+ setup_Key(&key, &salt, key_data, i);
+ ret = add_Keys(&keys, &key);
+ if (ret)
+ goto out;
+ continue;
+ }
+
+ /*
+ * This kvno is historical. Build an hdb_keyset for keys of
+ * this enctype and add them to the new key history.
+ */
+ for (k = 0; k < hist_keys->len; k++) {
+ if (hist_keys->val[k].kvno == key_data[i].key_data_kvno)
+ break;
+ }
+ if (hist_keys->len > k &&
+ hist_keys->val[k].kvno == key_data[i].key_data_kvno)
+ /* We've added all keys of this kvno already (see below) */
+ continue;
+
+ memset(&hkset, 0, sizeof (hkset)); /* set set_time */
+ hkset.kvno = key_data[i].key_data_kvno;
+ for (k = 0; k < n_key_data; k++) {
+ /* Find all keys of this kvno and add them to the new keyset */
+ if (key_data[k].key_data_kvno != hkset.kvno)
+ continue;
+
+ setup_Key(&key, &salt, key_data, k);
+ ret = add_Keys(&hkset.keys, &key);
+ if (ret) {
+ free_hdb_keyset(&hkset);
+ goto out;
+ }
+ }
+ ret = add_HDB_Ext_KeySet(hist_keys, &hkset);
+ free_hdb_keyset(&hkset);
+ if (ret)
+ goto out;
+ replace_hist_keys = 1;
+ }
+
+ if (replace_hist_keys)
+ /* No key history given -> leave it alone */
+ extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
+ if (extp != NULL) {
+ HDB_Ext_KeySet *old_hist_keys;
+
+ /*
+ * Try to keep the very useful set_time values from the old hist
+ * keys. kadm5 loses this info, so this heuristic is the best we
+ * can do.
+ */
+ old_hist_keys = &extp->data.u.hist_keys;
+ for (i = 0; i < old_hist_keys->len; i++) {
+ if (old_hist_keys->val[i].set_time == NULL)
+ continue;
+ for (k = 0; k < hist_keys->len; k++) {
+ if (hist_keys->val[k].kvno != old_hist_keys->val[k].kvno)
+ continue;
+ hist_keys->val[k].set_time = old_hist_keys->val[k].set_time;
+ old_hist_keys->val[k].set_time = NULL;
+ }
+ }
+ }
+
+ if (replace_hist_keys) {
+ /* If hist keys not given in key_data then don't blow away hist_keys */
+ ret = hdb_replace_extension(context->context, ent, &ext);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * A structure copy is more efficient here than this would be:
+ *
+ * copy_Keys(&keys, &ent->keys);
+ * free_Keys(&keys);
+ *
+ * Of course, the above hdb_replace_extension() is not at all efficient...
+ */
+ free_HDB_extension(&ext);
+ free_Keys(&ent->keys);
+ free_hdb_keyset(&hkset);
+ ent->keys = keys;
+ hdb_entry_set_pw_change_time(context->context, ent, 0);
+ hdb_entry_clear_password(context->context, ent);
+
+ return 0;
+
+out:
+ free_Keys(&keys);
+ free_HDB_extension(&ext);
+ return ret;
+}
+
+/*
+ * Set the keys of `ent' to `n_keys, keys'
+ */
+
+kadm5_ret_t
+_kadm5_set_keys3(kadm5_server_context *context,
+ hdb_entry *ent,
+ int n_keys,
+ krb5_keyblock *keyblocks)
+{
+ krb5_error_code ret;
+ int i;
+ unsigned len;
+ Key *keys;
+
+ len = n_keys;
+ keys = malloc (len * sizeof(*keys));
+ if (keys == NULL && len != 0)
+ return krb5_enomem(context->context);
+
+ _kadm5_init_keys (keys, len);
+
+ for(i = 0; i < n_keys; i++) {
+ keys[i].mkvno = NULL;
+ ret = krb5_copy_keyblock_contents (context->context,
+ &keyblocks[i],
+ &keys[i].key);
+ if(ret)
+ goto out;
+ keys[i].salt = NULL;
+ }
+ _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
+ ent->keys.len = len;
+ ent->keys.val = keys;
+
+ hdb_entry_set_pw_change_time(context->context, ent, 0);
+ hdb_entry_clear_password(context->context, ent);
+
+ return 0;
+ out:
+ _kadm5_free_keys (context->context, len, keys);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static int
+is_des_key_p(int keytype)
+{
+ return keytype == ETYPE_DES_CBC_CRC ||
+ keytype == ETYPE_DES_CBC_MD4 ||
+ keytype == ETYPE_DES_CBC_MD5;
+}
+
+
+/*
+ * Set the keys of `ent' to random keys and return them in `n_keys'
+ * and `new_keys'.
+ */
+
+kadm5_ret_t
+_kadm5_set_keys_randomly (kadm5_server_context *context,
+ hdb_entry *ent,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **new_keys,
+ int *n_keys)
+{
+ krb5_keyblock *kblock = NULL;
+ kadm5_ret_t ret = 0;
+ int des_keyblock;
+ size_t i, num_keys;
+ Key *keys;
+
+ ret = hdb_generate_key_set(context->context, ent->principal,
+ ks_tuple, n_ks_tuple, &keys, &num_keys, 1);
+ if (ret)
+ return ret;
+
+ kblock = malloc(num_keys * sizeof(kblock[0]));
+ if (kblock == NULL) {
+ ret = krb5_enomem(context->context);
+ _kadm5_free_keys (context->context, num_keys, keys);
+ return ret;
+ }
+ memset(kblock, 0, num_keys * sizeof(kblock[0]));
+
+ des_keyblock = -1;
+ for (i = 0; i < num_keys; i++) {
+
+ /*
+ * To make sure all des keys are the the same we generate only
+ * the first one and then copy key to all other des keys.
+ */
+
+ if (des_keyblock != -1 && is_des_key_p(keys[i].key.keytype)) {
+ ret = krb5_copy_keyblock_contents (context->context,
+ &kblock[des_keyblock],
+ &kblock[i]);
+ if (ret)
+ goto out;
+ kblock[i].keytype = keys[i].key.keytype;
+ } else {
+ ret = krb5_generate_random_keyblock (context->context,
+ keys[i].key.keytype,
+ &kblock[i]);
+ if (ret)
+ goto out;
+
+ if (is_des_key_p(keys[i].key.keytype))
+ des_keyblock = i;
+ }
+
+ ret = krb5_copy_keyblock_contents (context->context,
+ &kblock[i],
+ &keys[i].key);
+ if (ret)
+ goto out;
+ }
+
+out:
+ if(ret) {
+ for (i = 0; i < num_keys; ++i)
+ krb5_free_keyblock_contents(context->context, &kblock[i]);
+ free(kblock);
+ _kadm5_free_keys(context->context, num_keys, keys);
+ return ret;
+ }
+
+ _kadm5_free_keys(context->context, ent->keys.len, ent->keys.val);
+ ent->keys.val = keys;
+ ent->keys.len = num_keys;
+ if (n_keys && new_keys) {
+ *new_keys = kblock;
+ *n_keys = num_keys;
+ } else {
+ for (i = 0; i < num_keys; ++i)
+ krb5_free_keyblock_contents(context->context, &kblock[i]);
+ free(kblock);
+ }
+
+ hdb_entry_set_pw_change_time(context->context, ent, 0);
+ hdb_entry_clear_password(context->context, ent);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/set_modifier.c b/third_party/heimdal/lib/kadm5/set_modifier.c
new file mode 100644
index 0000000..dd0e427
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/set_modifier.c
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+RCSID("$Id$");
+
+kadm5_ret_t
+_kadm5_set_modifier(kadm5_server_context *context,
+ hdb_entry *ent)
+{
+ kadm5_ret_t ret;
+ if(ent->modified_by == NULL){
+ ent->modified_by = malloc(sizeof(*ent->modified_by));
+ if(ent->modified_by == NULL)
+ return krb5_enomem(context->context);
+ } else
+ free_Event(ent->modified_by);
+ ent->modified_by->time = time(NULL);
+ ret = krb5_copy_principal(context->context, context->caller,
+ &ent->modified_by->principal);
+ return ret;
+}
+
diff --git a/third_party/heimdal/lib/kadm5/setkey3_s.c b/third_party/heimdal/lib/kadm5/setkey3_s.c
new file mode 100644
index 0000000..584c194
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/setkey3_s.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1997-2001, 2003, 2005-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+struct setkey_principal_hook_ctx {
+ kadm5_server_context *context;
+ enum kadm5_hook_stage stage;
+ krb5_error_code code;
+ krb5_const_principal princ;
+ uint32_t flags;
+ size_t n_ks_tuple;
+ krb5_key_salt_tuple *ks_tuple;
+ size_t n_keys;
+ krb5_keyblock *keys;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+setkey_principal_hook_cb(krb5_context context,
+ const void *hook,
+ void *hookctx,
+ void *userctx)
+{
+ krb5_error_code ret;
+ const struct kadm5_hook_ftable *ftable = hook;
+ struct setkey_principal_hook_ctx *ctx = userctx;
+
+ ret = ftable->set_keys(context, hookctx,
+ ctx->stage, ctx->code,
+ ctx->princ, ctx->flags,
+ ctx->n_ks_tuple, ctx->ks_tuple,
+ ctx->n_keys, ctx->keys);
+ if (ret != 0 && ret != KRB5_PLUGIN_NO_HANDLE)
+ _kadm5_s_set_hook_error_message(ctx->context, ret, "setkey",
+ hook, ctx->stage);
+
+ /* only pre-commit plugins can abort */
+ if (ret == 0 || ctx->stage == KADM5_HOOK_STAGE_POSTCOMMIT)
+ ret = KRB5_PLUGIN_NO_HANDLE;
+
+ return ret;
+}
+
+static kadm5_ret_t
+setkey_principal_hook(kadm5_server_context *context,
+ enum kadm5_hook_stage stage,
+ krb5_error_code code,
+ krb5_const_principal princ,
+ uint32_t flags,
+ size_t n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ size_t n_keys,
+ krb5_keyblock *keyblocks)
+{
+ krb5_error_code ret;
+ struct setkey_principal_hook_ctx ctx;
+
+ ctx.context = context;
+ ctx.stage = stage;
+ ctx.code = code;
+ ctx.princ = princ;
+ ctx.flags = flags;
+ ctx.n_ks_tuple = n_ks_tuple;
+ ctx.ks_tuple = ks_tuple;
+ ctx.n_keys = n_keys;
+ ctx.keys = keyblocks;
+
+ ret = _krb5_plugin_run_f(context->context, &kadm5_hook_plugin_data,
+ 0, &ctx, setkey_principal_hook_cb);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ ret = 0;
+
+ return ret;
+}
+
+/**
+ * Server-side function to set new keys for a principal.
+ */
+kadm5_ret_t
+kadm5_s_setkey_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock *keyblocks, int n_keys)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry ent;
+ kadm5_ret_t ret = 0;
+ size_t i;
+
+ memset(&ent, 0, sizeof(ent));
+ if (!context->keep_open)
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret)
+ return ret;
+
+ ret = kadm5_log_init(context);
+ if (ret) {
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ /* NOTE: We do not use hdb_fetch_kvno() here (maybe we should?) */
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA,
+ 0, &ent);
+ if (ret) {
+ (void) kadm5_log_end(context);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ ret = setkey_principal_hook(context, KADM5_HOOK_STAGE_PRECOMMIT, 0,
+ princ, keepold ? KADM5_HOOK_FLAG_KEEPOLD : 0,
+ n_ks_tuple, ks_tuple, n_keys, keyblocks);
+ if (ret) {
+ (void) kadm5_log_end(context);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ if (keepold) {
+ ret = hdb_add_current_keys_to_history(context->context, &ent);
+ } else
+ ret = hdb_clear_extension(context->context, &ent,
+ choice_HDB_extension_data_hist_keys);
+
+ /*
+ * Though in practice all real calls to this function will pass an empty
+ * ks_tuple, and cannot in any case employ any salts that require
+ * additional data, we go the extra mile to set any requested salt type
+ * along with a zero length salt value. While we're at it we check that
+ * each ks_tuple's enctype matches the corresponding key enctype.
+ */
+ if (ret == 0) {
+ free_Keys(&ent.keys);
+ for (i = 0; i < n_keys; ++i) {
+ Key k;
+ Salt s;
+
+ k.mkvno = 0;
+ k.key = keyblocks[i];
+ if (n_ks_tuple == 0)
+ k.salt = 0;
+ else {
+ if (ks_tuple[i].ks_enctype != keyblocks[i].keytype) {
+ ret = KADM5_SETKEY3_ETYPE_MISMATCH;
+ break;
+ }
+ s.type = ks_tuple[i].ks_salttype;
+ s.salt.data = 0;
+ s.opaque = 0;
+ k.salt = &s;
+ }
+ if ((ret = add_Keys(&ent.keys, &k)) != 0)
+ break;
+ }
+ }
+
+ if (ret == 0) {
+ ent.kvno++;
+ ent.flags.require_pwchange = 0;
+ hdb_entry_set_pw_change_time(context->context, &ent, 0);
+ hdb_entry_clear_password(context->context, &ent);
+
+ if ((ret = hdb_seal_keys(context->context, context->db,
+ &ent)) == 0
+ && (ret = _kadm5_set_modifier(context, &ent)) == 0
+ && (ret = _kadm5_bump_pw_expire(context, &ent)) == 0)
+ ret = kadm5_log_modify(context, &ent,
+ KADM5_ATTRIBUTES | KADM5_PRINCIPAL |
+ KADM5_MOD_NAME | KADM5_MOD_TIME |
+ KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
+ }
+
+ (void) setkey_principal_hook(context, KADM5_HOOK_STAGE_POSTCOMMIT, ret,
+ princ, keepold, n_ks_tuple, ks_tuple,
+ n_keys, keyblocks);
+
+ hdb_free_entry(context->context, context->db, &ent);
+ (void) kadm5_log_end(context);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return _kadm5_error_code(ret);
+}
diff --git a/third_party/heimdal/lib/kadm5/test_pw_quality.c b/third_party/heimdal/lib/kadm5/test_pw_quality.c
new file mode 100644
index 0000000..6544afd
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/test_pw_quality.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2003, 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <getarg.h>
+
+#include <roken.h>
+#include <krb5.h>
+
+#include "admin.h"
+
+static int version_flag;
+static int help_flag;
+static char *principal;
+static char *password;
+
+static struct getargs args[] = {
+ { "principal", 0, arg_string, &principal, NULL, NULL },
+ { "password", 0, arg_string, &password, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+int num_args = sizeof(args) / sizeof(args[0]);
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_principal p;
+ const char *s;
+ krb5_data pw_data;
+
+ krb5_program_setup(&context, argc, argv, args, num_args, NULL);
+
+ if(help_flag)
+ krb5_std_usage(0, args, num_args);
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (principal == NULL)
+ krb5_errx(context, 1, "no principal given");
+ if (password == NULL)
+ krb5_errx(context, 1, "no password given");
+
+ ret = krb5_parse_name(context, principal, &p);
+ if (ret)
+ krb5_errx(context, 1, "krb5_parse_name: %s", principal);
+
+ pw_data.data = password;
+ pw_data.length = strlen(password);
+
+ kadm5_setup_passwd_quality_check (context, NULL, NULL);
+ ret = kadm5_add_passwd_quality_verifier(context, NULL);
+ if (ret)
+ krb5_errx(context, 1, "kadm5_add_passwd_quality_verifier");
+
+ s = kadm5_check_password_quality (context, p, &pw_data);
+ if (s)
+ krb5_errx(context, 1, "kadm5_check_password_quality:\n%s", s);
+
+ krb5_free_principal(context, p);
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/kadm5/version-script-client.map b/third_party/heimdal/lib/kadm5/version-script-client.map
new file mode 100644
index 0000000..56a4ff3
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/version-script-client.map
@@ -0,0 +1,69 @@
+HEIMDAL_KADM5_CLIENT_1.0 {
+ global:
+ et_kadm5_error_table;
+ initialize_kadm5_error_table;
+ initialize_kadm5_error_table_r;
+ kadm5_ad_init_with_password;
+ kadm5_ad_init_with_password_ctx;
+ kadm5_all_keys_are_bogus;
+ kadm5_c_chpass_principal;
+ kadm5_c_chpass_principal_with_key;
+ kadm5_c_create_principal;
+ kadm5_c_delete_principal;
+ kadm5_c_destroy;
+ kadm5_c_dup_context;
+ kadm5_c_flush;
+ kadm5_c_get_principal;
+ kadm5_c_get_principals;
+ kadm5_c_get_privs;
+ kadm5_c_init_with_creds;
+ kadm5_c_init_with_creds_ctx;
+ kadm5_c_init_with_password;
+ kadm5_c_init_with_password_ctx;
+ kadm5_c_init_with_skey;
+ kadm5_c_init_with_skey_ctx;
+ kadm5_c_iter_principals;
+ kadm5_c_get_privs;
+ kadm5_c_modify_principal;
+ kadm5_c_prune_principal;
+ kadm5_c_randkey_principal;
+ kadm5_c_rename_principal;
+ kadm5_chpass_principal;
+ kadm5_chpass_principal_with_key;
+ kadm5_create_principal;
+ kadm5_delete_principal;
+ kadm5_destroy;
+ kadm5_dup_context;
+ kadm5_flush;
+ kadm5_free_key_data;
+ kadm5_free_name_list;
+ kadm5_free_principal_ent;
+ kadm5_get_principal;
+ kadm5_get_principals;
+ kadm5_get_privs;
+ kadm5_init_with_creds;
+ kadm5_init_with_creds_ctx;
+ kadm5_init_with_password;
+ kadm5_init_with_password_ctx;
+ kadm5_init_with_skey;
+ kadm5_init_with_skey_ctx;
+ kadm5_iter_principals;
+ kadm5_modify_principal;
+ kadm5_randkey_principal;
+ kadm5_randkey_principal_3;
+ kadm5_rename_principal;
+ kadm5_ret_key_data;
+ kadm5_ret_principal_ent;
+ kadm5_ret_principal_ent_mask;
+ kadm5_ret_tl_data;
+ kadm5_some_keys_are_bogus;
+ kadm5_store_key_data;
+ kadm5_store_principal_ent;
+ kadm5_store_principal_ent_mask;
+ kadm5_store_tl_data;
+ _kadm5_client_recv;
+ _kadm5_client_send;
+ _kadm5_connect;
+ local:
+ *;
+};
diff --git a/third_party/heimdal/lib/kadm5/version-script.map b/third_party/heimdal/lib/kadm5/version-script.map
new file mode 100644
index 0000000..6de88fc
--- /dev/null
+++ b/third_party/heimdal/lib/kadm5/version-script.map
@@ -0,0 +1,98 @@
+# $Id$
+
+HEIMDAL_KAMD5_SERVER_1.0 {
+ global:
+ kadm5_ad_init_with_password;
+ kadm5_ad_init_with_password_ctx;
+ kadm5_all_keys_are_bogus;
+ kadm5_add_passwd_quality_verifier;
+ kadm5_check_password_quality;
+ kadm5_chpass_principal;
+ kadm5_chpass_principal_3;
+ kadm5_chpass_principal_with_key;
+ kadm5_chpass_principal_with_key_3;
+ kadm5_create_policy;
+ kadm5_create_principal;
+ kadm5_create_principal_3;
+ kadm5_delete_principal;
+ kadm5_destroy;
+ kadm5_dup_context;
+ kadm5_decrypt_key;
+ kadm5_delete_policy;
+ kadm5_flush;
+ kadm5_free_key_data;
+ kadm5_free_name_list;
+ kadm5_free_policy_ent;
+ kadm5_free_principal_ent;
+ kadm5_get_instance;
+ kadm5_get_policy;
+ kadm5_get_policies;
+ kadm5_get_principal;
+ kadm5_get_principals;
+ kadm5_get_privs;
+ kadm5_init_with_creds;
+ kadm5_init_with_creds_ctx;
+ kadm5_init_with_password;
+ kadm5_init_with_password_ctx;
+ kadm5_init_with_skey;
+ kadm5_init_with_skey_ctx;
+ kadm5_iter_principals;
+ kadm5_lock;
+ kadm5_modify_principal;
+ kadm5_modify_policy;
+ kadm5_prune_principal;
+ kadm5_randkey_principal;
+ kadm5_randkey_principal_3;
+ kadm5_rename_principal;
+ kadm5_ret_key_data;
+ kadm5_ret_principal_ent;
+ kadm5_ret_principal_ent_mask;
+ kadm5_ret_tl_data;
+ kadm5_setup_passwd_quality_check;
+ kadm5_setkey_principal;
+ kadm5_setkey_principal_3;
+ kadm5_some_keys_are_bogus;
+ kadm5_store_key_data;
+ kadm5_store_principal_ent;
+ kadm5_store_principal_ent_mask;
+ kadm5_store_principal_ent_nokeys;
+ kadm5_store_tl_data;
+ kadm5_unlock;
+ kadm5_s_init_with_password_ctx;
+ kadm5_s_init_with_password;
+ kadm5_s_init_with_skey_ctx;
+ kadm5_s_init_with_skey;
+ kadm5_s_init_with_creds_ctx;
+ kadm5_s_init_with_creds;
+ kadm5_s_chpass_principal_cond;
+ kadm5_s_create_principal_with_key;
+ kadm5_log_exclusivelock;
+ kadm5_log_set_version;
+ kadm5_log_sharedlock;
+ kadm5_log_signal_master;
+ kadm5_log_signal_socket;
+ kadm5_log_previous;
+ kadm5_log_goto_first;
+ kadm5_log_goto_end;
+ kadm5_log_foreach;
+ kadm5_log_get_version_fd;
+ kadm5_log_get_version;
+ kadm5_log_recover;
+ kadm5_log_replay;
+ kadm5_log_end;
+ kadm5_log_reinit;
+ kadm5_log_init;
+ kadm5_log_init_nb;
+ kadm5_log_init_nolock;
+ kadm5_log_init_sharedlock;
+ kadm5_log_next;
+ kadm5_log_nop;
+ kadm5_log_truncate;
+ kadm5_log_modify;
+ _kadm5_acl_check_permission;
+ _kadm5_unmarshal_params;
+ _kadm5_s_get_db;
+ _kadm5_privs_to_string;
+ local:
+ *;
+};