From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- third_party/heimdal/lib/kadm5/ChangeLog | 1389 ++++++++++ third_party/heimdal/lib/kadm5/Makefile.am | 233 ++ third_party/heimdal/lib/kadm5/NTMakefile | 296 +++ third_party/heimdal/lib/kadm5/acl.c | 242 ++ third_party/heimdal/lib/kadm5/ad.c | 1525 +++++++++++ third_party/heimdal/lib/kadm5/admin.h | 272 ++ third_party/heimdal/lib/kadm5/bump_pw_expire.c | 64 + third_party/heimdal/lib/kadm5/check-cracklib.pl | 112 + third_party/heimdal/lib/kadm5/chpass_c.c | 175 ++ third_party/heimdal/lib/kadm5/chpass_s.c | 466 ++++ third_party/heimdal/lib/kadm5/client_glue.c | 150 ++ third_party/heimdal/lib/kadm5/common_glue.c | 452 ++++ third_party/heimdal/lib/kadm5/context_s.c | 316 +++ third_party/heimdal/lib/kadm5/create_c.c | 114 + third_party/heimdal/lib/kadm5/create_s.c | 363 +++ third_party/heimdal/lib/kadm5/default_keys.c | 121 + third_party/heimdal/lib/kadm5/delete_c.c | 88 + third_party/heimdal/lib/kadm5/delete_s.c | 145 + third_party/heimdal/lib/kadm5/destroy_c.c | 58 + third_party/heimdal/lib/kadm5/destroy_s.c | 95 + third_party/heimdal/lib/kadm5/ent_setup.c | 276 ++ third_party/heimdal/lib/kadm5/error.c | 48 + third_party/heimdal/lib/kadm5/flush.c | 48 + third_party/heimdal/lib/kadm5/flush_c.c | 41 + third_party/heimdal/lib/kadm5/flush_s.c | 41 + third_party/heimdal/lib/kadm5/free.c | 92 + .../kadm5/fuzz-inputs-bin/test_marshall-ent0.bin | Bin 0 -> 191 bytes .../kadm5/fuzz-inputs-bin/test_marshall-ent1.bin | Bin 0 -> 139 bytes .../fuzz-inputs-packed/test_marshall-ent0.bin | Bin 0 -> 65 bytes .../fuzz-inputs-packed/test_marshall-ent1.bin | Bin 0 -> 46 bytes .../kadm5/fuzz-inputs-txt/test_marshall-ent0.txt | 101 + .../kadm5/fuzz-inputs-txt/test_marshall-ent1.txt | 54 + third_party/heimdal/lib/kadm5/get_c.c | 96 + third_party/heimdal/lib/kadm5/get_princs_c.c | 299 +++ third_party/heimdal/lib/kadm5/get_princs_s.c | 207 ++ third_party/heimdal/lib/kadm5/get_s.c | 412 +++ third_party/heimdal/lib/kadm5/init_c.c | 920 +++++++ third_party/heimdal/lib/kadm5/init_s.c | 280 ++ third_party/heimdal/lib/kadm5/iprop-commands.in | 187 ++ third_party/heimdal/lib/kadm5/iprop-log-version.rc | 36 + third_party/heimdal/lib/kadm5/iprop-log.8 | 254 ++ third_party/heimdal/lib/kadm5/iprop-log.c | 653 +++++ third_party/heimdal/lib/kadm5/iprop.8 | 246 ++ third_party/heimdal/lib/kadm5/iprop.h | 80 + .../heimdal/lib/kadm5/ipropd-master-version.rc | 36 + .../heimdal/lib/kadm5/ipropd-slave-version.rc | 36 + third_party/heimdal/lib/kadm5/ipropd_common.c | 266 ++ third_party/heimdal/lib/kadm5/ipropd_master.c | 1890 ++++++++++++++ third_party/heimdal/lib/kadm5/ipropd_slave.c | 1186 +++++++++ third_party/heimdal/lib/kadm5/kadm5-hook.h | 151 ++ third_party/heimdal/lib/kadm5/kadm5-pwcheck.h | 73 + third_party/heimdal/lib/kadm5/kadm5_err.et | 114 + third_party/heimdal/lib/kadm5/kadm5_locl.h | 91 + third_party/heimdal/lib/kadm5/kadm5_pwcheck.3 | 159 ++ third_party/heimdal/lib/kadm5/keys.c | 132 + .../heimdal/lib/kadm5/libkadm5srv-exports.def | 93 + .../heimdal/lib/kadm5/libkadm5srv-version.rc | 36 + third_party/heimdal/lib/kadm5/log.c | 2756 ++++++++++++++++++++ third_party/heimdal/lib/kadm5/marshall.c | 962 +++++++ third_party/heimdal/lib/kadm5/modify_c.c | 93 + third_party/heimdal/lib/kadm5/modify_s.c | 215 ++ third_party/heimdal/lib/kadm5/password_quality.c | 508 ++++ third_party/heimdal/lib/kadm5/private.h | 210 ++ third_party/heimdal/lib/kadm5/privs_c.c | 89 + third_party/heimdal/lib/kadm5/privs_s.c | 44 + third_party/heimdal/lib/kadm5/prune_c.c | 73 + third_party/heimdal/lib/kadm5/prune_s.c | 149 ++ third_party/heimdal/lib/kadm5/randkey_c.c | 154 ++ third_party/heimdal/lib/kadm5/randkey_s.c | 204 ++ third_party/heimdal/lib/kadm5/rename_c.c | 94 + third_party/heimdal/lib/kadm5/rename_s.c | 188 ++ third_party/heimdal/lib/kadm5/sample_hook.c | 286 ++ .../heimdal/lib/kadm5/sample_passwd_check.c | 87 + third_party/heimdal/lib/kadm5/send_recv.c | 99 + third_party/heimdal/lib/kadm5/server_glue.c | 150 ++ third_party/heimdal/lib/kadm5/server_hooks.c | 97 + third_party/heimdal/lib/kadm5/set_keys.c | 420 +++ third_party/heimdal/lib/kadm5/set_modifier.c | 54 + third_party/heimdal/lib/kadm5/setkey3_s.c | 220 ++ third_party/heimdal/lib/kadm5/test_pw_quality.c | 99 + .../heimdal/lib/kadm5/version-script-client.map | 69 + third_party/heimdal/lib/kadm5/version-script.map | 98 + 82 files changed, 22728 insertions(+) create mode 100644 third_party/heimdal/lib/kadm5/ChangeLog create mode 100644 third_party/heimdal/lib/kadm5/Makefile.am create mode 100644 third_party/heimdal/lib/kadm5/NTMakefile create mode 100644 third_party/heimdal/lib/kadm5/acl.c create mode 100644 third_party/heimdal/lib/kadm5/ad.c create mode 100644 third_party/heimdal/lib/kadm5/admin.h create mode 100644 third_party/heimdal/lib/kadm5/bump_pw_expire.c create mode 100644 third_party/heimdal/lib/kadm5/check-cracklib.pl create mode 100644 third_party/heimdal/lib/kadm5/chpass_c.c create mode 100644 third_party/heimdal/lib/kadm5/chpass_s.c create mode 100644 third_party/heimdal/lib/kadm5/client_glue.c create mode 100644 third_party/heimdal/lib/kadm5/common_glue.c create mode 100644 third_party/heimdal/lib/kadm5/context_s.c create mode 100644 third_party/heimdal/lib/kadm5/create_c.c create mode 100644 third_party/heimdal/lib/kadm5/create_s.c create mode 100644 third_party/heimdal/lib/kadm5/default_keys.c create mode 100644 third_party/heimdal/lib/kadm5/delete_c.c create mode 100644 third_party/heimdal/lib/kadm5/delete_s.c create mode 100644 third_party/heimdal/lib/kadm5/destroy_c.c create mode 100644 third_party/heimdal/lib/kadm5/destroy_s.c create mode 100644 third_party/heimdal/lib/kadm5/ent_setup.c create mode 100644 third_party/heimdal/lib/kadm5/error.c create mode 100644 third_party/heimdal/lib/kadm5/flush.c create mode 100644 third_party/heimdal/lib/kadm5/flush_c.c create mode 100644 third_party/heimdal/lib/kadm5/flush_s.c create mode 100644 third_party/heimdal/lib/kadm5/free.c create mode 100644 third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin create mode 100644 third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin create mode 100644 third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin create mode 100644 third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin create mode 100644 third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent0.txt create mode 100644 third_party/heimdal/lib/kadm5/fuzz-inputs-txt/test_marshall-ent1.txt create mode 100644 third_party/heimdal/lib/kadm5/get_c.c create mode 100644 third_party/heimdal/lib/kadm5/get_princs_c.c create mode 100644 third_party/heimdal/lib/kadm5/get_princs_s.c create mode 100644 third_party/heimdal/lib/kadm5/get_s.c create mode 100644 third_party/heimdal/lib/kadm5/init_c.c create mode 100644 third_party/heimdal/lib/kadm5/init_s.c create mode 100644 third_party/heimdal/lib/kadm5/iprop-commands.in create mode 100644 third_party/heimdal/lib/kadm5/iprop-log-version.rc create mode 100644 third_party/heimdal/lib/kadm5/iprop-log.8 create mode 100644 third_party/heimdal/lib/kadm5/iprop-log.c create mode 100644 third_party/heimdal/lib/kadm5/iprop.8 create mode 100644 third_party/heimdal/lib/kadm5/iprop.h create mode 100644 third_party/heimdal/lib/kadm5/ipropd-master-version.rc create mode 100644 third_party/heimdal/lib/kadm5/ipropd-slave-version.rc create mode 100644 third_party/heimdal/lib/kadm5/ipropd_common.c create mode 100644 third_party/heimdal/lib/kadm5/ipropd_master.c create mode 100644 third_party/heimdal/lib/kadm5/ipropd_slave.c create mode 100644 third_party/heimdal/lib/kadm5/kadm5-hook.h create mode 100644 third_party/heimdal/lib/kadm5/kadm5-pwcheck.h create mode 100644 third_party/heimdal/lib/kadm5/kadm5_err.et create mode 100644 third_party/heimdal/lib/kadm5/kadm5_locl.h create mode 100644 third_party/heimdal/lib/kadm5/kadm5_pwcheck.3 create mode 100644 third_party/heimdal/lib/kadm5/keys.c create mode 100644 third_party/heimdal/lib/kadm5/libkadm5srv-exports.def create mode 100644 third_party/heimdal/lib/kadm5/libkadm5srv-version.rc create mode 100644 third_party/heimdal/lib/kadm5/log.c create mode 100644 third_party/heimdal/lib/kadm5/marshall.c create mode 100644 third_party/heimdal/lib/kadm5/modify_c.c create mode 100644 third_party/heimdal/lib/kadm5/modify_s.c create mode 100644 third_party/heimdal/lib/kadm5/password_quality.c create mode 100644 third_party/heimdal/lib/kadm5/private.h create mode 100644 third_party/heimdal/lib/kadm5/privs_c.c create mode 100644 third_party/heimdal/lib/kadm5/privs_s.c create mode 100644 third_party/heimdal/lib/kadm5/prune_c.c create mode 100644 third_party/heimdal/lib/kadm5/prune_s.c create mode 100644 third_party/heimdal/lib/kadm5/randkey_c.c create mode 100644 third_party/heimdal/lib/kadm5/randkey_s.c create mode 100644 third_party/heimdal/lib/kadm5/rename_c.c create mode 100644 third_party/heimdal/lib/kadm5/rename_s.c create mode 100644 third_party/heimdal/lib/kadm5/sample_hook.c create mode 100644 third_party/heimdal/lib/kadm5/sample_passwd_check.c create mode 100644 third_party/heimdal/lib/kadm5/send_recv.c create mode 100644 third_party/heimdal/lib/kadm5/server_glue.c create mode 100644 third_party/heimdal/lib/kadm5/server_hooks.c create mode 100644 third_party/heimdal/lib/kadm5/set_keys.c create mode 100644 third_party/heimdal/lib/kadm5/set_modifier.c create mode 100644 third_party/heimdal/lib/kadm5/setkey3_s.c create mode 100644 third_party/heimdal/lib/kadm5/test_pw_quality.c create mode 100644 third_party/heimdal/lib/kadm5/version-script-client.map create mode 100644 third_party/heimdal/lib/kadm5/version-script.map (limited to 'third_party/heimdal/lib/kadm5') 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 + + * ipropd_master.c: Only log "sending AYT" once, pointed out by Dr + A V Le Blanc. + + +2008-01-21 Love Hörnquist Åstrand + + * default_keys.c: Use hdb_free_keys(). + +2008-01-11 Love Hörnquist Åstrand + + * Makefile.am: add check-cracklib.pl, flush.c, + sample_passwd_check.c + +2007-12-07 Love Hörnquist Åstrand + + * use hdb_db_dir() and hdb_default_db() + +2007-10-18 Love + + * 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 + + * iprop.8: fix spelling, From Antoine Jacoutt. + +2007-08-16 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * 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 + + * 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 + + * ipropd_master.c: Add more logging, to figure out what is + happening in the master. + +2007-08-01 Love Hörnquist Åstrand + + * 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 + + * ipropd_master.c (send_diffs): make current slave's version + uptodate when diff have been sent. + +2007-07-27 Love Hörnquist Åstrand + + * ipropd_slave.c: More comments and some more error checking. + +2007-07-26 Love Hörnquist Åstrand + + * init_c.c (get_cache_principal): make sure id is reset if we + fail. From Benjamin Bennet. + +2007-07-10 Love Hörnquist Åstrand + + * 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 + + * 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 + + * init_c.c: Try harder to use the right principal. + +2007-06-20 Love Hörnquist Åstrand + + * ipropd_slave.c: Catch return value from krb5_program_setup. From + Steven Luo. + +2007-05-08 Love Hörnquist Åstrand + + * 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 + + * 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 + + * admin.h: add support to get aliases + + * get_s.c: add support to get aliases + +2007-02-11 David Love + + * iprop-log.8: Small fixes, from David Love. + +2006-12-15 Love Hörnquist Åstrand + + * 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 + + * ipropd_master.c: Use strcspn to remove \n from string returned + by fgets. From Björn Sandell + +2006-11-30 Love Hörnquist Åstrand + + * 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 + + * 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 + + * ent_setup.c: Try to not leak memory. + +2006-10-07 Love Hörnquist Åstrand + + * Makefile.am: split build files into dist_ and noinst_ SOURCES + +2006-08-24 Love Hörnquist Åstrand + + * 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 + + * check-cracklib.pl: Add password reuse checking. From Harald + Barth. + +2006-06-14 Love Hörnquist Åstrand + + * 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 + + * ent_setup.c (attr_to_flags): Add KRB5_KDB_TRUSTED_FOR_DELEGATION + +2006-05-30 Love Hörnquist Åstrand + + * password_quality.c (kadm5_check_password_quality): set error + message in context. + +2006-05-13 Love Hörnquist Åstrand + + * iprop-log.c: Avoid shadowing. + + * rename_s.c: Avoid shadowing. + +2006-05-08 Love Hörnquist Åstrand + + * privs_c.c (kadm5_c_get_privs): privs is a uint32_t, let copy it + that way. + +2006-05-05 Love Hörnquist Åstrand + + * Rename u_intXX_t to uintXX_t + +2006-04-27 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * kadm5_err.et: (PASS_REUSE): Spelling, + from Václav H?la + +2006-01-25 Love Hörnquist Åstrand + + * 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 + + * Makefile.am (libkadm5clnt.la) doesn't depend on libhdb, remove + dependency + +2005-12-13 Love Hörnquist Åstrand + + * memset hdb_entry_ex before use + +2005-12-12 Love Hörnquist Åstrand + + * Wrap hdb_entry with hdb_entry_ex, patch originally + from Andrew Bartlet + +2005-11-30 Love Hörnquist Åstrand + + * 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 + + * 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 + + * iprop-log.8: More text about iprop-log. + +2005-08-24 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * 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 + + * kadm5_pwcheck.3: please mdoclint + +2005-05-25 Dave Love + + * kadm5_pwcheck.3: document kadm5_add_passwd_quality_verifier, + improve text + +2005-05-24 Dave Love + + * iprop.8: Added some info about defaults, fixed some markup. + +2005-05-23 Dave Love + + * 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 + + * init_c.c (_kadm5_c_init_context): fix memory leak in case of + failure + +2005-05-09 Dave Love + + * 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 + + * default_keys.c: #include , only print salt it its longer + then 0, use krb5_err instead of errx where appropriate + +2005-04-25 Love Hörnquist Åstrand + + * 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 + + * dump_log.c: use strlcpy + + * password_quality.c: don't use sizeof(pointer) + +2005-04-15 Love Hörnquist Åstrand + + * 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 + + * password_quality.c: add an end tag to the external password + quality check protocol + +2005-04-13 Love Hörnquist Åstrand + + * 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 + + * 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 + for hdb-ldb backend. + +2004-09-23 Johan Danielsson + + * ipropd_master.c: add help strings to some options + +2004-09-12 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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@REALMall*@REALM", before it just matched + "foo@REALMall*@REALM", but that is kind of lucky since + what really happen was that the last was stamped out, and + the it never strtok_r never needed to parse over it. + +2004-08-25 Love Hörnquist Åstrand + + * 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 + + * ipropd_slave.c: add --detach + +2004-07-06 Love Hörnquist Åstrand + + * ad.c: use new tsasl interface remove debug printf add upn to + computer-accounts + +2004-06-28 Love Hörnquist Åstrand + + * ad.c: implement kadm5_ad_init_with_password_ctx set more error + strings + +2004-06-21 Love Hörnquist Åstrand + + * 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 + + * ipropd_master.c (main): process all slaves, not just up to the + last slave sending data + (bug report from Björn Sandell ) + (*): only send one ARE_YOU_THERE + +2004-06-02 Love Hörnquist Åstrand + + * ad.c: use krb5_set_password_using_ccache + +2004-06-01 Love Hörnquist Åstrand + + * ad.c: try handle spn's better + +2004-05-31 Love Hörnquist Åstrand + + * ad.c: add expiration time + + * ad.c: add modify operations + + * ad.c: handle create and delete + +2004-05-27 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * set_keys.c (_kadm5_set_keys_randomly): make sure enctype to copy + enctype for des keys From: Andrew Bartlett + + * 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 + + * chpass_s.c (change): fix same-password-again by decrypting keys + and setting an error code From: Buck Huppmann + +2003-12-21 Love Hörnquist Åstrand + + * init_c.c (_kadm5_c_init_context): catch errors from strdup and + other krb5_ functions + +2003-12-08 Love Hörnquist Åstrand + + * rename_s.c (kadm5_s_rename_principal): allow principal to change + realm From Panasas Inc + +2003-12-07 Love Hörnquist Åstrand + + * destroy_c.c (kadm5_c_destroy): fix memory leaks, From Panasas, + Inc + +2003-11-23 Love Hörnquist Åstrand + + * iprop.h: don't include + + * 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 + + * ipropd_slave.c (receive_loop): when seeking over the entries we + already have, skip over the trailer. From: Jeffrey Hutzelman + + + * dump_log.c,ipropd_master.c,ipropd_slave.c, + replay_log.c,truncate_log.c: parse kdc.conf + From: Jeffrey Hutzelman + +2003-10-10 Love Hörnquist Åstrand + + * Makefile.am: += test_pw_quality + + * test_pw_quality.c: test program for verifying password quality + function + +2003-09-03 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * ipropd_slave.c (receive_everything): switch close and rename + From: Alf Wachsmann + +2003-07-03 Love Hörnquist Åstrand + + * 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 + + * truncate_log.c (main): add missing ``if (ret)'' + +2003-06-26 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * dump_log.c (print_entry): check return values from + krb5_data_alloc + +2003-04-01 Love Hörnquist Åstrand + + * 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 + + * 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 + + * 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 + + * 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 + + * private.h: rename header file flag macro + + * Makefile.am: generate kadm5-{protos,private}.h + +2002-08-15 Johan Danielsson + + * ipropd_master.c: check return value of krb5_sockaddr2address + +2002-07-04 Johan Danielsson + + * 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 + + * 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 + + * 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 + + * 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 + + * init_c.c: we have to create our own param struct before + marshaling + +2001-09-05 Johan Danielsson + + * Makefile.am: link with LIB_pidfile + + * iprop.h: include util.h for pidfile + +2001-08-31 Assar Westerlund + + * ipropd_slave.c (main): syslog with the correct name + +2001-08-30 Jacques Vidrine + + * ipropd_slave.c, ipropd_master.c (main): call pidfile + +2001-08-28 Assar Westerlund + + * Makefile.am (libkadm5srv_la_LDFLAGS): set version to 7:4:0 + +2001-08-24 Assar Westerlund + + * 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 + + * 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 + + * Makefile.am: add required library dependencies + +2001-07-03 Assar Westerlund + + * Makefile.am (libkadm5clnt_la_LDFLAGS): set version to 6:2:2 + +2001-06-29 Johan Danielsson + + * init_c.c: call krb5_get_init_creds_opt_set_default_flags + +2001-02-19 Johan Danielsson + + * replay_log.c: add --{start-end}-version flags to replay just + part of the log + +2001-02-15 Assar Westerlund + + * ipropd_master.c (main): fix select-loop to decrement ret + correctly. from "Brandon S. Allbery KF8NH" + +2001-01-30 Assar Westerlund + + * Makefile.am: bump versions + +2000-12-31 Assar Westerlund + + * 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 + + * Makefile.am (libkadm5srv_la_LDFLAGS): bump version to 7:2:0 + +2000-11-16 Assar Westerlund + + * 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 + + * Makefile.am (libkadm5srv_la_LDFLAGS): bump version to 7:1:0 + +2000-08-10 Assar Westerlund + + * acl.c (fetch_acl): fix wrong cases, use krb5_principal_match + +2000-08-07 Assar Westerlund + + * ipropd_master.c (main): ignore SIGPIPE + +2000-08-06 Assar Westerlund + + * ipropd_slave.c (receive_everything): make `fd' an int instead of + a pointer. From Derrick J Brashear + +2000-08-04 Johan Danielsson + + * admin.h: change void** to void* + +2000-07-25 Johan Danielsson + + * Makefile.am: bump versions to 7:0:0 and 6:0:2 + +2000-07-24 Assar Westerlund + + * 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 + + * 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 + + * 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 + + * 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 + + * set_keys.c (make_keys): also support `[kadmin]use_v4_salt' for + backwards compatability + +2000-06-26 Johan Danielsson + + * set_keys.c (_kadm5_set_keys): rewrite this to be more easily + adaptable to different salts + +2000-06-19 Johan Danielsson + + * get_s.c: pa_* -> KRB5_PADATA_* + +2000-06-16 Assar Westerlund + + * ipropd_slave.c: change default keytab to default keytab (as in + typically FILE:/etc/krb5.keytab) + +2000-06-08 Assar Westerlund + + * ipropd_slave.c: bug fixes, for actually writing the full dump to + the database. based on a patch from Love + +2000-06-07 Assar Westerlund + + * 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 + + * 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 + + * Makefile.am (libkadm5clnt_la_LDFLAGS): set version to 5:1:1 + +2000-05-12 Assar Westerlund + + * 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 + + * Makefile.am (LDADD): add LIB_openldap + +2000-04-03 Assar Westerlund + + * 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 + + * 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 + + * 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 + + * Makefile.am (libkadm5clnt_la_LDFLAGS): set version to 4:2:0 + +2000-01-28 Assar Westerlund + + * init_c.c (get_new_cache): make sure to request non-forwardable, + non-proxiable + +2000-01-06 Assar Westerlund + + * 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 + + * get_s.c (kadm5_s_get_principal): handle modified_by->principal + == NULL + +1999-12-20 Assar Westerlund + + * 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 + + * 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 + + * 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 + + * log.c (kadm5_log_foreach): pass context + + * dump_log.c: print more interesting things + +1999-12-02 Johan Danielsson + + * ipropd_master.c (process_msg): check for short reads + +1999-11-25 Assar Westerlund + + * 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 + + * context_s.c (find_db_spec): ugly cast work-around. + +1999-11-14 Assar Westerlund + + * 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 + + * password_quality.c (kadm5_setup_passwd_quality_check): use + correct types for function pointers + +1999-11-09 Johan Danielsson + + * 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 + + * Makefile.am (install-build-headers): use `cp' instead of + INSTALL_DATA + +1999-11-04 Assar Westerlund + + * 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 + + * 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 + + * 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 + + * 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 + + * init_c.c (_kadm5_c_init_context): use `krb5_get_krb_admin_hst'. + check return value from strdup + +1999-09-26 Assar Westerlund + + * acl.c (_kadm5_privs_to_string): forgot one strcpy_truncate -> + strlcpy + +1999-09-24 Johan Danielsson + + * dump_log.c: remove unused `optind' + + * replay_log.c: remove unused `optind' + +1999-09-13 Assar Westerlund + + * 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 + + * 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 + + * Makefile.am: update versions to 2:0:1 + +1999-07-24 Assar Westerlund + + * ent_setup.c (_kadm5_setup_entry): make princ_expire_time == 0 + and pw_expiration == 0 mean never + +1999-07-22 Assar Westerlund + + * log.c (kadm5_log_flush): extra cast + +1999-07-07 Assar Westerlund + + * marshall.c (store_principal_ent): encoding princ_expire_time and + pw_expiration in correct order + +1999-06-28 Assar Westerlund + + * 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 + + * ent_setup.c (_kadm5_setup_entry): handle 0 == unlimited + correctly. From Michal Vocu + +1999-06-15 Assar Westerlund + + * init_c.c (get_cred_cache): use get_default_username + +1999-05-23 Assar Westerlund + + * create_s.c (create_principal): if there's no default entry the + mask should be zero. + +1999-05-21 Assar Westerlund + + * init_c.c (get_cred_cache): use $USERNAME + +1999-05-17 Johan Danielsson + + * init_c.c (get_cred_cache): figure out principal + +1999-05-05 Johan Danielsson + + * send_recv.c: cleanup _kadm5_client_{send,recv} + +1999-05-04 Assar Westerlund + + * 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 + + * 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 + + * 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 + + * 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 +#ifdef HAVE_TSASL +#include +#endif +#include +#include +#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 + +#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 = /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 () { + 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 + +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 Binary files /dev/null and b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent0.bin 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 Binary files /dev/null and b/third_party/heimdal/lib/kadm5/fuzz-inputs-bin/test_marshall-ent1.bin 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 Binary files /dev/null and b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent0.bin 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 Binary files /dev/null and b/third_party/heimdal/lib/kadm5/fuzz-inputs-packed/test_marshall-ent1.bin 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 +#include "kadm5_locl.h" +#include + +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 +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#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, ¶ms); + if (ret == 0) { + ret = krb5_write_priv_message(context, ctx->ac, &s, ¶ms); + krb5_data_free(¶ms); + } + } + + 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 + + +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 +#include +#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(×tamp)); + + 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(×tamp)); + 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 +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#ifdef HAVE_UTIL_H +#include +#endif + +#include + +#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 +#include +#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 + +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), ""); + 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, ""); + + 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, ""); + 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, + ¤t_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, + ¤t_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, + ¤t_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, + ¤t_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, ×tamp) != 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 + +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_TIME_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_FILE_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif +#ifdef HAVE_NETINET_IN_H +#include +#endif +#ifdef HAVE_NETINET_TCP_H +#include +#endif +#ifdef HAVE_SYS_UN_H +#include +#endif +#ifdef HAVE_NETDB_H +#include +#endif +#include +#include +#include "admin.h" +#include "kadm5_err.h" +#include +#include +#include +#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, + ×tamp, &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, ¶ms->realm); + out: + krb5_storage_free(sp); + + return ret; +} + +#ifdef TEST +#include +#include +#include + +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 +#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 +#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 + +#include +#include +#include + +#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 : ""); + + 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 +#include +#include + +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 +#include +#include + +#include +#include + +#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: + *; +}; -- cgit v1.2.3