summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/kadmin
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/kadmin')
-rw-r--r--third_party/heimdal/kadmin/ChangeLog1048
-rw-r--r--third_party/heimdal/kadmin/Makefile.am99
-rw-r--r--third_party/heimdal/kadmin/NTMakefile135
-rw-r--r--third_party/heimdal/kadmin/add-random-users.c184
-rw-r--r--third_party/heimdal/kadmin/add_enctype.c171
-rw-r--r--third_party/heimdal/kadmin/ank.c530
-rw-r--r--third_party/heimdal/kadmin/check.c276
-rw-r--r--third_party/heimdal/kadmin/cpw.c232
-rw-r--r--third_party/heimdal/kadmin/del.c220
-rw-r--r--third_party/heimdal/kadmin/del_enctype.c132
-rw-r--r--third_party/heimdal/kadmin/dump.c87
-rw-r--r--third_party/heimdal/kadmin/ext.c221
-rw-r--r--third_party/heimdal/kadmin/get.c665
-rw-r--r--third_party/heimdal/kadmin/init.c351
-rw-r--r--third_party/heimdal/kadmin/kadm_conn.c301
-rw-r--r--third_party/heimdal/kadmin/kadmin-commands.in741
-rw-r--r--third_party/heimdal/kadmin/kadmin-version.rc36
-rw-r--r--third_party/heimdal/kadmin/kadmin.1741
-rw-r--r--third_party/heimdal/kadmin/kadmin.c312
-rw-r--r--third_party/heimdal/kadmin/kadmin_locl.h168
-rw-r--r--third_party/heimdal/kadmin/kadmind-version.rc36
-rw-r--r--third_party/heimdal/kadmin/kadmind.8194
-rw-r--r--third_party/heimdal/kadmin/kadmind.c318
-rw-r--r--third_party/heimdal/kadmin/load.c650
-rw-r--r--third_party/heimdal/kadmin/mod.c853
-rw-r--r--third_party/heimdal/kadmin/prune.c63
-rw-r--r--third_party/heimdal/kadmin/pw_quality.c60
-rw-r--r--third_party/heimdal/kadmin/random_password.c162
-rw-r--r--third_party/heimdal/kadmin/rename.c61
-rw-r--r--third_party/heimdal/kadmin/rpc.c1152
-rw-r--r--third_party/heimdal/kadmin/server.c1174
-rw-r--r--third_party/heimdal/kadmin/stash.c162
-rw-r--r--third_party/heimdal/kadmin/test_util.c90
-rw-r--r--third_party/heimdal/kadmin/util.c819
34 files changed, 12444 insertions, 0 deletions
diff --git a/third_party/heimdal/kadmin/ChangeLog b/third_party/heimdal/kadmin/ChangeLog
new file mode 100644
index 0000000..05dc1b1
--- /dev/null
+++ b/third_party/heimdal/kadmin/ChangeLog
@@ -0,0 +1,1048 @@
+2008-04-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadm_conn.c: Use unsigned where appropriate.
+
+2007-12-09 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadmin.c: Use hdb_db_dir().
+
+ * kadmind.c: Use hdb_db_dir().
+
+2007-07-26 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * util.c: Clear error string, just to be sure.
+
+2007-05-10 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadmin-commands.in: modify --pkinit-acl
+
+ * mod.c: add pk-init command
+
+2007-02-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadmin.8: document kadmin add_enctype functionallity.
+
+ * Makefile.am: Add new command, add_enctype.
+
+ * kadmin-commands.in: Add new command, add_enctype.
+
+ * add_enctype.c: Add support for adding a random key enctype to a
+ principal.
+
+2007-02-17 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * mod.c: add setting and displaying aliases
+
+ * get.c: add setting and displaying aliases
+
+ * kadmin-commands.in: add setting and displaying aliases
+
+2006-12-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * util.c: Make str2time_t parser more robust.
+
+ * Makefile.am: Add test_util test program.
+
+ * test_util.c: Test str2time_t parser.
+
+2006-12-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * add-random-users.c: Use strcspn to remove \n from fgets
+ result. Prompted by change by Ray Lai of OpenBSD via Björn
+ Sandell.
+
+2006-10-22 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * mod.c: Try to not leak memory.
+
+ * check.c: Try to not leak memory.
+
+2006-10-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: split build files into dist_ and noinst_ SOURCES
+
+2006-08-28 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadmin.c (help): use sl_slc_help().
+
+2006-08-24 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * util.c: Add KRB5_KDB_ALLOW_DIGEST
+
+2006-07-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * get.c (format_field): optionally print issuer and anchor.
+
+2006-06-21 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * check.c: Check if afs@REALM and afs/cellname@REALM both exists.
+
+2006-06-14 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * util.c (kdb_attrs): Add KRB5_KDB_ALLOW_KERBEROS4
+
+2006-06-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * mod.c (do_mod_entry): Add setting 1 delegation entry
+
+2006-06-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * server.c: Less shadowing.
+
+2006-05-13 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * Makefile.am: kadmin_SOURCES += add check.c
+
+ * kadmin_locl.h: Avoid shadowing.
+
+ * kadmin.8: Document the new check command.
+
+ * kadmin-commands.in: Add check command
+
+ * check.c: Check database for strange configurations on default
+ principals.
+
+2006-05-08 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * server.c (kadm_get_privs): one less "pointer targets in passing
+ argument differ in signedness" warning.
+
+2006-05-05 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * dump-format.txt: Moved to info documentation.
+
+ * Rename u_intXX_t to uintXX_t
+
+2006-05-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadmin.8: spelling, update .Dd
+
+2006-04-12 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * add-random-users.c: Catch empty file case. From Tobias
+ Stoeckmann.
+
+2006-04-07 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * random_password.c (generate_password): memory leak in error
+ condition case From Coverity NetBSD CID#1887
+
+2006-02-19 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cpw.c (cpw_entry): make sure ret have a defined value
+
+ * del.c (del_entry): make sure ret have a defined value
+
+ * mod.c: Return error code so that toplevel function can catch
+ them.
+
+2006-01-25 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * cpw.c (cpw_entry): return 1 on failure.
+
+ * rename.c (rename_entry): return 1 on failure.
+
+ * del.c (del_entry): return 1 on failure.
+
+ * ank.c (add_new_key): return 1 on failure.
+
+ * get.c: Add printing of pkinit-acls. Don't print password by
+ default. Return 1 on failure processing any of the principals.
+
+ * util.c (foreach_principal): If any of calls to `func' failes,
+ the first error is returned when all principals are processed.
+
+2005-12-01 Love Hörnquist Åstrand <lha@it.su.se>
+
+ * kadmin-commands.in: Add ank as an alias to add, it lost in
+ transition to slc, from Måns Nilsson.
+
+2005-09-14 Love Hörquist Åstrand <lha@it.su.se>
+
+ * dump-format.txt: Add extensions, fill in missing fields.
+
+2005-09-08 Love Hörquist Åstrand <lha@it.su.se>
+
+ * init.c (create_random_entry): create principal with random
+ password even though its disabled. From Andrew Bartlet
+ <abartlet@samba.org>
+
+2005-09-01 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadm_conn.c: Use socket_set_reuseaddr and socket_set_ipv6only.
+
+2005-08-11 Love Hörquist Åstrand <lha@it.su.se>
+
+ * get.c: Remove structure that is never used (sneaked in the large
+ TL_DATA patch).
+
+ * kadmin-commands.in: Rename password-quality to
+ verify-password-quality.
+
+ * get.c: Indent.
+
+ * server.c: Avoid shadowing exp().
+
+ * load.c: Parse extensions.
+
+ * kadmin_locl.h: Include <hex.h>.
+
+ * get.c: Extend struct field_name to have a subvalue and a
+ extra_mask. Use that to implement printing of KADM5_TL_DATA
+ options and fix a dependency bug (keys needed principal to print
+ the salting).
+
+2005-07-08 Love Hörquist Åstrand <lha@it.su.se>
+
+ * lower amount of shadow and const warnings
+
+2005-06-07 David Love <fx@gnu.org>
+
+ * dump-format.txt: Clarify, spelling and add examples.
+
+2005-05-30 Love Hörquist Åstrand <lha@it.su.se>
+
+ * util.c (kdb_attrs): add ok-as-delegate
+
+ * get.c (getit): init data.mask to 0. Problem found by Andrew
+ Bartlett <abartlet@samba.org>
+
+2005-05-09 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.c (main): catch -2 as EOF
+
+2005-05-03 Dave Love <d.love@dl.ac.uk>
+
+ * init.c (init): Don't disable forwardable for kadmin/changepw.
+
+2005-05-02 Dave Love <d.love@dl.ac.uk>
+
+ * kadmin.c (help): Don't use non-constant initializer for `fake'.
+
+2005-04-20 Love Hörquist Åstrand <lha@it.su.se>
+
+ * util.c (foreach_principal): initialize ret to make sure it have
+ a value
+
+2005-04-04 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.c: add verifier libraries with
+ kadm5_add_passwd_quality_verifier
+
+ * kadmin.c: add verifier libraries with
+ kadm5_add_passwd_quality_verifier
+
+ * load.c: max-life and max-renew is of unsigned int in asn1
+ compiler, use that for the parser too
+
+2005-03-26 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.8: List of attributes, from James F. Hranicky
+ <jfh@cise.ufl.edu>
+
+2005-01-19 Love Hörquist Åstrand <lha@it.su.se>
+
+ * dump.c (dump): handle errors
+
+2005-01-08 Love Hörquist Åstrand <lha@it.su.se>
+
+ * dump-format.txt: text dump format
+
+2004-12-08 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.8: use keeps around options, from OpenBSD
+
+ * kadmin.8: use keeps around options, "improve" spelling, from
+ openbsd
+
+2004-11-01 Love Hörquist Åstrand <lha@it.su.se>
+
+ * get.c (getit): always free columns
+
+ * ank.c (add_one_principal): catch error from
+ UI_UTIL_read_pw_string
+
+2004-10-31 Love Hörquist Åstrand <lha@it.su.se>
+
+ * del_enctype.c (del_enctype): fix off-by-one error in del_enctype
+ From: <ragge@ludd.luth.se>
+
+2004-08-13 Love Hörquist Åstrand <lha@it.su.se>
+
+ * get.c: print keytypes on long format
+
+2004-07-06 Love Hörquist Åstrand <lha@it.su.se>
+
+ * get.c (format_field): allow mod_name to be optional
+
+ * ext.c (do_ext_keytab): if there isn't any keydata, try using
+ kadm5_randkey_principal
+
+2004-07-02 Love Hörquist Åstrand <lha@it.su.se>
+
+ * load.c: make merge/load work again
+
+ * del.c: fix usage string
+
+ * ank.c: fix slc lossage
+
+2004-06-28 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.c: use kadm5_ad_init_with_password_ctx
+
+2004-06-27 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmin.8: document get -o and stash
+
+ * get.c: implement output column selection, similar to ps -o
+
+ * kadmin-commands.in: make get -l the default again, and add
+ column selection flag; sync list with get
+
+2004-06-24 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmin-commands.in: mod needs default kvno of -1
+
+2004-06-21 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmin: convert to use slc; also add stash subcommand
+
+2004-06-15 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.c (main): keytab mode requires principal name
+
+2004-06-12 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.c: drop keyfile, not used, found by
+ Elrond <elrond@samba-tng.org>
+
+ * kadmin.c: if keyfile is set, pass in to libkadm5 bug pointed out
+ by Elrond <elrond@samba-tng.org>
+
+2004-05-31 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.c: add --ad flag, XXX rewrite the init kadm5 interface
+
+2004-05-13 Johan Danielsson <joda@pdc.kth.se>
+
+ * nuke kerberos 4 kadmin goo
+
+2004-05-07 Johan Danielsson <joda@pdc.kth.se>
+
+ * util.c (str2time_t): fix end-of-day logic, from Duncan
+ McEwan/Mark Davies.
+
+2004-04-29 Love Hörquist Åstrand <lha@it.su.se>
+
+ * version4.c (handle_v4): make sure length is longer then 2,
+ Pointed out by Evgeny Demidov <demidov@gleg.net>
+
+ * kadmind.c: make kerberos4 support default turned off
+
+2004-03-24 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmin.8: update manpage
+
+ * mod.c: allow wildcarding principals, and make parameters a work
+ same as if prompted
+
+2004-03-08 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.8: document password-quality
+
+ * kadmin_locl.h: add prototype for password_quality
+
+ * kadmin.c: add password-quality/pwq command
+
+ * Makefile.am: kadmin_SOURCES += pw_quality.c
+
+ * pw_quality.c: test run the password quality function
+
+2004-03-07 Love Hörquist Åstrand <lha@it.su.se>
+
+ * ank.c (add_one_principal): even though the principal is disabled
+ (creation of random key/keydata), create it with a random password
+
+2003-12-07 Love Hörquist Åstrand <lha@it.su.se>
+
+ * init.c (create_random_entry): print error message on failure
+
+ * ank.c (add_one_principal): pass right argument to
+ kadm5_free_principal_ent From Panasas, Inc
+
+2003-11-18 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.c (main): move opening the logfile to after reading
+ kdc.conf move the loading of hdb keytab ops closer to where its
+ used From: Jeffrey Hutzelman <jhutz@cmu.edu>
+
+2003-10-04 Love Hörquist Åstrand <lha@it.su.se>
+
+ * util.c (str2time_t): allow whitespace between date and time
+ From: Bob Beck <beck@cvs.openbsd.org> and adharw@yahoo.com
+
+2003-09-03 Love Hörquist Åstrand <lha@it.su.se>
+
+ * ank.c: s/des_read_pw_string/UI_UTIL_read_pw_string/
+
+ * cpw.c: s/des_read_pw_string/UI_UTIL_read_pw_string/
+
+2003-08-21 Love Hörquist Åstrand <lha@it.su.se>
+
+ * get.c (print_entry_terse): handle error when unparsing name
+
+2003-08-18 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.c (main): use krb5_prepend_config_files_default, now all
+ options in kdc.conf is parsed, not just [kdc]key-file=
+
+ * kadmin.c (main): use krb5_prepend_config_files_default, now all
+ options in kdc.conf is parsed, not just [kdc]key-file=
+
+2003-04-14 Love Hörquist Åstrand <lha@it.su.se>
+
+ * util.c: cast argument to tolower to unsigned char, from
+ Christian Biere <christianbiere@gmx.de> via NetBSD
+
+2003-04-06 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.8: s/kerberos/Kerberos/
+
+2003-03-31 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmin.8: initialises -> initializes, from Perry E. Metzger"
+ <perry@piermont.com>
+
+ * kadmin.c: principal, not pricipal. From Thomas Klausner
+ <wiz@netbsd.org>
+
+2003-02-04 Love Hörquist Åstrand <lha@it.su.se>
+
+ * kadmind.8: spelling, from jmc <jmc@prioris.mini.pw.edu.pl>
+
+ * kadmin.8: spelling, from jmc <jmc@prioris.mini.pw.edu.pl>
+
+2003-01-29 Love Hörquist Åstrand <lha@it.su.se>
+
+ * server.c (kadmind_dispatch): kadm_chpass: require the password
+ to pass the password quality check in case the user changes the
+ user's own password kadm_chpass_with_key: disallow the user to
+ change it own password to a key, since that password might violate
+ the password quality check.
+
+2002-12-03 Johan Danielsson <joda@pdc.kth.se>
+
+ * util.c (get_response): print a newline if interrupted
+
+ * mod.c (mod_entry): check return value from edit_entry
+
+ * ank.c (add_one_principal): check return value from edit_entry
+
+ * ank.c (add_one_principal): don't continue if create_principal
+ fails
+
+ * init.c: check return value from edit_deltat
+
+ * init.c: add --help
+
+2002-10-29 Johan Danielsson <joda@pdc.kth.se>
+
+ * version4.c: speling (from Tomas Olsson)
+
+2002-10-23 Assar Westerlund <assar@kth.se>
+
+ * version4.c (decode_packet): check the length of the version
+ string and that rlen has a reasonable value
+
+2002-10-21 Johan Danielsson <joda@pdc.kth.se>
+
+ * version4.c: check size of rlen
+
+2002-09-10 Johan Danielsson <joda@pdc.kth.se>
+
+ * server.c: constify match_appl_version()
+
+ * version4.c: change some lingering krb_err_base
+
+2002-09-09 Jacques Vidrine <nectar@kth.se>
+
+ * server.c (kadmind_dispatch): while decoding arguments for
+ kadm_chpass_with_key, sanity check the number of keys given.
+ Potential problem pointed out by
+ Sebastian Krahmer <krahmer@suse.de>.
+
+2002-09-04 Johan Danielsson <joda@pdc.kth.se>
+
+ * load.c (parse_generation): return if there is no generation
+ (spotted by Daniel Kouril)
+
+2002-06-07 Jacques Vidrine <n@nectar.com>
+
+ * ank.c: do not attempt to free uninitialized pointer when
+ kadm5_randkey_principal fails.
+
+2002-06-07 Johan Danielsson <joda@pdc.kth.se>
+
+ * util.c: remove unused variable; reported by Hans Insulander
+
+2002-03-05 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmind.8: clarify some acl wording, and add an example file
+
+2002-02-11 Johan Danielsson <joda@pdc.kth.se>
+
+ * ext.c: no need to use the "modify" keytab anymore
+
+2001-09-20 Assar Westerlund <assar@sics.se>
+
+ * add-random-users.c: allocate several buffers for the list of
+ words, instead of one strdup per word (running under efence does
+ not work very well otherwise)
+
+2001-09-13 Assar Westerlund <assar@sics.se>
+
+ * add-random-users.c: allow specifying the number of users to
+ create
+
+2001-08-24 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am: rename variable name to avoid error from current
+ automake
+
+2001-08-22 Assar Westerlund <assar@sics.se>
+
+ * kadmin_locl.h: include libutil.h if it exists
+
+2001-08-10 Johan Danielsson <joda@pdc.kth.se>
+
+ * util.c: do something to handle C-c in prompts
+
+ * load.c: remove unused etypes code, and add parsing of the
+ generation field
+
+ * ank.c: add a --use-defaults option to just use default values
+ without questions
+
+ * kadmin.c: add "del" alias for delete
+
+ * cpw.c: call this operation "passwd" in usage
+
+ * kadmin_locl.h: prototype for set_defaults
+
+ * util.c (edit_entry): move setting of default values to a
+ separate function, set_defaults
+
+2001-08-01 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmin.c: print help message on bad options
+
+2001-07-31 Assar Westerlund <assar@sics.se>
+
+ * add-random-users.c (main): handle --version
+
+2001-07-30 Johan Danielsson <joda@pdc.kth.se>
+
+ * load.c: increase line buffer to 8k
+
+2001-06-12 Assar Westerlund <assar@sics.se>
+
+ * ext.c (ext_keytab): use the default modify keytab per default
+
+2001-05-17 Assar Westerlund <assar@sics.se>
+
+ * kadm_conn.c (start_server): fix krb5_eai_to_heim_errno call
+
+2001-05-15 Assar Westerlund <assar@sics.se>
+
+ * kadmin.c (main): some error cleaning required
+
+2001-05-14 Assar Westerlund <assar@sics.se>
+
+ * kadmind.c: new krb5_config_parse_file
+ * kadmin.c: new krb5_config_parse_file
+ * kadm_conn.c: update to new krb5_sockaddr2address
+
+2001-05-07 Assar Westerlund <assar@sics.se>
+
+ * kadmin_locl.h (foreach_principal): update prototype
+ * get.c (getit): new foreach_principal
+ * ext.c (ext_keytab): new foreach_principal
+ * del.c (del_entry): new foreach_principal
+ * cpw.c (cpw_entry): new foreach_principal
+ * util.c (foreach_principal): add `funcname' and try printing the
+ error string
+
+2001-05-04 Johan Danielsson <joda@pdc.kth.se>
+
+ * rename.c: fix argument number test
+
+2001-04-19 Johan Danielsson <joda@pdc.kth.se>
+
+ * del_enctype.c: fix argument count check after getarg change;
+ spotted by mark@MCS.VUW.AC.NZ
+
+2001-02-15 Assar Westerlund <assar@sics.se>
+
+ * kadmind.c (main): use a `struct sockaddr_storage' to be able to
+ store all types of addresses
+
+2001-02-07 Assar Westerlund <assar@sics.se>
+
+ * kadmin.c: add --keytab / _K, from Leif Johansson
+ <leifj@it.su.se>
+
+2001-01-29 Assar Westerlund <assar@sics.se>
+
+ * kadm_conn.c (spawn_child): close the newly created socket in the
+ packet, it's not used. from <shadow@dementia.org>
+ * version4.c (decode_packet): check success of
+ krb5_425_conv_principal. from <shadow@dementia.org>
+
+2001-01-12 Assar Westerlund <assar@sics.se>
+
+ * util.c (parse_attributes): make empty string mean no attributes,
+ specifying the empty string at the command line should give you no
+ attributes, but just pressing return at the prompt gives you
+ default attributes
+ (edit_entry): only pick up values from the default principal if they
+ aren't set in the principal being edited
+
+2001-01-04 Assar Westerlund <assar@sics.se>
+
+ * load.c (doit): print an error and bail out if storing an entry
+ in the database fails. The most likely reason for it failing is
+ out-of-space.
+
+2000-12-31 Assar Westerlund <assar@sics.se>
+
+ * kadmind.c (main): handle krb5_init_context failure consistently
+ * kadmin.c (main): handle krb5_init_context failure consistently
+ * add-random-users.c (add_user): handle krb5_init_context failure
+ consistently
+
+ * kadm_conn.c (spawn_child): use a struct sockaddr_storage
+
+2000-12-15 Johan Danielsson <joda@pdc.kth.se>
+
+ * get.c: avoid asprintf'ing NULL strings
+
+2000-12-14 Johan Danielsson <joda@pdc.kth.se>
+
+ * load.c: fix option parsing
+
+2000-11-16 Assar Westerlund <assar@sics.se>
+
+ * kadm_conn.c (wait_for_connection): check for fd's being too
+ large to select on
+
+2000-11-09 Johan Danielsson <joda@pdc.kth.se>
+
+ * get.c: don't try to print modifier name if it isn't set (from
+ Jacques A. Vidrine" <n@nectar.com>)
+
+2000-09-19 Assar Westerlund <assar@sics.se>
+
+ * server.c (kadmind_loop): send in keytab to v4 handling function
+ * version4.c: allow the specification of what keytab to use
+
+ * get.c (print_entry_long): actually print the actual saltvalue
+ used if it's not the default
+
+2000-09-10 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmin.c: add option parsing, and add `privs' as an alias for
+ `privileges'
+
+ * init.c: complain if there's no realm name specified
+
+ * rename.c: add option parsing
+
+ * load.c: add option parsing
+
+ * get.c: make `get' and `list' aliases to each other, but with
+ different defaults
+
+ * del_enctype.c: add option parsing
+
+ * del.c: add option parsing
+
+ * ank.c: calling the command `add' make more sense from an english
+ pov
+
+ * Makefile.am: add kadmin manpage
+
+ * kadmin.8: short manpage
+
+ * kadmin.c: `quit' should be a alias for `exit', not `help'
+
+2000-08-27 Assar Westerlund <assar@sics.se>
+
+ * server.c (handle_v5): do not try to perform stupid stunts when
+ printing errors
+
+2000-08-19 Assar Westerlund <assar@sics.se>
+
+ * util.c (str2time_t): add alias for `now'.
+
+2000-08-18 Assar Westerlund <assar@sics.se>
+
+ * server.c (handle_v5): accept any kadmin/admin@* principal as the
+ server
+ * kadmind.c: remove extra prototype of kadmind_loop
+ * kadmin_locl.h (kadmind_loop): add prototype
+
+ * init.c (usage): print init-usage and not add-dito
+
+2000-08-07 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmind.c: use roken_getsockname
+
+2000-08-07 Assar Westerlund <assar@sics.se>
+
+ * kadmind.c, kadm_conn.c: use socklen_t instead of int where
+ appropriate. From <thorpej@netbsd.org>
+
+2000-08-04 Johan Danielsson <joda@pdc.kth.se>
+
+ * Makefile.am: link with pidfile library
+
+ * kadmind.c: write a pid file, and setup password quality
+ functions
+
+ * kadmin_locl.h: util.h
+
+2000-07-27 Assar Westerlund <assar@sics.se>
+
+ * version4.c (decode_packet): be totally consistent with the
+ prototype of des_cbc_cksum
+ * kadmind.c: use sa_size instead of sa_len, some systems define
+ this to emulate anonymous unions
+ * kadm_conn.c: use sa_size instead of sa_len, some systems define
+ this to emulate anonymous unions
+
+2000-07-24 Assar Westerlund <assar@sics.se>
+
+ * kadmin.c (commands): add quit
+ * load.c (doit): truncate the log since there's no way of knowing
+ what changes are going to be added
+
+2000-07-23 Assar Westerlund <assar@sics.se>
+
+ * util.c (str2time_t): be more careful with strptime that might
+ zero out the `struct tm'
+
+2000-07-22 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadm_conn.c: make the parent process wait for children and
+ terminate after receiving a signal, also terminate on SIGINT
+
+2000-07-22 Assar Westerlund <assar@sics.se>
+
+ * version4.c: map both princ_expire_time and pw_expiration to v4
+ principal expiration
+
+2000-07-22 Johan Danielsson <joda@pdc.kth.se>
+
+ * version4.c (handle_v4): check for termination
+
+ * server.c (v5_loop): check for termination
+
+ * kadm_conn.c (wait_term): if we're doing something, set just set
+ a flag otherwise exit rightaway
+
+ * server.c: use krb5_read_priv_message; (v5_loop): check for EOF
+
+2000-07-21 Assar Westerlund <assar@sics.se>
+
+ * kadm_conn.c: remove sys/select.h. make signal handlers
+ type-correct and static
+
+ * kadmin_locl.h: add limits.h and sys/select.h
+
+2000-07-20 Assar Westerlund <assar@sics.se>
+
+ * init.c (init): also create `kadmin/hprop'
+ * kadmind.c: ports is a string argument
+ * kadm_conn.c (start_server): fix printf format
+
+ * kadmin_locl.h: add <sys/select.h>
+ * kadm_conn.c: remove sys/select.h. make signal handlers
+ type-correct and static
+
+ * kadmin_locl.h: add limits.h and sys/select.h
+
+2000-07-17 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadm_conn.c: put all processes in a new process group
+
+ * server.c (v5_loop): use krb5_{read,write}_priv_message
+
+2000-07-11 Johan Danielsson <joda@pdc.kth.se>
+
+ * version4.c: change log strings to match the v5 counterparts
+
+ * mod.c: allow setting kvno
+
+ * kadmind.c: if stdin is not a socket create and listen to sockets
+
+ * kadm_conn.c: socket creation functions
+
+ * util.c (deltat2str): treat 0 and INT_MAX as never
+
+2000-07-08 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (INCLUDES): add ../lib/krb5
+ * kadmin_locl.h: add krb5_locl.h (since we just use some stuff
+ from there)
+
+2000-06-07 Assar Westerlund <assar@sics.se>
+
+ * add-random-users.c: new testing program that adds a number of
+ randomly generated users
+
+2000-04-12 Assar Westerlund <assar@sics.se>
+
+ * cpw.c (do_cpw_entry): call set_password if no argument is given,
+ it will prompt for the password.
+ * kadmin.c: make help only print the commands that are actually
+ available.
+
+2000-04-03 Assar Westerlund <assar@sics.se>
+
+ * del_enctype.c (del_enctype): set ignore correctly
+
+2000-04-02 Assar Westerlund <assar@sics.se>
+
+ * kadmin.c (main): make parse errors a fatal error
+ * init.c (init): create changepw/kerberos with disallow-tgt and
+ pwchange attributes
+
+2000-03-23 Assar Westerlund <assar@sics.se>
+
+ * util.c (hex2n, parse_des_key): add
+ * server.c (kadmind_dispatch): add kadm_chpass_with_key
+ * cpw.c: add --key
+ * ank.c: add --key
+
+2000-02-16 Assar Westerlund <assar@sics.se>
+
+ * load.c (doit): check return value from parse_hdbflags2int
+ correctly
+
+2000-01-25 Assar Westerlund <assar@sics.se>
+
+ * load.c: checking all parsing for errors and all memory
+ allocations also
+
+2000-01-02 Assar Westerlund <assar@sics.se>
+
+ * server.c: check initial flag in ticket and allow users to change
+ their own password if it's set
+ * ext.c (do_ext_keytab): set timestamp
+
+1999-12-14 Assar Westerlund <assar@sics.se>
+
+ * del_enctype.c (usage): don't use arg_printusage
+
+1999-11-25 Assar Westerlund <assar@sics.se>
+
+ * del_enctype.c (del_enctype): try not to leak memory
+
+ * version4.c (kadm_ser_mod): use kadm5_s_modify_principal (no
+ _with_key)
+
+ * kadmin.c: add `del_enctype'
+
+ * del_enctype.c (del_enctype): new function for deleting enctypes
+ from a principal
+
+ * Makefile.am (kadmin_SOURCES): add del_enctype.c
+
+1999-11-09 Johan Danielsson <joda@pdc.kth.se>
+
+ * server.c: cope with old clients
+
+ * kadmin_locl.h: remove version string
+
+1999-10-17 Assar Westerlund <assar@sics.se>
+
+ * Makefile.am (kadmin_LDADD): add LIB_dlopen
+
+1999-10-01 Assar Westerlund <assar@sics.se>
+
+ * ank.c (add_one_principal): `password' can cactually be NULL in
+ the overwrite code, check for it.
+
+1999-09-20 Assar Westerlund <assar@sics.se>
+
+ * mod.c (mod_entry): print the correct principal name in error
+ messages. From Love <lha@e.kth.se>
+
+1999-09-10 Assar Westerlund <assar@sics.se>
+
+ * init.c (init): also create `changepw/kerberos'
+
+ * version4.c: only create you loose packets when we fail decoding
+ and not when an operation is not performed for some reason
+ (decode_packet): read the service key from the hdb
+ (dispatch, decode_packet): return proper error messages
+
+ * version4.c (kadm_ser_cpw): add password quality functions
+
+1999-08-27 Johan Danielsson <joda@pdc.kth.se>
+
+ * server.c (handle_v5): give more informative message if
+ KRB5_KT_NOTFOUND
+
+1999-08-26 Johan Danielsson <joda@pdc.kth.se>
+
+ * kadmind.c: use HDB keytabs
+
+1999-08-25 Assar Westerlund <assar@sics.se>
+
+ * cpw.c (set_password): use correct variable. From Love
+ <lha@e.kth.se>
+
+ * server.c (v5_loop): use correct error code
+
+ * ank.c (add_one_principal): initialize `default_ent'
+
+1999-08-21 Assar Westerlund <assar@sics.se>
+
+ * random_password.c: new file, stolen from krb4
+
+ * kadmin_locl.h: add prototype for random_password
+
+ * cpw.c: add support for --random-password
+
+ * ank.c: add support for --random-password
+
+ * Makefile.am (kadmin_SOURCES): add random_password.c
+
+1999-08-19 Assar Westerlund <assar@sics.se>
+
+ * util.c (edit_timet): break when we manage to parse the time not
+ the inverse.
+
+ * mod.c: add parsing of lots of options. From Love
+ <lha@stacken.kth.se>
+
+ * ank.c: add setting of expiration and password expiration
+
+ * kadmin_locl.h: update util.c prototypes
+
+ * util.c: move-around. clean-up, rename, make consistent (and
+ some other weird stuff). based on patches from Love
+ <lha@stacken.kth.se>
+
+ * version4.c (kadm_ser_cpw): initialize password
+ (handle_v4): remove unused variable `ret'
+
+1999-08-16 Assar Westerlund <assar@sics.se>
+
+ * version4.c (handle_v4): more error checking and more correct
+ error messages
+
+ * server.c (v5_loop, kadmind_loop): more error checking and more
+ correct error messages
+
+1999-07-24 Assar Westerlund <assar@sics.se>
+
+ * util.c (str2timeval, edit_time): functions for parsing and
+ editing times. Based on patches from Love <lha@stacken.kth.se>.
+ (edit_entry): call new functions
+
+ * mod.c (mod_entry): allow modifying expiration times
+
+ * kadmin_locl.h (str2timeval): add prototype
+
+ * ank.c (add_one_principal): allow setting expiration times
+
+1999-07-03 Assar Westerlund <assar@sics.se>
+
+ * server.c (v5_loop): handle data allocation with krb5_data_alloc
+ and check return value
+
+1999-06-23 Assar Westerlund <assar@sics.se>
+
+ * version4.c (kadm_ser_cpw): read the key in the strange order
+ it's sent
+
+ * util.c (edit_entry): look at default
+ (edit_time): always set mask even if value == 0
+
+ * kadmin_locl.h (edit_entry): update
+
+ * ank.c: make ank use the values of the default principal for
+ prompting
+
+ * version4.c (values_to_ent): convert key data correctly
+
+1999-05-23 Assar Westerlund <assar@sics.se>
+
+ * init.c (create_random_entry): more correct setting of mask
+
+1999-05-21 Assar Westerlund <assar@sics.se>
+
+ * server.c (handle_v5): read sendauth version correctly.
+
+1999-05-14 Assar Westerlund <assar@sics.se>
+
+ * version4.c (error_code): try to handle really old krb4
+ distributions
+
+1999-05-11 Assar Westerlund <assar@sics.se>
+
+ * init.c (init): initialize realm_max_life and realm_max_rlife
+
+1999-05-07 Assar Westerlund <assar@sics.se>
+
+ * ank.c (add_new_key): initialize more variables
+
+1999-05-04 Assar Westerlund <assar@sics.se>
+
+ * version4.c (kadm_ser_cpw): always allow a user to change her
+ password
+ (kadm_ser_*): make logging work
+ clean-up and restructure
+
+ * kadmin_locl.h (set_entry): add prototype
+
+ * kadmin.c (usage): update usage string
+
+ * init.c (init): new arguments realm-max-ticket-life and
+ realm-max-renewable-life
+
+ * util.c (edit_time, edit_attributes): don't do anything if it's
+ already set
+ (set_entry): new function
+
+ * ank.c (add_new_key): new options for setting max-ticket-life,
+ max-renewable-life, and attributes
+
+ * server.c (v5_loop): remove unused variable
+
+ * kadmin_locl.h: add prototypes
+
+ * version4.c: re-insert krb_err.h and other miss
+
+ * server.c (kadmind_loop): break-up and restructure
+
+ * version4.c: add ACL checks more error code checks restructure
+
+1999-05-03 Johan Danielsson <joda@pdc.kth.se>
+
+ * load.c: check for (un-)encrypted keys
+
+ * dump.c: use hdb_print_entry
+
+ * version4.c: version 4 support
+
+ * Makefile.am: link with krb4
+
+ * kadmin_locl.h: include <sys/un.h>
+
+ * server.c: move from lib/kadm5, and add basic support for krb4
+ kadmin protocol
+
+ * kadmind.c: move recvauth to kadmind_loop()
diff --git a/third_party/heimdal/kadmin/Makefile.am b/third_party/heimdal/kadmin/Makefile.am
new file mode 100644
index 0000000..d9b8fee
--- /dev/null
+++ b/third_party/heimdal/kadmin/Makefile.am
@@ -0,0 +1,99 @@
+# $Id$
+
+include $(top_srcdir)/Makefile.am.common
+
+AM_CPPFLAGS += $(INCLUDE_libintl) $(INCLUDE_readline) -I$(srcdir)/../lib/krb5 -I$(top_builddir)/include/gssapi
+
+bin_PROGRAMS = kadmin
+
+libexec_PROGRAMS = kadmind
+
+man_MANS = kadmin.1 kadmind.8
+
+noinst_PROGRAMS = add_random_users
+
+dist_kadmin_SOURCES = \
+ ank.c \
+ add_enctype.c \
+ check.c \
+ cpw.c \
+ del.c \
+ del_enctype.c \
+ dump.c \
+ ext.c \
+ get.c \
+ init.c \
+ kadmin.c \
+ load.c \
+ mod.c \
+ prune.c \
+ rename.c \
+ stash.c \
+ util.c \
+ pw_quality.c \
+ random_password.c \
+ kadmin_locl.h
+
+nodist_kadmin_SOURCES = \
+ kadmin-commands.c \
+ kadmin-commands.h
+
+$(kadmin_OBJECTS): kadmin-commands.h
+
+CLEANFILES = kadmin-commands.h kadmin-commands.c
+
+kadmin-commands.c kadmin-commands.h: kadmin-commands.in
+ $(SLC) $(srcdir)/kadmin-commands.in
+
+kadmind_SOURCES = \
+ rpc.c \
+ server.c \
+ kadmind.c \
+ kadmin_locl.h \
+ kadm_conn.c
+
+add_random_users_SOURCES = add-random-users.c
+
+test_util_SOURCES = test_util.c util.c
+
+TESTS = test_util
+
+check_PROGRAMS = $(TESTS)
+
+LDADD_common = \
+ $(top_builddir)/lib/hdb/libhdb.la \
+ $(top_builddir)/lib/krb5/libkrb5.la \
+ $(LIB_hcrypto) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_roken) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB)
+
+kadmind_LDADD = $(top_builddir)/lib/kadm5/libkadm5srv.la \
+ $(top_builddir)/lib/kadm5/libkadm5clnt.la \
+ ../lib/gssapi/libgssapi.la \
+ $(LDADD_common) \
+ $(LIB_pidfile) \
+ $(LIB_dlopen)
+
+kadmin_LDADD = \
+ $(top_builddir)/lib/kadm5/libkadm5clnt.la \
+ $(top_builddir)/lib/kadm5/libkadm5srv.la \
+ $(top_builddir)/lib/sl/libsl.la \
+ $(LIB_readline) \
+ $(LDADD_common) \
+ $(LIB_dlopen)
+
+add_random_users_LDADD = \
+ $(top_builddir)/lib/kadm5/libkadm5clnt.la \
+ $(top_builddir)/lib/kadm5/libkadm5srv.la \
+ $(LDADD_common) \
+ $(LIB_dlopen)
+
+test_util_LDADD = $(kadmin_LDADD)
+
+EXTRA_DIST = \
+ NTMakefile \
+ kadmin-version.rc \
+ kadmind-version.rc \
+ $(man_MANS) \
+ kadmin-commands.in
diff --git a/third_party/heimdal/kadmin/NTMakefile b/third_party/heimdal/kadmin/NTMakefile
new file mode 100644
index 0000000..c7f2b7f
--- /dev/null
+++ b/third_party/heimdal/kadmin/NTMakefile
@@ -0,0 +1,135 @@
+########################################################################
+#
+# 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=kadmin
+cincdirs=-I$(OBJ) -I$(INCDIR)\gssapi
+
+!include ../windows/NTMakefile.w32
+
+SBIN_PROGRAMS=$(SBINDIR)\kadmin.exe
+
+# Disable kadmind.exe since currently it doesn't build
+#LIBEXEC_PROGRAMS=$(LIBEXECDIR)\kadmind.exe
+#
+
+COMMON_LIBS= \
+ $(LIBHDB) \
+ $(LIBHEIMDAL) \
+ $(LIBROKEN)
+
+KADMIN_OBJS= \
+ $(OBJ)\ank.obj \
+ $(OBJ)\add_enctype.obj \
+ $(OBJ)\check.obj \
+ $(OBJ)\cpw.obj \
+ $(OBJ)\del.obj \
+ $(OBJ)\del_enctype.obj \
+ $(OBJ)\dump.obj \
+ $(OBJ)\ext.obj \
+ $(OBJ)\get.obj \
+ $(OBJ)\init.obj \
+ $(OBJ)\kadmin.obj \
+ $(OBJ)\load.obj \
+ $(OBJ)\mod.obj \
+ $(OBJ)\prune.obj \
+ $(OBJ)\rename.obj \
+ $(OBJ)\stash.obj \
+ $(OBJ)\util.obj \
+ $(OBJ)\pw_quality.obj \
+ $(OBJ)\random_password.obj \
+ $(OBJ)\kadmin-commands.obj \
+ $(OBJ)\kadmin-version.res
+
+KADMIN_LIBS= \
+ $(LIBKADM5CLNT) \
+ $(LIBKADM5SRV) \
+ $(LIBSL) \
+ $(COMMON_LIBS) \
+ $(LIBVERS) \
+ $(LIBCOMERR)
+
+INCFILES=$(OBJ)\kadmin-commands.h
+
+$(OBJ)\kadmin-commands.c $(OBJ)\kadmin-commands.h: kadmin-commands.in
+ cd $(OBJ)
+ $(CP) $(SRCDIR)\kadmin-commands.in $(OBJ)
+ $(BINDIR)\slc.exe kadmin-commands.in
+ cd $(SRCDIR)
+
+$(SBINDIR)\kadmin.exe: $(KADMIN_OBJS) $(KADMIN_LIBS)
+ $(EXECONLINK) Secur32.lib Shell32.lib
+ $(EXEPREP)
+
+KADMIND_OBJS= \
+ $(OBJ)\rpc.obj \
+ $(OBJ)\server.obj \
+ $(OBJ)\kadmind.obj \
+ $(OBJ)\kadm_conn.obj \
+ $(OBJ)\kadmind-version.res
+
+KADMIND_LIBS=\
+ $(LIBKADM5SRV) \
+ $(LIBGSSAPI) \
+ $(COMMON_LIBS)
+
+$(LIBEXECDIR)\kadmind.exe: $(KADMIND_OBJS) $(KADMIND_LIBS)
+ $(EXECONLINK) Secur32.lib Shell32.lib
+ $(EXEPREP)
+
+all:: $(INCFILES) $(SBIN_PROGRAMS) $(LIBEXEC_PROGRAMS)
+
+clean::
+ -$(RM) $(SBIN_PROGRAMS:.exe=.*)
+ -$(RM) $(LIBEXEC_PROGRAMS:.exe=.*)
+
+
+
+
+NOINST_PROGRAMS=$(OBJ)\add_random_users.exe
+
+$(OBJ)\add_random_users.exe: $(OBJ)\add_random_users.obj $(LIBKADM5SRV) $(LIBKADM5CLNT) $(COMMON_LIBS)
+ $(EXECONLINK) Secur32.lib Shell32.lib
+ $(EXEPREP_NODIST)
+
+#TEST_BINARIES=$(OBJ)\test_util.exe
+#
+#$(OBJ)\test_util.exe: $(OBJ)\test_util.obj $(OBJ)\util.obj $(KADMIN_LIBS)
+# $(EXECONLINK) Secur32.lib Shell32.lib
+# $(EXEPREP_NODIST)
+#
+#test-binaries: $(TEST_BINARIES)
+#
+#test-run:
+# cd $(OBJ)
+# test_util.exe
+# cd $(SRCDIR)
+#
+test:: #test-binaries test-run
diff --git a/third_party/heimdal/kadmin/add-random-users.c b/third_party/heimdal/kadmin/add-random-users.c
new file mode 100644
index 0000000..e2dc303
--- /dev/null
+++ b/third_party/heimdal/kadmin/add-random-users.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2000 - 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 "kadmin_locl.h"
+
+#define WORDS_FILENAME "/usr/share/dict/words"
+
+#define NUSERS 1000
+
+#define WORDBUF_SIZE 65535
+
+static unsigned
+read_words (const char *filename, char ***ret_w)
+{
+ unsigned n, alloc;
+ FILE *f;
+ char buf[256];
+ char **w = NULL;
+ char *wbuf = NULL, *wptr = NULL, *wend = NULL;
+
+ f = fopen (filename, "r");
+ if (f == NULL)
+ err (1, "cannot open %s", filename);
+ alloc = n = 0;
+ while (fgets (buf, sizeof(buf), f) != NULL) {
+ size_t len;
+
+ buf[strcspn(buf, "\r\n")] = '\0';
+ if (n >= alloc) {
+ alloc = max(alloc + 16, alloc * 2);
+ w = erealloc (w, alloc * sizeof(char *));
+ }
+ len = strlen(buf);
+ if (wptr + len + 1 >= wend) {
+ wptr = wbuf = emalloc (WORDBUF_SIZE);
+ wend = wbuf + WORDBUF_SIZE;
+ }
+ memmove (wptr, buf, len + 1);
+ w[n++] = wptr;
+ wptr += len + 1;
+ }
+ if (n == 0)
+ errx(1, "%s is an empty file, no words to try", filename);
+ *ret_w = w;
+ fclose(f);
+ return n;
+}
+
+static void
+add_user (krb5_context ctx, void *hndl, unsigned nwords, char **words)
+{
+ kadm5_principal_ent_rec princ;
+ char name[64];
+ int r1, r2;
+ krb5_error_code ret;
+ int mask;
+
+ r1 = rand();
+ r2 = rand();
+
+ snprintf (name, sizeof(name), "%s%d", words[r1 % nwords], r2 % 1000);
+
+ mask = KADM5_PRINCIPAL;
+
+ memset(&princ, 0, sizeof(princ));
+ ret = krb5_parse_name(ctx, name, &princ.principal);
+ if (ret)
+ krb5_err(ctx, 1, ret, "krb5_parse_name");
+
+ ret = kadm5_create_principal (hndl, &princ, mask, name);
+ if (ret)
+ krb5_err (ctx, 1, ret, "kadm5_create_principal");
+ kadm5_free_principal_ent(hndl, &princ);
+ printf ("%s\n", name);
+}
+
+static void
+add_users (const char *filename, unsigned n)
+{
+ krb5_error_code ret;
+ int i;
+ void *hndl;
+ krb5_context ctx;
+ unsigned nwords;
+ char **words;
+
+ ret = krb5_init_context(&ctx);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+ ret = kadm5_s_init_with_password_ctx(ctx,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ NULL, 0, 0,
+ &hndl);
+ if(ret)
+ krb5_err(ctx, 1, ret, "kadm5_init_with_password");
+
+ nwords = read_words (filename, &words);
+
+ for (i = 0; i < n; ++i)
+ add_user (ctx, hndl, nwords, words);
+ kadm5_destroy(hndl);
+ krb5_free_context(ctx);
+ free(words);
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "[filename [n]]");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+ int n = NUSERS;
+ const char *filename = WORDS_FILENAME;
+
+ setprogname(argv[0]);
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+ if (help_flag)
+ usage (0);
+ if (version_flag) {
+ print_version(NULL);
+ return 0;
+ }
+ srand (0);
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc > 0) {
+ if (argc > 1)
+ n = atoi(argv[1]);
+ filename = argv[0];
+ }
+
+ add_users (filename, n);
+ return 0;
+}
diff --git a/third_party/heimdal/kadmin/add_enctype.c b/third_party/heimdal/kadmin/add_enctype.c
new file mode 100644
index 0000000..d128ab7
--- /dev/null
+++ b/third_party/heimdal/kadmin/add_enctype.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 1999-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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+/*
+ * del_enctype principal enctypes...
+ */
+
+int
+add_enctype(struct add_enctype_options*opt, int argc, char **argv)
+{
+ kadm5_principal_ent_rec princ;
+ krb5_principal princ_ent = NULL;
+ krb5_error_code ret;
+ const char *princ_name;
+ int i, j;
+ krb5_key_data *new_key_data = NULL;
+ int n_etypes;
+ krb5_enctype *etypes;
+
+ if (!opt->random_key_flag) {
+ krb5_warnx (context, "only random key is supported now");
+ return 0;
+ }
+
+ memset(&princ, 0, sizeof(princ));
+ princ_name = argv[0];
+ n_etypes = argc - 1;
+ etypes = malloc (n_etypes * sizeof(*etypes));
+ if (etypes == NULL) {
+ krb5_warnx (context, "out of memory");
+ return 0;
+ }
+ argv++;
+ for (i = 0; i < n_etypes; ++i) {
+ ret = krb5_string_to_enctype(context, argv[i], &etypes[i]);
+ if (ret) {
+ krb5_warnx (context, "bad enctype \"%s\"", argv[i]);
+ goto out2;
+ }
+ }
+
+ ret = krb5_parse_name(context, princ_name, &princ_ent);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_parse_name %s", princ_name);
+ goto out2;
+ }
+
+ /* The principal might have zero keys, but it will still have a kvno! */
+ ret = kadm5_get_principal(kadm_handle, princ_ent, &princ,
+ KADM5_KVNO | KADM5_PRINCIPAL | KADM5_KEY_DATA);
+ if (ret) {
+ krb5_free_principal(context, princ_ent);
+ krb5_warnx(context, "no such principal: %s", princ_name);
+ goto out2;
+ }
+
+ /* Check that we got key data */
+ if (kadm5_all_keys_are_bogus(princ.n_key_data, princ.key_data)) {
+ krb5_warnx(context, "user lacks get-keys privilege");
+ goto out;
+ }
+
+ new_key_data = calloc(princ.n_key_data + n_etypes,
+ sizeof(*new_key_data));
+ if (new_key_data == NULL) {
+ krb5_warnx (context, "out of memory");
+ goto out;
+ }
+
+ for (i = 0; i < princ.n_key_data; ++i) {
+ krb5_key_data *key = &princ.key_data[i];
+
+ for (j = 0; j < n_etypes; ++j) {
+ if (etypes[j] == key->key_data_type[0]) {
+ /* XXX Should this be an error? The admin can del_enctype... */
+ krb5_warnx(context, "enctype %d already exists",
+ (int)etypes[j]);
+ goto out;
+ }
+ }
+ new_key_data[i] = *key;
+ }
+
+ for (i = 0; i < n_etypes; ++i) {
+ int n = princ.n_key_data + i;
+ krb5_keyblock keyblock;
+
+ memset(&new_key_data[n], 0, sizeof(new_key_data[n]));
+ new_key_data[n].key_data_ver = 2;
+ new_key_data[n].key_data_kvno = princ.kvno;
+
+ ret = krb5_generate_random_keyblock (context, etypes[i], &keyblock);
+ if (ret) {
+ krb5_warnx(context, "genernate enctype %d failed", (int)etypes[i]);
+ while (--i >= 0)
+ free(new_key_data[--n].key_data_contents[0]);
+ goto out;
+ }
+
+ /* key */
+ new_key_data[n].key_data_type[0] = etypes[i];
+ new_key_data[n].key_data_contents[0] = malloc(keyblock.keyvalue.length);
+ if (new_key_data[n].key_data_contents[0] == NULL) {
+ ret = ENOMEM;
+ krb5_warn(context, ret, "out of memory");
+ while (--i >= 0)
+ free(new_key_data[--n].key_data_contents[0]);
+ goto out;
+ }
+ new_key_data[n].key_data_length[0] = keyblock.keyvalue.length;
+ memcpy(new_key_data[n].key_data_contents[0],
+ keyblock.keyvalue.data,
+ keyblock.keyvalue.length);
+ krb5_free_keyblock_contents(context, &keyblock);
+
+ /* salt */
+ new_key_data[n].key_data_type[1] = KRB5_PW_SALT;
+ new_key_data[n].key_data_length[1] = 0;
+ new_key_data[n].key_data_contents[1] = NULL;
+
+ }
+
+ free (princ.key_data);
+ princ.n_key_data += n_etypes;
+ princ.key_data = new_key_data;
+ new_key_data = NULL;
+
+ ret = kadm5_modify_principal (kadm_handle, &princ, KADM5_KEY_DATA);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_modify_principal");
+out:
+ free(new_key_data);
+ krb5_free_principal (context, princ_ent);
+ kadm5_free_principal_ent(kadm_handle, &princ);
+out2:
+ free (etypes);
+ return ret != 0;
+}
diff --git a/third_party/heimdal/kadmin/ank.c b/third_party/heimdal/kadmin/ank.c
new file mode 100644
index 0000000..fba3450
--- /dev/null
+++ b/third_party/heimdal/kadmin/ank.c
@@ -0,0 +1,530 @@
+/*
+ * 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+/* No useful password policies for namespaces */
+#define NSPOLICY "default"
+
+/*
+ * fetch the default principal corresponding to `princ'
+ */
+
+static krb5_error_code
+get_default (kadm5_server_context *contextp,
+ krb5_principal princ,
+ kadm5_principal_ent_t default_ent)
+{
+ krb5_error_code ret;
+ krb5_principal def_principal;
+ krb5_const_realm realm = krb5_principal_get_realm(contextp->context, princ);
+
+ ret = krb5_make_principal (contextp->context, &def_principal,
+ realm, "default", NULL);
+ if (ret)
+ return ret;
+ ret = kadm5_get_principal (contextp, def_principal, default_ent,
+ KADM5_PRINCIPAL_NORMAL_MASK);
+ krb5_free_principal (contextp->context, def_principal);
+ return ret;
+}
+
+/*
+ * Add the principal `name' to the database.
+ * Prompt for all data not given by the input parameters.
+ */
+
+static krb5_error_code
+add_one_principal(const char *name,
+ int rand_key,
+ int rand_password,
+ int use_defaults,
+ char *password,
+ char *policy,
+ size_t nkstuple,
+ krb5_key_salt_tuple *kstuple,
+ krb5_key_data *key_data,
+ const char *max_ticket_life,
+ const char *max_renewable_life,
+ const char *attributes,
+ const char *expiration,
+ const char *pw_expiration)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ, defrec;
+ kadm5_principal_ent_rec *default_ent = NULL;
+ krb5_principal princ_ent = NULL;
+ krb5_timestamp pw_expire;
+ int mask = 0;
+ int default_mask = 0;
+ char pwbuf[1024];
+ char *princ_name = NULL;
+
+ memset(&princ, 0, sizeof(princ));
+ ret = krb5_parse_name(context, name, &princ_ent);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_parse_name");
+ return ret;
+ }
+
+ if (rand_password) {
+ ret = krb5_unparse_name(context, princ_ent, &princ_name);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_parse_name");
+ goto out;
+ }
+ }
+ princ.principal = princ_ent;
+ mask |= KADM5_PRINCIPAL;
+
+ ret = set_entry(context, &princ, &mask,
+ max_ticket_life, max_renewable_life,
+ expiration, pw_expiration, attributes, policy);
+ if (ret)
+ goto out;
+
+ default_ent = &defrec;
+ ret = get_default (kadm_handle, princ_ent, default_ent);
+ if (ret) {
+ default_ent = NULL;
+ default_mask = 0;
+ } else {
+ default_mask = KADM5_ATTRIBUTES | KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
+ KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION;
+ }
+
+ if(use_defaults)
+ set_defaults(&princ, &mask, default_ent, default_mask);
+ else
+ if(edit_entry(&princ, &mask, default_ent, default_mask))
+ goto out;
+ if(rand_key || key_data) {
+ princ.attributes |= KRB5_KDB_DISALLOW_ALL_TIX;
+ mask |= KADM5_ATTRIBUTES;
+ random_password (pwbuf, sizeof(pwbuf));
+ password = pwbuf;
+ } else if (rand_password) {
+ random_password (pwbuf, sizeof(pwbuf));
+ password = pwbuf;
+ } else if(password == NULL) {
+ char *prompt;
+ int aret;
+
+ ret = krb5_unparse_name(context, princ_ent, &princ_name);
+ if (ret)
+ goto out;
+ aret = asprintf (&prompt, "%s's Password: ", princ_name);
+ if (aret == -1) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "out of memory");
+ goto out;
+ }
+ ret = UI_UTIL_read_pw_string (pwbuf, sizeof(pwbuf), prompt,
+ UI_UTIL_FLAG_VERIFY |
+ UI_UTIL_FLAG_VERIFY_SILENT);
+ free (prompt);
+ if (ret) {
+ ret = KRB5_LIBOS_BADPWDMATCH;
+ krb5_set_error_message(context, ret, "failed to verify password");
+ goto out;
+ }
+ password = pwbuf;
+ }
+
+ ret = kadm5_create_principal(kadm_handle, &princ, mask, password);
+ if(ret) {
+ krb5_warn(context, ret, "kadm5_create_principal");
+ goto out;
+ }
+ /* Save requested password expiry before it's clobbered */
+ pw_expire = princ.pw_expiration;
+ if (rand_key) {
+ krb5_keyblock *new_keys;
+ int n_keys, i;
+ ret = kadm5_randkey_principal_3(kadm_handle, princ_ent, 0,
+ nkstuple, kstuple, &new_keys, &n_keys);
+ if(ret){
+ krb5_warn(context, ret, "kadm5_randkey_principal");
+ n_keys = 0;
+ }
+ for(i = 0; i < n_keys; i++)
+ krb5_free_keyblock_contents(context, &new_keys[i]);
+ if (n_keys > 0)
+ free(new_keys);
+ ret = kadm5_get_principal(kadm_handle, princ_ent, &princ,
+ KADM5_PRINCIPAL | KADM5_KVNO |
+ KADM5_ATTRIBUTES);
+ if (ret) {
+ krb5_warn(context, ret, "kadm5_get_principal");
+ goto out;
+ }
+ krb5_free_principal(context, princ_ent);
+ princ_ent = princ.principal;
+ princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
+ princ.pw_expiration = pw_expire;
+ /*
+ * Updating kvno w/o key data and vice-versa gives _kadm5_setup_entry()
+ * and _kadm5_set_keys2() headaches. But we used to, so we handle
+ * this in in those two functions. Might as well leave this code as
+ * it was then.
+ */
+ princ.kvno = 1;
+ kadm5_modify_principal(kadm_handle, &princ,
+ KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES | KADM5_KVNO);
+ } else if (key_data) {
+ ret = kadm5_chpass_principal_with_key (kadm_handle, princ_ent,
+ 3, key_data);
+ if (ret) {
+ krb5_warn(context, ret, "kadm5_chpass_principal_with_key");
+ }
+ kadm5_get_principal(kadm_handle, princ_ent, &princ,
+ KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
+ krb5_free_principal(context, princ_ent);
+ princ_ent = princ.principal;
+ princ.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
+ princ.pw_expiration = pw_expire;
+ kadm5_modify_principal(kadm_handle, &princ,
+ KADM5_PW_EXPIRATION | KADM5_ATTRIBUTES);
+ } else if (rand_password) {
+ printf ("added %s with password \"%s\"\n", princ_name, password);
+ }
+out:
+ free(princ_name);
+ kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
+ if(default_ent)
+ kadm5_free_principal_ent (kadm_handle, default_ent);
+ if (password != NULL) {
+ size_t len = strlen(password);
+ memset_s(password, len, 0, len);
+ }
+ return ret;
+}
+
+/*
+ * parse the string `key_string' into `key', returning 0 iff succesful.
+ */
+
+/*
+ * the ank command
+ */
+
+/*
+ * Parse arguments and add all the principals.
+ */
+
+int
+add_new_key(struct add_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ krb5_key_salt_tuple *kstuple = NULL;
+ krb5_key_data key_data[3];
+ krb5_key_data *kdp = NULL;
+ const char *enctypes;
+ size_t i, nkstuple;
+ int num;
+
+ num = 0;
+ if (opt->random_key_flag)
+ ++num;
+ if (opt->random_password_flag)
+ ++num;
+ if (opt->password_string)
+ ++num;
+ if (opt->key_string)
+ ++num;
+
+ if (num > 1) {
+ fprintf (stderr, "give only one of "
+ "--random-key, --random-password, --password, --key\n");
+ return 1;
+ }
+
+ enctypes = opt->enctypes_string;
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = krb5_config_get_string(context, NULL, "libdefaults",
+ "supported_enctypes", NULL);
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = "aes128-cts-hmac-sha1-96";
+ ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple);
+ if (ret) {
+ fprintf(stderr, "enctype(s) unknown\n");
+ return ret;
+ }
+
+
+ if (opt->key_string) {
+ const char *error;
+
+ if (parse_des_key (opt->key_string, key_data, &error)) {
+ fprintf(stderr, "failed parsing key \"%s\": %s\n",
+ opt->key_string, error);
+ free(kstuple);
+ return 1;
+ }
+ kdp = key_data;
+ }
+
+ for(i = 0; i < argc; i++) {
+ ret = add_one_principal(argv[i],
+ opt->random_key_flag,
+ opt->random_password_flag,
+ opt->use_defaults_flag,
+ opt->password_string,
+ opt->policy_string,
+ nkstuple,
+ kstuple,
+ kdp,
+ opt->max_ticket_life_string,
+ opt->max_renewable_life_string,
+ opt->attributes_string,
+ opt->expiration_time_string,
+ opt->pw_expiration_time_string);
+ if (ret) {
+ krb5_warn (context, ret, "adding %s", argv[i]);
+ break;
+ }
+ }
+ if (kdp) {
+ int16_t dummy = 3;
+ kadm5_free_key_data (kadm_handle, &dummy, key_data);
+ }
+ free(kstuple);
+ return ret != 0;
+}
+
+static krb5_error_code
+kstuple2etypes(kadm5_principal_ent_rec *rec,
+ int *maskp,
+ size_t nkstuple,
+ krb5_key_salt_tuple *kstuple)
+{
+ krb5_error_code ret;
+ HDB_EncTypeList etypes;
+ krb5_data buf;
+ size_t len, i;
+
+ etypes.len = 0;
+ if ((etypes.val = calloc(nkstuple, sizeof(etypes.val[0]))) == NULL)
+ return krb5_enomem(context);
+ for (i = 0; i < nkstuple; i++)
+ etypes.val[i] = kstuple[i].ks_enctype;
+ ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
+ &etypes, &len, ret);
+ if (ret == 0)
+ add_tl(rec, KRB5_TL_ETYPES, &buf);
+ free(etypes.val);
+ if (ret == 0)
+ (*maskp) |= KADM5_TL_DATA;
+ return ret;
+}
+
+/*
+ * Add the namespace `name' to the database.
+ * Prompt for all data not given by the input parameters.
+ */
+static krb5_error_code
+add_one_namespace(const char *name,
+ size_t nkstuple,
+ krb5_key_salt_tuple *kstuple,
+ const char *max_ticket_life,
+ const char *max_renewable_life,
+ const char *key_rotation_epoch,
+ const char *key_rotation_period,
+ const char *attributes)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ;
+ krb5_principal princ_ent = NULL;
+ int mask = 0;
+ int default_mask = 0;
+ HDB_extension ext;
+ krb5_data buf;
+ const char *comp0;
+ const char *comp1;
+ time_t kre;
+ char pwbuf[1024];
+ krb5_deltat krp;
+
+ if (!key_rotation_epoch) {
+ krb5_warnx(context, "key rotation epoch defaulted to \"now\"");
+ key_rotation_epoch = "now";
+ }
+ if (!key_rotation_period) {
+ krb5_warnx(context, "key rotation period defaulted to \"5d\"");
+ key_rotation_period = "5d";
+ }
+ if ((ret = str2time_t(key_rotation_epoch, &kre)) != 0) {
+ krb5_warn(context, ret, "invalid rotation epoch: %s",
+ key_rotation_epoch);
+ return ret;
+ }
+ if (ret == 0 && (ret = str2deltat(key_rotation_period, &krp)) != 0) {
+ krb5_warn(context, ret, "invalid rotation period: %s",
+ key_rotation_period);
+ return ret;
+ }
+
+ if (ret == 0) {
+ memset(&princ, 0, sizeof(princ));
+ princ.kvno = 1;
+ ret = krb5_parse_name(context, name, &princ_ent);
+ if (ret)
+ krb5_warn(context, ret, "krb5_parse_name");
+ else
+ princ.principal = princ_ent;
+ }
+ if (ret != 0)
+ return ret;
+
+ /*
+ * Check that namespace has exactly one component, and prepend
+ * WELLKNOWN/HOSTBASED-NAMESPACE
+ */
+ if (krb5_principal_get_num_comp(context, princ_ent) != 2
+ || (comp0 = krb5_principal_get_comp_string(context, princ_ent, 0)) == 0
+ || (comp1 = krb5_principal_get_comp_string(context, princ_ent, 1)) == 0
+ || *comp0 == 0 || *comp1 == 0
+ || strcmp(comp0, "krbtgt") == 0)
+ krb5_warn(context, ret = EINVAL,
+ "namespaces must have exactly two non-empty components "
+ "like host-base principal names");
+ if (ret == 0)
+ ret = krb5_principal_set_comp_string(context, princ_ent, 2, comp0);
+ if (ret == 0)
+ ret = krb5_principal_set_comp_string(context, princ_ent, 3, comp1);
+ if (ret == 0)
+ ret = krb5_principal_set_comp_string(context, princ_ent, 0,
+ "WELLKNOWN");
+ if (ret == 0)
+ ret = krb5_principal_set_comp_string(context, princ_ent, 1,
+ HDB_WK_NAMESPACE);
+
+ /* Set up initial key rotation extension */
+ if (ret == 0) {
+ KeyRotation kr;
+ size_t size;
+
+ /* Setup key rotation metadata in a convenient way */
+ kr.flags = int2KeyRotationFlags(0);
+ kr.base_key_kvno = 1;
+ /*
+ * Avoid kvnos 0/1/2 which don't normally appear in fully created
+ * principals.
+ */
+ kr.base_kvno = 3;
+
+ /* XXX: Sanity check */
+ kr.epoch = kre;
+ kr.period = krp;
+
+ memset(&ext, 0, sizeof(ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_key_rotation;
+ ext.data.u.key_rotation.len = 1;
+ ext.data.u.key_rotation.val = &kr;
+
+ ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
+ &ext, &size, ret);
+ add_tl(&princ, KRB5_TL_EXTENSION, &buf);
+ mask |= KADM5_TL_DATA;
+ }
+
+ if (ret == 0) {
+ mask |= KADM5_PRINCIPAL | KADM5_KVNO;
+
+ ret = set_entry(context, &princ, &mask,
+ max_ticket_life, max_renewable_life,
+ "never", "never", attributes, NSPOLICY);
+ }
+ if (ret == 0)
+ ret = edit_entry(&princ, &mask, NULL, default_mask);
+
+ if (ret == 0)
+ ret = kstuple2etypes(&princ, &mask, nkstuple, kstuple);
+
+ /* XXX Shouldn't need a password for this */
+ random_password(pwbuf, sizeof(pwbuf));
+ if (ret == 0) {
+ ret = kadm5_create_principal_3(kadm_handle, &princ, mask,
+ nkstuple, kstuple, pwbuf);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_create_principal_3");
+ }
+
+ kadm5_free_principal_ent(kadm_handle, &princ); /* frees princ_ent */
+ memset(pwbuf, 0, sizeof(pwbuf));
+ return ret;
+}
+
+int
+add_new_namespace(struct add_namespace_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ krb5_key_salt_tuple *kstuple = NULL;
+ const char *enctypes;
+ size_t i, nkstuple;
+
+ if (argc < 1) {
+ fprintf(stderr, "at least one namespace name required\n");
+ return 1;
+ }
+
+ enctypes = opt->enctypes_string;
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = krb5_config_get_string(context, NULL, "libdefaults",
+ "supported_enctypes", NULL);
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = "aes128-cts-hmac-sha1-96";
+ ret = krb5_string_to_keysalts2(context, enctypes, &nkstuple, &kstuple);
+ if (ret) {
+ fprintf(stderr, "enctype(s) unknown\n");
+ return ret;
+ }
+
+ for (i = 0; i < argc; i++) {
+ ret = add_one_namespace(argv[i], nkstuple, kstuple,
+ opt->max_ticket_life_string,
+ opt->max_renewable_life_string,
+ opt->key_rotation_epoch_string,
+ opt->key_rotation_period_string,
+ opt->attributes_string);
+ if (ret) {
+ krb5_warn(context, ret, "adding namespace %s", argv[i]);
+ break;
+ }
+ }
+
+ free(kstuple);
+ return ret != 0;
+}
diff --git a/third_party/heimdal/kadmin/check.c b/third_party/heimdal/kadmin/check.c
new file mode 100644
index 0000000..7a4350a
--- /dev/null
+++ b/third_party/heimdal/kadmin/check.c
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+/*
+ * Check database for strange configurations on default principals
+ */
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+static int
+get_check_entry(const char *name, kadm5_principal_ent_rec *ent)
+{
+ krb5_error_code ret;
+ krb5_principal principal;
+
+ ret = krb5_parse_name(context, name, &principal);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_unparse_name: %s", name);
+ return 1;
+ }
+
+ memset(ent, 0, sizeof(*ent));
+ ret = kadm5_get_principal(kadm_handle, principal, ent, KADM5_ATTRIBUTES);
+ krb5_free_principal(context, principal);
+ if(ret)
+ return 1;
+
+ return 0;
+}
+
+
+static int
+do_check_entry(krb5_principal principal, void *data)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ;
+ char *name;
+ int i;
+
+ ret = krb5_unparse_name(context, principal, &name);
+ if (ret)
+ return 1;
+
+ memset (&princ, 0, sizeof(princ));
+ ret = kadm5_get_principal(data, principal, &princ,
+ KADM5_PRINCIPAL | KADM5_KEY_DATA);
+ if(ret) {
+ krb5_warn(context, ret, "Failed to get principal: %s", name);
+ free(name);
+ return 0;
+ }
+
+ for (i = 0; i < princ.n_key_data; i++) {
+ size_t keysize;
+ ret = krb5_enctype_keysize(context,
+ princ.key_data[i].key_data_type[0],
+ &keysize);
+ if (ret == 0 && keysize != (size_t)princ.key_data[i].key_data_length[0]) {
+ krb5_warnx(context,
+ "Principal %s enctype %d, wrong length: %d\n",
+ name, princ.key_data[i].key_data_type[0],
+ princ.key_data[i].key_data_length[0]);
+ }
+ }
+
+ free(name);
+ kadm5_free_principal_ent(data, &princ);
+
+ return 0;
+}
+
+int
+check(void *opt, int argc, char **argv)
+{
+ kadm5_principal_ent_rec ent;
+ krb5_error_code ret;
+ char *realm = NULL, *p, *p2;
+ void *inner_kadm_handle = NULL;
+ int found;
+
+ if (argc == 0) {
+ ret = krb5_get_default_realm(context, &realm);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_get_default_realm");
+ goto fail;
+ }
+ } else {
+ realm = strdup(argv[0]);
+ if (realm == NULL) {
+ krb5_warnx(context, "malloc");
+ goto fail;
+ }
+ }
+
+ /*
+ * Check krbtgt/REALM@REALM
+ *
+ * For now, just check existance
+ */
+
+ if (asprintf(&p, "%s/%s@%s", KRB5_TGS_NAME, realm, realm) == -1) {
+ krb5_warn(context, errno, "asprintf");
+ goto fail;
+ }
+
+ ret = get_check_entry(p, &ent);
+ if (ret) {
+ fprintf(stderr,
+ "%s does not exist, are you sure %s is a realm in your database?\n",
+ p, realm);
+ free(p);
+ goto fail;
+ }
+ free(p);
+
+ kadm5_free_principal_ent(kadm_handle, &ent);
+
+ /*
+ * Check kadmin/admin@REALM
+ */
+
+ if (asprintf(&p, "kadmin/admin@%s", realm) == -1) {
+ krb5_warn(context, errno, "asprintf");
+ goto fail;
+ }
+
+ ret = get_check_entry(p, &ent);
+ if (ret) {
+ fprintf(stderr,
+ "%s does not exist, there is no way to do remote administration.\n",
+ p);
+ free(p);
+ goto fail;
+ }
+ free(p);
+
+ kadm5_free_principal_ent(kadm_handle, &ent);
+
+ /*
+ * Check kadmin/changepw@REALM
+ */
+
+ if (asprintf(&p, "kadmin/changepw@%s", realm) == -1) {
+ krb5_warn(context, errno, "asprintf");
+ goto fail;
+ }
+
+ ret = get_check_entry(p, &ent);
+ if (ret) {
+ fprintf(stderr,
+ "%s does not exist, there is no way to do change password.\n",
+ p);
+ free(p);
+ goto fail;
+ }
+ free(p);
+
+ kadm5_free_principal_ent(kadm_handle, &ent);
+
+ /*
+ * Check default@REALM
+ *
+ * Check that disallow-all-tix is set on the default principal
+ * (or that the entry does not exist)
+ */
+
+ if (asprintf(&p, "default@%s", realm) == -1) {
+ krb5_warn(context, errno, "asprintf");
+ goto fail;
+ }
+
+ ret = get_check_entry(p, &ent);
+ if (ret == 0) {
+ if ((ent.attributes & KRB5_KDB_DISALLOW_ALL_TIX) == 0) {
+ fprintf(stderr, "default template entry is not disabled\n");
+ ret = EINVAL;
+ }
+ kadm5_free_principal_ent(kadm_handle, &ent);
+
+ } else {
+ ret = 0;
+ }
+
+ free(p);
+
+ if (ret)
+ goto fail;
+
+ /*
+ * Check for duplicate afs keys
+ */
+
+ p2 = strdup(realm);
+ if (p2 == NULL) {
+ krb5_warn(context, errno, "malloc");
+ goto fail;
+ }
+ strlwr(p2);
+
+ if (asprintf(&p, "afs/%s@%s", p2, realm) == -1) {
+ krb5_warn(context, errno, "asprintf");
+ free(p2);
+ goto fail;
+ }
+ free(p2);
+
+ ret = get_check_entry(p, &ent);
+ free(p);
+ if (ret == 0) {
+ kadm5_free_principal_ent(kadm_handle, &ent);
+ found = 1;
+ } else
+ found = 0;
+
+ if (asprintf(&p, "afs@%s", realm) == -1) {
+ krb5_warn(context, errno, "asprintf");
+ goto fail;
+ }
+
+ ret = get_check_entry(p, &ent);
+ free(p);
+ if (ret == 0) {
+ kadm5_free_principal_ent(kadm_handle, &ent);
+ if (found) {
+ krb5_warnx(context, "afs@REALM and afs/cellname@REALM both exists");
+ goto fail;
+ }
+ }
+
+ ret = kadm5_dup_context(kadm_handle, &inner_kadm_handle);
+ if (ret == 0)
+ ret = foreach_principal("*", do_check_entry, "check", inner_kadm_handle);
+ if (inner_kadm_handle)
+ kadm5_destroy(inner_kadm_handle);
+ if (ret) {
+ krb5_warn(context, ret, "Could not iterate principals in realm");
+ goto fail;
+ }
+
+ free(realm);
+ return 0;
+fail:
+ free(realm);
+ return 1;
+}
diff --git a/third_party/heimdal/kadmin/cpw.c b/third_party/heimdal/kadmin/cpw.c
new file mode 100644
index 0000000..1397317
--- /dev/null
+++ b/third_party/heimdal/kadmin/cpw.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1997 - 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.
+ */
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+struct cpw_entry_data {
+ int keepold;
+ int random_key;
+ int random_password;
+ char *password;
+ krb5_key_data *key_data;
+ void *kadm_handle;
+};
+
+static int
+set_random_key(void *dup_kadm_handle, krb5_principal principal, int keepold)
+{
+ krb5_error_code ret;
+ int i;
+ krb5_keyblock *keys;
+ int num_keys;
+
+ ret = kadm5_randkey_principal_3(dup_kadm_handle, principal, keepold, 0,
+ NULL, &keys, &num_keys);
+ if(ret)
+ return ret;
+ for(i = 0; i < num_keys; i++)
+ krb5_free_keyblock_contents(context, &keys[i]);
+ free(keys);
+ return 0;
+}
+
+static int
+set_random_password(void *dup_kadm_handle,
+ krb5_principal principal,
+ int keepold)
+{
+ krb5_error_code ret;
+ char pw[128];
+ char *princ_name;
+
+ ret = krb5_unparse_name(context, principal, &princ_name);
+ if (ret)
+ return ret;
+
+ random_password(pw, sizeof(pw));
+ ret = kadm5_chpass_principal_3(dup_kadm_handle, principal, keepold, 0,
+ NULL, pw);
+ if (ret == 0)
+ printf ("%s's password set to \"%s\"\n", princ_name, pw);
+ free(princ_name);
+ memset_s(pw, sizeof(pw), 0, sizeof(pw));
+ return ret;
+}
+
+static int
+set_password(void *dup_kadm_handle,
+ krb5_principal principal,
+ char *password,
+ int keepold)
+{
+ krb5_error_code ret = 0;
+ char pwbuf[128];
+ int aret;
+
+ if(password == NULL) {
+ char *princ_name;
+ char *prompt;
+
+ ret = krb5_unparse_name(context, principal, &princ_name);
+ if (ret)
+ return ret;
+ aret = asprintf(&prompt, "%s's Password: ", princ_name);
+ free (princ_name);
+ if (aret == -1)
+ return ENOMEM;
+ ret = UI_UTIL_read_pw_string(pwbuf, sizeof(pwbuf), prompt,
+ UI_UTIL_FLAG_VERIFY |
+ UI_UTIL_FLAG_VERIFY_SILENT);
+ free (prompt);
+ if(ret){
+ return KRB5_LIBOS_BADPWDMATCH;
+ }
+ password = pwbuf;
+ }
+ if(ret == 0)
+ ret = kadm5_chpass_principal_3(dup_kadm_handle, principal, keepold, 0,
+ NULL, password);
+ memset_s(pwbuf, sizeof(pwbuf), 0, sizeof(pwbuf));
+ return ret;
+}
+
+static int
+set_key_data(void *dup_kadm_handle,
+ krb5_principal principal,
+ krb5_key_data *key_data,
+ int keepold)
+{
+ krb5_error_code ret;
+
+ ret = kadm5_chpass_principal_with_key_3(dup_kadm_handle, principal, keepold,
+ 3, key_data);
+ return ret;
+}
+
+static int
+do_cpw_entry(krb5_principal principal, void *data)
+{
+ struct cpw_entry_data *e = data;
+
+ if (e->random_key)
+ return set_random_key(e->kadm_handle, principal, e->keepold);
+ else if (e->random_password)
+ return set_random_password(e->kadm_handle, principal, e->keepold);
+ else if (e->key_data)
+ return set_key_data(e->kadm_handle, principal, e->key_data, e->keepold);
+ else
+ return set_password(e->kadm_handle, principal, e->password, e->keepold);
+}
+
+int
+cpw_entry(struct passwd_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ int i;
+ struct cpw_entry_data data;
+ int num;
+ int16_t n_key_data = 0;
+ krb5_key_data key_data[3];
+
+ memset(key_data, 0, sizeof(key_data));
+ data.kadm_handle = NULL;
+ ret = kadm5_dup_context(kadm_handle, &data.kadm_handle);
+ if (ret)
+ krb5_err(context, 1, ret, "Could not duplicate kadmin connection");
+ data.random_key = opt->random_key_flag;
+ data.random_password = opt->random_password_flag;
+ data.password = opt->password_string;
+ data.key_data = NULL;
+
+ /*
+ * --keepold is the the default, and it should mean "prune all old keys not
+ * needed to decrypt extant tickets".
+ */
+ num = 0;
+ data.keepold = 1;
+ if (opt->keepold_flag) {
+ data.keepold = 1;
+ num++;
+ }
+ if (opt->keepallold_flag) {
+ data.keepold = 2;
+ num++;
+ }
+ if (opt->pruneall_flag) {
+ data.keepold = 0;
+ num++;
+ }
+ if (num > 1) {
+ fprintf(stderr, "use only one of --keepold, --keepallold, and --pruneall\n");
+ return 1;
+ }
+
+ num = 0;
+ if (data.random_key)
+ ++num;
+ if (data.random_password)
+ ++num;
+ if (data.password)
+ ++num;
+ if (opt->key_string)
+ ++num;
+
+ if (num > 1) {
+ fprintf (stderr, "give only one of "
+ "--random-key, --random-password, --password, --key\n");
+ return 1;
+ }
+
+ if (opt->key_string) {
+ const char *error;
+
+ if (parse_des_key (opt->key_string, key_data, &error)) {
+ fprintf (stderr, "failed parsing key \"%s\": %s\n",
+ opt->key_string, error);
+ return 1;
+ }
+ n_key_data = sizeof(key_data)/sizeof(key_data[0]);
+ data.key_data = key_data;
+ }
+
+ for(i = 0; i < argc; i++)
+ ret = foreach_principal(argv[i], do_cpw_entry, "cpw", &data);
+
+ kadm5_destroy(data.kadm_handle);
+
+ if (opt->key_string)
+ kadm5_free_key_data(kadm_handle, &n_key_data, key_data);
+
+ return ret != 0;
+}
diff --git a/third_party/heimdal/kadmin/del.c b/third_party/heimdal/kadmin/del.c
new file mode 100644
index 0000000..320fe6e
--- /dev/null
+++ b/third_party/heimdal/kadmin/del.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1997 - 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.
+ */
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+static int
+do_del_entry(krb5_principal principal, void *data)
+{
+ return kadm5_delete_principal(data, principal);
+}
+
+int
+del_entry(void *opt, int argc, char **argv)
+{
+ int i;
+ krb5_error_code ret = 0;
+ void *dup_kadm_handle = NULL;
+
+ ret = kadm5_dup_context(kadm_handle, &dup_kadm_handle);
+
+ for (i = 0; ret == 0 && i < argc; i++)
+ ret = foreach_principal(argv[i], do_del_entry, "del", dup_kadm_handle);
+
+ if (dup_kadm_handle)
+ kadm5_destroy(dup_kadm_handle);
+ return ret != 0;
+}
+
+static int
+do_del_ns_entry(krb5_principal nsp, void *data)
+{
+ krb5_error_code ret;
+ krb5_principal p = NULL;
+ const char *comp0 = krb5_principal_get_comp_string(context, nsp, 0);
+ const char *comp1 = krb5_principal_get_comp_string(context, nsp, 1);
+
+ if (krb5_principal_get_num_comp(context, nsp) != 2) {
+ char *unsp = NULL;
+
+ ret = krb5_unparse_name(context, nsp, &unsp);
+ krb5_warn(context, ret,
+ "Not a valid namespace name (component count is not 2): %s",
+ unsp ? unsp : "<out of memory>");
+ free(unsp);
+ return EINVAL;
+ }
+
+ ret = krb5_make_principal(context, &p,
+ krb5_principal_get_realm(context, nsp),
+ "WELLKNOWN", HDB_WK_NAMESPACE, NULL);
+ if (ret == 0)
+ ret = krb5_principal_set_comp_string(context, p, 2, comp0);
+ if (ret == 0)
+ ret = krb5_principal_set_comp_string(context, p, 3, comp1);
+ if (ret == 0)
+ ret = kadm5_delete_principal(kadm_handle, p);
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+int
+del_namespace(void *opt, int argc, char **argv)
+{
+ int i;
+ krb5_error_code ret = 0;
+ void *dup_kadm_handle = NULL;
+
+ ret = kadm5_dup_context(kadm_handle, &dup_kadm_handle);
+ for (i = 0; ret == 0 && i < argc; i++)
+ ret = foreach_principal(argv[i], do_del_ns_entry, "del_ns",
+ dup_kadm_handle);
+ if (dup_kadm_handle)
+ kadm5_destroy(dup_kadm_handle);
+ return ret != 0;
+}
+
+int
+del_alias(void *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ size_t i;
+
+
+ if (argc < 1) {
+ krb5_warnx(context, "No aliases given");
+ return 1;
+ }
+
+ for (; argc; argc--, argv++) {
+ kadm5_principal_ent_rec princ;
+ krb5_principal p;
+ HDB_Ext_Aliases *a;
+ HDB_extension ext;
+ krb5_tl_data *tl;
+ krb5_data d;
+
+ if ((ret = krb5_parse_name(context, argv[0], &p))) {
+ krb5_warn(context, ret, "Invalid principal: %s", argv[0]);
+ return 1;
+ }
+
+ memset(&princ, 0, sizeof(princ));
+ ret = kadm5_get_principal(kadm_handle, p, &princ,
+ KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA);
+ if (ret) {
+ krb5_warn(context, ret, "Principal alias not found %s", argv[0]);
+ continue;
+ }
+
+ if (krb5_principal_compare(context, p, princ.principal)) {
+ krb5_warn(context, ret, "Not deleting principal %s because it is "
+ "not an alias; use 'delete' to delete the principal",
+ argv[0]);
+ continue;
+ }
+
+ a = &ext.data.u.aliases;
+ a->case_insensitive = 0;
+ a->aliases.len = 0;
+ a->aliases.val = 0;
+ if ((tl = get_tl(&princ, KRB5_TL_ALIASES)) == NULL) {
+ krb5_warnx(context, "kadm5_get_principal() found principal %s but "
+ "not its aliases", argv[0]);
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ krb5_free_principal(context, p);
+ return 1;
+ }
+
+ ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length,
+ a, NULL);
+ if (ret) {
+ krb5_warn(context, ret, "Principal alias list could not be decoded");
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ krb5_free_principal(context, p);
+ return 1;
+ }
+
+ /*
+ * Remove alias, but also, don't assume it appears only once in aliases
+ * list.
+ */
+ i = 0;
+ while (i < a->aliases.len) {
+ if (!krb5_principal_compare(context, p, &a->aliases.val[i])) {
+ i++;
+ continue;
+ }
+ free_Principal(&a->aliases.val[i]);
+ if (i + 1 < a->aliases.len)
+ memmove(&a->aliases.val[i],
+ &a->aliases.val[i + 1],
+ sizeof(a->aliases.val[i]) * (a->aliases.len - (i + 1)));
+ if (a->aliases.len)
+ a->aliases.len--;
+ continue;
+ }
+
+ krb5_data_zero(&d);
+ ext.data.element = choice_HDB_extension_data_aliases;
+ ext.mandatory = 0;
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length, &ext, &i, ret);
+ free_HDB_Ext_Aliases(a);
+ if (ret == 0) {
+ int16_t len = d.length;
+
+ if (len < 0 || d.length != (size_t)len) {
+ krb5_warnx(context, "Too many aliases; does not fit in 32767 bytes");
+ ret = EOVERFLOW;
+ } else {
+ add_tl(&princ, KRB5_TL_EXTENSION, &d);
+ krb5_data_zero(&d);
+ }
+ }
+ if (ret == 0) {
+ ret = kadm5_modify_principal(kadm_handle, &princ,
+ KADM5_PRINCIPAL | KADM5_TL_DATA);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_modify_principal");
+ }
+
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ krb5_free_principal(context, p);
+ krb5_data_free(&d);
+ p = NULL;
+ }
+
+ return ret == 0 ? 0 : 1;
+}
diff --git a/third_party/heimdal/kadmin/del_enctype.c b/third_party/heimdal/kadmin/del_enctype.c
new file mode 100644
index 0000000..c32ce14
--- /dev/null
+++ b/third_party/heimdal/kadmin/del_enctype.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 1999-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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+/*
+ * del_enctype principal enctypes...
+ */
+
+int
+del_enctype(void *opt, int argc, char **argv)
+{
+ kadm5_principal_ent_rec princ;
+ krb5_principal princ_ent = NULL;
+ krb5_error_code ret;
+ const char *princ_name;
+ int i, j, k;
+ krb5_key_data *new_key_data;
+ int n_etypes;
+ krb5_enctype *etypes;
+ krb5_key_data *key;
+
+ memset (&princ, 0, sizeof(princ));
+ princ_name = argv[0];
+ n_etypes = argc - 1;
+ etypes = malloc (n_etypes * sizeof(*etypes));
+ if (etypes == NULL) {
+ krb5_warnx (context, "out of memory");
+ return 0;
+ }
+ argv++;
+ for (i = 0; i < n_etypes; ++i) {
+ ret = krb5_string_to_enctype (context, argv[i], &etypes[i]);
+ if (ret) {
+ krb5_warnx (context, "bad enctype \"%s\"", argv[i]);
+ goto out2;
+ }
+ }
+
+ ret = krb5_parse_name(context, princ_name, &princ_ent);
+ if (ret) {
+ krb5_warn (context, ret, "krb5_parse_name %s", princ_name);
+ goto out2;
+ }
+
+ ret = kadm5_get_principal(kadm_handle, princ_ent, &princ,
+ KADM5_PRINCIPAL | KADM5_KEY_DATA);
+ if (ret) {
+ krb5_free_principal (context, princ_ent);
+ krb5_warnx (context, "no such principal: %s", princ_name);
+ goto out2;
+ }
+
+ if (kadm5_all_keys_are_bogus(princ.n_key_data, princ.key_data)) {
+ krb5_warnx(context, "user lacks get-keys privilege");
+ goto out;
+ }
+
+ new_key_data = malloc(princ.n_key_data * sizeof(*new_key_data));
+ if (new_key_data == NULL && princ.n_key_data != 0) {
+ krb5_warnx (context, "out of memory");
+ goto out;
+ }
+
+ for (i = 0, j = 0; i < princ.n_key_data; ++i) {
+ int docopy = 1;
+ key = &princ.key_data[i];
+
+ for (k = 0; k < n_etypes; ++k) {
+ if (etypes[k] == key->key_data_type[0]) {
+ docopy = 0;
+ break;
+ }
+ }
+ if (docopy) {
+ new_key_data[j++] = *key;
+ } else {
+ int16_t ignore = 1;
+
+ kadm5_free_key_data (kadm_handle, &ignore, key);
+ }
+ }
+
+ free (princ.key_data);
+ if (j == 0) {
+ free(new_key_data);
+ new_key_data = NULL;
+ }
+ princ.n_key_data = j;
+ princ.key_data = new_key_data;
+
+ ret = kadm5_modify_principal (kadm_handle, &princ, KADM5_KEY_DATA);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_modify_principal");
+out:
+ krb5_free_principal (context, princ_ent);
+ kadm5_free_principal_ent(kadm_handle, &princ);
+out2:
+ free (etypes);
+ return ret != 0;
+}
diff --git a/third_party/heimdal/kadmin/dump.c b/third_party/heimdal/kadmin/dump.c
new file mode 100644
index 0000000..0f2ed74
--- /dev/null
+++ b/third_party/heimdal/kadmin/dump.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 1997-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.
+ */
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+#include <kadm5/private.h>
+
+extern int local_flag;
+
+int
+dump(struct dump_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ FILE *f;
+ struct hdb_print_entry_arg parg;
+ HDB *db = NULL;
+
+ if (!local_flag) {
+ krb5_warnx(context, "dump is only available in local (-l) mode");
+ return 0;
+ }
+
+ db = _kadm5_s_get_db(kadm_handle);
+
+ if (argc == 0)
+ f = stdout;
+ else
+ f = fopen(argv[0], "w");
+
+ if (f == NULL) {
+ krb5_warn(context, errno, "open: %s", argv[0]);
+ goto out;
+ }
+ ret = db->hdb_open(context, db, O_RDONLY, 0600);
+ if (ret) {
+ krb5_warn(context, ret, "hdb_open");
+ goto out;
+ }
+
+ if (!opt->format_string || strcmp(opt->format_string, "Heimdal") == 0) {
+ parg.fmt = HDB_DUMP_HEIMDAL;
+ } else if (opt->format_string && strcmp(opt->format_string, "MIT") == 0) {
+ parg.fmt = HDB_DUMP_MIT;
+ fprintf(f, "kdb5_util load_dump version 5\n"); /* 5||6, either way */
+ } else {
+ krb5_errx(context, 1, "Supported dump formats: Heimdal and MIT");
+ }
+ parg.out = f;
+ hdb_foreach(context, db, opt->decrypt_flag ? HDB_F_DECRYPT : 0,
+ hdb_print_entry, &parg);
+
+ db->hdb_close(context, db);
+out:
+ if(f && f != stdout)
+ fclose(f);
+ return 0;
+}
diff --git a/third_party/heimdal/kadmin/ext.c b/third_party/heimdal/kadmin/ext.c
new file mode 100644
index 0000000..04d4d79
--- /dev/null
+++ b/third_party/heimdal/kadmin/ext.c
@@ -0,0 +1,221 @@
+/*
+ * 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+struct ext_keytab_data {
+ krb5_keytab keytab;
+ int keep;
+ int random_key_flag;
+ size_t nkstuple;
+ krb5_key_salt_tuple *kstuple;
+ void *kadm_handle;
+};
+
+static int
+do_ext_keytab(krb5_principal principal, void *data)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ;
+ struct ext_keytab_data *e = data;
+ krb5_keytab_entry *keys = NULL;
+ krb5_keyblock *k = NULL;
+ size_t i;
+ int n_k = 0;
+ uint32_t mask;
+ char *unparsed = NULL;
+
+ mask = KADM5_PRINCIPAL;
+ if (!e->random_key_flag)
+ mask |= KADM5_KVNO | KADM5_KEY_DATA;
+
+ ret = kadm5_get_principal(e->kadm_handle, principal, &princ, mask);
+ if (ret)
+ return ret;
+
+ ret = krb5_unparse_name(context, principal, &unparsed);
+ if (ret)
+ goto out;
+
+ if (!e->random_key_flag) {
+ if (princ.n_key_data == 0) {
+ krb5_warnx(context, "principal has no keys, or user lacks "
+ "get-keys privilege for %s", unparsed);
+ goto out;
+ }
+ /*
+ * kadmin clients and servers from master between 1.5 and 1.6
+ * can have corrupted a principal's keys in the HDB. If some
+ * are bogus but not all are, then that must have happened.
+ *
+ * If all keys are bogus then the server may be a pre-1.6,
+ * post-1.5 server and the client lacks get-keys privilege, or
+ * the keys are corrupted. We can't tell here.
+ */
+ if (kadm5_all_keys_are_bogus(princ.n_key_data, princ.key_data)) {
+ krb5_warnx(context, "user lacks get-keys privilege for %s",
+ unparsed);
+ goto out;
+ }
+ if (kadm5_some_keys_are_bogus(princ.n_key_data, princ.key_data)) {
+ krb5_warnx(context, "some keys for %s are corrupted in the HDB",
+ unparsed);
+ }
+ keys = calloc(sizeof(*keys), princ.n_key_data);
+ if (keys == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ for (i = 0; i < princ.n_key_data; i++) {
+ krb5_key_data *kd = &princ.key_data[i];
+
+ /* Don't extract bogus keys */
+ if (kadm5_all_keys_are_bogus(1, kd))
+ continue;
+
+ keys[i].principal = princ.principal;
+ keys[i].vno = kd->key_data_kvno;
+ keys[i].keyblock.keytype = kd->key_data_type[0];
+ keys[i].keyblock.keyvalue.length = kd->key_data_length[0];
+ keys[i].keyblock.keyvalue.data = kd->key_data_contents[0];
+ keys[i].timestamp = time(NULL);
+ n_k++;
+ }
+ } else if (e->random_key_flag) {
+ ret = kadm5_randkey_principal_3(e->kadm_handle, principal, e->keep,
+ e->nkstuple, e->kstuple, &k, &n_k);
+ if (ret)
+ goto out;
+
+ keys = calloc(sizeof(*keys), n_k);
+ if (keys == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ for (i = 0; i < n_k; i++) {
+ keys[i].principal = principal;
+ keys[i].vno = princ.kvno + 1; /* XXX get entry again */
+ keys[i].keyblock = k[i];
+ keys[i].timestamp = time(NULL);
+ }
+ }
+
+ if (n_k == 0)
+ krb5_warn(context, ret, "no keys written to keytab for %s", unparsed);
+
+ for (i = 0; i < n_k; i++) {
+ ret = krb5_kt_add_entry(context, e->keytab, &keys[i]);
+ if (ret)
+ krb5_warn(context, ret, "krb5_kt_add_entry(%lu)", (unsigned long)i);
+ }
+
+ out:
+ kadm5_free_principal_ent(e->kadm_handle, &princ);
+ if (k) {
+ for (i = 0; i < n_k; i++)
+ memset(k[i].keyvalue.data, 0, k[i].keyvalue.length);
+ free(k);
+ }
+ free(unparsed);
+ free(keys);
+ return ret;
+}
+
+int
+ext_keytab(struct ext_keytab_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ struct ext_keytab_data data;
+ const char *enctypes;
+ size_t i;
+
+ data.kadm_handle = NULL;
+ ret = kadm5_dup_context(kadm_handle, &data.kadm_handle);
+ if (ret)
+ krb5_err(context, 1, ret, "Could not duplicate kadmin connection");
+ data.random_key_flag = opt->random_key_flag;
+ data.keep = 1;
+ i = 0;
+ if (opt->keepallold_flag) {
+ data.keep = 2;
+ i++;
+ }
+ if (opt->keepold_flag) {
+ data.keep = 1;
+ i++;
+ }
+ if (opt->pruneall_flag) {
+ data.keep = 0;
+ i++;
+ }
+ if (i > 1) {
+ fprintf(stderr,
+ "use only one of --keepold, --keepallold, or --pruneall\n");
+ return EINVAL;
+ }
+
+ if (opt->keytab_string == NULL)
+ ret = krb5_kt_default(context, &data.keytab);
+ else
+ ret = krb5_kt_resolve(context, opt->keytab_string, &data.keytab);
+
+ if(ret){
+ krb5_warn(context, ret, "krb5_kt_resolve");
+ return 1;
+ }
+ enctypes = opt->enctypes_string;
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = krb5_config_get_string(context, NULL, "libdefaults",
+ "supported_enctypes", NULL);
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = "aes128-cts-hmac-sha1-96";
+ ret = krb5_string_to_keysalts2(context, enctypes, &data.nkstuple,
+ &data.kstuple);
+ if (ret) {
+ fprintf(stderr, "enctype(s) unknown\n");
+ krb5_kt_close(context, data.keytab);
+ return ret;
+ }
+
+ for(i = 0; i < argc; i++) {
+ ret = foreach_principal(argv[i], do_ext_keytab, "ext", &data);
+ if (ret)
+ break;
+ }
+
+ kadm5_destroy(data.kadm_handle);
+ krb5_kt_close(context, data.keytab);
+ free(data.kstuple);
+ return ret != 0;
+}
diff --git a/third_party/heimdal/kadmin/get.c b/third_party/heimdal/kadmin/get.c
new file mode 100644
index 0000000..1942d63
--- /dev/null
+++ b/third_party/heimdal/kadmin/get.c
@@ -0,0 +1,665 @@
+/*
+ * 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+#include <parse_units.h>
+#include <rtbl.h>
+
+static struct field_name {
+ const char *fieldname;
+ unsigned int fieldvalue;
+ unsigned int subvalue;
+ uint32_t extra_mask;
+ const char *default_header;
+ const char *def_longheader;
+ unsigned int flags;
+} field_names[] = {
+ { "principal", KADM5_PRINCIPAL, 0, 0, "Principal", "Principal", 0 },
+ { "princ_expire_time", KADM5_PRINC_EXPIRE_TIME, 0, 0, "Expiration", "Principal expires", 0 },
+ { "pw_expiration", KADM5_PW_EXPIRATION, 0, 0, "PW-exp", "Password expires", 0 },
+ { "last_pwd_change", KADM5_LAST_PWD_CHANGE, 0, 0, "PW-change", "Last password change", 0 },
+ { "max_life", KADM5_MAX_LIFE, 0, 0, "Max life", "Max ticket life", 0 },
+ { "max_rlife", KADM5_MAX_RLIFE, 0, 0, "Max renew", "Max renewable life", 0 },
+ { "mod_time", KADM5_MOD_TIME, 0, 0, "Mod time", "Last modified", 0 },
+ { "mod_name", KADM5_MOD_NAME, 0, 0, "Modifier", "Modifier", 0 },
+ { "attributes", KADM5_ATTRIBUTES, 0, 0, "Attributes", "Attributes", 0 },
+ { "kvno", KADM5_KVNO, 0, 0, "Kvno", "Kvno", RTBL_ALIGN_RIGHT },
+ { "mkvno", KADM5_MKVNO, 0, 0, "Mkvno", "Mkvno", RTBL_ALIGN_RIGHT },
+ { "last_success", KADM5_LAST_SUCCESS, 0, 0, "Last login", "Last successful login", 0 },
+ { "last_failed", KADM5_LAST_FAILED, 0, 0, "Last fail", "Last failed login", 0 },
+ { "fail_auth_count", KADM5_FAIL_AUTH_COUNT, 0, 0, "Fail count", "Failed login count", RTBL_ALIGN_RIGHT },
+ { "policy", KADM5_POLICY, 0, 0, "Policy", "Policy", 0 },
+ { "keytypes", KADM5_KEY_DATA, 0, KADM5_PRINCIPAL | KADM5_KVNO, "Keytypes", "Keytypes", 0 },
+ { "server-keytypes", KADM5_TL_DATA, KRB5_TL_ETYPES, 0, "Server keytypes", "Supported keytypes (servers)", 0 },
+ { "password", KADM5_TL_DATA, KRB5_TL_PASSWORD, KADM5_KEY_DATA, "Password", "Password", 0 },
+ { "pkinit-acl", KADM5_TL_DATA, KRB5_TL_PKINIT_ACL, 0, "PK-INIT ACL", "PK-INIT ACL", 0 },
+ { "aliases", KADM5_TL_DATA, KRB5_TL_ALIASES, 0, "Aliases", "Aliases", 0 },
+ { "hist-kvno-diff-clnt", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_CLNT, 0, "Clnt hist keys", "Historic keys allowed for client", 0 },
+ { "hist-kvno-diff-svc", KADM5_TL_DATA, KRB5_TL_HIST_KVNO_DIFF_SVC, 0, "Svc hist keys", "Historic keys allowed for service", 0 },
+ { NULL, 0, 0, 0, NULL, NULL, 0 }
+};
+
+struct field_info {
+ struct field_name *ff;
+ char *header;
+ struct field_info *next;
+};
+
+struct get_entry_data {
+ void (*format)(struct get_entry_data*, kadm5_principal_ent_t);
+ rtbl_t table;
+ uint32_t mask;
+ uint32_t extra_mask;
+ struct field_info *chead, **ctail;
+ const char *krb5_config_fname;
+ void *kadm_handle;
+ uint32_t n;
+ int upto;
+};
+
+static int
+add_column(struct get_entry_data *data, struct field_name *ff, const char *header)
+{
+ struct field_info *f = malloc(sizeof(*f));
+ if (f == NULL)
+ return ENOMEM;
+ f->ff = ff;
+ if(header)
+ f->header = strdup(header);
+ else
+ f->header = NULL;
+ f->next = NULL;
+ *data->ctail = f;
+ data->ctail = &f->next;
+ data->mask |= ff->fieldvalue;
+ data->extra_mask |= ff->extra_mask;
+ if(data->table != NULL)
+ rtbl_add_column_by_id(data->table, ff->fieldvalue,
+ header ? header : ff->default_header, ff->flags);
+ return 0;
+}
+
+/*
+ * return 0 iff `salt' actually is the same as the current salt in `k'
+ */
+
+static int
+cmp_salt (const krb5_salt *salt, const krb5_key_data *k)
+{
+ if (salt->salttype != (size_t)k->key_data_type[1])
+ return 1;
+ if (salt->saltvalue.length != (size_t)k->key_data_length[1])
+ return 1;
+ return memcmp (salt->saltvalue.data, k->key_data_contents[1],
+ salt->saltvalue.length);
+}
+
+static void
+format_keytype(krb5_key_data *k, krb5_salt *def_salt, char *buf, size_t buf_len)
+{
+ krb5_error_code ret;
+ char *s;
+ int aret;
+
+ buf[0] = '\0';
+ ret = krb5_enctype_to_string (context,
+ k->key_data_type[0],
+ &s);
+ if (ret) {
+ aret = asprintf (&s, "unknown(%d)", k->key_data_type[0]);
+ if (aret == -1)
+ return; /* Nothing to do here, we have no way to pass the err */
+ }
+ strlcpy(buf, s, buf_len);
+ free(s);
+
+ strlcat(buf, "(", buf_len);
+
+ ret = krb5_salttype_to_string (context,
+ k->key_data_type[0],
+ k->key_data_type[1],
+ &s);
+ if (ret) {
+ aret = asprintf (&s, "unknown(%d)", k->key_data_type[1]);
+ if (aret == -1)
+ return; /* Again, nothing else to do... */
+ }
+ strlcat(buf, s, buf_len);
+ free(s);
+
+ aret = 0;
+ if (cmp_salt(def_salt, k) == 0)
+ s = strdup("");
+ else if(k->key_data_length[1] == 0)
+ s = strdup("()");
+ else
+ aret = asprintf (&s, "(%.*s)", k->key_data_length[1],
+ (char *)k->key_data_contents[1]);
+ if (aret == -1 || s == NULL)
+ return; /* Again, nothing else we can do... */
+ strlcat(buf, s, buf_len);
+ free(s);
+ aret = asprintf (&s, "[%d]", k->key_data_kvno);
+ if (aret == -1)
+ return;
+ strlcat(buf, ")", buf_len);
+
+ strlcat(buf, s, buf_len);
+ free(s);
+}
+
+static int
+is_special_file(const char *fname)
+{
+#ifdef WIN32
+ if (strcasecmp(fname, "con") == 0 || strcasecmp(fname, "nul") == 0 ||
+ strcasecmp(fname, "aux") == 0 || strcasecmp(fname, "prn") == 0)
+ return 1;
+ if ((strncasecmp(fname, "com", sizeof("com") - 1) == 0 ||
+ strncasecmp(fname, "lpt", sizeof("lpt") - 1) == 0) &&
+ fname[sizeof("lpt")] >= '0' && fname[sizeof("lpt")] <= '9' &&
+ fname[sizeof("lpt") + 1] == '\0')
+ return 1;
+#else
+ if (strncmp(fname, "/dev/", sizeof("/dev/") - 1) == 0)
+ return 1;
+#endif
+ return 0;
+}
+
+static char *
+write_krb5_config(krb5_tl_data *tl,
+ const char *fn,
+ uint32_t i)
+{
+ char *s = NULL;
+ FILE *f = NULL;
+
+ if (fn == NULL)
+ return NULL;
+ if (i == 0 || is_special_file(fn))
+ s = strdup(fn);
+ else if (asprintf(&s, "%s-%u", fn, i) == -1)
+ s = NULL;
+ if (s == NULL)
+ krb5_err(context, 1, errno, "Out of memory");
+
+ /* rk_dumpdata() doesn't allow error checking :( */
+ if ((f = fopen(s, "w")) &&
+ fwrite(tl->tl_data_contents, tl->tl_data_length, 1, f) != 1)
+ krb5_warn(context, errno, "Could not write to %s", fn);
+ if (f && fclose(f))
+ krb5_warn(context, errno, "Could not write to %s", fn);
+ return s;
+}
+
+static void
+format_field(struct get_entry_data *data,
+ kadm5_principal_ent_t princ,
+ unsigned int field,
+ unsigned int subfield,
+ char *buf,
+ size_t buf_len,
+ int condensed)
+{
+ krb5_error_code ret;
+
+ switch(field) {
+ case KADM5_PRINCIPAL:
+ if(condensed)
+ krb5_unparse_name_fixed_short(context, princ->principal, buf, buf_len);
+ else
+ krb5_unparse_name_fixed(context, princ->principal, buf, buf_len);
+ break;
+
+ case KADM5_PRINC_EXPIRE_TIME:
+ time_t2str(princ->princ_expire_time, buf, buf_len, !condensed);
+ break;
+
+ case KADM5_PW_EXPIRATION:
+ time_t2str(princ->pw_expiration, buf, buf_len, !condensed);
+ break;
+
+ case KADM5_LAST_PWD_CHANGE:
+ time_t2str(princ->last_pwd_change, buf, buf_len, !condensed);
+ break;
+
+ case KADM5_MAX_LIFE:
+ deltat2str(princ->max_life, buf, buf_len);
+ break;
+
+ case KADM5_MAX_RLIFE:
+ deltat2str(princ->max_renewable_life, buf, buf_len);
+ break;
+
+ case KADM5_MOD_TIME:
+ time_t2str(princ->mod_date, buf, buf_len, !condensed);
+ break;
+
+ case KADM5_MOD_NAME:
+ if (princ->mod_name == NULL)
+ strlcpy(buf, "unknown", buf_len);
+ else if(condensed)
+ krb5_unparse_name_fixed_short(context, princ->mod_name, buf, buf_len);
+ else
+ krb5_unparse_name_fixed(context, princ->mod_name, buf, buf_len);
+ break;
+ case KADM5_ATTRIBUTES:
+ attributes2str (princ->attributes, buf, buf_len);
+ break;
+ case KADM5_KVNO:
+ snprintf(buf, buf_len, "%d", princ->kvno);
+ break;
+ case KADM5_MKVNO:
+ /* XXX libkadm5srv decrypts the keys, so mkvno is always 0. */
+ strlcpy(buf, "unknown", buf_len);
+ break;
+ case KADM5_LAST_SUCCESS:
+ time_t2str(princ->last_success, buf, buf_len, !condensed);
+ break;
+ case KADM5_LAST_FAILED:
+ time_t2str(princ->last_failed, buf, buf_len, !condensed);
+ break;
+ case KADM5_FAIL_AUTH_COUNT:
+ snprintf(buf, buf_len, "%d", princ->fail_auth_count);
+ break;
+ case KADM5_POLICY:
+ if(princ->policy != NULL)
+ strlcpy(buf, princ->policy, buf_len);
+ else
+ strlcpy(buf, "none", buf_len);
+ break;
+ case KADM5_KEY_DATA:{
+ krb5_salt def_salt;
+ int i;
+ char buf2[1024];
+
+ ret = krb5_get_pw_salt(context, princ->principal, &def_salt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_pw_salt");
+
+ *buf = '\0';
+ for (i = 0; i < princ->n_key_data; ++i) {
+ format_keytype(&princ->key_data[i], &def_salt, buf2, sizeof(buf2));
+ if(i > 0)
+ strlcat(buf, ", ", buf_len);
+ strlcat(buf, buf2, buf_len);
+ }
+ krb5_free_salt (context, def_salt);
+ break;
+ }
+ case KADM5_TL_DATA: {
+ krb5_tl_data *tl;
+
+ for (tl = princ->tl_data; tl != NULL; tl = tl->tl_data_next)
+ if ((unsigned)tl->tl_data_type == subfield)
+ break;
+ if (tl == NULL) {
+ strlcpy(buf, "", buf_len);
+ break;
+ }
+
+ switch (subfield) {
+ case KRB5_TL_PASSWORD:
+ snprintf(buf, buf_len, "\"%.*s\"",
+ (int)tl->tl_data_length,
+ (const char *)tl->tl_data_contents);
+ break;
+ case KRB5_TL_ETYPES: {
+ HDB_EncTypeList etypes;
+ size_t i, size;
+ char *str;
+
+ ret = decode_HDB_EncTypeList(tl->tl_data_contents,
+ tl->tl_data_length,
+ &etypes, &size);
+ if (ret) {
+ snprintf(buf, buf_len, "failed to decode server etypes");
+ break;
+ }
+ buf[0] = '\0';
+ for (i = 0; i < etypes.len; i++) {
+ ret = krb5_enctype_to_string(context, etypes.val[i], &str);
+ if (ret == 0) {
+ if (i)
+ strlcat(buf, ",", buf_len);
+ strlcat(buf, str, buf_len);
+ krb5_xfree(str);
+ }
+ }
+ free_HDB_EncTypeList(&etypes);
+ break;
+ }
+ case KRB5_TL_PKINIT_ACL: {
+ HDB_Ext_PKINIT_acl acl;
+ size_t size;
+ size_t i;
+
+ ret = decode_HDB_Ext_PKINIT_acl(tl->tl_data_contents,
+ tl->tl_data_length,
+ &acl,
+ &size);
+ if (ret) {
+ snprintf(buf, buf_len, "failed to decode ACL");
+ break;
+ }
+
+ buf[0] = '\0';
+ for (i = 0; i < acl.len; i++) {
+ strlcat(buf, "subject: ", buf_len);
+ strlcat(buf, acl.val[i].subject, buf_len);
+ if (acl.val[i].issuer) {
+ strlcat(buf, " issuer:", buf_len);
+ strlcat(buf, *acl.val[i].issuer, buf_len);
+ }
+ if (acl.val[i].anchor) {
+ strlcat(buf, " anchor:", buf_len);
+ strlcat(buf, *acl.val[i].anchor, buf_len);
+ }
+ if (i + 1 < acl.len)
+ strlcat(buf, ", ", buf_len);
+ }
+ free_HDB_Ext_PKINIT_acl(&acl);
+ break;
+ }
+ case KRB5_TL_KRB5_CONFIG: {
+ char *fname;
+
+ fname = write_krb5_config(tl, data->krb5_config_fname, data->n);
+ if (fname) {
+ strlcat(buf, fname, buf_len);
+ free(fname);
+ }
+ break;
+ }
+ case KRB5_TL_ALIASES: {
+ HDB_Ext_Aliases alias;
+ size_t size;
+ size_t i;
+
+ ret = decode_HDB_Ext_Aliases(tl->tl_data_contents,
+ tl->tl_data_length,
+ &alias,
+ &size);
+ if (ret) {
+ snprintf(buf, buf_len, "failed to decode alias");
+ break;
+ }
+ buf[0] = '\0';
+ for (i = 0; i < alias.aliases.len; i++) {
+ char *p;
+ ret = krb5_unparse_name(context, &alias.aliases.val[i], &p);
+ if (ret)
+ break;
+ if (i > 0)
+ strlcat(buf, " ", buf_len);
+ strlcat(buf, p, buf_len);
+ free(p);
+ }
+ free_HDB_Ext_Aliases(&alias);
+ break;
+ }
+ default:
+ snprintf(buf, buf_len, "unknown type %d", subfield);
+ break;
+ }
+ break;
+ }
+ default:
+ strlcpy(buf, "<unknown>", buf_len);
+ break;
+ }
+}
+
+static void
+print_entry_short(struct get_entry_data *data, kadm5_principal_ent_t princ)
+{
+ char buf[1024];
+ struct field_info *f;
+
+ for(f = data->chead; f != NULL; f = f->next) {
+ format_field(data, princ, f->ff->fieldvalue, f->ff->subvalue, buf,
+ sizeof(buf), 1);
+ rtbl_add_column_entry_by_id(data->table, f->ff->fieldvalue, buf);
+ }
+}
+
+static void
+print_entry_long(struct get_entry_data *data, kadm5_principal_ent_t princ)
+{
+ char buf[1024];
+ struct field_info *f;
+ int width = 0;
+
+ for(f = data->chead; f != NULL; f = f->next) {
+ int w = strlen(f->header ? f->header : f->ff->def_longheader);
+ if(w > width)
+ width = w;
+ }
+ for(f = data->chead; f != NULL; f = f->next) {
+ format_field(data, princ, f->ff->fieldvalue, f->ff->subvalue, buf,
+ sizeof(buf), 0);
+ printf("%*s: %s\n", width, f->header ? f->header : f->ff->def_longheader, buf);
+ }
+ printf("\n");
+}
+
+static int
+do_get_entry(krb5_principal principal, void *data)
+{
+ kadm5_principal_ent_rec princ;
+ krb5_error_code ret;
+ struct get_entry_data *e = data;
+
+ if (e->upto == 0)
+ return EINTR;
+ if (e->upto > 0)
+ e->upto--;
+
+ memset(&princ, 0, sizeof(princ));
+ ret = kadm5_get_principal(e->kadm_handle, principal,
+ &princ,
+ e->mask | e->extra_mask);
+ if (ret == 0) {
+ (e->format)(e, &princ);
+ kadm5_free_principal_ent(e->kadm_handle, &princ);
+ }
+
+ e->n++;
+ return ret;
+}
+
+static void
+free_columns(struct get_entry_data *data)
+{
+ struct field_info *f, *next;
+ for(f = data->chead; f != NULL; f = next) {
+ free(f->header);
+ next = f->next;
+ free(f);
+ }
+ data->chead = NULL;
+ data->ctail = &data->chead;
+}
+
+static int
+setup_columns(struct get_entry_data *data, const char *column_info)
+{
+ char buf[1024], *q;
+ char *field, *header;
+ struct field_name *f;
+
+ while(strsep_copy(&column_info, ",", buf, sizeof(buf)) != -1) {
+ q = buf;
+ field = strsep(&q, "=");
+ header = strsep(&q, "=");
+ for(f = field_names; f->fieldname != NULL; f++) {
+ if(strcasecmp(field, f->fieldname) == 0) {
+ add_column(data, f, header);
+ break;
+ }
+ }
+ if(f->fieldname == NULL) {
+ krb5_warnx(context, "unknown field name \"%s\"", field);
+ free_columns(data);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+do_list_entry(krb5_principal principal, void *data)
+{
+ char buf[1024];
+ int *upto = data;
+ krb5_error_code ret;
+
+ if (*upto == 0)
+ return EINTR;
+ if (*upto > 0)
+ (*upto)--;
+
+ ret = krb5_unparse_name_fixed_short(context, principal, buf, sizeof(buf));
+ if (ret != 0)
+ return ret;
+ printf("%s\n", buf);
+ return 0;
+}
+
+static int
+listit(const char *funcname, int upto, int argc, char **argv)
+{
+ int i;
+ krb5_error_code ret, saved_ret = 0;
+
+ for (i = 0; i < argc; i++) {
+ ret = foreach_principal(argv[i], do_list_entry, funcname, &upto);
+ if (saved_ret == 0 && ret != 0)
+ saved_ret = ret;
+ }
+ return saved_ret != 0;
+}
+
+#define DEFAULT_COLUMNS_SHORT "principal,princ_expire_time,pw_expiration,last_pwd_change,max_life,max_rlife"
+#define DEFAULT_COLUMNS_LONG "principal,princ_expire_time,pw_expiration,last_pwd_change,max_life,max_rlife,kvno,mkvno,last_success,last_failed,fail_auth_count,mod_time,mod_name,attributes,server-keytypes,keytypes,pkinit-acl,aliases"
+
+static int
+getit(struct get_options *opt, const char *name, int argc, char **argv)
+{
+ int i;
+ krb5_error_code ret;
+ struct get_entry_data data;
+
+ if(opt->long_flag == -1 && (opt->short_flag == 1 || opt->terse_flag == 1))
+ opt->long_flag = 0;
+ if(opt->short_flag == -1 && (opt->long_flag == 1 || opt->terse_flag == 1))
+ opt->short_flag = 0;
+ if(opt->terse_flag == -1 && (opt->long_flag == 1 || opt->short_flag == 1))
+ opt->terse_flag = 0;
+ if(opt->long_flag == 0 && opt->short_flag == 0 && opt->terse_flag == 0)
+ opt->short_flag = 1;
+
+ if (opt->terse_flag)
+ return listit(name, opt->upto_integer, argc, argv);
+
+ data.kadm_handle = NULL;
+ ret = kadm5_dup_context(kadm_handle, &data.kadm_handle);
+ if (ret)
+ krb5_err(context, 1, ret, "Could not duplicate kadmin connection");
+ data.table = NULL;
+ data.chead = NULL;
+ data.ctail = &data.chead;
+ data.mask = 0;
+ data.extra_mask = 0;
+ data.krb5_config_fname = opt->krb5_config_file_string;
+ data.upto = opt->upto_integer;
+ data.n = 0;
+
+ if(opt->short_flag) {
+ data.table = rtbl_create();
+ rtbl_set_separator(data.table, " ");
+ data.format = print_entry_short;
+ } else
+ data.format = print_entry_long;
+ if(opt->column_info_string == NULL) {
+ if(opt->long_flag)
+ ret = setup_columns(&data, DEFAULT_COLUMNS_LONG);
+ else
+ ret = setup_columns(&data, DEFAULT_COLUMNS_SHORT);
+ } else
+ ret = setup_columns(&data, opt->column_info_string);
+
+ if(ret != 0) {
+ if(data.table != NULL)
+ rtbl_destroy(data.table);
+ return 0;
+ }
+
+ for(i = 0; i < argc; i++)
+ ret = foreach_principal(argv[i], do_get_entry, name, &data);
+
+ kadm5_destroy(data.kadm_handle);
+
+ if(data.table != NULL) {
+ rtbl_format(data.table, stdout);
+ rtbl_destroy(data.table);
+ }
+ free_columns(&data);
+ return ret != 0;
+}
+
+int
+get_entry(struct get_options *opt, int argc, char **argv)
+{
+ return getit(opt, "get", argc, argv);
+}
+
+int
+list_princs(struct list_options *opt, int argc, char **argv)
+{
+ struct get_options get_opt;
+
+ if(sizeof(struct get_options) != sizeof(struct list_options)) {
+ krb5_warnx(context, "programmer error: sizeof(struct get_options) != sizeof(struct list_options)");
+ return 0;
+ }
+ memset(&get_opt, 0, sizeof(get_opt));
+ get_opt.long_flag = opt->long_flag;
+ get_opt.short_flag = opt->short_flag;
+ get_opt.terse_flag = opt->terse_flag;
+ get_opt.column_info_string = opt->column_info_string;
+ get_opt.upto_integer = opt->upto_integer;
+ return getit(&get_opt, "list", argc, argv);
+}
diff --git a/third_party/heimdal/kadmin/init.c b/third_party/heimdal/kadmin/init.c
new file mode 100644
index 0000000..8a3725e
--- /dev/null
+++ b/third_party/heimdal/kadmin/init.c
@@ -0,0 +1,351 @@
+/*
+ * Copyright (c) 1997-2004 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+#include <kadm5/private.h>
+
+#define CRE_DUP_OK 1
+
+static kadm5_ret_t
+create_random_entry(krb5_principal princ,
+ unsigned max_life,
+ unsigned max_rlife,
+ uint32_t attributes,
+ unsigned flags)
+{
+ kadm5_principal_ent_rec ent;
+ kadm5_ret_t ret;
+ int mask = 0;
+ krb5_keyblock *keys;
+ int n_keys, i;
+ char *name;
+
+ ret = krb5_unparse_name(context, princ, &name);
+ if (ret) {
+ krb5_warn(context, ret, "failed to unparse principal name");
+ return ret;
+ }
+
+ memset(&ent, 0, sizeof(ent));
+ ent.principal = princ;
+ mask |= KADM5_PRINCIPAL;
+ if (max_life) {
+ ent.max_life = max_life;
+ mask |= KADM5_MAX_LIFE;
+ }
+ if (max_rlife) {
+ ent.max_renewable_life = max_rlife;
+ mask |= KADM5_MAX_RLIFE;
+ }
+ ent.attributes |= attributes | KRB5_KDB_DISALLOW_ALL_TIX;
+ mask |= KADM5_ATTRIBUTES | KADM5_KEY_DATA;
+
+ /*
+ * Create the entry with no keys or password.
+ *
+ * XXX Note that using kadm5_s_*() here means that `kadmin init` must
+ * always be local (`kadmin -l init`). This might seem like a very
+ * obvious thing, but since our KDC daemons support multiple realms
+ * there is no reason that `init SOME.REALM.EXAMPLE` couldn't be
+ * remoted.
+ *
+ * Granted, one might want all such operations to be local anyways --
+ * perhaps for authorization reasons, since we don't really have that
+ * great a story for authorization in kadmind at this time, especially
+ * for realm creation.
+ */
+ ret = kadm5_s_create_principal_with_key(kadm_handle, &ent, mask);
+ if(ret) {
+ if (ret == KADM5_DUP && (flags & CRE_DUP_OK))
+ goto out;
+ krb5_warn(context, ret, "create_random_entry(%s): create failed",
+ name);
+ goto out;
+ }
+
+ /* Replace the string2key based keys with real random bytes */
+ ret = kadm5_randkey_principal(kadm_handle, princ, &keys, &n_keys);
+ if(ret) {
+ krb5_warn(context, ret, "create_random_entry(%s): randkey failed",
+ name);
+ goto out;
+ }
+ for(i = 0; i < n_keys; i++)
+ krb5_free_keyblock_contents(context, &keys[i]);
+ free(keys);
+ ret = kadm5_get_principal(kadm_handle, princ, &ent,
+ KADM5_PRINCIPAL | KADM5_ATTRIBUTES);
+ if(ret) {
+ krb5_warn(context, ret, "create_random_entry(%s): "
+ "unable to get principal", name);
+ goto out;
+ }
+ ent.attributes &= (~KRB5_KDB_DISALLOW_ALL_TIX);
+ ent.kvno = 1;
+ ret = kadm5_modify_principal(kadm_handle, &ent,
+ KADM5_ATTRIBUTES|KADM5_KVNO);
+ kadm5_free_principal_ent (kadm_handle, &ent);
+ if(ret) {
+ krb5_warn(context, ret, "create_random_entry(%s): "
+ "unable to modify principal", name);
+ goto out;
+ }
+ out:
+ free(name);
+ return ret;
+}
+
+extern int local_flag;
+
+int
+init(struct init_options *opt, int argc, char **argv)
+{
+ kadm5_ret_t ret;
+ int i;
+ HDB *db;
+ krb5_deltat max_life = 0, max_rlife = 0;
+
+ if (!local_flag) {
+ krb5_warnx(context, "init is only available in local (-l) mode");
+ return 1;
+ }
+
+ if (opt->realm_max_ticket_life_string) {
+ if (str2deltat (opt->realm_max_ticket_life_string, &max_life) != 0) {
+ krb5_warnx (context, "unable to parse \"%s\"",
+ opt->realm_max_ticket_life_string);
+ return 1;
+ }
+ }
+ if (opt->realm_max_renewable_life_string) {
+ if (str2deltat (opt->realm_max_renewable_life_string, &max_rlife) != 0) {
+ krb5_warnx (context, "unable to parse \"%s\"",
+ opt->realm_max_renewable_life_string);
+ return 1;
+ }
+ }
+
+ db = _kadm5_s_get_db(kadm_handle);
+
+ ret = db->hdb_open(context, db, O_RDWR | O_CREAT, 0600);
+ if(ret){
+ krb5_warn(context, ret, "hdb_open");
+ return 1;
+ }
+ ret = kadm5_log_reinit(kadm_handle, 0);
+ if (ret) {
+ krb5_warn(context, ret, "Failed iprop log initialization");
+ return 1;
+ }
+ ret = kadm5_log_end(kadm_handle);
+ db->hdb_close(context, db);
+ if (ret) {
+ krb5_warn(context, ret, "Failed iprop log initialization");
+ return 1;
+ }
+
+ for(i = 0; i < argc; i++){
+ krb5_principal princ = NULL;
+ const char *realm = argv[i];
+
+ if (opt->realm_max_ticket_life_string == NULL) {
+ max_life = 0;
+ if(edit_deltat ("Realm max ticket life", &max_life, NULL, 0)) {
+ return 1;
+ }
+ }
+ if (opt->realm_max_renewable_life_string == NULL) {
+ max_rlife = 0;
+ if(edit_deltat("Realm max renewable ticket life", &max_rlife,
+ NULL, 0)) {
+ return 1;
+ }
+ }
+
+ /* Create `krbtgt/REALM' */
+ ret = krb5_make_principal(context, &princ, realm,
+ KRB5_TGS_NAME, realm, NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, max_life, max_rlife, 0, 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create %s@%s", KRB5_TGS_NAME,
+ realm);
+ return 1;
+ }
+
+ if (opt->bare_flag)
+ continue;
+
+ /* Create `kadmin/changepw' */
+ ret = krb5_make_principal(context, &princ, realm, "kadmin",
+ "changepw", NULL);
+ /*
+ * The Windows XP (at least) password changing protocol
+ * request the `kadmin/changepw' ticket with `renewable_ok,
+ * renewable, forwardable' and so fails if we disallow
+ * forwardable here.
+ */
+ if (ret == 0)
+ ret = create_random_entry(princ, 5*60, 5*60,
+ KRB5_KDB_DISALLOW_TGT_BASED|
+ KRB5_KDB_PWCHANGE_SERVICE|
+ KRB5_KDB_DISALLOW_POSTDATED|
+ KRB5_KDB_DISALLOW_RENEWABLE|
+ KRB5_KDB_DISALLOW_PROXIABLE|
+ KRB5_KDB_REQUIRES_PRE_AUTH,
+ 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create kadmin/changepw@%s",
+ realm);
+ return 1;
+ }
+
+ /* Create `kadmin/admin' */
+ ret = krb5_make_principal(context, &princ, realm,
+ "kadmin", "admin", NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, 60*60, 60*60,
+ KRB5_KDB_REQUIRES_PRE_AUTH, 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create kadmin/admin@%s", realm);
+ return 1;
+ }
+
+ /* Create `changepw/kerberos' (for v4 compat) */
+ ret = krb5_make_principal(context, &princ, realm,
+ "changepw", "kerberos", NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, 60*60, 60*60,
+ KRB5_KDB_DISALLOW_TGT_BASED|
+ KRB5_KDB_PWCHANGE_SERVICE, 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create changepw/kerberos@%s",
+ realm);
+ return 1;
+ }
+
+ /* Create `kadmin/hprop' for database propagation */
+ ret = krb5_make_principal(context, &princ, realm,
+ "kadmin", "hprop", NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, 60*60, 60*60,
+ KRB5_KDB_REQUIRES_PRE_AUTH|
+ KRB5_KDB_DISALLOW_TGT_BASED, 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create kadmin/hprop@%s", realm);
+ return 1;
+ }
+
+ /* Create `WELLKNOWN/ANONYMOUS' for anonymous as-req */
+ ret = krb5_make_principal(context, &princ, realm, KRB5_WELLKNOWN_NAME,
+ KRB5_ANON_NAME, NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, 60*60, 60*60,
+ KRB5_KDB_REQUIRES_PRE_AUTH, 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create %s/%s@%s",
+ KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, realm);
+ return 1;
+ }
+
+ /* Create `WELLKNOWN/FEDERATED' for GSS preauth */
+ ret = krb5_make_principal(context, &princ, realm,
+ KRB5_WELLKNOWN_NAME, KRB5_FEDERATED_NAME, NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, 60*60, 60*60,
+ KRB5_KDB_REQUIRES_PRE_AUTH, 0);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create %s/%s@%s",
+ KRB5_WELLKNOWN_NAME, KRB5_FEDERATED_NAME, realm);
+ return 1;
+ }
+
+ /*
+ * Create `WELLKNONW/org.h5l.fast-cookie@WELLKNOWN:ORG.H5L' for FAST cookie.
+ *
+ * There can be only one.
+ */
+ if (i == 0) {
+ ret = krb5_make_principal(context, &princ, KRB5_WELLKNOWN_ORG_H5L_REALM,
+ KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL);
+ if (ret == 0)
+ ret = create_random_entry(princ, 60*60, 60*60,
+ KRB5_KDB_REQUIRES_PRE_AUTH|
+ KRB5_KDB_DISALLOW_TGT_BASED|
+ KRB5_KDB_DISALLOW_ALL_TIX, CRE_DUP_OK);
+ krb5_free_principal(context, princ);
+ if (ret && ret != KADM5_DUP) {
+ krb5_warn(context, ret,
+ "Failed to create %s/org.h5l.fast-cookie@%s",
+ KRB5_WELLKNOWN_NAME, KRB5_WELLKNOWN_ORG_H5L_REALM);
+ return 1;
+ }
+ }
+
+ /* Create `default' */
+ {
+ kadm5_principal_ent_rec ent;
+ int mask = 0;
+
+ memset (&ent, 0, sizeof(ent));
+ mask |= KADM5_PRINCIPAL;
+ mask |= KADM5_MAX_LIFE;
+ mask |= KADM5_MAX_RLIFE;
+ mask |= KADM5_ATTRIBUTES;
+ ent.max_life = 24 * 60 * 60;
+ ent.max_renewable_life = 7 * ent.max_life;
+ ent.attributes = KRB5_KDB_DISALLOW_ALL_TIX;
+ ret = krb5_make_principal(context, &ent.principal, realm,
+ "default", NULL);
+ if (ret == 0)
+ ret = kadm5_create_principal(kadm_handle, &ent, mask, "");
+ if (ret) {
+ krb5_warn(context, ret, "Failed to create default@%s", realm);
+ return 1;
+ }
+
+ krb5_free_principal(context, ent.principal);
+ }
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/kadmin/kadm_conn.c b/third_party/heimdal/kadmin/kadm_conn.c
new file mode 100644
index 0000000..0eeaf50
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadm_conn.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2000 - 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.
+ */
+
+#include "kadmin_locl.h"
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
+extern int daemon_child;
+
+struct kadm_port {
+ char *port;
+ unsigned short def_port;
+ struct kadm_port *next;
+} *kadm_ports;
+
+static void
+add_kadm_port(krb5_context contextp, const char *service, unsigned int port)
+{
+ struct kadm_port *p;
+ p = malloc(sizeof(*p));
+ if(p == NULL) {
+ krb5_warnx(contextp, "failed to allocate %lu bytes\n",
+ (unsigned long)sizeof(*p));
+ return;
+ }
+
+ p->port = strdup(service);
+ p->def_port = port;
+
+ p->next = kadm_ports;
+ kadm_ports = p;
+}
+
+static void
+add_standard_ports (krb5_context contextp)
+{
+ add_kadm_port(contextp, "kerberos-adm", 749);
+}
+
+/*
+ * parse the set of space-delimited ports in `str' and add them.
+ * "+" => all the standard ones
+ * otherwise it's port|service[/protocol]
+ */
+
+void
+parse_ports(krb5_context contextp, const char *str)
+{
+ char p[128];
+
+ while(strsep_copy(&str, " \t", p, sizeof(p)) != -1) {
+ if(strcmp(p, "+") == 0)
+ add_standard_ports(contextp);
+ else
+ add_kadm_port(contextp, p, 0);
+ }
+}
+
+static pid_t pgrp;
+sig_atomic_t term_flag, doing_useful_work;
+
+static RETSIGTYPE
+sigchld(int sig)
+{
+ int status;
+ /*
+ * waitpid() is async safe. will return -1 or 0 on no more zombie
+ * children
+ */
+ while ((waitpid(-1, &status, WNOHANG)) > 0)
+ ;
+ SIGRETURN(0);
+}
+
+static RETSIGTYPE
+terminate(int sig)
+{
+ if(getpid() == pgrp) {
+ /* parent */
+ term_flag = 1;
+ signal(sig, SIG_IGN);
+ killpg(pgrp, sig);
+ } else {
+ /* child */
+ if(doing_useful_work)
+ term_flag = 1;
+ else
+ exit(0);
+ }
+ SIGRETURN(0);
+}
+
+static int
+spawn_child(krb5_context contextp, int *socks,
+ unsigned int num_socks, int this_sock)
+{
+ int e;
+ size_t i;
+ struct sockaddr_storage __ss;
+ struct sockaddr *sa = (struct sockaddr *)&__ss;
+ socklen_t sa_size = sizeof(__ss);
+ krb5_socket_t s;
+ pid_t pid;
+ krb5_address addr;
+ char buf[128];
+ size_t buf_len;
+
+ s = accept(socks[this_sock], sa, &sa_size);
+ if(rk_IS_BAD_SOCKET(s)) {
+ krb5_warn(contextp, rk_SOCK_ERRNO, "accept");
+ return 1;
+ }
+ e = krb5_sockaddr2address(contextp, sa, &addr);
+ if(e)
+ krb5_warn(contextp, e, "krb5_sockaddr2address");
+ else {
+ e = krb5_print_address (&addr, buf, sizeof(buf),
+ &buf_len);
+ if(e)
+ krb5_warn(contextp, e, "krb5_print_address");
+ else
+ krb5_warnx(contextp, "connection from %s", buf);
+ krb5_free_address(contextp, &addr);
+ }
+
+ pid = fork();
+ if(pid == 0) {
+ for(i = 0; i < num_socks; i++)
+ rk_closesocket(socks[i]);
+ dup2(s, STDIN_FILENO);
+ dup2(s, STDOUT_FILENO);
+ if(s != STDIN_FILENO && s != STDOUT_FILENO)
+ rk_closesocket(s);
+ return 0;
+ } else {
+ rk_closesocket(s);
+ }
+ return 1;
+}
+
+static void
+wait_for_connection(krb5_context contextp,
+ krb5_socket_t *socks, unsigned int num_socks)
+{
+ unsigned int i;
+ int e;
+ fd_set orig_read_set, read_set;
+ int status, max_fd = -1;
+
+ FD_ZERO(&orig_read_set);
+
+ for(i = 0; i < num_socks; i++) {
+#ifdef FD_SETSIZE
+ if (socks[i] >= FD_SETSIZE)
+ errx (1, "fd too large");
+#endif
+ FD_SET(socks[i], &orig_read_set);
+ max_fd = max(max_fd, socks[i]);
+ }
+
+ pgrp = getpid();
+
+ /* systemd may cause setpgid to fail with EPERM */
+ if(setpgid(0, pgrp) < 0 && errno != EPERM)
+ err(1, "setpgid");
+
+ signal(SIGTERM, terminate);
+ signal(SIGINT, terminate);
+ signal(SIGCHLD, sigchld);
+
+ while (term_flag == 0) {
+ read_set = orig_read_set;
+ e = select(max_fd + 1, &read_set, NULL, NULL, NULL);
+ if(rk_IS_SOCKET_ERROR(e)) {
+ if(rk_SOCK_ERRNO != EINTR)
+ krb5_warn(contextp, rk_SOCK_ERRNO, "select");
+ } else if(e == 0)
+ krb5_warnx(contextp, "select returned 0");
+ else {
+ for(i = 0; i < num_socks; i++) {
+ if(FD_ISSET(socks[i], &read_set))
+ if(spawn_child(contextp, socks, num_socks, i) == 0)
+ return;
+ }
+ }
+ }
+ signal(SIGCHLD, SIG_IGN);
+
+ while ((waitpid(-1, &status, WNOHANG)) > 0)
+ ;
+
+ exit(0);
+}
+
+
+void
+start_server(krb5_context contextp, const char *port_str)
+{
+ int e;
+ struct kadm_port *p;
+
+ krb5_socket_t *socks = NULL, *tmp;
+ unsigned int num_socks = 0;
+ int i;
+
+ if (port_str == NULL)
+ port_str = "+";
+
+ parse_ports(contextp, port_str);
+
+ for(p = kadm_ports; p; p = p->next) {
+ struct addrinfo hints, *ai, *ap;
+ char portstr[32];
+ memset (&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_STREAM;
+
+ e = getaddrinfo(NULL, p->port, &hints, &ai);
+ if(e) {
+ snprintf(portstr, sizeof(portstr), "%u", p->def_port);
+ e = getaddrinfo(NULL, portstr, &hints, &ai);
+ }
+
+ if(e) {
+ krb5_warn(contextp, krb5_eai_to_heim_errno(e, errno),
+ "%s", portstr);
+ continue;
+ }
+ i = 0;
+ for(ap = ai; ap; ap = ap->ai_next)
+ i++;
+ tmp = realloc(socks, (num_socks + i) * sizeof(*socks));
+ if(tmp == NULL)
+ krb5_err(contextp, 1, errno, "failed to reallocate %lu bytes",
+ (unsigned long)(num_socks + i) * sizeof(*socks));
+ socks = tmp;
+ for(ap = ai; ap; ap = ap->ai_next) {
+ krb5_socket_t s = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
+ if(rk_IS_BAD_SOCKET(s)) {
+ krb5_warn(contextp, rk_SOCK_ERRNO, "socket");
+ continue;
+ }
+
+ socket_set_reuseaddr(s, 1);
+ socket_set_ipv6only(s, 1);
+
+ if (rk_IS_SOCKET_ERROR(bind (s, ap->ai_addr, ap->ai_addrlen))) {
+ krb5_warn(contextp, rk_SOCK_ERRNO, "bind");
+ rk_closesocket(s);
+ continue;
+ }
+ if (rk_IS_SOCKET_ERROR(listen (s, SOMAXCONN))) {
+ krb5_warn(contextp, rk_SOCK_ERRNO, "listen");
+ rk_closesocket(s);
+ continue;
+ }
+
+ socket_set_keepalive(s, 1);
+ socks[num_socks++] = s;
+ }
+ freeaddrinfo (ai);
+ }
+ if(num_socks == 0)
+ krb5_errx(contextp, 1, "no sockets to listen to - exiting");
+
+ roken_detach_finish(NULL, daemon_child);
+
+ wait_for_connection(contextp, socks, num_socks);
+ free(socks);
+}
diff --git a/third_party/heimdal/kadmin/kadmin-commands.in b/third_party/heimdal/kadmin/kadmin-commands.in
new file mode 100644
index 0000000..0872b47
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmin-commands.in
@@ -0,0 +1,741 @@
+/*
+ * Copyright (c) 2004 - 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$ */
+
+command = {
+ name = "stash"
+ name = "kstash"
+ option = {
+ long = "enctype"
+ short = "e"
+ type = "string"
+ help = "encryption type"
+ default = "des3-cbc-sha1"
+ }
+ option = {
+ long = "key-file"
+ short = "k"
+ type = "string"
+ argument = "file"
+ help = "master key file"
+ }
+ option = {
+ long = "convert-file"
+ type = "flag"
+ help = "just convert keyfile to new format"
+ }
+ option = {
+ long = "random-password"
+ type = "flag"
+ help = "use a random password (and print the password to stdout)"
+ }
+ option = {
+ long = "master-key-fd"
+ type = "integer"
+ argument = "fd"
+ help = "filedescriptor to read passphrase from"
+ default = "-1"
+ }
+ help = "Writes the Kerberos master key to a file used by the KDC. \nLocal (-l) mode only."
+}
+command = {
+ name = "dump"
+ option = {
+ long = "decrypt"
+ short = "d"
+ type = "flag"
+ help = "decrypt keys"
+ }
+ option = {
+ long = "format"
+ short = "f"
+ type = "string"
+ help = "dump format, mit or heimdal (default: heimdal)"
+ }
+ argument = "[dump-file]"
+ min_args = "0"
+ max_args = "1"
+ help = "Dumps the database in a human readable format to the specified file, \nor the standard out. Local (-l) mode only."
+}
+
+command = {
+ name = "init"
+ option = {
+ long = "realm-max-ticket-life"
+ type = "string"
+ help = "realm max ticket lifetime"
+ }
+ option = {
+ long = "realm-max-renewable-life"
+ type = "string"
+ help = "realm max renewable lifetime"
+ }
+ option = {
+ long = "bare"
+ type = "flag"
+ help = "only create krbtgt for realm"
+ }
+ argument = "realm..."
+ min_args = "1"
+ help = "Initializes the default principals for a realm. Creates the database\nif necessary. Local (-l) mode only."
+}
+command = {
+ name = "load"
+ argument = "file"
+ min_args = "1"
+ max_args = "1"
+ help = "Loads a previously dumped file. Local (-l) mode only."
+}
+command = {
+ name = "merge"
+ argument = "file"
+ min_args = "1"
+ max_args = "1"
+ help = "Merges the contents of a dump file into the database. Local (-l) mode only."
+}
+command = {
+ name = "add"
+ name = "ank"
+ name = "add_new_key"
+ function = "add_new_key"
+ option = {
+ long = "random-key"
+ short = "r"
+ type = "flag"
+ help = "set random key"
+ }
+ option = {
+ long = "random-password"
+ type = "flag"
+ help = "set random password"
+ }
+ option = {
+ long = "enctypes"
+ short = "e"
+ type = "string"
+ help = "encryption type(s)"
+ }
+ option = {
+ long = "password"
+ short = "p"
+ type = "string"
+ help = "principal's password"
+ }
+ option = {
+ long = "key"
+ type = "string"
+ help = "DES-key in hex"
+ }
+ option = {
+ long = "max-ticket-life"
+ type = "string"
+ argument ="lifetime"
+ help = "max ticket lifetime"
+ }
+ option = {
+ long = "max-renewable-life"
+ type = "string"
+ argument = "lifetime"
+ help = "max renewable life"
+ }
+ option = {
+ long = "attributes"
+ type = "string"
+ argument = "attributes"
+ help = "principal attributes"
+ }
+ option = {
+ long = "expiration-time"
+ type = "string"
+ argument = "time"
+ help = "principal expiration time"
+ }
+ option = {
+ long = "pw-expiration-time"
+ type = "string"
+ argument = "time"
+ help = "password expiration time"
+ }
+ option = {
+ long = "hist-kvno-diff-clnt"
+ type = "integer"
+ argument = "kvno diff"
+ help = "historic keys allowed for client"
+ default = "-1"
+ }
+ option = {
+ long = "hist-kvno-diff-svc"
+ type = "integer"
+ argument = "kvno diff"
+ help = "historic keys allowed for service"
+ default = "-1"
+ }
+ option = {
+ long = "use-defaults"
+ type = "flag"
+ help = "use default values"
+ }
+ option = {
+ long = "policy"
+ type = "string"
+ argument = "policy"
+ help = "policy name"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Adds a principal to the database."
+}
+command = {
+ name = "add_namespace"
+ name = "add_ns"
+ function = "add_new_namespace"
+ option = {
+ long = "enctypes"
+ short = "e"
+ type = "string"
+ help = "encryption type(s)"
+ }
+ option = {
+ long = "max-ticket-life"
+ type = "string"
+ argument ="lifetime"
+ help = "max ticket lifetime"
+ }
+ option = {
+ long = "max-renewable-life"
+ type = "string"
+ argument = "lifetime"
+ help = "max renewable life"
+ }
+ option = {
+ long = "key-rotation-epoch"
+ type = "string"
+ argument = "time"
+ help = "absolute start time (or +timespec for relative to now with default unit of month)"
+ }
+ option = {
+ long = "key-rotation-period"
+ type = "string"
+ argument = "time"
+ help = "automatic key rotation period"
+ }
+ option = {
+ long = "attributes"
+ type = "string"
+ argument = "attributes"
+ help = "principal attributes"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Adds a namespace of virtual principals with derived keys to the database."
+}
+command = {
+ name = "modify_namespace"
+ name = "mod_ns"
+ function = "modify_namespace"
+ option = {
+ long = "enctypes"
+ short = "e"
+ type = "strings"
+ help = "encryption type(s)"
+ }
+ option = {
+ long = "max-ticket-life"
+ type = "string"
+ argument ="lifetime"
+ help = "max ticket lifetime"
+ }
+ option = {
+ long = "max-renewable-life"
+ type = "string"
+ argument = "lifetime"
+ help = "max renewable life"
+ }
+ option = {
+ long = "attributes"
+ type = "string"
+ argument = "attributes"
+ help = "principal attributes"
+ }
+ option = {
+ long = "krb5-config-file"
+ short = "C"
+ type = "string"
+ help = "filename to save the principal's krb5.confg in"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Modifies a namespace of virtual principals with derived keys to the database."
+}
+command = {
+ name = "modify_namespace_key_rotation"
+ name = "mod_ns_kr"
+ function = "modify_ns_kr"
+ option = {
+ long = "force"
+ short = "f"
+ type = "flag"
+ help = "change schedule even if it would revoke some extant tickets"
+ }
+ option = {
+ long = "keep-base-key"
+ short = "k"
+ type = "flag"
+ help = "keep current base key for new key rotation schedule"
+ }
+ option = {
+ long = "revoke-old"
+ short = "r"
+ type = "string"
+ argument = "time"
+ help = "delete base keys older than this to revoke old tickets"
+ }
+ option = {
+ long = "new-key-rotation-epoch"
+ type = "string"
+ argument = "time"
+ help = "new start time relative to now"
+ }
+ option = {
+ long = "new-key-rotation-period"
+ type = "string"
+ argument = "time"
+ help = "new automatic key rotation period"
+ }
+ argument = "principal..."
+ min_args = "1"
+ max_args = "1"
+ help = "Adds or changes new key rotation schedule for the given namespace."
+}
+command = {
+ name = "passwd"
+ name = "cpw"
+ name = "change_password"
+ function = "cpw_entry"
+ option = {
+ long = "random-key"
+ short = "r"
+ type = "flag"
+ help = "set random key"
+ }
+ option = {
+ long = "random-password"
+ type = "flag"
+ help = "set random password"
+ }
+ option = {
+ long = "enctypes"
+ short = "e"
+ type = "string"
+ help = "encryption type(s)"
+ }
+ option = {
+ long = "password"
+ short = "p"
+ type = "string"
+ help = "principal's password"
+ }
+ option = {
+ long = "key"
+ type = "string"
+ help = "DES key in hex"
+ }
+ option = {
+ long = "keepold"
+ type = "flag"
+ help = "keep old keys/password needed to decrypt extant tickets (default)"
+ }
+ option = {
+ long = "keepallold"
+ type = "flag"
+ help = "keep all old keys/password"
+ }
+ option = {
+ long = "pruneall"
+ type = "flag"
+ help = "delete all old keys"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Changes the password of one or more principals matching the expressions."
+}
+command = {
+ name = "delete"
+ name = "del"
+ name = "del_entry"
+ function = "del_entry"
+ argument = "principal..."
+ min_args = "1"
+ help = "Deletes all principals matching the expressions."
+}
+command = {
+ name = "delete_namespace"
+ name = "del_ns"
+ function = "del_namespace"
+ argument = "principal..."
+ min_args = "1"
+ help = "Deletes the given virtual principal namespaces"
+}
+command = {
+ name = "del_enctype"
+ argument = "principal enctype..."
+ min_args = "2"
+ help = "Delete all the mentioned enctypes for principal."
+}
+command = {
+ name = "add_enctype"
+ option = {
+ long = "random-key"
+ short = "r"
+ type = "flag"
+ help = "set random key"
+ }
+ argument = "principal enctype..."
+ min_args = "2"
+ help = "Add new enctypes for principal."
+}
+command = {
+ name = "ext_keytab"
+ option = {
+ long = "keytab"
+ short = "k"
+ type = "string"
+ help = "keytab to use"
+ argument = "keytab"
+ }
+ option = {
+ long = "random-key"
+ short = "r"
+ type = "flag"
+ help = "set random key"
+ }
+ option = {
+ long = "enctypes"
+ short = "e"
+ type = "string"
+ help = "encryption type(s)"
+ }
+ option = {
+ long = "keepold"
+ type = "flag"
+ help = "keep old keys/password needed to decrypt extant tickets (default)"
+ }
+ option = {
+ long = "keepallold"
+ type = "flag"
+ help = "keep all old keys/password"
+ }
+ option = {
+ long = "pruneall"
+ type = "flag"
+ help = "delete all old keys"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Extracts the keys of all principals matching the expressions, and stores them in a keytab."
+}
+command = {
+ name = "get"
+ name = "get_entry"
+ function = "get_entry"
+ /* Options added to list should be added here; not the reverse */
+ option = {
+ long = "long"
+ short = "l"
+ type = "flag"
+ help = "long format"
+ default = "-1"
+ }
+ option = {
+ long = "short"
+ short = "s"
+ type = "flag"
+ help = "short format"
+ }
+ option = {
+ long = "terse"
+ short = "t"
+ type = "flag"
+ help = "terse format"
+ }
+ option = {
+ long = "column-info"
+ short = "o"
+ type = "string"
+ help = "columns to print for short output"
+ }
+ option = {
+ long = "krb5-config-file"
+ short = "C"
+ type = "string"
+ help = "filename to save the principal's krb5.conf in"
+ }
+ option = {
+ long = "upto"
+ type = "integer"
+ default = "-1"
+ help = "maximum number of principals to get/list"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Shows information about principals matching the expressions."
+}
+command = {
+ name = "rename"
+ function = "rename_entry"
+ argument = "from to"
+ min_args = "2"
+ max_args = "2"
+ help = "Renames a principal."
+}
+command = {
+ name = "modify"
+ name = "mod"
+ function = "mod_entry"
+ option = {
+ long = "max-ticket-life"
+ type = "string"
+ argument ="lifetime"
+ help = "max ticket lifetime"
+ }
+ option = {
+ long = "max-renewable-life"
+ type = "string"
+ argument = "lifetime"
+ help = "max renewable life"
+ }
+ option = {
+ long = "attributes"
+ short = "a"
+ type = "string"
+ argument = "attributes"
+ help = "principal attributes"
+ }
+ option = {
+ long = "expiration-time"
+ type = "string"
+ argument = "time"
+ help = "principal expiration time"
+ }
+ option = {
+ long = "pw-expiration-time"
+ type = "string"
+ argument = "time"
+ help = "password expiration time"
+ }
+ option = {
+ long = "kvno"
+ type = "integer"
+ help = "key version number"
+ default = "-1"
+ }
+ option = {
+ long = "constrained-delegation"
+ type = "strings"
+ argument = "principal"
+ help = "allowed target principals"
+ }
+ option = {
+ long = "alias"
+ type = "strings"
+ argument = "principal"
+ help = "aliases"
+ }
+ option = {
+ long = "pkinit-acl"
+ type = "strings"
+ argument = "subject dn"
+ help = "aliases"
+ }
+ option = {
+ long = "policy"
+ type = "string"
+ argument = "policy"
+ help = "policy name"
+ }
+ option = {
+ long = "service-enctypes"
+ short = "e"
+ type = "strings"
+ argument = "enctype"
+ help = "set enctypes supported by service"
+ }
+ option = {
+ long = "hist-kvno-diff-clnt"
+ type = "integer"
+ argument = "kvno diff"
+ help = "historic keys allowed for client"
+ default = "-1"
+ }
+ option = {
+ long = "hist-kvno-diff-svc"
+ type = "integer"
+ argument = "kvno diff"
+ help = "historic keys allowed for service"
+ default = "-1"
+ }
+ option = {
+ long = "krb5-config-file"
+ short = "C"
+ type = "string"
+ help = "krb5.conf to save in principal record"
+ }
+ argument = "principal"
+ min_args = "1"
+ max_args = "1"
+ help = "Modifies some attributes of the specified principal."
+}
+command = {
+ name = "add_alias"
+ function = "add_alias"
+ argument = "principal"
+ min_args = "2"
+ help = "Add one or more aliases to the given principal."
+}
+command = {
+ name = "del_alias"
+ function = "del_alias"
+ argument = "principal"
+ min_args = "1"
+ help = "Delete one or more aliases without deleting their canonical principals."
+}
+command = {
+ name = "prune"
+ argument = "principal"
+ option = {
+ long = "kvno"
+ type = "integer"
+ help = "key version number"
+ default = "0"
+ }
+ min_args = "1"
+ max_args = "1"
+ help = "Delete keys from history by max-ticket-life or kvno."
+}
+command = {
+ name = "privileges"
+ name = "privs"
+ function = "get_privs"
+ help = "Shows which operations you are allowed to perform."
+}
+command = {
+ name = "list"
+ function = "list_princs"
+ /* XXX sync options with "get" */
+ option = {
+ long = "long"
+ short = "l"
+ type = "flag"
+ help = "long format"
+ }
+ option = {
+ long = "short"
+ short = "s"
+ type = "flag"
+ help = "short format"
+ }
+ option = {
+ long = "terse"
+ short = "t"
+ type = "flag"
+ help = "terse format"
+ default = "-1"
+ }
+ option = {
+ long = "column-info"
+ short = "o"
+ type = "string"
+ help = "columns to print for short output"
+ }
+ option = {
+ long = "krb5-config-file"
+ type = "string"
+ help = "only use this option with the get command"
+ }
+ option = {
+ long = "upto"
+ type = "integer"
+ default = "-1"
+ help = "maximum number of principals to get/list"
+ }
+ argument = "principal..."
+ min_args = "1"
+ help = "Lists principals in a terse format. Equivalent to \"get -t\"."
+}
+command = {
+ name = "verify-password-quality"
+ name = "pwq"
+ function = "password_quality"
+ argument = "principal password"
+ min_args = "2"
+ max_args = "2"
+ help = "Try run the password quality function locally (not doing RPC out to server)."
+}
+command = {
+ name = "check"
+ function = "check"
+ argument = "[realm]"
+ min_args = "0"
+ max_args = "1"
+ help = "Check the realm (if not given, the default realm) for configuration errors."
+}
+command = {
+ name = "lock"
+ function = "lock"
+ argument = ""
+ min_args = "0"
+ max_args = "0"
+ help = "Lock the database for writing (use with care)."
+}
+command = {
+ name = "unlock"
+ function = "unlock"
+ argument = ""
+ min_args = "0"
+ max_args = "0"
+ help = "Unlock the database."
+}
+command = {
+ name = "help"
+ name = "?"
+ argument = "[command]"
+ min_args = "0"
+ max_args = "1"
+ help = "Help! I need somebody."
+}
+command = {
+ name = "exit"
+ name = "quit"
+ function = "exit_kadmin"
+ help = "Quits."
+}
diff --git a/third_party/heimdal/kadmin/kadmin-version.rc b/third_party/heimdal/kadmin/kadmin-version.rc
new file mode 100644
index 0000000..d040588
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmin-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 "Kerberos Administration Tool"
+#define RC_FILE_ORIG_0409 "kadmin.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kadmin/kadmin.1 b/third_party/heimdal/kadmin/kadmin.1
new file mode 100644
index 0000000..8b9f75e
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmin.1
@@ -0,0 +1,741 @@
+.\" Copyright (c) 2000 - 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 Feb 22, 2007
+.Dt KADMIN 1
+.Os HEIMDAL
+.Sh NAME
+.Nm kadmin
+.Nd Kerberos administration utility
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl p Ar string \*(Ba Fl Fl principal= Ns Ar string
+.Op Fl K Ar string \*(Ba Fl Fl keytab= Ns Ar string
+.Op Fl c Ar file \*(Ba Fl Fl config-file= Ns Ar file
+.Op Fl k Ar file \*(Ba Fl Fl key-file= Ns Ar file
+.Op Fl r Ar realm \*(Ba Fl Fl realm= Ns Ar realm
+.Op Fl a Ar host \*(Ba Fl Fl admin-server= Ns Ar host
+.Op Fl s Ar port number \*(Ba Fl Fl server-port= Ns Ar port number
+.Op Fl l | Fl Fl local
+.Op Fl h | Fl Fl help
+.Op Fl v | Fl Fl version
+.Op Ar command
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+program is used to make modifications to the Kerberos database, either remotely via the
+.Xr kadmind 8
+daemon, or locally (with the
+.Fl l
+option).
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Fl p Ar string , Fl Fl principal= Ns Ar string
+principal to authenticate as
+.It Fl K Ar string , Fl Fl keytab= Ns Ar string
+keytab for authentication principal
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+location of config file
+.It Fl H Ar HDB , Fl Fl hdb= Ns Ar HDB
+location of HDB
+.It Fl k Ar file , Fl Fl key-file= Ns Ar file
+location of master key file
+.It Fl r Ar realm , Fl Fl realm= Ns Ar realm
+realm to use
+.It Fl a Ar host , Fl Fl admin-server= Ns Ar host
+server to contact
+.It Fl s Ar port number , Fl Fl server-port= Ns Ar port number
+port to use
+.It Fl l , Fl Fl local
+local admin mode
+.El
+.Pp
+If no
+.Ar command
+is given on the command line,
+.Nm
+will prompt for commands to process. Some of the commands that take
+one or more principals as argument
+.Ns ( Nm delete ,
+.Nm ext_keytab ,
+.Nm get ,
+.Nm modify ,
+and
+.Nm passwd )
+will accept a glob style wildcard, and perform the operation on all
+matching principals.
+.Pp
+Commands include:
+.\" not using a list here, since groff apparently gets confused
+.\" with nested Xo/Xc
+.Pp
+.Nm add
+.Op Fl r | Fl Fl random-key
+.Op Fl Fl enctypes= Ns Ar string
+.Op Fl Fl random-password
+.Op Fl p Ar string \*(Ba Fl Fl password= Ns Ar string
+.Op Fl Fl key= Ns Ar string
+.Op Fl Fl max-ticket-life= Ns Ar lifetime
+.Op Fl Fl max-renewable-life= Ns Ar lifetime
+.Op Fl Fl attributes= Ns Ar attributes
+.Op Fl Fl expiration-time= Ns Ar time
+.Op Fl Fl pw-expiration-time= Ns Ar time
+.Op Fl Fl policy= Ns Ar policy-name
+.Op Fl Fl use-defaults
+.Ar principal...
+.Bd -ragged -offset indent
+Adds a new principal to the database. The options not passed on the
+command line will be promped for.
+If enctypes to use are not given, then the
+.Ar [libdefaults] supported_enctypes
+configuration parameter will be used on the client side to select
+enctypes, defaulting to
+.Ar aes128-cts-hmac-sha1-96.
+For compatibility with MIT, the enctypes string is a space- or
+comma-separated list of enctype:salttype.
+If
+.Fl Fl keepold
+is given, then old keys needed to decrypt extant tickets are
+kept, and all other old keys are deleted.
+If
+.Fl Fl keepallold
+is given then all old keys are kept. If
+.Fl Fl pruneall is given then all old keys are removed.
+The
+.Fl Fl keepold
+behavior is the default if none of these are given.
+The only policy supported by Heimdal servers is
+.Ql default .
+.Pp
+If some parameters are not given then they will be prompted for
+unless the
+.Fl Fl use-defaults
+option is given, in which case defaults will be taken from the
+principal named
+.Dq default .
+.Pp
+This command has the following aliases:
+.Nm ank ,
+.Nm add_new_key .
+.Ed
+.Pp
+.Nm add_alias
+.Ar principal
+.Ar alias...
+.Bd -ragged -offset indent
+Adds one or more aliases to the given principal.
+.Pp
+There are two types of aliases: hard, and soft.
+A soft alias is an alias of a principal of the form
+.Ar WELLKNOWN/REFERRALS/TARGET@target_realm
+or
+.Ar WELLKNOWN/REFERRALS/TARGET/arbitrary-component@target_realm .
+A hard alias is an alias of any normal principal, even if in a
+different realm.
+.Pp
+Hard aliases are treated as distinct principals sharing
+attributes and keys with their canonical principals.
+If a client requests canonicalization of a hard alias name, the
+KDC will use the canonical name in the ticket issued as long as
+the alias and canonical names are in the same realm.
+Conversely, if a client does not request canonicalization, or if
+the hard alias and the canonical name have different realms, then
+the KDC will issue a ticket for the alias name.
+.Pp
+Soft aliases can only be used to configure the production of
+referrals by the KDC.
+When a client requests a ticket for a principal that turns out to
+be a soft alias, the KDC will respond with a referral to the
+alias' canonical name's realm.
+.Pp
+Soft aliasing compares favorably to using
+.Ar [domain_realm]
+entries in the KDC's
+.Ar krb5.conf :
+soft aliases may be managed via the
+.Nm kadmin
+command and its
+.Nm add_alias
+and
+.Nm del_alias
+sub-commands rather than having to edit the KDC's configuration
+file and having to restart the KDC.
+.Pp
+There are two methods for configuring the issuance of referrals
+for entire namespaces of hostnames.
+A soft alias of the form
+.Ar WELLKNOWN/HOSTBASED-NAMESPACE/service/namespace-fqdn@REALM
+(see
+.Nm add_namespace
+below) will cause all requests for host-based principals in the
+given namespace to be referred to the given realm.
+Alternatively, the KDC will issue referrals for all host-based
+service principals whose hostname component matches a
+.Ar [domain_realm]
+entry in the KDC's
+.Ar krb5.conf
+file referring to a different realm.
+.Ed
+.Pp
+.Nm add_namespace
+.Ar Fl Fl key-rotation-epoch= Ns Ar time
+.Ar Fl Fl key-rotation-period= Ns Ar time
+.Op Fl Fl enctypes= Ns Ar string
+.Op Fl Fl max-ticket-life= Ns Ar lifetime
+.Op Fl Fl max-renewable-life= Ns Ar lifetime
+.Op Fl Fl attributes= Ns Ar attributes
+.Ar host-based-principal...
+.Bd -ragged -offset indent
+Adds a new namespace of virtual host-based or domain-based
+principals to the database, whose keys will be automatically
+derived from base keys stored in the namespace record, and which
+keys will be rotated automatically.
+The namespace names are of the same form as host-based principal
+names:
+.Ar service/hostname@REALM
+and these will match all host-based or domain-based service names
+where hostname component of such a principal ends in the labels
+of the hostname in the namespace name.
+.Pp
+The service name component may be a wild-card (underscore,
+.Ar _ ),
+in which case it will match any service.
+.Pp
+For example,
+.Ar bar.baz.example@BAZ.EXAMPLE
+will match
+.Ar host/foo.bar.baz.example@BAZ.EXAMPLE
+but not
+.Ar host/foobar.baz.example@BAZ.EXAMPLE .
+.Pp
+Note well that services are expected to
+.Ar ext_keytab
+or otherwise re-fetch their keytabs at least as often as one
+quarter of the key rotation period, otherwise they risk not
+having keys they need to decrypt tickets with.
+.Pp
+The epoch must be given as either an absolute time,
+.Ar "now",
+or as
+.Ar "+<N>[<unit>]"
+where
+.Ar N
+is a natural and
+.Ar unit
+is one "s", "m", "h", "day", "week", "month", defaulting to
+"month".
+The default key rotation period is
+.Ar 7d .
+The default enctypes is as for the
+.Nm add
+command.
+.Pp
+Note that namespaces are stored as principals whose names are of the form
+.Ar WELLKNOWN/HOSTBASED-NAMESPACE/service/namespace.fqdn@REALM ,
+with the
+.Ar service
+.Pp
+This command has the following alias:
+.Nm add_ns .
+.Ed
+.Pp
+.Nm add_enctype
+.Op Fl r | Fl Fl random-key
+.Ar principal enctypes...
+.Pp
+.Bd -ragged -offset indent
+Adds a new encryption type to the principal, only random key are
+supported.
+.Ed
+.Pp
+.Nm delete
+.Ar principal...
+.Bd -ragged -offset indent
+Removes a principal.
+It is an error to delete an alias.
+To remove a principal's alias or aliases, use the
+.Nm del_alias
+command.
+To remove a principal given an alias, first
+.Nm get
+the principal to get its canonical name and then delete that.
+.Ed
+.Pp
+.Nm del_alias
+.Ar alias...
+.Bd -ragged -offset indent
+Deletes the given aliases, but not their canonical principals.
+.Pp
+This command has the following aliases:
+.Nm del ,
+.Nm del_entry .
+.Ed
+.Pp
+.Nm del_enctype
+.Ar principal enctypes...
+.Bd -ragged -offset indent
+Removes some enctypes from a principal; this can be useful if the
+service belonging to the principal is known to not handle certain
+enctypes.
+.Ed
+.Pp
+.Nm prune
+.Oo Fl Fl kvno= Ns Ar number
+.Oc
+.Ar principal
+.Bd -ragged -offset indent
+Deletes the named principal's keys of the given kvno. If a kvno is
+not given then this deletes all the named principal's keys that are
+too old to be needed for decrypting tickets issued using those keys
+(i.e., any such tickets are necessarily expired). The determination
+of "too old" is made using the max-ticket-life attribute of the
+principal; though in practice that max ticket life is also constrained
+by the max-ticket-life of the client principals and the krbtgt
+principals, those are not consulted here.
+.Ed
+.Pp
+.Nm ext_keytab
+.Oo Fl k Ar keytab \*(Ba Xo
+.Op Fl Fl random-key
+.Op Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall
+.Op Fl Fl enctypes= Ns Ar string
+.Fl Fl keytab= Ns Ar string
+.Xc
+.Oc
+.Ar principal...
+.Bd -ragged -offset indent
+Creates a keytab with the keys of the specified principals. Requires
+get-keys rights, otherwise the principal's keys are changed and saved in
+the keytab.
+.Pp
+If the
+.Fl Fl random-key
+option is given then new randomly-generated keys will be set on
+the principal.
+.Pp
+If enctypes to use are not given, then the
+.Ar [libdefaults] supported_enctypes
+configuration parameter will be used on the client side to select
+enctypes, defaulting to
+.Ar aes128-cts-hmac-sha1-96.
+For compatibility with MIT, the enctypes string is a space- or
+comma-separated list of enctype:salttype.
+If
+.Fl Fl keepold
+is given, then old keys needed to decrypt extant tickets are
+kept, and all other old keys are deleted.
+If
+.Fl Fl keepallold
+is given then all old keys are kept. If
+.Fl Fl pruneall is given then all old keys are removed.
+The
+.Fl Fl keepold
+behavior is the default if none of these are given.
+.Ed
+.Pp
+.Nm get
+.Op Fl l | Fl Fl long
+.Op Fl s | Fl Fl short
+.Op Fl t | Fl Fl terse
+.Op Fl o Ar string | Fl Fl column-info= Ns Ar string
+.Op Fl C Ar path | Fl Fl krb5-config-file= Ns Ar path
+.Op Fl Fl upto= Ns Ar number
+.Ar principal...
+.Bd -ragged -offset indent
+Lists the matching principals, short prints the result as a table,
+while long format produces a more verbose output.
+If the
+.Fl Fl upto= Ns Ar number
+option is given, then only up to that many principals will be
+listed.
+.Pp
+Which columns to print can be selected with the
+.Fl o
+option. The argument is a comma separated list of column names
+optionally appended with an equal sign
+.Pq Sq =
+and a column header. Which columns are printed by default differ
+slightly between short and long output.
+.Pp
+The default terse output format is similar to
+.Fl s o Ar principal= ,
+just printing the names of matched principals.
+.Pp
+If
+.Fl C
+or
+.Fl Fl krb5-config-file
+is given and the principal has krb5 config file contents saved
+in its HDB entry, then that will be saved in the given file.
+Note that if multiple principals are requested, then the second,
+third, and so on will have -1, -2, and so on appended to the
+given filename unless the given filename is a device name.
+.Pp
+Possible column names include:
+.Li principal ,
+.Li princ_expire_time ,
+.Li pw_expiration ,
+.Li last_pwd_change ,
+.Li max_life ,
+.Li max_rlife ,
+.Li mod_time ,
+.Li mod_name ,
+.Li attributes ,
+.Li kvno ,
+.Li mkvno ,
+.Li last_success ,
+.Li last_failed ,
+.Li fail_auth_count ,
+.Li policy ,
+and
+.Li keytypes .
+.Ed
+.Pp
+.Nm modify
+.Oo Fl a Ar attributes \*(Ba Xo
+.Fl Fl attributes= Ns Ar attributes
+.Xc
+.Oc
+.Op Fl Fl max-ticket-life= Ns Ar lifetime
+.Op Fl Fl max-renewable-life= Ns Ar lifetime
+.Op Fl Fl expiration-time= Ns Ar time
+.Op Fl Fl pw-expiration-time= Ns Ar time
+.Op Fl Fl kvno= Ns Ar number
+.Op Fl Fl policy= Ns Ar policy-name
+.Op Fl Fl alias= Ns Ar alias-name
+.Op Fl Fl constrained-delegation= Ns Ar principal-name
+.Op Fl Fl pkinit-acl= Ns Ar subject-name
+.Op Fl Fl service-enctypes= Ns Ar enctype
+.Op Fl C Ar path | Fl Fl krb5-config-file= Ns Ar path
+.Ar principal...
+.Bd -ragged -offset indent
+Modifies certain attributes of a principal. If run without command
+line options, you will be prompted. With command line options, it will
+only change the ones specified.
+.Pp
+The
+.Fl Fl alias= Ns Ar alias-name
+option may be given multiple times.
+If this option is used at all, the complete list of aliases must
+be given, with one option per-alias.
+If the list given has fewer aliases than the principal had prior
+to the modification, then the missing aliases will be deleted.
+.Pp
+Use the
+.Nm add_alias
+command instead to add an alias to avoid having to list all
+existing aliases to keep.
+.Pp
+The
+.Fl Fl alias=
+option without a value allows the user to set an empty list of
+aliases.
+Use the
+.Nm del_alias
+command to delete one or more aliases.
+.Pp
+The only policy supported by Heimdal is
+.Ql default .
+.Pp
+If a krb5 config file is given, it will be saved in the entry.
+.Pp
+Possible attributes are:
+.Bl -tag -width Ds
+.It new-princ
+not used
+.It support-desmd5
+not used
+.It pwchange-service
+for kadmin/admin style service principals
+.It requires-pw-change
+force the user to change their password
+.It requires-hw-auth
+.It requires-pre-auth
+.It allow-digest
+allow NTLM for this user in the KDC's digest service
+.It trusted-for-delegation
+.It ok-as-delegate
+allow forwarding of tickets to this service principal
+.It disallow-client
+disallow issuance of tickets for this principal as a client
+.It disallow-svr
+disallow issuance of tickets for this principal as a server
+.It disallow-all-tix
+disallow issuance of tickets for this principal as a client or
+server
+.It disallow-dup-skey
+not used
+.It disallow-proxiable
+disallow proxiable tickets
+.It disallow-renewable ,
+disallow reneable tickets
+.It disallow-tgt-based ,
+require initial tickets for this service, such as password
+changing services
+.It disallow-forwardable
+disallow forwardable tickets
+.It disallow-postdated
+disallow postdated tickets
+.It no-auth-data-reqd
+do not include a PAC in tickets issued to this service
+.It auth-data-reqd
+do include a PAC in tickets issued to this service even if the
+.Li disable_pac
+KDC configuration parameter is set to true
+.El
+.Pp
+Attributes may be negated with a "-", e.g.,
+.Pp
+kadmin -l modify -a -disallow-proxiable user
+.Pp
+The
+.Fl Fl constrained-delegation= Ns Ar principal-name
+option is not currently implemented.
+.Pp
+The
+.Fl Fl pkinit-acl= Ns Ar subject-name
+option authorizes clients with certificates with the given
+subject distinguished name to get tickets for the principal using
+PKINIT.
+This option can be given multiple times.
+The PKINIT ACLs set with this option will replace the existing
+ones.
+.Pp
+The
+.Fl Fl service-enctypes= Ns Ar enctype
+option indicates that the service supports the given enctype
+regardless of whether the service has long-term keys of that
+enctype.
+This option can be given multiple times and will replace the
+existing set of enctypes supported by the service.
+If a service principal does not have any supported enctypes then
+the KDC will assume that it supports only the enctypes of all of
+its long-term keys.
+.Pp
+This command has the following alias:
+.Nm mod .
+.Ed
+.Pp
+.Nm passwd
+.Op Fl Fl keepold | Fl Fl keepallold | Fl Fl pruneall
+.Op Fl Fl enctypes= Ns Ar string
+.Op Fl r | Fl Fl random-key
+.Op Fl Fl random-password
+.Oo Fl p Ar string \*(Ba Xo
+.Fl Fl password= Ns Ar string
+.Xc
+.Oc
+.Op Fl Fl key= Ns Ar string
+.Ar principal...
+.Bd -ragged -offset indent
+Changes the password of an existing principal.
+If enctypes to use are not given, then the
+.Ar [libdefaults] supported_enctypes
+configuration parameter will be used on the client side to select
+enctypes, defaulting to
+.Ar aes128-cts-hmac-sha1-96.
+For compatibility with MIT, the enctypes string is a space- or
+comma-separated list of enctype:salttype.
+If
+.Fl Fl keepold
+is given, then old keys needed to decrypt extant tickets are
+kept, and all other old keys are deleted.
+If
+.Fl Fl keepallold
+is given then all old keys are kept. If
+.Fl Fl pruneall is given then all old keys are removed.
+The
+.Fl Fl keepold
+behavior is the default if none of these are given.
+.Pp
+This command has the following aliases:
+.Nm cpw ,
+.Nm change_password .
+.Ed
+.Pp
+.Nm verify-password-quality
+.Ar principal
+.Ar password
+.Bd -ragged -offset indent
+Run the password quality check function locally.
+You can run this on the host that is configured to run the kadmind
+process to verify that your configuration file is correct.
+The verification is done locally, if kadmin is run in remote mode,
+no rpc call is done to the server. NOTE: if the environment has
+verify-password-quality configured to use a back-end that stores
+password history (such as heimdal-history), running
+verify-quality-password will cause an update to the password
+database meaning that merely verifying the quality of the password
+using verify-quality-password invalidates the use of that
+principal/password in the future.
+.Pp
+This command has the following alias:
+.Nm pwq .
+.Ed
+.Pp
+.Nm privileges
+.Bd -ragged -offset indent
+Lists the operations you are allowed to perform. These include
+.Li add ,
+.Li add_enctype ,
+.Li change-password ,
+.Li delete ,
+.Li del_enctype ,
+.Li get ,
+.Li get-keys ,
+.Li list ,
+and
+.Li modify .
+.Pp
+This command has the following alias:
+.Nm privs .
+.Ed
+.Pp
+.Nm rename
+.Ar from to
+.Bd -ragged -offset indent
+Renames a principal. This is normally transparent, but since keys are
+salted with the principal name, they will have a non-standard salt,
+and clients which are unable to cope with this will fail. Kerberos 4
+suffers from this.
+.Ed
+.Pp
+.Nm check
+.Op Ar realm
+.Pp
+.Bd -ragged -offset indent
+Check database for strange configurations on important principals. If
+no realm is given, the default realm is used.
+.Ed
+.Pp
+When running in local mode, the following commands can also be used:
+.Pp
+.Nm dump
+.Op Fl d | Fl Fl decrypt
+.Op Fl f Ns Ar format | Fl Fl format= Ns Ar format
+.Op Ar dump-file
+.Bd -ragged -offset indent
+Writes the database in
+.Dq machine readable text
+form to the specified file, or standard out. If the database is
+encrypted, the dump will also have encrypted keys, unless
+.Fl Fl decrypt
+is used. If
+.Fl Fl format=MIT
+is used then the dump will be in MIT format. Otherwise it will be in
+Heimdal format.
+.Ed
+.Pp
+.Nm init
+.Op Fl Fl realm-max-ticket-life= Ns Ar string
+.Op Fl Fl realm-max-renewable-life= Ns Ar string
+.Op Fl Fl bare
+.Ar realm
+.Bd -ragged -offset indent
+Initializes the Kerberos database with entries for a new realm.
+It's possible to have more than one realm served by one server
+with the same database.
+.Pp
+If the
+.Fl Fl bare
+option is given, then only the root krbtgt principal for that
+realm will be created.
+.Ed
+.Pp
+.Nm load
+.Ar file
+.Bd -ragged -offset indent
+Reads a previously dumped database, and re-creates that database from
+scratch.
+.Ed
+.Pp
+.Nm merge
+.Ar file
+.Bd -ragged -offset indent
+Similar to
+.Nm load
+but just modifies the database with the entries in the dump file.
+.Ed
+.Pp
+.Nm stash
+.Oo Fl e Ar enctype \*(Ba Xo
+.Fl Fl enctype= Ns Ar enctype
+.Xc
+.Oc
+.Oo Fl k Ar keyfile \*(Ba Xo
+.Fl Fl key-file= Ns Ar keyfile
+.Xc
+.Oc
+.Op Fl Fl convert-file
+.Op Fl Fl master-key-fd= Ns Ar fd
+.Op Fl Fl random-password
+.Bd -ragged -offset indent
+Writes the Kerberos master key to a file used by the KDC.
+.Pp
+If the
+.Fl Fl convert-file
+option is given then convert an existing file to the new format.
+If the
+.Fl Fl master-key-fd= Ns Ar fd
+option is given the the password will be read from the given file
+descriptor.
+If the
+.Fl Fl random-password
+option is given then a password will be generated randomly.
+.Pp
+This command has the following alias:
+.Nm kstash .
+.Ed
+.Pp
+.Nm exit
+.Bd -ragged -offset indent
+Exits
+.Nm kadmin .
+.Pp
+This command has the following alias:
+.Nm quit .
+.Ed
+.\".Sh ENVIRONMENT
+.\".Sh FILES
+.\".Sh EXAMPLES
+.\".Sh DIAGNOSTICS
+.Sh SEE ALSO
+.Xr kadmind 8 ,
+.Xr kdc 8
+.\".Sh STANDARDS
+.\".Sh HISTORY
+.\".Sh AUTHORS
+.\".Sh BUGS
diff --git a/third_party/heimdal/kadmin/kadmin.c b/third_party/heimdal/kadmin/kadmin.c
new file mode 100644
index 0000000..607ba03
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmin.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1997 - 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.
+ */
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+#include <sl.h>
+
+static char *config_file;
+static char *keyfile;
+int local_flag;
+static int ad_flag;
+static int help_flag;
+static int version_flag;
+static char *hdb;
+static char *realm;
+static char *admin_server;
+static int server_port = 0;
+static char *client_name;
+static char *keytab;
+static char *check_library = NULL;
+static char *check_function = NULL;
+static getarg_strings policy_libraries = { 0, NULL };
+
+static struct getargs args[] = {
+ { "principal", 'p', arg_string, &client_name,
+ "principal to authenticate as", NULL },
+ { "keytab", 'K', arg_string, &keytab,
+ "keytab for authentication principal", NULL },
+ {
+ "config-file", 'c', arg_string, &config_file,
+ "location of config file", "file"
+ },
+ {
+ "key-file", 'k', arg_string, &keyfile,
+ "location of master key file", "file"
+ },
+ {
+ "hdb", 'H', arg_string, &hdb,
+ "HDB to use", "hdb"
+ },
+ {
+ "realm", 'r', arg_string, &realm,
+ "realm to use", "realm"
+ },
+ {
+ "admin-server", 'a', arg_string, &admin_server,
+ "server to contact", "host"
+ },
+ {
+ "server-port", 's', arg_integer, &server_port,
+ "port to use", "port number"
+ },
+ { "ad", 0, arg_flag, &ad_flag, "active directory admin mode",
+ NULL },
+#ifdef HAVE_DLOPEN
+ { "check-library", 0, arg_string, &check_library,
+ "library to load password check function from", "library" },
+ { "check-function", 0, arg_string, &check_function,
+ "password check function to load", "function" },
+ { "policy-libraries", 0, arg_strings, &policy_libraries,
+ "password check function to load", "function" },
+#endif
+ { "local", 'l', arg_flag, &local_flag, "local admin mode", NULL },
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 'v', arg_flag, &version_flag, NULL, NULL }
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+
+krb5_context context;
+void *kadm_handle;
+
+int
+help(void *opt, int argc, char **argv)
+{
+ sl_slc_help(commands, argc, argv);
+ return 0;
+}
+
+static int exit_seen = 0;
+
+int
+exit_kadmin (void *opt, int argc, char **argv)
+{
+ exit_seen = 1;
+ return 0;
+}
+
+int
+lock(void *opt, int argc, char **argv)
+{
+ return kadm5_lock(kadm_handle);
+}
+
+int
+unlock(void *opt, int argc, char **argv)
+{
+ return kadm5_unlock(kadm_handle);
+}
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, num_args, NULL, "[command]");
+ exit (ret);
+}
+
+int
+get_privs(void *opt, int argc, char **argv)
+{
+ uint32_t privs;
+ char str[128];
+ kadm5_ret_t ret;
+
+ ret = kadm5_get_privs(kadm_handle, &privs);
+ if(ret)
+ krb5_warn(context, ret, "kadm5_get_privs");
+ else{
+ ret =_kadm5_privs_to_string(privs, str, sizeof(str));
+ if (ret == 0)
+ printf("%s\n", str);
+ else
+ printf("privs: 0x%x\n", (unsigned int)privs);
+ }
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ char **files;
+ kadm5_config_params conf;
+ int optidx = 0;
+ int exit_status = 0;
+ int aret;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ 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 (config_file == NULL) {
+ aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1)
+ 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");
+
+ memset(&conf, 0, sizeof(conf));
+ if(realm) {
+ krb5_set_default_realm(context, realm); /* XXX should be fixed
+ some other way */
+ conf.realm = realm;
+ conf.mask |= KADM5_CONFIG_REALM;
+ }
+
+ if (hdb) {
+ conf.dbname = hdb;
+ conf.mask |= KADM5_CONFIG_DBNAME;
+ }
+
+ if (admin_server) {
+ conf.admin_server = admin_server;
+ conf.mask |= KADM5_CONFIG_ADMIN_SERVER;
+ }
+
+ if (server_port) {
+ conf.kadmind_port = htons(server_port);
+ conf.mask |= KADM5_CONFIG_KADMIND_PORT;
+ }
+
+ if (keyfile) {
+ conf.stash_file = keyfile;
+ conf.mask |= KADM5_CONFIG_STASH_FILE;
+ }
+
+ if(local_flag) {
+ int i;
+
+ kadm5_setup_passwd_quality_check (context,
+ check_library, check_function);
+
+ for (i = 0; i < policy_libraries.num_strings; i++) {
+ ret = kadm5_add_passwd_quality_verifier(context,
+ policy_libraries.strings[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
+ }
+ ret = kadm5_add_passwd_quality_verifier(context, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
+
+ ret = kadm5_s_init_with_password_ctx(context,
+ KADM5_ADMIN_SERVICE,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+ } else if (ad_flag) {
+ if (client_name == NULL)
+ krb5_errx(context, 1, "keytab mode require principal name");
+ ret = kadm5_ad_init_with_password_ctx(context,
+ client_name,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+ } else if (keytab) {
+ if (client_name == NULL)
+ krb5_errx(context, 1, "keytab mode require principal name");
+ ret = kadm5_c_init_with_skey_ctx(context,
+ client_name,
+ keytab,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+ } else
+ ret = kadm5_c_init_with_password_ctx(context,
+ client_name,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &conf, 0, 0,
+ &kadm_handle);
+
+ if(ret)
+ krb5_err(context, 1, ret, "kadm5_init_with_password");
+
+ signal(SIGINT, SIG_IGN); /* ignore signals for now, the sl command
+ parser will handle SIGINT its own way;
+ we should really take care of this in
+ each function, f.i `get' might be
+ interruptable, but not `create' */
+ if (argc != 0) {
+ ret = sl_command (commands, argc, argv);
+ if(ret == -1)
+ sl_did_you_mean(commands, argv[0]);
+ else if (ret == -2)
+ ret = 0;
+ if(ret != 0)
+ exit_status = 1;
+ } else {
+ while(!exit_seen) {
+ ret = sl_command_loop(commands, "kadmin> ", NULL);
+ if (ret == -2) {
+ exit_seen = 1;
+ } else if (ret != 0) {
+ exit_status = 1;
+ if (!isatty(STDIN_FILENO))
+ exit_seen = 1;
+ }
+ }
+ }
+
+ kadm5_destroy(kadm_handle);
+ krb5_free_context(context);
+ return exit_status;
+}
diff --git a/third_party/heimdal/kadmin/kadmin_locl.h b/third_party/heimdal/kadmin/kadmin_locl.h
new file mode 100644
index 0000000..6ad36b9
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmin_locl.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1997-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 __ADMIN_LOCL_H__
+#define __ADMIN_LOCL_H__
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETINET_IN6_H
+#include <netinet/in6.h>
+#endif
+#ifdef HAVE_NETINET6_IN6_H
+#include <netinet6/in6.h>
+#endif
+
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+#ifdef HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#include <err.h>
+#include <roken.h>
+#include <krb5.h>
+#include <krb5_locl.h>
+#include <hdb.h>
+#include <hdb_err.h>
+#include <hex.h>
+#include <kadm5/admin.h>
+#include <kadm5/private.h>
+#include <kadm5/kadm5_err.h>
+#include <parse_time.h>
+#include <getarg.h>
+
+extern krb5_context context;
+extern void * kadm_handle;
+extern int list_chunk_size;
+
+#undef ALLOC
+#define ALLOC(X) ((X) = malloc(sizeof(*(X))))
+
+/* util.c */
+
+void attributes2str(krb5_flags, char *, size_t);
+int str2attributes(const char *, krb5_flags *);
+int parse_attributes (const char *, krb5_flags *, int *, int);
+int edit_attributes (const char *, krb5_flags *, int *, int);
+
+int parse_policy (const char *, char **, int *, int);
+int edit_policy (const char *, char **, int *, int);
+
+void time_t2str(time_t, char *, size_t, int);
+int str2time_t (const char *, time_t *);
+int parse_timet (const char *, krb5_timestamp *, int *, int);
+int edit_timet (const char *, krb5_timestamp *, int *,
+ int);
+
+void deltat2str(unsigned, char *, size_t);
+int str2deltat(const char *, krb5_deltat *);
+int parse_deltat (const char *, krb5_deltat *, int *, int);
+int edit_deltat (const char *, krb5_deltat *, int *, int);
+
+int edit_entry(kadm5_principal_ent_t, int *, kadm5_principal_ent_t, int);
+void set_defaults(kadm5_principal_ent_t, int *, kadm5_principal_ent_t, int);
+int set_entry(krb5_context, kadm5_principal_ent_t, int *,
+ const char *, const char *, const char *,
+ const char *, const char *, const char *);
+int
+foreach_principal(const char *, int (*)(krb5_principal, void*),
+ const char *, void *);
+
+int parse_des_key (const char *, krb5_key_data *, const char **);
+
+/* random_password.c */
+
+void
+random_password(char *, size_t);
+
+/* kadm_conn.c */
+
+extern sig_atomic_t term_flag, doing_useful_work;
+
+void parse_ports(krb5_context, const char*);
+void start_server(krb5_context, const char*);
+
+/* server.c */
+
+krb5_error_code
+kadmind_loop (krb5_context, krb5_keytab, int, int);
+
+/* rpc.c */
+
+int
+handle_mit(krb5_context, void *, size_t, int, int);
+
+/* mod.c */
+
+void
+add_tl(kadm5_principal_ent_rec *, int, krb5_data *);
+
+krb5_tl_data *
+get_tl(kadm5_principal_ent_rec *, int);
+
+#endif /* __ADMIN_LOCL_H__ */
diff --git a/third_party/heimdal/kadmin/kadmind-version.rc b/third_party/heimdal/kadmin/kadmind-version.rc
new file mode 100644
index 0000000..090bc81
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmind-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 "Kerberos Administration Server"
+#define RC_FILE_ORIG_0409 "kadmind.exe"
+
+#include "../windows/version.rc"
diff --git a/third_party/heimdal/kadmin/kadmind.8 b/third_party/heimdal/kadmin/kadmind.8
new file mode 100644
index 0000000..9ff1f52
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmind.8
@@ -0,0 +1,194 @@
+.\" Copyright (c) 2002 - 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 December 8, 2004
+.Dt KADMIND 8
+.Os HEIMDAL
+.Sh NAME
+.Nm kadmind
+.Nd "server for administrative access to Kerberos database"
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl k Ar file \*(Ba Xo
+.Fl Fl key-file= Ns Ar file
+.Xc
+.Oc
+.Op Fl Fl keytab= Ns Ar keytab
+.Oo Fl r Ar realm \*(Ba Xo
+.Fl Fl realm= Ns Ar realm
+.Xc
+.Oc
+.Op Fl d | Fl Fl debug
+.Oo Fl p Ar port \*(Ba Xo
+.Fl Fl ports= Ns Ar port
+.Xc
+.Oc
+.Ek
+.Sh DESCRIPTION
+.Nm
+listens for requests for changes to the Kerberos database and performs
+these, subject to permissions. When starting, if stdin is a socket it
+assumes that it has been started by
+.Xr inetd 8 ,
+otherwise it behaves as a daemon, forking processes for each new
+connection. The
+.Fl Fl debug
+option causes
+.Nm
+to accept exactly one connection, which is useful for debugging.
+.Pp
+The
+.Xr kpasswdd 8
+daemon is responsible for the Kerberos 5 password changing protocol
+(used by
+.Xr kpasswd 1 ) .
+.Pp
+This daemon should only be run on the master server, and not on any
+slaves.
+.Pp
+Principals are always allowed to change their own password and list
+their own principal. Apart from that, doing any operation requires
+permission explicitly added in the ACL file
+.Pa /var/heimdal/kadmind.acl .
+The format of this file is:
+.Bd -ragged
+.Va principal
+.Va rights
+.Op Va principal-pattern
+.Ed
+.Pp
+Where rights is any (comma separated) combination of:
+.Bl -bullet -compact
+.It
+change-password or cpw
+.It
+list
+.It
+delete
+.It
+modify
+.It
+add
+.It
+get
+.It
+get-keys
+.It
+all (everything except get-keys)
+.El
+.Pp
+And the optional
+.Ar principal-pattern
+restricts the rights to operations on principals that match the
+glob-style pattern.
+.Pp
+Supported options:
+.Bl -tag -width Ds
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+location of config file
+.It Fl k Ar file , Fl Fl key-file= Ns Ar file
+location of master key file
+.It Fl Fl keytab= Ns Ar keytab
+what keytab to use
+.It Fl r Ar realm , Fl Fl realm= Ns Ar realm
+realm to use
+.It Fl d , Fl Fl debug
+enable debugging
+.It Fl p Ar port , Fl Fl ports= Ns Ar port
+ports to listen to. By default, if run as a daemon, it listens to port
+749, but you can add any number of ports with this option. The port
+string is a whitespace separated list of port specifications, with the
+special string
+.Dq +
+representing the default port.
+.El
+.\".Sh ENVIRONMENT
+.Sh FILES
+.Pa /var/heimdal/kadmind.acl
+.Sh EXAMPLES
+This will cause
+.Nm
+to listen to port 4711 in addition to any
+compiled in defaults:
+.Pp
+.D1 Nm Fl Fl ports Ns Li "=\*[q]+ 4711\*[q] &"
+.Pp
+This acl file will grant Joe all rights, and allow Mallory to view and
+add host principals, as well as extract host principal keys (e.g., into
+keytabs).
+.Bd -literal -offset indent
+joe/admin@EXAMPLE.COM all
+mallory/admin@EXAMPLE.COM add,get-keys host/*@EXAMPLE.COM
+.Ed
+.Sh CONFIGURATION FILE
+kadmind uses the following configuration parameters from the
+.Ar [kadmin]
+section of
+.Ar krb5.conf:
+.Bl -tag -width Ds -offset indent
+.It password_lifetime
+.El
+.Pp
+kadmind uses the following configuration parameters from the per-realm entries
+in the
+.Ar [realms]
+section of
+.Ar krb5.conf:
+.Bl -tag -width Ds -offset indent
+.It supported_enctypes
+.El
+.Pp
+kadmind uses the following configuration parameters from the
+.Ar [password_quality]
+section of
+.Ar krb5.conf:
+.Bl -tag -width Ds -offset indent
+.It enforce_on_admin_set
+.It min_length
+.It min_classes
+.It external_program
+.It check_library
+.It check_function
+.It policy_libraries
+.It policies
+.El
+.Sh SEE ALSO
+.Xr kpasswd 1 ,
+.Xr kadmin 1 ,
+.Xr kdc 8 ,
+.Xr kpasswdd 8
diff --git a/third_party/heimdal/kadmin/kadmind.c b/third_party/heimdal/kadmin/kadmind.c
new file mode 100644
index 0000000..4ea513e
--- /dev/null
+++ b/third_party/heimdal/kadmin/kadmind.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1997-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.
+ */
+
+#include "kadmin_locl.h"
+#include "heim_threads.h"
+#include "krb5-protos.h"
+
+static char *check_library = NULL;
+static char *check_function = NULL;
+static getarg_strings policy_libraries = { 0, NULL };
+static char *config_file;
+static char sHDB[] = "HDBGET:";
+static char *keytab_str = sHDB;
+#ifndef WIN32
+static char *fuzz_file;
+static char *fuzz_client_name;
+static char *fuzz_keytab_name;
+static char *fuzz_service_name;
+static char *fuzz_admin_server;
+#endif
+static int help_flag;
+static int version_flag;
+static int debug_flag;
+static int readonly_flag;
+static char *port_str;
+char *realm;
+int list_chunk_size = -1;
+
+static int detach_from_console = -1;
+int daemon_child = -1;
+
+static struct getargs args[] = {
+ {
+ "config-file", 'c', arg_string, &config_file,
+ "location of config file", "file"
+ },
+ {
+ "keytab", 0, arg_string, &keytab_str,
+ "what keytab to use", "keytab"
+ },
+ { "realm", 'r', arg_string, &realm,
+ "realm to use", "realm"
+ },
+#ifdef HAVE_DLOPEN
+ { "check-library", 0, arg_string, &check_library,
+ "library to load password check function from", "library" },
+ { "check-function", 0, arg_string, &check_function,
+ "password check function to load", "function" },
+ { "policy-libraries", 0, arg_strings, &policy_libraries,
+ "password check function to load", "function" },
+#endif
+ { "debug", 'd', arg_flag, &debug_flag,
+ "enable debugging", NULL
+ },
+ { "list-chunk-size", 0, arg_integer,&list_chunk_size,
+ "set the LIST streaming count of names per chunk", "NUMBER"
+ },
+ {
+ "detach", 0 , arg_flag, &detach_from_console,
+ "detach from console", NULL
+ },
+ {
+ "daemon-child", 0 , arg_integer, &daemon_child,
+ "private argument, do not use", NULL
+ },
+ { "ports", 'p', arg_string, &port_str,
+ "ports to listen to", "port" },
+ { "read-only", 0, arg_flag, &readonly_flag,
+ "read-only operations", NULL },
+#ifndef WIN32
+ { "fuzz-file", 0, arg_string, &fuzz_file,
+ "Kadmin RPC body for fuzzing", "FILE" },
+ { "fuzz-client", 0, arg_string, &fuzz_client_name,
+ "Client name for fuzzing", "PRINCIPAL" },
+ { "fuzz-keytab", 0, arg_string, &fuzz_keytab_name,
+ "Keytab for fuzzing", "KEYTAB" },
+ { "fuzz-server", 0, arg_string, &fuzz_admin_server,
+ "Name of kadmind self instance", "HOST:PORT" },
+#endif
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 'v', arg_flag, &version_flag, NULL, NULL }
+};
+
+static int num_args = sizeof(args) / sizeof(args[0]);
+
+krb5_context context;
+
+static void
+usage(int ret)
+{
+ arg_printusage (args, num_args, NULL, "");
+ exit (ret);
+}
+
+static void *fuzz_thread(void *);
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ char **files;
+ int optidx = 0;
+ int i;
+ krb5_log_facility *logfacility;
+ krb5_keytab keytab;
+ krb5_socket_t sfd = rk_INVALID_SOCKET;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, num_args, argc, argv, &optidx)) {
+ warnx("error at argument `%s'", argv[optidx]);
+ usage(1);
+ }
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ if (detach_from_console > 0 && daemon_child == -1)
+ daemon_child = roken_detach_prep(argc, argv, "--daemon-child");
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ argc -= optidx;
+ argv += optidx;
+ if (argc != 0)
+ usage(1);
+
+ if (config_file == NULL) {
+ int aret;
+
+ aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1)
+ 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");
+
+ ret = krb5_openlog(context, "kadmind", &logfacility);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_openlog");
+ ret = krb5_set_warn_dest(context, logfacility);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_set_warn_dest");
+
+ 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");
+
+ kadm5_setup_passwd_quality_check (context, check_library, check_function);
+
+ for (i = 0; i < policy_libraries.num_strings; i++) {
+ ret = kadm5_add_passwd_quality_verifier(context,
+ policy_libraries.strings[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
+ }
+ ret = kadm5_add_passwd_quality_verifier(context, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_add_passwd_quality_verifier");
+
+ if(debug_flag) {
+ int debug_port;
+
+ if(port_str == NULL)
+ debug_port = krb5_getportbyname (context, "kerberos-adm",
+ "tcp", 749);
+ else
+ debug_port = htons(atoi(port_str));
+ mini_inetd(debug_port, &sfd);
+ } else {
+#ifdef _WIN32
+ start_server(context, port_str);
+#else
+ struct sockaddr_storage __ss;
+ struct sockaddr *sa = (struct sockaddr *)&__ss;
+ socklen_t sa_size = sizeof(__ss);
+
+ /*
+ * Check if we are running inside inetd or not, if not, start
+ * our own server.
+ */
+
+ if(roken_getsockname(STDIN_FILENO, sa, &sa_size) < 0 &&
+ rk_SOCK_ERRNO == ENOTSOCK) {
+ start_server(context, port_str);
+ }
+#endif /* _WIN32 */
+ sfd = STDIN_FILENO;
+
+ socket_set_keepalive(sfd, 1);
+ }
+
+ if(realm)
+ krb5_set_default_realm(context, realm); /* XXX */
+
+#ifndef WIN32
+ if (fuzz_file) {
+ HEIMDAL_THREAD_ID tid;
+
+ if (fuzz_admin_server == NULL)
+ errx(1, "If --fuzz-file is given then --fuzz-server must be too");
+ HEIMDAL_THREAD_create(&tid, fuzz_thread, NULL);
+ }
+#endif
+
+ kadmind_loop(context, keytab, sfd, readonly_flag);
+
+ return 0;
+}
+
+#ifndef WIN32
+static void *
+fuzz_thread(void *arg)
+{
+ kadm5_config_params conf;
+ krb5_error_code ret;
+ krb5_context context2;
+ krb5_storage *sp;
+ krb5_data reply;
+ void *server_handle = NULL;
+ int fd;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.admin_server = fuzz_admin_server;
+
+ fd = open(fuzz_file, O_RDONLY);
+ if (fd < 0)
+ err(1, "Could not open fuzz file %s", fuzz_file);
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ err(1, "Could not read fuzz file %s", fuzz_file);
+ (void) close(fd);
+
+ ret = krb5_init_context(&context2);
+ if (ret)
+ errx(1, "Fuzzing failed: krb5_init_context failed: %d", ret);
+ ret = kadm5_c_init_with_skey_ctx(context2,
+ fuzz_client_name,
+ fuzz_keytab_name,
+ fuzz_service_name ?
+ fuzz_service_name :
+ KADM5_ADMIN_SERVICE,
+ &conf,
+ 0, /* struct_version */
+ 0, /* api_version */
+ &server_handle);
+ if (ret)
+ errx(1, "Fuzzing failed: kadm5_c_init_with_skey_ctx failed: %d", ret);
+
+ ret = _kadm5_connect(server_handle, 1 /* want_write */);
+ if (ret)
+ errx(1, "Fuzzing failed: Could not connect to self (%s): "
+ "_kadm5_connect failed: %d", fuzz_admin_server, ret);
+ ret = _kadm5_client_send(server_handle, sp);
+ if (ret)
+ errx(1, "Fuzzing failed: Could not send request to self (%s): "
+ "_kadm5_client_send failed: %d", fuzz_admin_server, ret);
+ krb5_data_zero(&reply);
+ ret = _kadm5_client_recv(server_handle, &reply);
+ if (ret)
+ errx(1, "Fuzzing failed: Could not read reply from self (%s): "
+ "_kadm5_client_recv failed: %d", fuzz_admin_server, ret);
+ krb5_storage_free(sp);
+ krb5_data_free(&reply);
+ fprintf(stderr, "Fuzzed with %s", fuzz_file);
+ exit(0);
+
+ return NULL;
+}
+#endif
diff --git a/third_party/heimdal/kadmin/load.c b/third_party/heimdal/kadmin/load.c
new file mode 100644
index 0000000..f62f8b9
--- /dev/null
+++ b/third_party/heimdal/kadmin/load.c
@@ -0,0 +1,650 @@
+/*
+ * 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 <limits.h>
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+#include <kadm5/private.h>
+
+struct entry {
+ char *principal;
+ char *key;
+ char *max_life;
+ char *max_renew;
+ char *created;
+ char *modified;
+ char *valid_start;
+ char *valid_end;
+ char *pw_end;
+ char *flags;
+ char *generation;
+ char *extensions;
+};
+
+static char *
+skip_next(char *p)
+{
+ while(*p && !isspace((unsigned char)*p))
+ p++;
+ *p++ = 0;
+ while(*p && isspace((unsigned char)*p))
+ p++;
+ return p;
+}
+
+/*
+ * Parse the time in `s', returning:
+ * -1 if error parsing
+ * 0 if none present
+ * 1 if parsed ok
+ */
+
+static int
+parse_time_string(time_t *t, const char *s)
+{
+ int year, month, date, hour, minute, second;
+ struct tm tm;
+
+ if(strcmp(s, "-") == 0)
+ return 0;
+ if(sscanf(s, "%04d%02d%02d%02d%02d%02d",
+ &year, &month, &date, &hour, &minute, &second) != 6)
+ return -1;
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month - 1;
+ tm.tm_mday = date;
+ tm.tm_hour = hour;
+ tm.tm_min = minute;
+ tm.tm_sec = second;
+ tm.tm_isdst = 0;
+ *t = timegm(&tm);
+ return 1;
+}
+
+/*
+ * parse time, allocating space in *t if it's there
+ */
+
+static int
+parse_time_string_alloc (time_t **t, const char *s)
+{
+ time_t tmp;
+ int ret;
+
+ *t = NULL;
+ ret = parse_time_string (&tmp, s);
+ if (ret == 1) {
+ *t = malloc (sizeof (**t));
+ if (*t == NULL)
+ krb5_errx (context, 1, "malloc: out of memory");
+ **t = tmp;
+ }
+ return ret;
+}
+
+/*
+ * see parse_time_string for calling convention
+ */
+
+static int
+parse_integer(unsigned int *u, const char *s)
+{
+ if(strcmp(s, "-") == 0)
+ return 0;
+ if (sscanf(s, "%u", u) != 1)
+ return -1;
+ return 1;
+}
+
+static int
+parse_integer_alloc (unsigned int **u, const char *s)
+{
+ unsigned int tmp;
+ int ret;
+
+ *u = NULL;
+ ret = parse_integer (&tmp, s);
+ if (ret == 1) {
+ *u = malloc (sizeof (**u));
+ if (*u == NULL)
+ krb5_errx (context, 1, "malloc: out of memory");
+ **u = tmp;
+ }
+ return ret;
+}
+
+/*
+ * Parse dumped keys in `str' and store them in `ent'
+ * return -1 if parsing failed
+ */
+
+static int
+parse_keys(hdb_entry *ent, char *str)
+{
+ krb5_error_code ret;
+ int tmp;
+ char *p;
+ size_t i;
+
+ p = strsep(&str, ":");
+ if (sscanf(p, "%d", &tmp) != 1)
+ return 1;
+ ent->kvno = tmp;
+ p = strsep(&str, ":");
+ while(p){
+ Key *key;
+ key = realloc(ent->keys.val,
+ (ent->keys.len + 1) * sizeof(*ent->keys.val));
+ if(key == NULL)
+ krb5_errx (context, 1, "realloc: out of memory");
+ ent->keys.val = key;
+ key = ent->keys.val + ent->keys.len;
+ ent->keys.len++;
+ memset(key, 0, sizeof(*key));
+ if(sscanf(p, "%d", &tmp) == 1) {
+ key->mkvno = malloc(sizeof(*key->mkvno));
+ *key->mkvno = tmp;
+ } else
+ key->mkvno = NULL;
+ p = strsep(&str, ":");
+ if (sscanf(p, "%d", &tmp) != 1)
+ return 1;
+ key->key.keytype = tmp;
+ p = strsep(&str, ":");
+ ret = krb5_data_alloc(&key->key.keyvalue, (strlen(p) - 1) / 2 + 1);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_data_alloc");
+ for(i = 0; i < strlen(p); i += 2) {
+ if(sscanf(p + i, "%02x", &tmp) != 1)
+ return 1;
+ ((u_char*)key->key.keyvalue.data)[i / 2] = tmp;
+ }
+ p = strsep(&str, ":");
+ if(strcmp(p, "-") != 0){
+ unsigned type;
+ size_t p_len;
+
+ if(sscanf(p, "%u/", &type) != 1)
+ return 1;
+ p = strchr(p, '/');
+ if(p == NULL)
+ return 1;
+ p++;
+ p_len = strlen(p);
+
+ key->salt = calloc(1, sizeof(*key->salt));
+ if (key->salt == NULL)
+ krb5_errx (context, 1, "malloc: out of memory");
+ key->salt->type = type;
+
+ if (p_len) {
+ if(*p == '\"') {
+ ret = krb5_data_copy(&key->salt->salt, p + 1, p_len - 2);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_data_copy");
+ } else {
+ ret = krb5_data_alloc(&key->salt->salt,
+ (p_len - 1) / 2 + 1);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_data_alloc");
+ for(i = 0; i < p_len; i += 2){
+ if (sscanf(p + i, "%02x", &tmp) != 1)
+ return 1;
+ ((u_char*)key->salt->salt.data)[i / 2] = tmp;
+ }
+ }
+ } else
+ krb5_data_zero (&key->salt->salt);
+ }
+ p = strsep(&str, ":");
+ }
+ return 0;
+}
+
+/*
+ * see parse_time_string for calling convention
+ */
+
+static int
+parse_event(Event *ev, char *s)
+{
+ krb5_error_code ret;
+ char *p;
+
+ if(strcmp(s, "-") == 0)
+ return 0;
+ memset(ev, 0, sizeof(*ev));
+ p = strsep(&s, ":");
+ if(parse_time_string(&ev->time, p) != 1)
+ return -1;
+ p = strsep(&s, ":");
+ ret = krb5_parse_name(context, p, &ev->principal);
+ if (ret)
+ return -1;
+ return 1;
+}
+
+static int
+parse_event_alloc (Event **ev, char *s)
+{
+ Event tmp;
+ int ret;
+
+ *ev = NULL;
+ ret = parse_event (&tmp, s);
+ if (ret == 1) {
+ *ev = malloc (sizeof (**ev));
+ if (*ev == NULL)
+ krb5_errx (context, 1, "malloc: out of memory");
+ **ev = tmp;
+ }
+ return ret;
+}
+
+static int
+parse_hdbflags2int(HDBFlags *f, const char *s)
+{
+ int ret;
+ unsigned int tmp;
+
+ ret = parse_integer (&tmp, s);
+ if (ret == 1)
+ *f = int2HDBFlags (tmp);
+ return ret;
+}
+
+static int
+parse_generation(char *str, GENERATION **gen)
+{
+ char *p;
+ int v;
+
+ if(strcmp(str, "-") == 0 || *str == '\0') {
+ *gen = NULL;
+ return 0;
+ }
+ *gen = calloc(1, sizeof(**gen));
+
+ p = strsep(&str, ":");
+ if(parse_time_string(&(*gen)->time, p) != 1)
+ return -1;
+ p = strsep(&str, ":");
+ if(sscanf(p, "%d", &v) != 1)
+ return -1;
+ (*gen)->usec = v;
+ p = strsep(&str, ":");
+ if(sscanf(p, "%d", &v) != 1)
+ return -1;
+ (*gen)->gen = v - 1; /* XXX gets bumped in _hdb_store */
+ return 0;
+}
+
+/* On error modify strp to point to the problem element */
+static int
+parse_extensions(char **strp, HDB_extensions **e)
+{
+ char *str = *strp;
+ char *p;
+ int ret;
+
+ if(strcmp(str, "-") == 0 || *str == '\0') {
+ *e = NULL;
+ return 0;
+ }
+ *e = calloc(1, sizeof(**e));
+
+ p = strsep(&str, ":");
+
+ while (p) {
+ HDB_extension ext;
+ ssize_t len;
+ void *d;
+
+ len = strlen(p);
+ d = emalloc(len);
+
+ len = hex_decode(p, d, len);
+ if (len < 0) {
+ free(d);
+ *strp = p;
+ return -1;
+ }
+
+ ret = decode_HDB_extension(d, len, &ext, NULL);
+ free(d);
+ if (ret) {
+ *strp = p;
+ return -1;
+ }
+ d = realloc((*e)->val, ((*e)->len + 1) * sizeof((*e)->val[0]));
+ if (d == NULL)
+ abort();
+ (*e)->val = d;
+ (*e)->val[(*e)->len] = ext;
+ (*e)->len++;
+
+ p = strsep(&str, ":");
+ }
+
+ return 0;
+}
+
+/* XXX: Principal names with '\n' cannot be dumped or loaded */
+static int
+my_fgetln(FILE *f, char **bufp, size_t *szp, size_t *lenp)
+{
+ size_t len;
+ size_t sz = *szp;
+ char *buf = *bufp;
+ char *n;
+
+ if (!buf) {
+ buf = malloc(sz ? sz : 8192);
+ if (!buf)
+ return ENOMEM;
+ if (!sz)
+ sz = 8192;
+ }
+
+ len = 0;
+ while (fgets(&buf[len], sz-len, f) != NULL) {
+ len += strlen(&buf[len]);
+ if (buf[len-1] == '\n')
+ break;
+ if (feof(f))
+ break;
+ if (sz > SIZE_MAX/2 ||
+ (n = realloc(buf, sz += 1 + (sz >> 1))) == NULL) {
+ free(buf);
+ *bufp = NULL;
+ *szp = 0;
+ *lenp = 0;
+ return ENOMEM;
+ }
+ buf = n;
+ }
+ *bufp = buf;
+ *szp = sz;
+ *lenp = len;
+ return 0; /* *len == 0 || no EOL -> EOF */
+}
+
+/*
+ * Parse the dump file in `filename' and create the database (merging
+ * iff merge)
+ */
+
+static int
+doit(const char *filename, int mergep)
+{
+ krb5_error_code ret = 0;
+ krb5_error_code ret2 = 0;
+ FILE *f;
+ char *line = NULL;
+ size_t linesz = 0;
+ size_t linelen = 0;
+ char *p;
+ int lineno;
+ int flags = O_RDWR;
+ struct entry e;
+ hdb_entry ent;
+ HDB *db = _kadm5_s_get_db(kadm_handle);
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ krb5_warn(context, errno, "fopen(%s)", filename);
+ return 1;
+ }
+ /*
+ * We don't have a version number in the dump, so we don't know which iprop
+ * log entries to keep, if any. We throw the log away.
+ *
+ * We could merge the ipropd-master/slave dump/load here as an option, in
+ * which case we would first load the dump.
+ *
+ * If we're merging, first recover unconfirmed records in the existing log.
+ */
+ if (mergep)
+ ret = kadm5_log_init(kadm_handle);
+ if (ret == 0)
+ ret = kadm5_log_reinit(kadm_handle, 0);
+ if (ret) {
+ fclose (f);
+ krb5_warn(context, ret, "kadm5_log_reinit");
+ return 1;
+ }
+
+ if (!mergep)
+ flags |= O_CREAT | O_TRUNC;
+ ret = db->hdb_open(context, db, flags, 0600);
+ if (ret){
+ krb5_warn(context, ret, "hdb_open");
+ fclose(f);
+ return 1;
+ }
+ (void) db->hdb_set_sync(context, db, 0);
+ for (lineno = 1;
+ (ret2 = my_fgetln(f, &line, &linesz, &linelen)) == 0 && linelen > 0;
+ ++lineno) {
+ p = line;
+ while (isspace((unsigned char)*p))
+ p++;
+
+ e.principal = p;
+ for (p = line; *p; p++){
+ if (*p == '\\') /* Support '\n' escapes??? */
+ p++;
+ else if (isspace((unsigned char)*p)) {
+ *p = 0;
+ break;
+ }
+ }
+ p = skip_next(p);
+
+ e.key = p;
+ p = skip_next(p);
+
+ e.created = p;
+ p = skip_next(p);
+
+ e.modified = p;
+ p = skip_next(p);
+
+ e.valid_start = p;
+ p = skip_next(p);
+
+ e.valid_end = p;
+ p = skip_next(p);
+
+ e.pw_end = p;
+ p = skip_next(p);
+
+ e.max_life = p;
+ p = skip_next(p);
+
+ e.max_renew = p;
+ p = skip_next(p);
+
+ e.flags = p;
+ p = skip_next(p);
+
+ e.generation = p;
+ p = skip_next(p);
+
+ e.extensions = p;
+ skip_next(p);
+
+ memset(&ent, 0, sizeof(ent));
+ ret2 = krb5_parse_name(context, e.principal, &ent.principal);
+ if (ret2) {
+ const char *msg = krb5_get_error_message(context, ret);
+ fprintf(stderr, "%s:%d:%s (%s)\n",
+ filename, lineno, msg, e.principal);
+ krb5_free_error_message(context, msg);
+ ret = 1;
+ continue;
+ }
+
+ if (parse_keys(&ent, e.key)) {
+ fprintf (stderr, "%s:%d:error parsing keys (%s)\n",
+ filename, lineno, e.key);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+
+ if (parse_event(&ent.created_by, e.created) == -1) {
+ fprintf (stderr, "%s:%d:error parsing created event (%s)\n",
+ filename, lineno, e.created);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+ if (parse_event_alloc (&ent.modified_by, e.modified) == -1) {
+ fprintf (stderr, "%s:%d:error parsing event (%s)\n",
+ filename, lineno, e.modified);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+ if (parse_time_string_alloc (&ent.valid_start, e.valid_start) == -1) {
+ fprintf (stderr, "%s:%d:error parsing time (%s)\n",
+ filename, lineno, e.valid_start);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+ if (parse_time_string_alloc (&ent.valid_end, e.valid_end) == -1) {
+ fprintf (stderr, "%s:%d:error parsing time (%s)\n",
+ filename, lineno, e.valid_end);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+ if (parse_time_string_alloc (&ent.pw_end, e.pw_end) == -1) {
+ fprintf (stderr, "%s:%d:error parsing time (%s)\n",
+ filename, lineno, e.pw_end);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+
+ if (parse_integer_alloc (&ent.max_life, e.max_life) == -1) {
+ fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n",
+ filename, lineno, e.max_life);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+
+ }
+ if (parse_integer_alloc (&ent.max_renew, e.max_renew) == -1) {
+ fprintf (stderr, "%s:%d:error parsing lifetime (%s)\n",
+ filename, lineno, e.max_renew);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+
+ if (parse_hdbflags2int (&ent.flags, e.flags) != 1) {
+ fprintf (stderr, "%s:%d:error parsing flags (%s)\n",
+ filename, lineno, e.flags);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+
+ if(parse_generation(e.generation, &ent.generation) == -1) {
+ fprintf (stderr, "%s:%d:error parsing generation (%s)\n",
+ filename, lineno, e.generation);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+
+ if (parse_extensions(&e.extensions, &ent.extensions) == -1) {
+ fprintf (stderr, "%s:%d:error parsing extension (%s)\n",
+ filename, lineno, e.extensions);
+ hdb_free_entry (context, db, &ent);
+ ret = 1;
+ continue;
+ }
+
+ ret2 = db->hdb_store(context, db, HDB_F_REPLACE, &ent);
+ hdb_free_entry (context, db, &ent);
+ if (ret2) {
+ krb5_warn(context, ret2, "db_store");
+ break;
+ }
+ }
+ free(line);
+ if (ret2)
+ ret = ret2;
+ ret2 = db->hdb_set_sync(context, db, 1);
+ if (ret2) {
+ krb5_err(context, 1, ret2, "failed to sync the HDB");
+ ret = ret2;
+ }
+ (void) kadm5_log_end(kadm_handle);
+ ret2 = db->hdb_close(context, db);
+ if (ret2)
+ ret = ret2;
+ fclose(f);
+ return ret != 0;
+}
+
+
+extern int local_flag;
+
+static int
+loadit(int mergep, const char *name, int argc, char **argv)
+{
+ if(!local_flag) {
+ krb5_warnx(context, "%s is only available in local (-l) mode", name);
+ return 0;
+ }
+
+ return doit(argv[0], mergep);
+}
+
+int
+load(void *opt, int argc, char **argv)
+{
+ return loadit(0, "load", argc, argv);
+}
+
+int
+merge(void *opt, int argc, char **argv)
+{
+ return loadit(1, "merge", argc, argv);
+}
diff --git a/third_party/heimdal/kadmin/mod.c b/third_party/heimdal/kadmin/mod.c
new file mode 100644
index 0000000..2d4bd5d
--- /dev/null
+++ b/third_party/heimdal/kadmin/mod.c
@@ -0,0 +1,853 @@
+/*
+ * 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+void
+add_tl(kadm5_principal_ent_rec *princ, int type, krb5_data *data)
+{
+ krb5_tl_data *tl, **ptl;
+
+ tl = ecalloc(1, sizeof(*tl));
+ tl->tl_data_next = NULL;
+ tl->tl_data_type = type;
+ tl->tl_data_length = data->length;
+ tl->tl_data_contents = data->data;
+
+ if (tl->tl_data_length < 0 || data->length != (size_t)tl->tl_data_length)
+ errx(1, "TL data overflow");
+
+ princ->n_tl_data++;
+ ptl = &princ->tl_data;
+ while (*ptl != NULL)
+ ptl = &(*ptl)->tl_data_next;
+ *ptl = tl;
+
+ return;
+}
+
+/*
+ * Find a TL data of type KRB5_TL_EXTENSION that has an extension of type
+ * `etype' in it.
+ */
+krb5_tl_data *
+get_tl(kadm5_principal_ent_rec *princ, int type)
+{
+ krb5_tl_data *tl = princ->tl_data;
+
+ while (tl && tl->tl_data_type != type)
+ tl = tl->tl_data_next;
+ return tl;
+}
+
+static void
+add_constrained_delegation(krb5_context contextp,
+ kadm5_principal_ent_rec *princ,
+ struct getarg_strings *strings)
+{
+ krb5_error_code ret;
+ HDB_extension ext;
+ krb5_data buf;
+ size_t size = 0;
+
+ memset(&ext, 0, sizeof(ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_allowed_to_delegate_to;
+
+ if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
+ ext.data.u.allowed_to_delegate_to.val = NULL;
+ ext.data.u.allowed_to_delegate_to.len = 0;
+ } else {
+ krb5_principal p;
+ int i;
+
+ ext.data.u.allowed_to_delegate_to.val =
+ calloc(strings->num_strings,
+ sizeof(ext.data.u.allowed_to_delegate_to.val[0]));
+ ext.data.u.allowed_to_delegate_to.len = strings->num_strings;
+
+ for (i = 0; i < strings->num_strings; i++) {
+ ret = krb5_parse_name(contextp, strings->strings[i], &p);
+ if (ret)
+ abort();
+ ret = copy_Principal(p, &ext.data.u.allowed_to_delegate_to.val[i]);
+ if (ret)
+ abort();
+ krb5_free_principal(contextp, p);
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
+ &ext, &size, ret);
+ free_HDB_extension(&ext);
+ if (ret)
+ abort();
+ if (buf.length != size)
+ abort();
+
+ add_tl(princ, KRB5_TL_EXTENSION, &buf);
+}
+
+static void
+add_aliases(krb5_context contextp, kadm5_principal_ent_rec *princ,
+ struct getarg_strings *strings)
+{
+ krb5_error_code ret = 0;
+ HDB_extension ext;
+ krb5_data buf;
+ krb5_principal p;
+ size_t size = 0;
+ int i;
+
+ memset(&ext, 0, sizeof(ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_aliases;
+ ext.data.u.aliases.case_insensitive = 0;
+
+ if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
+ ext.data.u.aliases.aliases.val = NULL;
+ ext.data.u.aliases.aliases.len = 0;
+ } else {
+ ext.data.u.aliases.aliases.val =
+ calloc(strings->num_strings,
+ sizeof(ext.data.u.aliases.aliases.val[0]));
+ ext.data.u.aliases.aliases.len = strings->num_strings;
+
+ for (i = 0; ret == 0 && i < strings->num_strings; i++) {
+ ret = krb5_parse_name(contextp, strings->strings[i], &p);
+ if (ret)
+ krb5_err(contextp, 1, ret, "Could not parse alias %s",
+ strings->strings[i]);
+ if (ret == 0)
+ ret = copy_Principal(p, &ext.data.u.aliases.aliases.val[i]);
+ if (ret)
+ krb5_err(contextp, 1, ret, "Could not copy parsed alias %s",
+ strings->strings[i]);
+ krb5_free_principal(contextp, p);
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
+ &ext, &size, ret);
+ free_HDB_extension(&ext);
+ if (ret)
+ abort();
+ if (buf.length != size)
+ abort();
+
+ add_tl(princ, KRB5_TL_EXTENSION, &buf);
+}
+
+static void
+add_pkinit_acl(krb5_context contextp, kadm5_principal_ent_rec *princ,
+ struct getarg_strings *strings)
+{
+ krb5_error_code ret;
+ HDB_extension ext;
+ krb5_data buf;
+ size_t size = 0;
+ int i;
+
+ memset(&ext, 0, sizeof(ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_pkinit_acl;
+ ext.data.u.aliases.case_insensitive = 0;
+
+ if (strings->num_strings == 1 && strings->strings[0][0] == '\0') {
+ ext.data.u.pkinit_acl.val = NULL;
+ ext.data.u.pkinit_acl.len = 0;
+ } else {
+ ext.data.u.pkinit_acl.val =
+ calloc(strings->num_strings,
+ sizeof(ext.data.u.pkinit_acl.val[0]));
+ ext.data.u.pkinit_acl.len = strings->num_strings;
+
+ for (i = 0; i < strings->num_strings; i++) {
+ ext.data.u.pkinit_acl.val[i].subject = estrdup(strings->strings[i]);
+ }
+ }
+
+ ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
+ &ext, &size, ret);
+ free_HDB_extension(&ext);
+ if (ret)
+ abort();
+ if (buf.length != size)
+ abort();
+
+ add_tl(princ, KRB5_TL_EXTENSION, &buf);
+}
+
+static krb5_error_code
+add_etypes(krb5_context contextp,
+ kadm5_principal_ent_rec *princ,
+ struct getarg_strings *strings)
+{
+ krb5_error_code ret = 0;
+ HDB_EncTypeList etypes;
+ krb5_data buf;
+ size_t i, size;
+
+ etypes.len = strings->num_strings;
+ if ((etypes.val = calloc(strings->num_strings,
+ sizeof(etypes.val[0]))) == NULL)
+ krb5_err(contextp, 1, ret, "Out of memory");
+
+ for (i = 0; i < strings->num_strings; i++) {
+ krb5_enctype etype;
+
+ ret = krb5_string_to_enctype(contextp, strings->strings[i], &etype);
+ if (ret) {
+ krb5_warn(contextp, ret, "Could not parse enctype %s",
+ strings->strings[i]);
+ free(etypes.val);
+ return ret;
+ }
+ etypes.val[i] = etype;
+ }
+
+ if (ret == 0) {
+ ASN1_MALLOC_ENCODE(HDB_EncTypeList, buf.data, buf.length,
+ &etypes, &size, ret);
+ }
+ if (ret || buf.length != size)
+ abort();
+ add_tl(princ, KRB5_TL_ETYPES, &buf);
+ free(etypes.val);
+ return 0;
+}
+
+static void
+add_kvno_diff(krb5_context contextp, kadm5_principal_ent_rec *princ,
+ int is_svc_diff, krb5_kvno kvno_diff)
+{
+ krb5_error_code ret;
+ HDB_extension ext;
+ krb5_data buf;
+ size_t size = 0;
+
+ if (kvno_diff < 0)
+ return;
+ if (kvno_diff > 2048)
+ kvno_diff = 2048;
+
+ ext.mandatory = 0;
+ if (is_svc_diff) {
+ ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
+ ext.data.u.hist_kvno_diff_svc = (unsigned int)kvno_diff;
+ } else {
+ ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
+ ext.data.u.hist_kvno_diff_clnt = (unsigned int)kvno_diff;
+ }
+ ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
+ &ext, &size, ret);
+ if (ret)
+ abort();
+ if (buf.length != size)
+ abort();
+
+ add_tl(princ, KRB5_TL_EXTENSION, &buf);
+}
+
+static void
+add_krb5_config(kadm5_principal_ent_rec *princ, const char *fname)
+{
+ HDB_extension ext;
+ krb5_data buf;
+ size_t size;
+ int ret;
+
+ memset(&ext, 0, sizeof(ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_krb5_config;
+
+ if ((ret = rk_undumpdata(fname,
+ &ext.data.u.krb5_config.data,
+ &ext.data.u.krb5_config.length))) {
+ krb5_warn(context, ret, "Could not read %s", fname);
+ return;
+ }
+
+ ASN1_MALLOC_ENCODE(HDB_extension, buf.data, buf.length,
+ &ext, &size, ret);
+ free_HDB_extension(&ext);
+ if (ret)
+ abort();
+ if (buf.length != size)
+ abort();
+ add_tl(princ, KRB5_TL_EXTENSION, &buf);
+}
+
+struct mod_data {
+ struct modify_namespace_key_rotation_options *opt_ns_kr;
+ struct modify_namespace_options *opt_ns;
+ struct modify_options *opt;
+ void *kadm_handle;
+};
+
+static int
+do_mod_entry(krb5_principal principal, void *data)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ;
+ int mask = 0;
+ struct mod_data *m = data;
+ struct modify_options *e = m->opt;
+
+ memset (&princ, 0, sizeof(princ));
+ ret = kadm5_get_principal(m->kadm_handle, principal, &princ,
+ KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
+ KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
+ KADM5_PRINC_EXPIRE_TIME |
+ KADM5_PW_EXPIRATION);
+ if(ret)
+ return ret;
+
+ if(e->max_ticket_life_string ||
+ e->max_renewable_life_string ||
+ e->expiration_time_string ||
+ e->pw_expiration_time_string ||
+ e->attributes_string ||
+ e->policy_string ||
+ e->kvno_integer != -1 ||
+ e->service_enctypes_strings.num_strings ||
+ e->constrained_delegation_strings.num_strings ||
+ e->alias_strings.num_strings ||
+ e->pkinit_acl_strings.num_strings ||
+ e->krb5_config_file_string ||
+ e->hist_kvno_diff_clnt_integer != -1 ||
+ e->hist_kvno_diff_svc_integer != -1) {
+ ret = set_entry(context, &princ, &mask,
+ e->max_ticket_life_string,
+ e->max_renewable_life_string,
+ e->expiration_time_string,
+ e->pw_expiration_time_string,
+ e->attributes_string,
+ e->policy_string);
+ if(e->kvno_integer != -1) {
+ princ.kvno = e->kvno_integer;
+ mask |= KADM5_KVNO;
+ }
+ if (e->constrained_delegation_strings.num_strings) {
+ add_constrained_delegation(context, &princ,
+ &e->constrained_delegation_strings);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->alias_strings.num_strings) {
+ add_aliases(context, &princ, &e->alias_strings);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->pkinit_acl_strings.num_strings) {
+ add_pkinit_acl(context, &princ, &e->pkinit_acl_strings);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->service_enctypes_strings.num_strings) {
+ ret = add_etypes(context, &princ, &e->service_enctypes_strings);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->hist_kvno_diff_clnt_integer != -1) {
+ add_kvno_diff(context, &princ, 0, e->hist_kvno_diff_clnt_integer);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->hist_kvno_diff_svc_integer != -1) {
+ add_kvno_diff(context, &princ, 1, e->hist_kvno_diff_svc_integer);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->krb5_config_file_string) {
+ add_krb5_config(&princ, e->krb5_config_file_string);
+ mask |= KADM5_TL_DATA;
+ }
+ } else
+ ret = edit_entry(&princ, &mask, NULL, 0);
+ if(ret == 0) {
+ ret = kadm5_modify_principal(m->kadm_handle, &princ, mask);
+ if(ret)
+ krb5_warn(context, ret, "kadm5_modify_principal");
+ }
+
+ kadm5_free_principal_ent(m->kadm_handle, &princ);
+ return ret;
+}
+
+int
+mod_entry(struct modify_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ struct mod_data data;
+ int i;
+
+ data.kadm_handle = NULL;
+ data.opt_ns_kr = NULL;
+ data.opt_ns = NULL;
+ data.opt = opt;
+
+ ret = kadm5_dup_context(kadm_handle, &data.kadm_handle);
+ for (i = 0; ret == 0 && i < argc; i++)
+ ret = foreach_principal(argv[i], do_mod_entry, "mod", &data);
+ if (data.kadm_handle)
+ kadm5_destroy(data.kadm_handle);
+ return ret != 0;
+}
+
+static int
+do_mod_ns_entry(krb5_principal principal, void *data)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ;
+ int mask = 0;
+ struct mod_data *m = data;
+ struct modify_namespace_options *e = m->opt_ns;
+
+ memset (&princ, 0, sizeof(princ));
+ ret = kadm5_get_principal(m->kadm_handle, principal, &princ,
+ KADM5_PRINCIPAL | KADM5_ATTRIBUTES |
+ KADM5_MAX_LIFE | KADM5_MAX_RLIFE |
+ KADM5_PRINC_EXPIRE_TIME |
+ KADM5_PW_EXPIRATION);
+ if(ret)
+ return ret;
+
+ if(e->max_ticket_life_string ||
+ e->max_renewable_life_string ||
+ e->attributes_string ||
+ e->enctypes_strings.num_strings ||
+ e->krb5_config_file_string) {
+ ret = set_entry(context, &princ, &mask, e->max_ticket_life_string,
+ e->max_renewable_life_string, NULL, NULL,
+ e->attributes_string, NULL);
+ if (e->enctypes_strings.num_strings) {
+ ret = add_etypes(context, &princ, &e->enctypes_strings);
+ mask |= KADM5_TL_DATA;
+ }
+ if (e->krb5_config_file_string) {
+ add_krb5_config(&princ, e->krb5_config_file_string);
+ mask |= KADM5_TL_DATA;
+ }
+ } else
+ ret = edit_entry(&princ, &mask, NULL, 0);
+ if(ret == 0) {
+ ret = kadm5_modify_principal(m->kadm_handle, &princ, mask);
+ if(ret)
+ krb5_warn(context, ret, "kadm5_modify_principal");
+ }
+
+ kadm5_free_principal_ent(m->kadm_handle, &princ);
+ return ret;
+}
+
+int
+modify_namespace(struct modify_namespace_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ struct mod_data data;
+ int i;
+
+ data.kadm_handle = NULL;
+ data.opt_ns_kr = NULL;
+ data.opt_ns = opt;
+ data.opt = NULL;
+
+ ret = kadm5_dup_context(kadm_handle, &data.kadm_handle);
+ for (i = 0; ret == 0 && i < argc; i++)
+ ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", &data);
+ if (data.kadm_handle)
+ kadm5_destroy(data.kadm_handle);
+ return ret != 0;
+}
+
+#if 0
+struct modify_namespace_key_rotation_options {
+ int force_flag;
+ int keep_base_key_flag;
+ char* revoke_old_string;
+ char* new_key_rotation_epoch_string;
+ char* new_key_rotation_period_string;
+};
+#endif
+
+static int
+princ2kstuple(kadm5_principal_ent_rec *princ,
+ unsigned int kvno,
+ krb5_key_salt_tuple **kstuple,
+ size_t *nkstuple)
+{
+ krb5_error_code ret = 0;
+ HDB_EncTypeList etypes;
+ krb5_tl_data *tl;
+ size_t i;
+
+ *kstuple = 0;
+ *nkstuple = 0;
+ etypes.len = 0;
+ etypes.val = 0;
+ for (tl = princ->tl_data; tl; tl = tl->tl_data_next) {
+ if (tl->tl_data_type != KRB5_TL_ETYPES || tl->tl_data_length < 0)
+ continue;
+ ret = decode_HDB_EncTypeList(tl->tl_data_contents, tl->tl_data_length,
+ &etypes, NULL);
+ if (ret)
+ break;
+ *nkstuple = etypes.len;
+ *kstuple = ecalloc(etypes.len, sizeof(kstuple[0][0]));
+ for (i = 0; i < etypes.len; i++) {
+ (*kstuple)[i].ks_enctype = etypes.val[i];
+ (*kstuple)[i].ks_salttype = 0;
+ }
+ return 0;
+ }
+ if (princ->n_key_data > 0) {
+ *kstuple = ecalloc(1, sizeof(kstuple[0][0]));
+ *nkstuple = 1;
+ for (i = 0; i < princ->n_key_data; i++) {
+ if (princ->key_data->key_data_kvno == kvno) {
+ (*kstuple)[0].ks_enctype = princ->key_data->key_data_type[0];
+ (*kstuple)[0].ks_salttype = princ->key_data->key_data_type[1];
+ return 0;
+ }
+ }
+ }
+ krb5_warnx(context, "Could not determine what enctypes to generate "
+ "keys for; recreate namespace?");
+ return EINVAL;
+}
+
+static int
+randkey_kr(kadm5_principal_ent_rec *princ,
+ unsigned int old_kvno,
+ unsigned int kvno)
+{
+ krb5_key_salt_tuple *kstuple = 0;
+ krb5_error_code ret = 0;
+ size_t nkstuple = 0;
+
+ /*
+ * We might be using kadm5clnt, so we'll use kadm5_randkey_principal_3(),
+ * which will generate new keys on the server side. This allows a race,
+ * but it will be detected by the key rotation update checks in lib/kadm5
+ * and lib/hdb.
+ */
+ ret = princ2kstuple(princ, old_kvno, &kstuple, &nkstuple);
+ if (ret == 0)
+ ret = kadm5_randkey_principal_3(kadm_handle, princ->principal, 1,
+ nkstuple, kstuple, NULL, NULL);
+ free(kstuple);
+ return ret;
+}
+
+static int
+do_mod_ns_kr(krb5_principal principal, void *data)
+{
+ krb5_error_code ret;
+ kadm5_principal_ent_rec princ;
+ struct modify_namespace_key_rotation_options *e = data;
+ HDB_Ext_KeyRotation existing;
+ HDB_Ext_KeyRotation new_kr;
+ HDB_extension ext;
+ KeyRotation new_krs[3];
+ krb5_tl_data *tl;
+ krb5_data d;
+ time_t now = time(NULL);
+ size_t size;
+ int freeit = 0;
+
+ d.data = 0;
+ d.length = 0;
+ new_kr.len = 0;
+ new_kr.val = new_krs;
+ ext.mandatory = 0;
+ ext.data.element = choice_HDB_extension_data_key_rotation;
+ ext.data.u.key_rotation.len = 0;
+ ext.data.u.key_rotation.val = 0;
+ existing.len = 0;
+ existing.val = 0;
+ memset(&new_krs, 0, sizeof(new_krs));
+ memset(&princ, 0, sizeof(princ));
+
+ if (e->force_flag || e->revoke_old_string) {
+ krb5_warnx(context, "--force and --revoke-old not implemented yet");
+ return ENOTSUP;
+ }
+
+ ret = kadm5_get_principal(kadm_handle, principal, &princ,
+ KADM5_PRINCIPAL | KADM5_KVNO |
+ KADM5_KEY_DATA | KADM5_TL_DATA);
+ if (ret == 0) {
+ freeit = 1;
+ for (tl = princ.tl_data; tl; tl = tl->tl_data_next) {
+ if (tl->tl_data_type != KRB5_TL_KRB5_CONFIG)
+ continue;
+ ret = decode_HDB_Ext_KeyRotation(tl->tl_data_contents,
+ tl->tl_data_length, &existing, NULL);
+ if (ret) {
+ krb5_warn(context, ret, "unable to decode existing key "
+ "rotation schedule");
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ return ret;
+ }
+ }
+ if (!existing.len) {
+ krb5_warnx(context, "no key rotation schedule; "
+ "re-create namespace?");
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ return EINVAL;
+ }
+ }
+
+ if (ret) {
+ krb5_warn(context, ret, "No such namespace");
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ return ret;
+ }
+
+ if (existing.len > 1)
+ new_kr.val[1] = existing.val[0];
+ if (existing.len > 2)
+ new_kr.val[2] = existing.val[1];
+ new_kr.val[0].flags = existing.val[0].flags;
+ new_kr.val[0].base_kvno = princ.kvno + 2; /* XXX Compute better */
+ new_kr.val[0].base_key_kvno = existing.val[0].base_key_kvno + 1;
+ if (e->new_key_rotation_epoch_string) {
+ if ((ret = str2time_t(e->new_key_rotation_epoch_string,
+ &new_kr.val[0].epoch)))
+ krb5_warn(context, ret, "Invalid epoch specification: %s",
+ e->new_key_rotation_epoch_string);
+ } else {
+ new_kr.val[0].epoch = existing.val[0].epoch +
+ existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno);
+ }
+ if (ret == 0 && e->new_key_rotation_period_string) {
+ time_t t;
+
+ if ((ret = str2time_t(e->new_key_rotation_period_string, &t)))
+ krb5_warn(context, ret, "Invalid period specification: %s",
+ e->new_key_rotation_period_string);
+ else
+ new_kr.val[0].period = t;
+ } else {
+ new_kr.val[0].period = existing.val[0].period +
+ existing.val[0].period * (princ.kvno - new_kr.val[0].base_kvno);
+ }
+ if (new_kr.val[0].epoch < now) {
+ krb5_warnx(context, "New epoch cannot be in the past");
+ ret = EINVAL;
+ }
+ if (new_kr.val[0].epoch < 30) {
+ krb5_warnx(context, "New period cannot be less than 30s");
+ ret = EINVAL;
+ }
+ if (ret == 0)
+ ret = randkey_kr(&princ, princ.kvno, new_kr.val[0].base_key_kvno);
+ ext.data.u.key_rotation = new_kr;
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length,
+ &ext, &size, ret);
+ if (ret == 0)
+ add_tl(&princ, KRB5_TL_EXTENSION, &d);
+ if (ret == 0) {
+ ret = kadm5_modify_principal(kadm_handle, &princ,
+ KADM5_PRINCIPAL | KADM5_TL_DATA);
+ if (ret)
+ krb5_warn(context, ret, "Could not update namespace");
+ }
+
+ krb5_data_free(&d);
+ free_HDB_Ext_KeyRotation(&existing);
+ if (freeit)
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ return ret;
+}
+
+int
+modify_ns_kr(struct modify_namespace_key_rotation_options *opt,
+ int argc,
+ char **argv)
+{
+ krb5_error_code ret = 0;
+ struct mod_data data;
+ int i;
+
+ data.kadm_handle = NULL;
+ data.opt_ns_kr = opt;
+ data.opt_ns = NULL;
+ data.opt = NULL;
+
+ ret = kadm5_dup_context(kadm_handle, &data.kadm_handle);
+ for (i = 0; ret == 0 && i < argc; i++)
+ ret = foreach_principal(argv[i], do_mod_ns_kr, "mod_ns", opt);
+ if (data.kadm_handle)
+ kadm5_destroy(data.kadm_handle);
+ return ret != 0;
+}
+
+#define princ_realm(P) ((P)->realm)
+#define princ_num_comp(P) ((P)->name.name_string.len)
+#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
+
+static int
+princ_cmp(const void *a, const void *b)
+{
+ krb5_const_principal pa = a;
+ krb5_const_principal pb = b;
+ size_t i;
+ int r;
+
+ r = strcmp(princ_realm(pa), princ_realm(pb));
+ if (r == 0)
+ r = princ_num_comp(pa) - princ_num_comp(pb);
+ for (i = 0; r == 0 && i < princ_num_comp(pa); i++)
+ r = strcmp(princ_ncomp(pa, i), princ_ncomp(pb, i));
+ return r;
+}
+
+/* Sort and remove dups */
+static void
+uniq(HDB_Ext_Aliases *a)
+{
+ size_t i = 0;
+
+ qsort(a->aliases.val, a->aliases.len, sizeof(a->aliases.val[0]),
+ princ_cmp);
+
+ /* While there are at least two principals left to look at... */
+ while (i + 1 < a->aliases.len) {
+ if (princ_cmp(&a->aliases.val[i], &a->aliases.val[i + 1])) {
+ /* ...if they are different, increment i and loop */
+ i++;
+ continue;
+ }
+ /* ...else drop the one on the right and loop w/o incrementing i */
+ free_Principal(&a->aliases.val[i + 1]);
+ if (i + 2 < a->aliases.len)
+ memmove(&a->aliases.val[i + 1],
+ &a->aliases.val[i + 2],
+ sizeof(a->aliases.val[i + 1]) * (a->aliases.len - (i + 2)));
+ a->aliases.len--;
+ }
+}
+
+int
+add_alias(void *opt, int argc, char **argv)
+{
+ kadm5_principal_ent_rec princ;
+ krb5_error_code ret;
+ krb5_principal p = NULL;
+ HDB_Ext_Aliases *a;
+ HDB_extension ext;
+ krb5_tl_data *tl = NULL;
+ krb5_data d;
+ size_t i;
+
+ memset(&princ, 0, sizeof(princ));
+ krb5_data_zero(&d);
+
+ if (argc < 2) {
+ krb5_warnx(context, "Principal not given");
+ return 1;
+ }
+ ret = krb5_parse_name(context, argv[0], &p);
+ if (ret) {
+ krb5_warn(context, ret, "Invalid principal: %s", argv[0]);
+ return 1;
+ }
+
+ ret = kadm5_get_principal(kadm_handle, p, &princ,
+ KADM5_PRINCIPAL_NORMAL_MASK | KADM5_TL_DATA);
+ if (ret) {
+ krb5_warn(context, ret, "Principal not found %s", argv[0]);
+ return 1;
+ }
+ krb5_free_principal(context, p);
+ p = NULL;
+
+ a = &ext.data.u.aliases;
+ a->case_insensitive = 0;
+ a->aliases.len = 0;
+ a->aliases.val = 0;
+ if ((tl = get_tl(&princ, KRB5_TL_ALIASES))) {
+ ret = decode_HDB_Ext_Aliases(tl->tl_data_contents, tl->tl_data_length,
+ a, NULL);
+ if (ret) {
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ krb5_warn(context, ret, "Principal has invalid aliases extension "
+ "contents: %s", argv[0]);
+ return 1;
+ }
+ }
+
+ argv++;
+ argc--;
+
+ a->aliases.val = realloc(a->aliases.val,
+ sizeof(a->aliases.val[0]) * (a->aliases.len + argc));
+ if (a->aliases.val == NULL)
+ krb5_err(context, 1, errno, "Out of memory");
+ for (i = 0; ret == 0 && i < argc; i++) {
+ ret = krb5_parse_name(context, argv[i], &p);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_parse_name");
+ break;
+ }
+ ret = copy_Principal(p, &a->aliases.val[a->aliases.len]);
+ krb5_free_principal(context, p);
+ if (ret == 0)
+ a->aliases.len++;
+ }
+ uniq(a);
+
+ ext.data.element = choice_HDB_extension_data_aliases;
+ ext.mandatory = 0;
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(HDB_extension, d.data, d.length, &ext, &i, ret);
+ free_HDB_extension(&ext);
+ if (ret == 0) {
+ int16_t len = d.length;
+
+ if (len < 0 || d.length != (size_t)len) {
+ krb5_warnx(context, "Too many aliases; does not fit in 32767 bytes");
+ ret = EOVERFLOW;
+ }
+ }
+ if (ret == 0) {
+ add_tl(&princ, KRB5_TL_EXTENSION, &d);
+ krb5_data_zero(&d);
+ ret = kadm5_modify_principal(kadm_handle, &princ,
+ KADM5_PRINCIPAL | KADM5_TL_DATA);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_modify_principal");
+ }
+
+ kadm5_free_principal_ent(kadm_handle, &princ);
+ krb5_data_free(&d);
+ return ret == 0 ? 0 : 1;
+}
diff --git a/third_party/heimdal/kadmin/prune.c b/third_party/heimdal/kadmin/prune.c
new file mode 100644
index 0000000..69d14eb
--- /dev/null
+++ b/third_party/heimdal/kadmin/prune.c
@@ -0,0 +1,63 @@
+/*
+ * 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+int
+prune(struct prune_options *opt, int argc, char **argv)
+{
+ krb5_error_code ret = 0;
+ krb5_principal princ_ent = NULL;
+
+ if (argc == 0) {
+ krb5_warnx(context, "prune: missing principal name argument");
+ return 0;
+ }
+ if (argc > 1) {
+ krb5_warnx(context, "prune: too many arguments");
+ return 0;
+ }
+
+ ret = krb5_parse_name(context, argv[0], &princ_ent);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_parse_name %s", argv[0]);
+ goto out2;
+ }
+
+ ret = kadm5_prune_principal(kadm_handle, princ_ent, opt->kvno_integer);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_prune_principal");
+
+out2:
+ return ret != 0;
+}
diff --git a/third_party/heimdal/kadmin/pw_quality.c b/third_party/heimdal/kadmin/pw_quality.c
new file mode 100644
index 0000000..23b1362
--- /dev/null
+++ b/third_party/heimdal/kadmin/pw_quality.c
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+int
+password_quality(void *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_principal principal;
+ krb5_data pw_data;
+ const char *s;
+
+ ret = krb5_parse_name(context, argv[0], &principal);
+ if(ret){
+ krb5_warn(context, ret, "krb5_parse_name(%s)", argv[0]);
+ return 0;
+ }
+ pw_data.data = argv[1];
+ pw_data.length = strlen(argv[1]);
+
+ s = kadm5_check_password_quality (context, principal, &pw_data);
+ if (s)
+ krb5_warnx(context, "kadm5_check_password_quality: %s", s);
+
+ krb5_free_principal(context, principal);
+
+ return 0;
+}
diff --git a/third_party/heimdal/kadmin/random_password.c b/third_party/heimdal/kadmin/random_password.c
new file mode 100644
index 0000000..bf8bf8b
--- /dev/null
+++ b/third_party/heimdal/kadmin/random_password.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 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 "kadmin_locl.h"
+
+/* This file defines some a function that generates a random password,
+ that can be used when creating a large amount of principals (such
+ as for a batch of students). Since this is a political matter, you
+ should think about how secure generated passwords has to be.
+
+ Both methods defined here will give you at least 55 bits of
+ entropy.
+ */
+
+/* If you want OTP-style passwords, define OTP_STYLE */
+
+#ifdef OTP_STYLE
+#include <otp.h>
+#else
+static void generate_password(char **pw, int num_classes, ...);
+#endif
+
+void
+random_password(char *pw, size_t len)
+{
+#ifdef OTP_STYLE
+ {
+ OtpKey newkey;
+
+ krb5_generate_random_block(&newkey, sizeof(newkey));
+ otp_print_stddict (newkey, pw, len);
+ strlwr(pw);
+ }
+#else
+ char *pass;
+ generate_password(&pass, 3,
+ "abcdefghijklmnopqrstuvwxyz", 7,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ", 2,
+ "@$%&*()-+=:,/<>1234567890", 1);
+ strlcpy(pw, pass, len);
+ len = strlen(pass);
+ memset_s(pass, len, 0, len);
+ free(pass);
+#endif
+}
+
+/* some helper functions */
+
+#ifndef OTP_STYLE
+/* return a random value in range 0-127 */
+static int
+RND(unsigned char *key, int keylen, int *left)
+{
+ if(*left == 0){
+ krb5_generate_random_block(key, keylen);
+ *left = keylen;
+ }
+ (*left)--;
+ return ((unsigned char*)key)[*left];
+}
+
+/* This a helper function that generates a random password with a
+ number of characters from a set of character classes.
+
+ If there are n classes, and the size of each class is Pi, and the
+ number of characters from each class is Ni, the number of possible
+ passwords are (given that the character classes are disjoint):
+
+ n n
+ ----- / ---- \
+ | | Ni | \ |
+ | | Pi | \ Ni| !
+ | | ---- * | / |
+ | | Ni! | /___ |
+ i=1 \ i=1 /
+
+ Since it uses the RND function above, neither the size of each
+ class, nor the total length of the generated password should be
+ larger than 127 (without fixing RND).
+
+ */
+static void
+generate_password(char **pw, int num_classes, ...)
+{
+ struct {
+ const char *str;
+ int len;
+ int freq;
+ } *classes;
+ va_list ap;
+ int len, i;
+ unsigned char rbuf[8]; /* random buffer */
+ int rleft = 0;
+
+ *pw = NULL;
+
+ classes = malloc(num_classes * sizeof(*classes));
+ if(classes == NULL)
+ return;
+ va_start(ap, num_classes);
+ len = 0;
+ for(i = 0; i < num_classes; i++){
+ classes[i].str = va_arg(ap, const char*);
+ classes[i].len = strlen(classes[i].str);
+ classes[i].freq = va_arg(ap, int);
+ len += classes[i].freq;
+ }
+ va_end(ap);
+ *pw = malloc(len + 1);
+ if(*pw == NULL) {
+ free(classes);
+ return;
+ }
+ for(i = 0; i < len; i++) {
+ int j;
+ int x = RND(rbuf, sizeof(rbuf), &rleft) % (len - i);
+ int t = 0;
+ for(j = 0; j < num_classes; j++) {
+ if(x < t + classes[j].freq) {
+ (*pw)[i] = classes[j].str[RND(rbuf, sizeof(rbuf), &rleft)
+ % classes[j].len];
+ classes[j].freq--;
+ break;
+ }
+ t += classes[j].freq;
+ }
+ }
+ (*pw)[len] = '\0';
+ memset_s(rbuf, sizeof(rbuf), 0, sizeof(rbuf));
+ free(classes);
+}
+#endif
diff --git a/third_party/heimdal/kadmin/rename.c b/third_party/heimdal/kadmin/rename.c
new file mode 100644
index 0000000..cdd7de2
--- /dev/null
+++ b/third_party/heimdal/kadmin/rename.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+int
+rename_entry(void *opt, int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_principal princ1, princ2;
+
+ ret = krb5_parse_name(context, argv[0], &princ1);
+ if(ret){
+ krb5_warn(context, ret, "krb5_parse_name(%s)", argv[0]);
+ return ret != 0;
+ }
+ ret = krb5_parse_name(context, argv[1], &princ2);
+ if(ret){
+ krb5_free_principal(context, princ1);
+ krb5_warn(context, ret, "krb5_parse_name(%s)", argv[1]);
+ return ret != 0;
+ }
+ ret = kadm5_rename_principal(kadm_handle, princ1, princ2);
+ if(ret)
+ krb5_warn(context, ret, "rename");
+ krb5_free_principal(context, princ1);
+ krb5_free_principal(context, princ2);
+ return ret != 0;
+}
+
diff --git a/third_party/heimdal/kadmin/rpc.c b/third_party/heimdal/kadmin/rpc.c
new file mode 100644
index 0000000..8a176da
--- /dev/null
+++ b/third_party/heimdal/kadmin/rpc.c
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (c) 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 "kadmin_locl.h"
+
+#include <gssapi.h>
+#include <gssapi_krb5.h>
+#include <gssapi_spnego.h>
+
+#define CHECK(x) \
+ do { \
+ int __r; \
+ if ((__r = (x))) { \
+ krb5_errx(dcontext, 1, "Failed (%d) on %s:%d", \
+ __r, __FILE__, __LINE__); \
+ } \
+ } while(0)
+
+static krb5_context dcontext;
+
+#define INSIST(x) CHECK(!(x))
+
+#define VERSION2 0x12345702
+
+#define LAST_FRAGMENT 0x80000000
+
+#define RPC_VERSION 2
+#define KADM_SERVER 2112
+#define VVERSION 2
+#define FLAVOR_GSS 6
+#define FLAVOR_GSS_VERSION 1
+
+struct opaque_auth {
+ uint32_t flavor;
+ krb5_data data;
+};
+
+struct call_header {
+ uint32_t xid;
+ uint32_t rpcvers;
+ uint32_t prog;
+ uint32_t vers;
+ uint32_t proc;
+ struct opaque_auth cred;
+ struct opaque_auth verf;
+};
+
+enum {
+ RPG_DATA = 0,
+ RPG_INIT = 1,
+ RPG_CONTINUE_INIT = 2,
+ RPG_DESTROY = 3
+};
+
+enum {
+ rpg_privacy = 3
+};
+
+/*
+struct chrand_ret {
+ krb5_ui_4 api_version;
+ kadm5_ret_t ret;
+ int n_keys;
+ krb5_keyblock *keys;
+};
+*/
+
+
+struct gcred {
+ uint32_t version;
+ uint32_t proc;
+ uint32_t seq_num;
+ uint32_t service;
+ krb5_data handle;
+};
+
+static int
+parse_name(const unsigned char *p, size_t len,
+ const gss_OID oid, char **name)
+{
+ size_t l;
+
+ if (len < 4)
+ return 1;
+
+ /* TOK_ID */
+ if (memcmp(p, "\x04\x01", 2) != 0)
+ return 1;
+ len -= 2;
+ p += 2;
+
+ /* MECH_LEN */
+ l = (p[0] << 8) | p[1];
+ len -= 2;
+ p += 2;
+ if (l < 2 || len < l)
+ return 1;
+
+ /* oid wrapping */
+ if (p[0] != 6 || p[1] != l - 2)
+ return 1;
+ p += 2;
+ l -= 2;
+ len -= 2;
+
+ /* MECH */
+ if (l != oid->length || memcmp(p, oid->elements, oid->length) != 0)
+ return 1;
+ len -= l;
+ p += l;
+
+ /* MECHNAME_LEN */
+ if (len < 4)
+ return 1;
+ l = (unsigned long)p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
+ len -= 4;
+ p += 4;
+
+ /* MECH NAME */
+ if (len != l)
+ return 1;
+
+ *name = malloc(l + 1);
+ INSIST(*name != NULL);
+ memcpy(*name, p, l);
+ (*name)[l] = '\0';
+
+ return 0;
+}
+
+
+
+static void
+gss_error(krb5_context contextp,
+ gss_OID mech, OM_uint32 type, OM_uint32 error)
+{
+ OM_uint32 new_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ OM_uint32 ret;
+
+ do {
+ ret = gss_display_status (&new_stat,
+ error,
+ type,
+ mech,
+ &msg_ctx,
+ &status_string);
+ krb5_warnx(contextp, "%.*s",
+ (int)status_string.length,
+ (char *)status_string.value);
+ gss_release_buffer (&new_stat, &status_string);
+ } while (!GSS_ERROR(ret) && msg_ctx != 0);
+}
+
+static void
+gss_print_errors (krb5_context contextp,
+ OM_uint32 maj_stat, OM_uint32 min_stat)
+{
+ gss_error(contextp, GSS_C_NO_OID, GSS_C_GSS_CODE, maj_stat);
+ gss_error(contextp, GSS_C_NO_OID, GSS_C_MECH_CODE, min_stat);
+}
+
+static int
+read_data(krb5_storage *sp, krb5_storage *msg, size_t len)
+{
+ char buf[1024];
+
+ while (len) {
+ size_t tlen = len;
+ ssize_t slen;
+
+ if (tlen > sizeof(buf))
+ tlen = sizeof(buf);
+
+ slen = krb5_storage_read(sp, buf, tlen);
+ INSIST((size_t)slen == tlen);
+
+ slen = krb5_storage_write(msg, buf, tlen);
+ INSIST((size_t)slen == tlen);
+
+ len -= tlen;
+ }
+ return 0;
+}
+
+static int
+collect_framents(krb5_storage *sp, krb5_storage *msg)
+{
+ krb5_error_code ret;
+ uint32_t len;
+ int last_fragment;
+ size_t total_len = 0;
+
+ do {
+ ret = krb5_ret_uint32(sp, &len);
+ if (ret)
+ return ret;
+
+ last_fragment = (len & LAST_FRAGMENT);
+ len &= ~LAST_FRAGMENT;
+
+ CHECK(read_data(sp, msg, len));
+ total_len += len;
+
+ } while(!last_fragment || total_len == 0);
+
+ return 0;
+}
+
+static krb5_error_code
+store_data_xdr(krb5_storage *sp, krb5_data data)
+{
+ krb5_error_code ret;
+ size_t res;
+
+ ret = krb5_store_data(sp, data);
+ if (ret)
+ return ret;
+ res = 4 - (data.length % 4);
+ if (res != 4) {
+ static const char zero[4] = { 0, 0, 0, 0 };
+
+ ret = krb5_storage_write(sp, zero, res);
+ if((size_t)ret != res)
+ return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
+ }
+ return 0;
+}
+
+static krb5_error_code
+ret_data_xdr(krb5_storage *sp, krb5_data *data)
+{
+ krb5_error_code ret;
+ ret = krb5_ret_data(sp, data);
+ if (ret)
+ return ret;
+
+ if ((data->length % 4) != 0) {
+ char buf[4];
+ size_t res;
+
+ res = 4 - (data->length % 4);
+ if (res != 4) {
+ ret = krb5_storage_read(sp, buf, res);
+ if((size_t)ret != res)
+ return (ret < 0)? errno : krb5_storage_get_eof_code(sp);
+ }
+ }
+ return 0;
+}
+
+static krb5_error_code
+ret_auth_opaque(krb5_storage *msg, struct opaque_auth *ao)
+{
+ krb5_error_code ret;
+ ret = krb5_ret_uint32(msg, &ao->flavor);
+ if (ret) return ret;
+ ret = ret_data_xdr(msg, &ao->data);
+ return ret;
+}
+
+static int
+ret_gcred(krb5_data *data, struct gcred *gcred)
+{
+ krb5_storage *sp;
+
+ memset(gcred, 0, sizeof(*gcred));
+
+ sp = krb5_storage_from_data(data);
+ INSIST(sp != NULL);
+
+ CHECK(krb5_ret_uint32(sp, &gcred->version));
+ CHECK(krb5_ret_uint32(sp, &gcred->proc));
+ CHECK(krb5_ret_uint32(sp, &gcred->seq_num));
+ CHECK(krb5_ret_uint32(sp, &gcred->service));
+ CHECK(ret_data_xdr(sp, &gcred->handle));
+
+ krb5_storage_free(sp);
+
+ return 0;
+}
+
+static krb5_error_code
+store_gss_init_res(krb5_storage *sp, krb5_data handle,
+ OM_uint32 maj_stat, OM_uint32 min_stat,
+ uint32_t seq_window, gss_buffer_t gout)
+{
+ krb5_error_code ret;
+ krb5_data out;
+
+ out.data = gout->value;
+ out.length = gout->length;
+
+ ret = store_data_xdr(sp, handle);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, maj_stat);
+ if (ret) return ret;
+ ret = krb5_store_uint32(sp, min_stat);
+ if (ret) return ret;
+ ret = store_data_xdr(sp, out);
+ return ret;
+}
+
+static int
+store_string_xdr(krb5_storage *sp, const char *str)
+{
+ krb5_data c;
+ if (str) {
+ c.data = rk_UNCONST(str);
+ c.length = strlen(str) + 1;
+ } else
+ krb5_data_zero(&c);
+
+ return store_data_xdr(sp, c);
+}
+
+static int
+ret_string_xdr(krb5_storage *sp, char **str)
+{
+ krb5_data c;
+ *str = NULL;
+ CHECK(ret_data_xdr(sp, &c));
+ if (c.length) {
+ *str = malloc(c.length + 1);
+ INSIST(*str != NULL);
+ memcpy(*str, c.data, c.length);
+ (*str)[c.length] = '\0';
+ }
+ krb5_data_free(&c);
+ return 0;
+}
+
+static int
+store_principal_xdr(krb5_context contextp,
+ krb5_storage *sp,
+ krb5_principal p)
+{
+ char *str;
+ CHECK(krb5_unparse_name(contextp, p, &str));
+ CHECK(store_string_xdr(sp, str));
+ free(str);
+ return 0;
+}
+
+static int
+ret_principal_xdr(krb5_context contextp,
+ krb5_storage *sp,
+ krb5_principal *p)
+{
+ char *str;
+ *p = NULL;
+ CHECK(ret_string_xdr(sp, &str));
+ if (str) {
+ CHECK(krb5_parse_name(contextp, str, p));
+ free(str);
+ }
+ return 0;
+}
+
+static int
+store_principal_ent(krb5_context contextp,
+ krb5_storage *sp,
+ kadm5_principal_ent_rec *ent)
+{
+ int i;
+
+ CHECK(store_principal_xdr(contextp, sp, ent->principal));
+ CHECK(krb5_store_uint32(sp, ent->princ_expire_time));
+ CHECK(krb5_store_uint32(sp, ent->pw_expiration));
+ CHECK(krb5_store_uint32(sp, ent->last_pwd_change));
+ CHECK(krb5_store_uint32(sp, ent->max_life));
+ CHECK(krb5_store_int32(sp, ent->mod_name == NULL));
+ if (ent->mod_name)
+ CHECK(store_principal_xdr(contextp, sp, ent->mod_name));
+ CHECK(krb5_store_uint32(sp, ent->mod_date));
+ CHECK(krb5_store_uint32(sp, ent->attributes));
+ CHECK(krb5_store_uint32(sp, ent->kvno));
+ CHECK(krb5_store_uint32(sp, ent->mkvno));
+ CHECK(store_string_xdr(sp, ent->policy));
+ CHECK(krb5_store_int32(sp, ent->aux_attributes));
+ CHECK(krb5_store_int32(sp, ent->max_renewable_life));
+ CHECK(krb5_store_int32(sp, ent->last_success));
+ CHECK(krb5_store_int32(sp, ent->last_failed));
+ CHECK(krb5_store_int32(sp, ent->fail_auth_count));
+ CHECK(krb5_store_int32(sp, ent->n_key_data));
+ CHECK(krb5_store_int32(sp, ent->n_tl_data));
+ CHECK(krb5_store_int32(sp, ent->n_tl_data == 0));
+ if (ent->n_tl_data) {
+ krb5_tl_data *tp;
+
+ for (tp = ent->tl_data; tp; tp = tp->tl_data_next) {
+ krb5_data c;
+ c.length = tp->tl_data_length;
+ c.data = tp->tl_data_contents;
+
+ CHECK(krb5_store_int32(sp, 0)); /* last item */
+ CHECK(krb5_store_int32(sp, tp->tl_data_type));
+ CHECK(store_data_xdr(sp, c));
+ }
+ CHECK(krb5_store_int32(sp, 1)); /* last item */
+ }
+
+ CHECK(krb5_store_int32(sp, ent->n_key_data));
+ for (i = 0; i < ent->n_key_data; i++) {
+ CHECK(krb5_store_uint32(sp, 2));
+ CHECK(krb5_store_uint32(sp, ent->kvno));
+ CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[0]));
+ CHECK(krb5_store_uint32(sp, ent->key_data[i].key_data_type[1]));
+ }
+
+ return 0;
+}
+
+static int
+ret_principal_ent(krb5_context contextp,
+ krb5_storage *sp,
+ kadm5_principal_ent_rec *ent)
+{
+ uint32_t flag, num;
+ size_t i;
+
+ memset(ent, 0, sizeof(*ent));
+
+ CHECK(ret_principal_xdr(contextp, sp, &ent->principal));
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->princ_expire_time = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->pw_expiration = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->last_pwd_change = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->max_life = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ if (flag == 0)
+ CHECK(ret_principal_xdr(contextp, sp, &ent->mod_name));
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->mod_date = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->attributes = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->kvno = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->mkvno = flag;
+ CHECK(ret_string_xdr(sp, &ent->policy));
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->aux_attributes = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->max_renewable_life = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->last_success = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->last_failed = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->fail_auth_count = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->n_key_data = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->n_tl_data = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ if (flag == 0) {
+ krb5_tl_data **tp = &ent->tl_data;
+ size_t count = 0;
+
+ while(1) {
+ krb5_data c;
+ CHECK(krb5_ret_uint32(sp, &flag)); /* last item */
+ if (flag)
+ break;
+ *tp = calloc(1, sizeof(**tp));
+ INSIST(*tp != NULL);
+ CHECK(krb5_ret_uint32(sp, &flag));
+ (*tp)->tl_data_type = flag;
+ CHECK(ret_data_xdr(sp, &c));
+ (*tp)->tl_data_length = c.length;
+ (*tp)->tl_data_contents = c.data;
+ tp = &(*tp)->tl_data_next;
+
+ count++;
+ }
+ INSIST((size_t)ent->n_tl_data == count);
+ } else {
+ INSIST(ent->n_tl_data == 0);
+ }
+
+ CHECK(krb5_ret_uint32(sp, &num));
+ INSIST(num == (uint32_t)ent->n_key_data);
+
+ ent->key_data = calloc(num, sizeof(ent->key_data[0]));
+ INSIST(ent->key_data != NULL);
+
+ for (i = 0; i < num; i++) {
+ CHECK(krb5_ret_uint32(sp, &flag)); /* data version */
+ INSIST(flag > 1);
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->kvno = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->key_data[i].key_data_type[0] = flag;
+ CHECK(krb5_ret_uint32(sp, &flag));
+ ent->key_data[i].key_data_type[1] = flag;
+ }
+ CHECK(i == num);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+proc_create_principal(kadm5_server_context *contextp,
+ krb5_storage *in,
+ krb5_storage *out)
+{
+ uint32_t version, mask;
+ kadm5_principal_ent_rec ent;
+ krb5_error_code ret;
+ char *password;
+
+ memset(&ent, 0, sizeof(ent));
+
+ CHECK(krb5_ret_uint32(in, &version));
+ INSIST(version == VERSION2);
+ CHECK(ret_principal_ent(contextp->context, in, &ent));
+ CHECK(krb5_ret_uint32(in, &mask));
+ CHECK(ret_string_xdr(in, &password));
+
+ INSIST(ent.principal);
+
+
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, ent.principal);
+ if (ret)
+ goto fail;
+
+ ret = kadm5_create_principal(contextp, &ent, mask, password);
+
+ fail:
+ krb5_warn(contextp->context, ret, "create principal");
+ CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+ CHECK(krb5_store_uint32(out, ret)); /* code */
+
+ free(password);
+ kadm5_free_principal_ent(contextp, &ent);
+}
+
+static void
+proc_delete_principal(kadm5_server_context *contextp,
+ krb5_storage *in,
+ krb5_storage *out)
+{
+ uint32_t version;
+ krb5_principal princ;
+ krb5_error_code ret;
+
+ CHECK(krb5_ret_uint32(in, &version));
+ INSIST(version == VERSION2);
+ CHECK(ret_principal_xdr(contextp->context, in, &princ));
+
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
+ if (ret)
+ goto fail;
+
+ ret = kadm5_delete_principal(contextp, princ);
+
+ fail:
+ krb5_warn(contextp->context, ret, "delete principal");
+ CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+ CHECK(krb5_store_uint32(out, ret)); /* code */
+
+ krb5_free_principal(contextp->context, princ);
+}
+
+static void
+proc_get_principal(kadm5_server_context *contextp,
+ krb5_storage *in,
+ krb5_storage *out)
+{
+ uint32_t version, mask;
+ krb5_principal princ;
+ kadm5_principal_ent_rec ent;
+ krb5_error_code ret;
+
+ memset(&ent, 0, sizeof(ent));
+
+ CHECK(krb5_ret_uint32(in, &version));
+ INSIST(version == VERSION2);
+ CHECK(ret_principal_xdr(contextp->context, in, &princ));
+ CHECK(krb5_ret_uint32(in, &mask));
+
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
+ if(ret)
+ goto fail;
+
+ ret = kadm5_get_principal(contextp, princ, &ent, mask);
+
+ fail:
+ krb5_warn(contextp->context, ret, "get principal principal");
+
+ CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+ CHECK(krb5_store_uint32(out, ret)); /* code */
+ if (ret == 0) {
+ CHECK(store_principal_ent(contextp->context, out, &ent));
+ }
+ krb5_free_principal(contextp->context, princ);
+ kadm5_free_principal_ent(contextp, &ent);
+}
+
+static void
+proc_chrand_principal_v2(kadm5_server_context *contextp,
+ krb5_storage *in,
+ krb5_storage *out)
+{
+ krb5_error_code ret;
+ krb5_principal princ;
+ uint32_t version;
+ krb5_keyblock *new_keys;
+ int n_keys;
+
+ CHECK(krb5_ret_uint32(in, &version));
+ INSIST(version == VERSION2);
+ CHECK(ret_principal_xdr(contextp->context, in, &princ));
+
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+ if(ret)
+ goto fail;
+
+ ret = kadm5_randkey_principal(contextp, princ,
+ &new_keys, &n_keys);
+
+ fail:
+ krb5_warn(contextp->context, ret, "rand key principal");
+
+ CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+ CHECK(krb5_store_uint32(out, ret));
+ if (ret == 0) {
+ int i;
+ CHECK(krb5_store_int32(out, n_keys));
+
+ for(i = 0; i < n_keys; i++){
+ CHECK(krb5_store_uint32(out, new_keys[i].keytype));
+ CHECK(store_data_xdr(out, new_keys[i].keyvalue));
+ krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
+ }
+ free(new_keys);
+ }
+ krb5_free_principal(contextp->context, princ);
+}
+
+static void
+proc_init(kadm5_server_context *contextp,
+ krb5_storage *in,
+ krb5_storage *out)
+{
+ CHECK(krb5_store_uint32(out, VERSION2)); /* api version */
+ CHECK(krb5_store_uint32(out, 0)); /* code */
+ CHECK(krb5_store_uint32(out, 0)); /* code */
+}
+
+struct krb5_proc {
+ const char *name;
+ void (*func)(kadm5_server_context *, krb5_storage *, krb5_storage *);
+} rwprocs[] = {
+ { "NULL", NULL },
+ { "create principal", proc_create_principal },
+ { "delete principal", proc_delete_principal },
+ { "modify principal", NULL },
+ { "rename principal", NULL },
+ { "get principal", proc_get_principal },
+ { "chpass principal", NULL },
+ { "chrand principal", proc_chrand_principal_v2 },
+ { "create policy", NULL },
+ { "delete policy", NULL },
+ { "modify policy", NULL },
+ { "get policy", NULL },
+ { "get privs", NULL },
+ { "init", proc_init },
+ { "get principals", NULL },
+ { "get polices", NULL },
+ { "setkey principal", NULL },
+ { "setkey principal v4", NULL },
+ { "create principal v3", NULL },
+ { "chpass principal v3", NULL },
+ { "chrand principal v3", NULL },
+ { "setkey principal v3", NULL }
+}, roprocs[] = {
+ { "NULL", NULL },
+ { "create principal", NULL },
+ { "delete principal", NULL },
+ { "modify principal", NULL },
+ { "rename principal", NULL },
+ { "get principal", proc_get_principal },
+ { "chpass principal", NULL },
+ { "chrand principal", NULL },
+ { "create policy", NULL },
+ { "delete policy", NULL },
+ { "modify policy", NULL },
+ { "get policy", NULL },
+ { "get privs", NULL },
+ { "init", NULL },
+ { "get principals", NULL },
+ { "get polices", NULL },
+ { "setkey principal", NULL },
+ { "setkey principal v4", NULL },
+ { "create principal v3", NULL },
+ { "chpass principal v3", NULL },
+ { "chrand principal v3", NULL },
+ { "setkey principal v3", NULL }
+};
+
+static krb5_error_code
+copyheader(krb5_storage *sp, krb5_data *data)
+{
+ off_t off;
+ ssize_t sret;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+
+ CHECK(krb5_data_alloc(data, off));
+ INSIST((size_t)off == data->length);
+ krb5_storage_seek(sp, 0, SEEK_SET);
+ sret = krb5_storage_read(sp, data->data, data->length);
+ INSIST(sret == off);
+ INSIST(off == krb5_storage_seek(sp, 0, SEEK_CUR));
+
+ return 0;
+}
+
+struct gctx {
+ krb5_data handle;
+ gss_ctx_id_t ctx;
+ uint32_t seq_num;
+ int done;
+ int inprogress;
+};
+
+static int
+process_stream(krb5_context contextp,
+ unsigned char *buf,
+ size_t ilen,
+ krb5_storage *sp,
+ int readonly)
+{
+ krb5_error_code ret;
+ krb5_storage *msg, *reply, *dreply;
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc gin, gout;
+ struct gctx gctx;
+ void *server_handle = NULL;
+
+ memset(&gctx, 0, sizeof(gctx));
+
+ msg = krb5_storage_emem();
+ reply = krb5_storage_emem();
+ dreply = krb5_storage_emem();
+
+ if (msg == NULL || reply == NULL || dreply == NULL) {
+ if (msg != NULL)
+ krb5_storage_free(msg);
+ if (reply != NULL)
+ krb5_storage_free(reply);
+ if (dreply != NULL)
+ krb5_storage_free(dreply);
+ return krb5_enomem(contextp);
+ }
+
+ /*
+ * First packet comes partly from the caller
+ */
+
+ INSIST(ilen >= 4);
+
+ while (1) {
+ struct call_header chdr;
+ struct gcred gcred;
+ uint32_t mtype;
+ krb5_data headercopy;
+
+ krb5_storage_truncate(dreply, 0);
+ krb5_storage_truncate(reply, 0);
+ krb5_storage_truncate(msg, 0);
+
+ krb5_data_zero(&headercopy);
+ memset(&chdr, 0, sizeof(chdr));
+ memset(&gcred, 0, sizeof(gcred));
+
+ /*
+ * This is very icky to handle the the auto-detection between
+ * the Heimdal protocol and the MIT ONC-RPC based protocol.
+ */
+
+ if (ilen) {
+ int last_fragment;
+ unsigned long len;
+ ssize_t slen;
+ unsigned char tmp[4];
+
+ if (ilen < 4) {
+ memcpy(tmp, buf, ilen);
+ slen = krb5_storage_read(sp, tmp + ilen, sizeof(tmp) - ilen);
+ INSIST((size_t)slen == sizeof(tmp) - ilen);
+
+ ilen = sizeof(tmp);
+ buf = tmp;
+ }
+ INSIST(ilen >= 4);
+
+ _krb5_get_int(buf, &len, 4);
+ last_fragment = (len & LAST_FRAGMENT) != 0;
+ len &= ~LAST_FRAGMENT;
+
+ ilen -= 4;
+ buf += 4;
+
+ if (ilen) {
+ if (len < ilen) {
+ slen = krb5_storage_write(msg, buf, len);
+ INSIST((size_t)slen == len);
+ ilen -= len;
+ len = 0;
+ } else {
+ slen = krb5_storage_write(msg, buf, ilen);
+ INSIST((size_t)slen == ilen);
+ len -= ilen;
+ }
+ }
+
+ CHECK(read_data(sp, msg, len));
+
+ if (!last_fragment) {
+ ret = collect_framents(sp, msg);
+ if (ret == HEIM_ERR_EOF)
+ krb5_errx(contextp, 0, "client disconnected");
+ INSIST(ret == 0);
+ }
+ } else {
+
+ ret = collect_framents(sp, msg);
+ if (ret == HEIM_ERR_EOF)
+ krb5_errx(contextp, 0, "client disconnected");
+ INSIST(ret == 0);
+ }
+ krb5_storage_seek(msg, 0, SEEK_SET);
+
+ CHECK(krb5_ret_uint32(msg, &chdr.xid));
+ CHECK(krb5_ret_uint32(msg, &mtype));
+ CHECK(krb5_ret_uint32(msg, &chdr.rpcvers));
+ CHECK(krb5_ret_uint32(msg, &chdr.prog));
+ CHECK(krb5_ret_uint32(msg, &chdr.vers));
+ CHECK(krb5_ret_uint32(msg, &chdr.proc));
+ CHECK(ret_auth_opaque(msg, &chdr.cred));
+ CHECK(copyheader(msg, &headercopy));
+ CHECK(ret_auth_opaque(msg, &chdr.verf));
+
+ INSIST(chdr.rpcvers == RPC_VERSION);
+ INSIST(chdr.prog == KADM_SERVER);
+ INSIST(chdr.vers == VVERSION);
+ INSIST(chdr.cred.flavor == FLAVOR_GSS);
+
+ CHECK(ret_gcred(&chdr.cred.data, &gcred));
+
+ INSIST(gcred.version == FLAVOR_GSS_VERSION);
+
+ if (gctx.done) {
+ INSIST(chdr.verf.flavor == FLAVOR_GSS);
+
+ /* from first byte to last of credential */
+ gin.value = headercopy.data;
+ gin.length = headercopy.length;
+ gout.value = chdr.verf.data.data;
+ gout.length = chdr.verf.data.length;
+
+ maj_stat = gss_verify_mic(&min_stat, gctx.ctx, &gin, &gout, NULL);
+ INSIST(maj_stat == GSS_S_COMPLETE);
+ }
+
+ switch(gcred.proc) {
+ case RPG_DATA: {
+ krb5_data data;
+ int conf_state;
+ uint32_t seq;
+ krb5_storage *sp1;
+ struct krb5_proc *procs = readonly ? roprocs : rwprocs;
+
+ INSIST(gcred.service == rpg_privacy);
+
+ INSIST(gctx.done);
+
+ INSIST(krb5_data_cmp(&gcred.handle, &gctx.handle) == 0);
+
+ CHECK(ret_data_xdr(msg, &data));
+
+ gin.value = data.data;
+ gin.length = data.length;
+
+ maj_stat = gss_unwrap(&min_stat, gctx.ctx, &gin, &gout,
+ &conf_state, NULL);
+ krb5_data_free(&data);
+ INSIST(maj_stat == GSS_S_COMPLETE);
+ INSIST(conf_state != 0);
+
+ sp1 = krb5_storage_from_mem(gout.value, gout.length);
+ INSIST(sp1 != NULL);
+
+ CHECK(krb5_ret_uint32(sp1, &seq));
+ INSIST (seq == gcred.seq_num);
+
+ /*
+ * Check sequence number
+ */
+ INSIST(seq > gctx.seq_num);
+ gctx.seq_num = seq;
+
+ /*
+ * If contextp is setup, priv data have the seq_num stored
+ * first in the block, so add it here before users data is
+ * added.
+ */
+ CHECK(krb5_store_uint32(dreply, gctx.seq_num));
+
+ if (chdr.proc >= sizeof(rwprocs)/sizeof(rwprocs[0])) {
+ krb5_warnx(contextp, "proc number out of array");
+ } else if (procs[chdr.proc].func == NULL) {
+ if (readonly && rwprocs[chdr.proc].func)
+ krb5_warnx(contextp,
+ "proc '%s' not allowed (readonly mode)",
+ procs[chdr.proc].name);
+ else
+ krb5_warnx(contextp, "proc '%s' never implemented",
+ procs[chdr.proc].name);
+ } else {
+ krb5_warnx(contextp, "proc %s", procs[chdr.proc].name);
+ INSIST(server_handle != NULL);
+ (*procs[chdr.proc].func)(server_handle, sp, dreply);
+ }
+ krb5_storage_free(sp);
+ gss_release_buffer(&min_stat, &gout);
+
+ break;
+ }
+ case RPG_INIT:
+ INSIST(gctx.inprogress == 0);
+ INSIST(gctx.ctx == NULL);
+
+ gctx.inprogress = 1;
+ HEIM_FALLTHROUGH;
+ case RPG_CONTINUE_INIT: {
+ gss_name_t src_name = GSS_C_NO_NAME;
+ krb5_data in;
+
+ INSIST(gctx.inprogress);
+
+ CHECK(ret_data_xdr(msg, &in));
+
+ gin.value = in.data;
+ gin.length = in.length;
+ gout.value = NULL;
+ gout.length = 0;
+
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &gctx.ctx,
+ GSS_C_NO_CREDENTIAL,
+ &gin,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &src_name,
+ NULL,
+ &gout,
+ NULL,
+ NULL,
+ NULL);
+ if (GSS_ERROR(maj_stat)) {
+ gss_print_errors(contextp, maj_stat, min_stat);
+ krb5_errx(contextp, 1, "gss error, exit");
+ }
+ if ((maj_stat & GSS_S_CONTINUE_NEEDED) == 0) {
+ kadm5_config_params realm_params;
+ gss_buffer_desc bufp;
+ char *client;
+
+ gctx.done = 1;
+
+ memset(&realm_params, 0, sizeof(realm_params));
+
+ maj_stat = gss_export_name(&min_stat, src_name, &bufp);
+ INSIST(maj_stat == GSS_S_COMPLETE);
+
+ CHECK(parse_name(bufp.value, bufp.length,
+ GSS_KRB5_MECHANISM, &client));
+
+ gss_release_buffer(&min_stat, &bufp);
+
+ krb5_warnx(contextp, "%s connected", client);
+
+ ret = kadm5_s_init_with_password_ctx(contextp,
+ client,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &realm_params,
+ 0, 0,
+ &server_handle);
+ INSIST(ret == 0);
+ }
+
+ INSIST(gctx.ctx != GSS_C_NO_CONTEXT);
+
+ CHECK(krb5_store_uint32(dreply, 0));
+ CHECK(store_gss_init_res(dreply, gctx.handle,
+ maj_stat, min_stat, 1, &gout));
+ if (gout.value)
+ gss_release_buffer(&min_stat, &gout);
+ if (src_name)
+ gss_release_name(&min_stat, &src_name);
+
+ break;
+ }
+ case RPG_DESTROY:
+ krb5_errx(contextp, 1, "client destroyed gss contextp");
+ default:
+ krb5_errx(contextp, 1, "client sent unknown gsscode %d",
+ (int)gcred.proc);
+ }
+
+ krb5_data_free(&gcred.handle);
+ krb5_data_free(&chdr.cred.data);
+ krb5_data_free(&chdr.verf.data);
+ krb5_data_free(&headercopy);
+
+ CHECK(krb5_store_uint32(reply, chdr.xid));
+ CHECK(krb5_store_uint32(reply, 1)); /* REPLY */
+ CHECK(krb5_store_uint32(reply, 0)); /* MSG_ACCEPTED */
+
+ if (!gctx.done) {
+ krb5_data data;
+
+ CHECK(krb5_store_uint32(reply, 0)); /* flavor_none */
+ CHECK(krb5_store_uint32(reply, 0)); /* length */
+
+ CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
+
+ CHECK(krb5_storage_to_data(dreply, &data));
+ INSIST((size_t)krb5_storage_write(reply, data.data, data.length) == data.length);
+ krb5_data_free(&data);
+
+ } else {
+ uint32_t seqnum = htonl(gctx.seq_num);
+ krb5_data data;
+
+ gin.value = &seqnum;
+ gin.length = sizeof(seqnum);
+
+ maj_stat = gss_get_mic(&min_stat, gctx.ctx, 0, &gin, &gout);
+ INSIST(maj_stat == GSS_S_COMPLETE);
+
+ data.data = gout.value;
+ data.length = gout.length;
+
+ CHECK(krb5_store_uint32(reply, FLAVOR_GSS));
+ CHECK(store_data_xdr(reply, data));
+ gss_release_buffer(&min_stat, &gout);
+
+ CHECK(krb5_store_uint32(reply, 0)); /* SUCCESS */
+
+ CHECK(krb5_storage_to_data(dreply, &data));
+
+ if (gctx.inprogress) {
+ ssize_t sret;
+ gctx.inprogress = 0;
+ sret = krb5_storage_write(reply, data.data, data.length);
+ INSIST((size_t)sret == data.length);
+ krb5_data_free(&data);
+ } else {
+ int conf_state;
+
+ gin.value = data.data;
+ gin.length = data.length;
+
+ maj_stat = gss_wrap(&min_stat, gctx.ctx, 1, 0,
+ &gin, &conf_state, &gout);
+ INSIST(maj_stat == GSS_S_COMPLETE);
+ INSIST(conf_state != 0);
+ krb5_data_free(&data);
+
+ data.data = gout.value;
+ data.length = gout.length;
+
+ store_data_xdr(reply, data);
+ gss_release_buffer(&min_stat, &gout);
+ }
+ }
+
+ {
+ krb5_data data;
+ ssize_t sret;
+ CHECK(krb5_storage_to_data(reply, &data));
+ CHECK(krb5_store_uint32(sp, data.length | LAST_FRAGMENT));
+ sret = krb5_storage_write(sp, data.data, data.length);
+ INSIST((size_t)sret == data.length);
+ krb5_data_free(&data);
+ }
+
+ }
+}
+
+
+int
+handle_mit(krb5_context contextp,
+ void *buf,
+ size_t len,
+ krb5_socket_t sock,
+ int readonly)
+{
+ krb5_storage *sp;
+
+ dcontext = contextp;
+
+ sp = krb5_storage_from_socket(sock);
+ INSIST(sp != NULL);
+
+ process_stream(contextp, buf, len, sp, readonly);
+
+ return 0;
+}
diff --git a/third_party/heimdal/kadmin/server.c b/third_party/heimdal/kadmin/server.c
new file mode 100644
index 0000000..281822a
--- /dev/null
+++ b/third_party/heimdal/kadmin/server.c
@@ -0,0 +1,1174 @@
+/*
+ * 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 "kadmin_locl.h"
+#include <krb5-private.h>
+
+static kadm5_ret_t check_aliases(kadm5_server_context *,
+ kadm5_principal_ent_rec *,
+ kadm5_principal_ent_rec *);
+
+/*
+ * All the iter_cb stuff is about online listing of principals via
+ * kadm5_iter_principals(). Search for "LIST" to see more commentary.
+ */
+struct iter_cb_data {
+ krb5_context context;
+ krb5_auth_context ac;
+ krb5_storage *rsp;
+ kadm5_ret_t ret;
+ size_t n;
+ size_t i;
+ int fd;
+ unsigned int initial:1;
+ unsigned int stop:1;
+};
+
+/*
+ * This function sends the current chunk of principal listing and checks if the
+ * client requested that the listing stop.
+ */
+static int
+iter_cb_send_now(struct iter_cb_data *d)
+{
+ struct timeval tv;
+ krb5_data out;
+
+ krb5_data_zero(&out);
+
+ if (!d->stop) {
+ fd_set fds;
+ int nfds;
+
+ /*
+ * The client can send us one message to interrupt the iteration.
+ *
+ * TODO: Maybe we should have the client send a message every N chunks
+ * so we can clock the listing and have a chance to receive any
+ * interrupt message from the client?
+ */
+ FD_ZERO(&fds);
+ FD_SET(d->fd, &fds);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ nfds = select(d->fd + 1, &fds, NULL, NULL, &tv);
+ if (nfds == -1) {
+ d->ret = errno;
+ } else if (nfds > 0) {
+ /*
+ * And it did. We'll throw this message away. It should be a NOP
+ * call, which we'd throw away anyways. If the client's stop
+ * message arrives after we're done anyways, well, it will be
+ * processed as a NOP and thrown away.
+ */
+ d->stop = 1;
+ d->ret = krb5_read_priv_message(d->context, d->ac, &d->fd, &out);
+ krb5_data_free(&out);
+ if (d->ret == HEIM_ERR_EOF)
+ exit(0);
+ }
+ }
+ d->i = 0;
+ d->ret = krb5_storage_to_data(d->rsp, &out);
+ if (d->ret == 0)
+ d->ret = krb5_write_priv_message(d->context, d->ac, &d->fd, &out);
+ krb5_data_free(&out);
+ krb5_storage_free(d->rsp);
+ if ((d->rsp = krb5_storage_emem()) == NULL)
+ return krb5_enomem(d->context);
+ return d->ret;
+}
+
+static int
+iter_cb(void *cbdata, const char *p)
+{
+ struct iter_cb_data *d = cbdata;
+ krb5_error_code ret = 0;
+ size_t n = d->n;
+
+ /* Convince the compiler that `-(int)d->n' is defined */
+ if (n == 0 || n > INT_MAX)
+ return ERANGE;
+ if (d->rsp == NULL && (d->rsp = krb5_storage_emem()) == NULL)
+ return krb5_enomem(d->context);
+ if (d->i == 0) {
+ /* Every chunk starts with a result code */
+ ret = krb5_store_int32(d->rsp, d->ret);
+ if (ret)
+ return ret;
+ if (d->ret)
+ return ret;
+ }
+ if (d->initial) {
+ /*
+ * We'll send up to `d->n' entries per-write. We send a negative
+ * number to indicate we accepted the client's proposal that we speak
+ * the online LIST protocol.
+ *
+ * Note that if we're here then we've already placed a result code in
+ * this reply (see above).
+ */
+ d->initial = 0;
+ ret = krb5_store_int32(d->rsp, -(int)n); /* Princs per-chunk */
+ if (ret == 0)
+ ret = iter_cb_send_now(d);
+ if (ret)
+ return ret;
+ /*
+ * Now that we've sent the acceptance reply, put a result code as the
+ * first thing in the next reply, which will have the first chunk of
+ * the listing.
+ */
+ ret = krb5_store_int32(d->rsp, d->ret);
+ if (ret)
+ return ret;
+ if (d->ret)
+ return ret;
+ }
+
+ if (p) {
+ ret = krb5_store_string(d->rsp, p);
+ d->i++;
+ } else {
+ /*
+ * We get called with `p == NULL' when the listing is done. This
+ * forces us to iter_cb_send_now(d) below, but also forces us to have a
+ * properly formed reply (i.e., that we have a result code as the first
+ * item), even if the chunk is otherwise empty (`d->i == 0').
+ */
+ d->i = n;
+ }
+
+ if (ret == 0 && d->i == n)
+ ret = iter_cb_send_now(d); /* Chunk finished; send it */
+ if (d->stop)
+ return EINTR;
+ return ret;
+}
+
+static kadm5_ret_t
+kadmind_dispatch(void *kadm_handlep, krb5_boolean initial,
+ krb5_data *in, krb5_auth_context ac, int fd,
+ krb5_data *out, int readonly)
+{
+ kadm5_ret_t ret = 0;
+ kadm5_ret_t ret_sp = 0;
+ int32_t cmd, mask, kvno, tmp;
+ kadm5_server_context *contextp = kadm_handlep;
+ char client[128], name[128], name2[128];
+ const char *op = "";
+ krb5_principal princ = NULL, princ2 = NULL;
+ kadm5_principal_ent_rec ent, ent_prev;
+ char *password = NULL, *expression;
+ krb5_keyblock *new_keys;
+ krb5_key_salt_tuple *ks_tuple = NULL;
+ int keepold = FALSE;
+ int n_ks_tuple = 0;
+ int n_keys;
+ char **princs;
+ int n_princs;
+ int keys_ok = 0;
+ krb5_storage *rsp; /* response goes here */
+ krb5_storage *sp;
+ int len;
+
+ memset(&ent, 0, sizeof(ent));
+ memset(&ent_prev, 0, sizeof(ent_prev));
+ krb5_data_zero(out);
+
+ rsp = krb5_storage_emem();
+ if (rsp == NULL)
+ return krb5_enomem(contextp->context);
+
+ sp = krb5_storage_from_data(in);
+ if (sp == NULL) {
+ krb5_storage_free(rsp);
+ return krb5_enomem(contextp->context);
+ }
+
+ ret = krb5_unparse_name_fixed(contextp->context, contextp->caller,
+ client, sizeof(client));
+ if (ret == 0)
+ ret = krb5_ret_int32(sp, &cmd);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+
+ switch(cmd){
+ case kadm_nop:{
+ /*
+ * In the future we could use this for versioning.
+ *
+ * We used to respond to NOPs with KADM5_FAILURE. Now we respond with
+ * zero. In the future we could send back a protocol version number
+ * and use NOPs for protocol version negotiation.
+ *
+ * In the meantime, this gets called only if a client wants to
+ * interrupt a long-running LIST operation.
+ */
+ op = "NOP";
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0 && tmp == 0) {
+ /*
+ * Reply not wanted. This would be a LIST interrupt request.
+ */
+ krb5_storage_free(rsp);
+ krb5_storage_free(sp);
+ return 0;
+ }
+ ret_sp = krb5_store_int32(rsp, ret = 0);
+ break;
+ }
+ case kadm_get:{
+ op = "GET";
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_UNK_PRINC);
+ goto fail;
+ }
+ ret = krb5_ret_int32(sp, &mask);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+
+ mask |= KADM5_PRINCIPAL;
+ krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+
+ /* If the caller doesn't have KADM5_PRIV_GET, we're done. */
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET, princ);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+
+ /* Then check to see if it is ok to return keys */
+ if ((mask & KADM5_KEY_DATA) != 0) {
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_GET_KEYS,
+ princ);
+ if (ret == 0) {
+ keys_ok = 1;
+ } else if ((mask == (KADM5_PRINCIPAL|KADM5_KEY_DATA)) ||
+ (mask == (KADM5_PRINCIPAL|KADM5_KVNO|KADM5_KEY_DATA))) {
+ /*
+ * Requests for keys will get bogus keys, which is useful if
+ * the client just wants to see what (kvno, enctype)s the
+ * principal has keys for, but terrible if the client wants to
+ * write the keys into a keytab or modify the principal and
+ * write the bogus keys back to the server.
+ *
+ * We use a heuristic to detect which case we're handling here.
+ * If the client only asks for the flags in the above
+ * condition, then it's very likely a kadmin ext_keytab,
+ * add_enctype, or other request that should not see bogus
+ * keys. We deny them.
+ *
+ * The kadmin get command can be coaxed into making a request
+ * with the same mask. But the default long and terse output
+ * modes request other things too, so in all likelihood this
+ * heuristic will not hurt any kadmin get uses.
+ */
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+ }
+
+ ret = kadm5_get_principal(kadm_handlep, princ, &ent, mask);
+ ret_sp = krb5_store_int32(rsp, ret);
+ if (ret == 0) {
+ if (ret_sp == 0 && keys_ok)
+ ret_sp = kadm5_store_principal_ent(rsp, &ent);
+ else if (ret_sp == 0)
+ ret_sp = kadm5_store_principal_ent_nokeys(rsp, &ent);
+ }
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ break;
+ }
+ case kadm_delete:{
+ op = "DELETE";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret == 0)
+ ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ if (ret == 0) {
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_DELETE, princ);
+ krb5_warnx(contextp->context, "%s: %s %s (%s)", client, op, name,
+ ret == 0 ? "granted" : "denied");
+ }
+
+ /*
+ * There's no need to check that the caller has permission to
+ * delete the victim principal's aliases.
+ */
+ if (ret == 0)
+ ret = kadm5_delete_principal(kadm_handlep, princ);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_create:{
+ op = "CREATE";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = kadm5_ret_principal_ent(sp, &ent);
+ if(ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+ ret = krb5_ret_int32(sp, &mask);
+ if(ret){
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ goto fail;
+ }
+ ret = krb5_ret_string(sp, &password);
+ if(ret){
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ goto fail;
+ }
+ krb5_unparse_name_fixed(contextp->context, ent.principal,
+ name, sizeof(name));
+ krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD,
+ ent.principal);
+ if(ret){
+ ret_sp = krb5_store_int32(rsp, ret);
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ goto fail;
+ }
+ if ((mask & KADM5_TL_DATA)) {
+ /*
+ * Also check that the caller can create the aliases, if the
+ * new principal has any.
+ */
+ ret = check_aliases(contextp, &ent, NULL);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_BAD_PRINCIPAL);
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ goto fail;
+ }
+ }
+ ret = kadm5_create_principal(kadm_handlep, &ent,
+ mask, password);
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_modify:{
+ op = "MODIFY";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = kadm5_ret_principal_ent(sp, &ent);
+ if(ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ ret = krb5_ret_int32(sp, &mask);
+ if(ret){
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ kadm5_free_principal_ent(contextp, &ent);
+ goto fail;
+ }
+ krb5_unparse_name_fixed(contextp->context, ent.principal,
+ name, sizeof(name));
+ krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_MODIFY,
+ ent.principal);
+ if(ret){
+ ret_sp = krb5_store_int32(rsp, ret);
+ kadm5_free_principal_ent(contextp, &ent);
+ goto fail;
+ }
+ if ((mask & KADM5_TL_DATA)) {
+ /*
+ * Also check that the caller can create aliases that are in
+ * the new entry but not the old one. There's no need to
+ * check that the caller can delete aliases it wants to
+ * drop. See also handling of rename.
+ */
+ ret = kadm5_get_principal(kadm_handlep, ent.principal, &ent_prev, mask);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ kadm5_free_principal_ent(contextp, &ent);
+ goto fail;
+ }
+ ret = check_aliases(contextp, &ent, &ent_prev);
+ kadm5_free_principal_ent(contextp, &ent_prev);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_BAD_PRINCIPAL);
+ kadm5_free_principal_ent(contextp, &ent);
+ goto fail;
+ }
+ }
+ ret = kadm5_modify_principal(kadm_handlep, &ent, mask);
+ kadm5_free_principal_ent(kadm_handlep, &ent);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_prune:{
+ op = "PRUNE";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret == 0)
+ ret = krb5_ret_int32(sp, &kvno);
+ if (ret == HEIM_ERR_EOF) {
+ kvno = 0;
+ } else if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+
+ ret = kadm5_prune_principal(kadm_handlep, princ, kvno);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_rename:{
+ op = "RENAME";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret == 0)
+ ret = krb5_ret_principal(sp, &princ2);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+
+ krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ krb5_unparse_name_fixed(contextp->context, princ2,
+ name2, sizeof(name2));
+ krb5_warnx(contextp->context, "%s: %s %s -> %s",
+ client, op, name, name2);
+ ret = _kadm5_acl_check_permission(contextp,
+ KADM5_PRIV_ADD,
+ princ2);
+ if (ret == 0) {
+ /*
+ * Also require modify for the principal. For backwards
+ * compatibility, allow delete permission on the old name to
+ * cure lack of modify permission on the old name.
+ */
+ ret = _kadm5_acl_check_permission(contextp,
+ KADM5_PRIV_MODIFY,
+ princ);
+ if (ret) {
+ ret = _kadm5_acl_check_permission(contextp,
+ KADM5_PRIV_DELETE,
+ princ);
+ }
+ }
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+
+ ret = kadm5_rename_principal(kadm_handlep, princ, princ2);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_chpass:{
+ krb5_boolean is_self_cpw, allow_self_cpw;
+
+ op = "CHPASS";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret == 0)
+ ret = krb5_ret_string(sp, &password);
+ if (ret == 0)
+ ret = krb5_ret_int32(sp, &keepold);
+ if (ret == HEIM_ERR_EOF)
+ ret = 0;
+ if (ret == 0) {
+ ret = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ if (ret == 0)
+ krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+ }
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+
+ /*
+ * Change password requests are subject to ACLs unless the principal is
+ * changing their own password and the initial ticket flag is set, and
+ * the allow_self_change_password configuration option is TRUE.
+ */
+ is_self_cpw =
+ krb5_principal_compare(contextp->context, contextp->caller, princ);
+ allow_self_cpw =
+ krb5_config_get_bool_default(contextp->context, NULL, TRUE,
+ "kadmin", "allow_self_change_password", NULL);
+ if (!(is_self_cpw && initial && allow_self_cpw)) {
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+ }
+
+ ret = kadm5_chpass_principal_3(kadm_handlep, princ, keepold, 0, NULL,
+ password);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_chpass_with_key:{
+ int i;
+ krb5_key_data *key_data;
+ int n_key_data;
+
+ op = "CHPASS_WITH_KEY";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret == 0)
+ ret = krb5_ret_int32(sp, &n_key_data);
+ if (ret == 0) {
+ ret = krb5_ret_int32(sp, &keepold);
+ if (ret == HEIM_ERR_EOF)
+ ret = 0;
+ }
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+
+ /* n_key_data will be squeezed into an int16_t below. */
+ if (n_key_data < 0 || n_key_data >= 1 << 16 ||
+ (size_t)n_key_data > UINT_MAX/sizeof(*key_data)) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ ret = ERANGE;
+ goto fail;
+ }
+
+ key_data = malloc (n_key_data * sizeof(*key_data));
+ if (key_data == NULL && n_key_data != 0) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ ret = krb5_enomem(contextp->context);
+ goto fail;
+ }
+
+ for (i = 0; i < n_key_data; ++i) {
+ ret = kadm5_ret_key_data (sp, &key_data[i]);
+ if (ret) {
+ int16_t dummy = i;
+
+ kadm5_free_key_data (contextp, &dummy, key_data);
+ free (key_data);
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ }
+
+ /*
+ * The change is only allowed if the user is on the CPW ACL,
+ * this it to force password quality check on the user.
+ */
+
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+ ret_sp = krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ if (ret_sp == 0)
+ krb5_warnx(contextp->context, "%s: %s %s (%s)", client, op, name,
+ ret ? "denied" : "granted");
+ if(ret) {
+ int16_t dummy = n_key_data;
+
+ kadm5_free_key_data (contextp, &dummy, key_data);
+ free (key_data);
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ ret = kadm5_chpass_principal_with_key_3(kadm_handlep, princ, keepold,
+ n_key_data, key_data);
+ {
+ int16_t dummy = n_key_data;
+ kadm5_free_key_data (contextp, &dummy, key_data);
+ }
+ free (key_data);
+ ret_sp = krb5_store_int32(rsp, ret);
+ break;
+ }
+ case kadm_randkey:{
+ size_t i;
+
+ op = "RANDKEY";
+ if (readonly) {
+ ret_sp = krb5_store_int32(rsp, ret = KADM5_READ_ONLY);
+ goto fail;
+ }
+ ret = krb5_ret_principal(sp, &princ);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ krb5_unparse_name_fixed(contextp->context, princ, name, sizeof(name));
+ krb5_warnx(contextp->context, "%s: %s %s", client, op, name);
+ /*
+ * The change is allowed if at least one of:
+ * a) it's for the principal him/herself and this was an initial ticket
+ * b) the user is on the CPW ACL.
+ */
+
+ if (initial
+ && krb5_principal_compare (contextp->context, contextp->caller,
+ princ))
+ ret = 0;
+ else
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_CPW, princ);
+
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, ret);
+ goto fail;
+ }
+
+ /*
+ * See comments in kadm5_c_randkey_principal() regarding the
+ * protocol.
+ */
+ ret = krb5_ret_int32(sp, &keepold);
+ if (ret != 0 && ret != HEIM_ERR_EOF) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+
+ ret = krb5_ret_int32(sp, &n_ks_tuple);
+ if (ret == HEIM_ERR_EOF) {
+ const char *enctypes;
+ size_t n;
+
+ enctypes = krb5_config_get_string(contextp->context, NULL,
+ "realms",
+ krb5_principal_get_realm(contextp->context,
+ princ),
+ "supported_enctypes", NULL);
+ if (enctypes == NULL || enctypes[0] == '\0')
+ enctypes = "aes128-cts-hmac-sha1-96";
+ ret = krb5_string_to_keysalts2(contextp->context, enctypes,
+ &n, &ks_tuple);
+ n_ks_tuple = n;
+ }
+ if (ret != 0) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); /* XXX */
+ goto fail;
+ }
+
+ if (n_ks_tuple < 0) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE); /* XXX */
+ ret = EOVERFLOW;
+ goto fail;
+ }
+ free(ks_tuple);
+ if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ ret = errno;
+ goto fail;
+ }
+
+ for (i = 0; i < n_ks_tuple; i++) {
+ ret = krb5_ret_int32(sp, &ks_tuple[i].ks_enctype);
+ if (ret != 0) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ free(ks_tuple);
+ goto fail;
+ }
+ ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype);
+ if (ret != 0) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ free(ks_tuple);
+ goto fail;
+ }
+ }
+ ret = kadm5_randkey_principal_3(kadm_handlep, princ, keepold,
+ n_ks_tuple, ks_tuple, &new_keys,
+ &n_keys);
+ free(ks_tuple);
+
+ ret_sp = krb5_store_int32(rsp, ret);
+ if (ret == 0 && ret_sp == 0){
+ ret_sp = krb5_store_int32(rsp, n_keys);
+ for (i = 0; i < n_keys; i++){
+ if (ret_sp == 0)
+ ret_sp = krb5_store_keyblock(rsp, new_keys[i]);
+ krb5_free_keyblock_contents(contextp->context, &new_keys[i]);
+ }
+ free(new_keys);
+ }
+ break;
+ }
+ case kadm_get_privs:{
+ uint32_t privs;
+ ret = kadm5_get_privs(kadm_handlep, &privs);
+ if (ret == 0)
+ ret_sp = krb5_store_uint32(rsp, privs);
+ break;
+ }
+ case kadm_get_princs:{
+ op = "LIST";
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ /* See kadm5_c_iter_principals() */
+ if (tmp == 0x55555555) {
+ /* Want online iteration */
+ ret = krb5_ret_string(sp, &expression);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ if (expression[0] == '\0') {
+ free(expression);
+ expression = NULL;
+ }
+ } else if (tmp) {
+ ret = krb5_ret_string(sp, &expression);
+ if (ret) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ goto fail;
+ }
+ }else
+ expression = NULL;
+ krb5_warnx(contextp->context, "%s: %s %s", client, op,
+ expression ? expression : "*");
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_LIST, NULL);
+ if(ret){
+ ret_sp = krb5_store_int32(rsp, ret);
+ free(expression);
+ goto fail;
+ }
+ if (fd > -1 && tmp == 0x55555555) {
+ struct iter_cb_data iter_cbdata;
+ int n;
+
+ /*
+ * The client proposes that we speak the online variation of LIST
+ * by sending a magic value in the int32 that is meant to be a
+ * boolean for "an expression follows". The client must send an
+ * expression in this case because the server might be an old one,
+ * so even if the caller to kadm5_get/iter_principals() passed NULL
+ * for the expression, the client must send something ("*").
+ *
+ * The list of principals will be streamed in multiple replies.
+ *
+ * The first reply will have just a return code and a negative
+ * count of maximum number of names per-subsequent reply. See
+ * `iter_cb()'.
+ *
+ * The second reply, third, .., nth replies will have a return code
+ * followed by 50 names, except the last reply must have fewer than
+ * 50 names -zero if need be- so the client can deterministically
+ * notice the end of the stream.
+ */
+
+ n = list_chunk_size;
+ if (n < 0)
+ n = krb5_config_get_int_default(contextp->context, NULL, -1,
+ "kadmin", "list_chunk_size", NULL);
+ if (n < 0)
+ n = 50;
+ if (n > 500)
+ n = 500;
+ if ((iter_cbdata.rsp = krb5_storage_emem()) == NULL) {
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ ret = krb5_enomem(contextp->context);
+ goto fail;
+ }
+ iter_cbdata.context = contextp->context;
+ iter_cbdata.initial = 1;
+ iter_cbdata.stop = 0;
+ iter_cbdata.ret = 0;
+ iter_cbdata.ac = ac;
+ iter_cbdata.fd = fd;
+ iter_cbdata.n = n;
+ iter_cbdata.i = 0;
+
+ /*
+ * All sending of replies will happen in iter_cb, except for the
+ * final chunk with the final result code.
+ */
+ iter_cbdata.ret = kadm5_iter_principals(kadm_handlep, expression,
+ iter_cb, &iter_cbdata);
+ /* Send terminating chunk */
+ iter_cb(&iter_cbdata, NULL);
+ /* Final result */
+ ret = krb5_store_int32(rsp, iter_cbdata.ret);
+ krb5_storage_free(iter_cbdata.rsp);
+ } else {
+ ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs);
+ ret_sp = krb5_store_int32(rsp, ret);
+ if (ret == 0 && ret_sp == 0) {
+ int i;
+
+ ret_sp = krb5_store_int32(rsp, n_princs);
+ for (i = 0; ret_sp == 0 && i < n_princs; i++)
+ ret_sp = krb5_store_string(rsp, princs[i]);
+ kadm5_free_name_list(kadm_handlep, princs, &n_princs);
+ }
+ }
+ free(expression);
+ break;
+ }
+ default:
+ krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd);
+ ret_sp = krb5_store_int32(rsp, KADM5_FAILURE);
+ break;
+ }
+
+fail:
+ if (password != NULL) {
+ len = strlen(password);
+ memset_s(password, len, 0, len);
+ free(password);
+ }
+ krb5_storage_to_data(rsp, out);
+ krb5_storage_free(rsp);
+ krb5_storage_free(sp);
+ krb5_free_principal(contextp->context, princ);
+ krb5_free_principal(contextp->context, princ2);
+ if (ret)
+ krb5_warn(contextp->context, ret, "%s", op);
+ if (out->length == 0)
+ krb5_warn(contextp->context, ret, "%s: reply failed", op);
+ else if (ret_sp)
+ krb5_warn(contextp->context, ret, "%s: reply incomplete", op);
+ if (ret_sp)
+ return ret_sp;
+ return 0;
+}
+
+struct iter_aliases_ctx {
+ HDB_Ext_Aliases aliases;
+ krb5_tl_data *tl;
+ int alias_idx;
+ int done;
+};
+
+static kadm5_ret_t
+iter_aliases(kadm5_principal_ent_rec *from,
+ struct iter_aliases_ctx *ctx,
+ krb5_principal *out)
+{
+ HDB_extension ext;
+ kadm5_ret_t ret;
+ size_t size;
+
+ *out = NULL;
+
+ if (ctx->done > 0)
+ return 0;
+ if (from == NULL) {
+ ctx->done = 1;
+ return 0;
+ }
+
+ if (ctx->done == 0) {
+ if (ctx->alias_idx < ctx->aliases.aliases.len) {
+ *out = &ctx->aliases.aliases.val[ctx->alias_idx++];
+ return 0;
+ }
+ /* Out of aliases in this TL, step to next TL */
+ ctx->tl = ctx->tl->tl_data_next;
+ } else if (ctx->done < 0) {
+ /* Setup iteration context */
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->done = 0;
+ ctx->aliases.aliases.val = NULL;
+ ctx->aliases.aliases.len = 0;
+ ctx->tl = from->tl_data;
+ }
+
+ free_HDB_Ext_Aliases(&ctx->aliases);
+ ctx->alias_idx = 0;
+
+ /* Find TL with aliases */
+ for (; ctx->tl != NULL; ctx->tl = ctx->tl->tl_data_next) {
+ if (ctx->tl->tl_data_type != KRB5_TL_EXTENSION)
+ continue;
+
+ ret = decode_HDB_extension(ctx->tl->tl_data_contents,
+ ctx->tl->tl_data_length,
+ &ext, &size);
+ if (ret)
+ return ret;
+ if (ext.data.element == choice_HDB_extension_data_aliases &&
+ ext.data.u.aliases.aliases.len > 0) {
+ ctx->aliases = ext.data.u.aliases;
+ break;
+ }
+ free_HDB_extension(&ext);
+ }
+
+ if (ctx->tl != NULL && ctx->aliases.aliases.len > 0) {
+ *out = &ctx->aliases.aliases.val[ctx->alias_idx++];
+ return 0;
+ }
+
+ ctx->done = 1;
+ return 0;
+}
+
+static kadm5_ret_t
+check_aliases(kadm5_server_context *contextp,
+ kadm5_principal_ent_rec *add_princ,
+ kadm5_principal_ent_rec *del_princ)
+{
+ kadm5_ret_t ret;
+ struct iter_aliases_ctx iter;
+ struct iter_aliases_ctx iter_del;
+ krb5_principal new_name, old_name;
+ int match;
+
+ /*
+ * Yeah, this is O(N^2). Gathering and sorting all the aliases
+ * would be a bit of a pain; if we ever have principals with enough
+ * aliases for this to be a problem, we can fix it then.
+ */
+ for (iter.done = -1; iter.done != 1;) {
+ match = 0;
+ ret = iter_aliases(add_princ, &iter, &new_name);
+ if (ret)
+ return ret;
+ if (iter.done == 1)
+ break;
+ for (iter_del.done = -1; iter_del.done != 1;) {
+ ret = iter_aliases(del_princ, &iter_del, &old_name);
+ if (ret)
+ return ret;
+ if (iter_del.done == 1)
+ break;
+ if (!krb5_principal_compare(contextp->context, new_name, old_name))
+ continue;
+ free_HDB_Ext_Aliases(&iter_del.aliases);
+ match = 1;
+ break;
+ }
+ if (match)
+ continue;
+ ret = _kadm5_acl_check_permission(contextp, KADM5_PRIV_ADD, new_name);
+ if (ret) {
+ free_HDB_Ext_Aliases(&iter.aliases);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void
+v5_loop (krb5_context contextp,
+ krb5_auth_context ac,
+ krb5_boolean initial,
+ void *kadm_handlep,
+ krb5_socket_t fd,
+ int readonly)
+{
+ krb5_error_code ret;
+ krb5_data in, out;
+
+ for (;;) {
+ doing_useful_work = 0;
+ if(term_flag)
+ exit(0);
+ ret = krb5_read_priv_message(contextp, ac, &fd, &in);
+ if(ret == HEIM_ERR_EOF)
+ exit(0);
+ if(ret)
+ krb5_err(contextp, 1, ret, "krb5_read_priv_message");
+ doing_useful_work = 1;
+ ret = kadmind_dispatch(kadm_handlep, initial, &in, ac, fd, &out,
+ readonly);
+ if (ret)
+ krb5_err(contextp, 1, ret, "kadmind_dispatch");
+ krb5_data_free(&in);
+ if (out.length)
+ ret = krb5_write_priv_message(contextp, ac, &fd, &out);
+ krb5_data_free(&out);
+ if(ret)
+ krb5_err(contextp, 1, ret, "krb5_write_priv_message");
+ }
+}
+
+static krb5_boolean
+match_appl_version(const void *data, const char *appl_version)
+{
+ unsigned minor;
+ if(sscanf(appl_version, "KADM0.%u", &minor) != 1)
+ return 0;
+ /*XXX*/
+ *(unsigned*)(intptr_t)data = minor;
+ return 1;
+}
+
+static void
+handle_v5(krb5_context contextp,
+ krb5_keytab keytab,
+ krb5_socket_t fd,
+ int readonly)
+{
+ krb5_error_code ret;
+ krb5_ticket *ticket;
+ char *server_name;
+ char *client;
+ void *kadm_handlep;
+ krb5_boolean initial;
+ krb5_auth_context ac = NULL;
+ unsigned kadm_version = 1;
+ kadm5_config_params realm_params;
+
+ ret = krb5_recvauth_match_version(contextp, &ac, &fd,
+ match_appl_version, &kadm_version,
+ NULL, KRB5_RECVAUTH_IGNORE_VERSION,
+ keytab, &ticket);
+ if (ret) {
+ krb5_err(contextp, 1, ret, "krb5_recvauth");
+ return;
+ }
+ ret = krb5_unparse_name(contextp, ticket->server, &server_name);
+ if (ret) {
+ krb5_err(contextp, 1, ret, "krb5_unparse_name");
+ krb5_free_ticket(contextp, ticket);
+ return;
+ }
+ if (strncmp(server_name, KADM5_ADMIN_SERVICE,
+ strlen(KADM5_ADMIN_SERVICE)) != 0) {
+ krb5_errx(contextp, 1, "ticket for strange principal (%s)", server_name);
+ krb5_free_ticket(contextp, ticket);
+ free(server_name);
+ return;
+ }
+ free(server_name);
+
+ memset(&realm_params, 0, sizeof(realm_params));
+
+ if(kadm_version == 1) {
+ krb5_data params;
+ ret = krb5_read_priv_message(contextp, ac, &fd, &params);
+ if (ret) {
+ krb5_err(contextp, 1, ret, "krb5_read_priv_message");
+ krb5_free_ticket(contextp, ticket);
+ return;
+ }
+ ret = _kadm5_unmarshal_params(contextp, &params, &realm_params);
+ if (ret) {
+ krb5_err(contextp, 1, ret,
+ "Could not read or parse kadm5 parameters");
+ krb5_free_ticket(contextp, ticket);
+ return;
+ }
+ }
+
+ initial = ticket->ticket.flags.initial;
+ ret = krb5_unparse_name(contextp, ticket->client, &client);
+ krb5_free_ticket(contextp, ticket);
+ if (ret) {
+ krb5_err(contextp, 1, ret, "krb5_unparse_name");
+ return;
+ }
+ ret = kadm5_s_init_with_password_ctx(contextp,
+ client,
+ NULL,
+ KADM5_ADMIN_SERVICE,
+ &realm_params,
+ 0, 0,
+ &kadm_handlep);
+ if (ret) {
+ krb5_err(contextp, 1, ret, "kadm5_init_with_password_ctx");
+ return;
+ }
+ v5_loop(contextp, ac, initial, kadm_handlep, fd, readonly);
+}
+
+krb5_error_code
+kadmind_loop(krb5_context contextp,
+ krb5_keytab keytab,
+ krb5_socket_t sock,
+ int readonly)
+{
+ u_char buf[sizeof(KRB5_SENDAUTH_VERSION) + 4];
+ ssize_t n;
+ unsigned long len;
+
+ n = krb5_net_read(contextp, &sock, buf, 4);
+ if(n == 0)
+ exit(0);
+ if(n < 0)
+ krb5_err(contextp, 1, errno, "read");
+ _krb5_get_int(buf, &len, 4);
+
+ if (len == sizeof(KRB5_SENDAUTH_VERSION)) {
+
+ n = krb5_net_read(contextp, &sock, buf + 4, len);
+ if (n < 0)
+ krb5_err (contextp, 1, errno, "reading sendauth version");
+ if (n == 0)
+ krb5_errx (contextp, 1, "EOF reading sendauth version");
+
+ if(memcmp(buf + 4, KRB5_SENDAUTH_VERSION, len) == 0) {
+ handle_v5(contextp, keytab, sock, readonly);
+ return 0;
+ }
+ len += 4;
+ } else
+ len = 4;
+
+ handle_mit(contextp, buf, len, sock, readonly);
+
+ return 0;
+}
+
diff --git a/third_party/heimdal/kadmin/stash.c b/third_party/heimdal/kadmin/stash.c
new file mode 100644
index 0000000..c336230
--- /dev/null
+++ b/third_party/heimdal/kadmin/stash.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2004 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 "kadmin_locl.h"
+#include "kadmin-commands.h"
+
+extern int local_flag;
+
+int
+stash(struct stash_options *opt, int argc, char **argv)
+{
+ char buf[1024+1];
+ krb5_error_code ret;
+ krb5_enctype enctype;
+ hdb_master_key mkey;
+ int aret;
+
+ if(!local_flag) {
+ krb5_warnx(context, "stash is only available in local (-l) mode");
+ return 0;
+ }
+
+ ret = krb5_string_to_enctype(context, opt->enctype_string, &enctype);
+ if(ret) {
+ krb5_warn(context, ret, "%s", opt->enctype_string);
+ return 0;
+ }
+
+ if(opt->key_file_string == NULL) {
+ aret = asprintf(&opt->key_file_string, "%s/m-key", hdb_db_dir(context));
+ if (aret == -1)
+ errx(1, "out of memory");
+ }
+
+ ret = hdb_read_master_key(context, opt->key_file_string, &mkey);
+ if(ret && ret != ENOENT) {
+ krb5_warn(context, ret, "reading master key from %s",
+ opt->key_file_string);
+ return 0;
+ }
+
+ if (opt->convert_file_flag) {
+ if (ret)
+ krb5_warn(context, ret, "reading master key from %s",
+ opt->key_file_string);
+ hdb_free_master_key(context, mkey);
+ return 0;
+ } else {
+ krb5_keyblock key;
+ krb5_salt salt;
+ salt.salttype = KRB5_PW_SALT;
+ /* XXX better value? */
+ salt.saltvalue.data = NULL;
+ salt.saltvalue.length = 0;
+ if(opt->master_key_fd_integer != -1) {
+ ssize_t n;
+ n = read(opt->master_key_fd_integer, buf, sizeof(buf)-1);
+ if(n == 0)
+ krb5_warnx(context, "end of file reading passphrase");
+ else if(n < 0) {
+ krb5_warn(context, errno, "reading passphrase");
+ n = 0;
+ }
+ buf[n] = '\0';
+ buf[strcspn(buf, "\r\n")] = '\0';
+ } else if (opt->random_password_flag) {
+ random_password (buf, sizeof(buf));
+ printf("Using random master stash password: %s\n", buf);
+ } else {
+ if(UI_UTIL_read_pw_string(buf, sizeof(buf), "Master key: ",
+ UI_UTIL_FLAG_VERIFY)) {
+ hdb_free_master_key(context, mkey);
+ return 0;
+ }
+ }
+ ret = krb5_string_to_key_salt(context, enctype, buf, salt, &key);
+ if (ret == 0)
+ ret = hdb_add_master_key(context, &key, &mkey);
+ if (ret)
+ krb5_warn(context, errno, "setting master key");
+ krb5_free_keyblock_contents(context, &key);
+ }
+
+ {
+ char *new = NULL, *old = NULL;
+
+ aret = asprintf(&old, "%s.old", opt->key_file_string);
+ if (aret == -1) {
+ ret = ENOMEM;
+ goto out;
+ }
+ aret = asprintf(&new, "%s.new", opt->key_file_string);
+ if (aret == -1) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ if(unlink(new) < 0 && errno != ENOENT) {
+ ret = errno;
+ goto out;
+ }
+ krb5_warnx(context, "writing key to \"%s\"", opt->key_file_string);
+ ret = hdb_write_master_key(context, new, mkey);
+ if(ret)
+ unlink(new);
+ else {
+ unlink(old);
+#ifndef NO_POSIX_LINKS
+ if(link(opt->key_file_string, old) < 0 && errno != ENOENT) {
+ ret = errno;
+ unlink(new);
+ } else {
+#endif
+ if(rename(new, opt->key_file_string) < 0) {
+ ret = errno;
+ }
+#ifndef NO_POSIX_LINKS
+ }
+#endif
+ }
+ out:
+ free(old);
+ free(new);
+ if(ret)
+ krb5_warn(context, errno, "writing master key file");
+ }
+
+ hdb_free_master_key(context, mkey);
+ return 0;
+}
diff --git a/third_party/heimdal/kadmin/test_util.c b/third_party/heimdal/kadmin/test_util.c
new file mode 100644
index 0000000..56e4d11
--- /dev/null
+++ b/third_party/heimdal/kadmin/test_util.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 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 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 "kadmin_locl.h"
+
+krb5_context context;
+void *kadm_handle;
+
+struct {
+ const char *str;
+ int ret;
+ time_t t;
+} ts[] = {
+ { "2006-12-22 18:09:00", 0, 1166810940 },
+ { "2006-12-22", 0, 1166831999 },
+ { "2006-12-22 23:59:59", 0, 1166831999 }
+};
+
+static int
+test_time(void)
+{
+ int i, errors = 0;
+
+ for (i = 0; i < sizeof(ts)/sizeof(ts[0]); i++) {
+ time_t t;
+ int ret;
+
+ ret = str2time_t (ts[i].str, &t);
+ if (ret != ts[i].ret) {
+ printf("%d: %d is wrong ret\n", i, ret);
+ errors++;
+ }
+ else if (t != ts[i].t) {
+ printf("%d: %d is wrong time\n", i, (int)t);
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = 0;
+ ret += test_time();
+
+ krb5_free_context(context);
+
+ return ret;
+}
+
diff --git a/third_party/heimdal/kadmin/util.c b/third_party/heimdal/kadmin/util.c
new file mode 100644
index 0000000..6b83bb7
--- /dev/null
+++ b/third_party/heimdal/kadmin/util.c
@@ -0,0 +1,819 @@
+/*
+ * 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 "kadmin_locl.h"
+#include <parse_units.h>
+
+/*
+ * util.c - functions for parsing, unparsing, and editing different
+ * types of data used in kadmin.
+ */
+
+static int
+get_response(const char *prompt, const char *def, char *buf, size_t len);
+
+/*
+ * attributes
+ */
+
+struct units kdb_attrs[] = {
+ { "auth-data-reqd", KRB5_KDB_AUTH_DATA_REQUIRED },
+ { "no-auth-data-reqd", KRB5_KDB_NO_AUTH_DATA_REQUIRED },
+ { "disallow-client", KRB5_KDB_DISALLOW_CLIENT },
+ { "virtual", KRB5_KDB_VIRTUAL },
+ { "virtual-keys", KRB5_KDB_VIRTUAL_KEYS },
+ { "materialize", KRB5_KDB_MATERIALIZE },
+ { "allow-digest", KRB5_KDB_ALLOW_DIGEST },
+ { "allow-kerberos4", KRB5_KDB_ALLOW_KERBEROS4 },
+ { "trusted-for-delegation", KRB5_KDB_TRUSTED_FOR_DELEGATION },
+ { "ok-as-delegate", KRB5_KDB_OK_AS_DELEGATE },
+ { "new-princ", KRB5_KDB_NEW_PRINC },
+ { "support-desmd5", KRB5_KDB_SUPPORT_DESMD5 },
+ { "pwchange-service", KRB5_KDB_PWCHANGE_SERVICE },
+ { "disallow-svr", KRB5_KDB_DISALLOW_SVR },
+ { "requires-pw-change", KRB5_KDB_REQUIRES_PWCHANGE },
+ { "requires-hw-auth", KRB5_KDB_REQUIRES_HW_AUTH },
+ { "requires-pre-auth", KRB5_KDB_REQUIRES_PRE_AUTH },
+ { "disallow-all-tix", KRB5_KDB_DISALLOW_ALL_TIX },
+ { "disallow-dup-skey", KRB5_KDB_DISALLOW_DUP_SKEY },
+ { "disallow-proxiable", KRB5_KDB_DISALLOW_PROXIABLE },
+ { "disallow-renewable", KRB5_KDB_DISALLOW_RENEWABLE },
+ { "disallow-tgt-based", KRB5_KDB_DISALLOW_TGT_BASED },
+ { "disallow-forwardable", KRB5_KDB_DISALLOW_FORWARDABLE },
+ { "disallow-postdated", KRB5_KDB_DISALLOW_POSTDATED },
+ { NULL, 0 }
+};
+
+/*
+ * convert the attributes in `attributes' into a printable string
+ * in `str, len'
+ */
+
+void
+attributes2str(krb5_flags attributes, char *str, size_t len)
+{
+ unparse_flags (attributes, kdb_attrs, str, len);
+}
+
+/*
+ * convert the string in `str' into attributes in `flags'
+ * return 0 if parsed ok, else -1.
+ */
+
+int
+str2attributes(const char *str, krb5_flags *flags)
+{
+ int res;
+
+ res = parse_flags (str, kdb_attrs, *flags);
+ if (res < 0)
+ return res;
+ else {
+ *flags = res;
+ return 0;
+ }
+}
+
+/*
+ * try to parse the string `resp' into attributes in `attr', also
+ * setting the `bit' in `mask' if attributes are given and valid.
+ */
+
+int
+parse_attributes (const char *resp, krb5_flags *attr, int *mask, int bit)
+{
+ krb5_flags tmp = *attr;
+
+ if (str2attributes(resp, &tmp) == 0) {
+ *attr = tmp;
+ if (mask)
+ *mask |= bit;
+ return 0;
+ } else if(*resp == '?') {
+ print_flags_table (kdb_attrs, stderr);
+ } else {
+ fprintf (stderr, "Unable to parse \"%s\"\n", resp);
+ }
+ return -1;
+}
+
+/*
+ * allow the user to edit the attributes in `attr', prompting with `prompt'
+ */
+
+int
+edit_attributes (const char *prompt, krb5_flags *attr, int *mask, int bit)
+{
+ char buf[1024], resp[1024];
+
+ if (mask && (*mask & bit))
+ return 0;
+
+ attributes2str(*attr, buf, sizeof(buf));
+ for (;;) {
+ if(get_response("Attributes", buf, resp, sizeof(resp)) != 0)
+ return 1;
+ if (resp[0] == '\0')
+ break;
+ if (parse_attributes (resp, attr, mask, bit) == 0)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * try to parse the string `resp' into policy in `attr', also
+ * setting the `bit' in `mask' if attributes are given and valid.
+ */
+
+#define VALID_POLICY_NAME_CHARS \
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"
+
+int
+parse_policy (const char *resp, char **policy, int *mask, int bit)
+{
+ if (strspn(resp, VALID_POLICY_NAME_CHARS) == strlen(resp) &&
+ *resp != '\0') {
+
+ *policy = strdup(resp);
+ if (*policy == NULL) {
+ fprintf (stderr, "Out of memory");
+ return -1;
+ }
+ if (mask)
+ *mask |= bit;
+ return 0;
+ } else if(*resp == '?') {
+ print_flags_table (kdb_attrs, stderr);
+ } else {
+ fprintf (stderr, "Unable to parse \"%s\"\n", resp);
+ }
+ return -1;
+}
+
+/*
+ * allow the user to edit the attributes in `attr', prompting with `prompt'
+ */
+
+int
+edit_policy (const char *prompt, char **policy, int *mask, int bit)
+{
+ char buf[1024], resp[1024];
+
+ if (mask && (*mask & bit))
+ return 0;
+
+ buf[0] = '\0';
+ strlcpy(buf, "default", sizeof (buf));
+ for (;;) {
+ if(get_response("Policy", buf, resp, sizeof(resp)) != 0)
+ return 1;
+ if (resp[0] == '\0')
+ break;
+ if (parse_policy (resp, policy, mask, bit) == 0)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * time_t
+ * the special value 0 means ``never''
+ */
+
+/*
+ * Convert the time `t' to a string representation in `str' (of max
+ * size `len'). If include_time also include time, otherwise just
+ * date.
+ */
+
+void
+time_t2str(time_t t, char *str, size_t len, int include_time)
+{
+ if(t) {
+ if(include_time)
+ strftime(str, len, "%Y-%m-%d %H:%M:%S UTC", gmtime(&t));
+ else
+ strftime(str, len, "%Y-%m-%d", gmtime(&t));
+ } else
+ snprintf(str, len, "never");
+}
+
+/*
+ * Convert the time representation in `str' to a time in `time'.
+ * Return 0 if succesful, else -1.
+ */
+
+int
+str2time_t (const char *str, time_t *t)
+{
+ const char *p;
+ struct tm tm, tm2;
+
+ memset (&tm, 0, sizeof (tm));
+ memset (&tm2, 0, sizeof (tm2));
+
+ while(isspace((unsigned char)*str))
+ str++;
+
+ if (str[0] == '+') {
+ str++;
+ *t = parse_time(str, "month");
+ if (*t < 0)
+ return -1;
+ *t += time(NULL);
+ return 0;
+ }
+ if (str[0] == '-') {
+ str++;
+ *t = parse_time(str, "month");
+ if (*t < 0)
+ return -1;
+ *t = time(NULL) - *t;
+ return 0;
+ }
+
+ if(strcasecmp(str, "never") == 0) {
+ *t = 0;
+ return 0;
+ }
+
+ if(strcasecmp(str, "now") == 0) {
+ *t = time(NULL);
+ return 0;
+ }
+
+ p = strptime (str, "%Y-%m-%d", &tm);
+
+ if (p == NULL)
+ return -1;
+
+ while(isspace((unsigned char)*p))
+ p++;
+
+ /* XXX this is really a bit optimistic, we should really complain
+ if there was a problem parsing the time */
+ if(p[0] != '\0' && strptime (p, "%H:%M:%S", &tm2) != NULL) {
+ tm.tm_hour = tm2.tm_hour;
+ tm.tm_min = tm2.tm_min;
+ tm.tm_sec = tm2.tm_sec;
+ } else {
+ /* Do it on the end of the day */
+ tm.tm_hour = 23;
+ tm.tm_min = 59;
+ tm.tm_sec = 59;
+ }
+
+ *t = tm2time (tm, 0);
+ return 0;
+}
+
+/*
+ * try to parse the time in `resp' storing it in `value'
+ */
+
+int
+parse_timet (const char *resp, krb5_timestamp *value, int *mask, int bit)
+{
+ time_t tmp;
+
+ if (str2time_t(resp, &tmp) == 0) {
+ *value = tmp;
+ if(mask)
+ *mask |= bit;
+ return 0;
+ }
+ if(*resp != '?')
+ fprintf (stderr, "Unable to parse time \"%s\"\n", resp);
+ fprintf (stderr, "Print date on format YYYY-mm-dd [hh:mm:ss]\n");
+ return -1;
+}
+
+/*
+ * allow the user to edit the time in `value'
+ */
+
+int
+edit_timet (const char *prompt, krb5_timestamp *value, int *mask, int bit)
+{
+ char buf[1024], resp[1024];
+
+ if (mask && (*mask & bit))
+ return 0;
+
+ time_t2str (*value, buf, sizeof (buf), 0);
+
+ for (;;) {
+ if(get_response(prompt, buf, resp, sizeof(resp)) != 0)
+ return 1;
+ if (parse_timet (resp, value, mask, bit) == 0)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * deltat
+ * the special value 0 means ``unlimited''
+ */
+
+/*
+ * convert the delta_t value in `t' into a printable form in `str, len'
+ */
+
+void
+deltat2str(unsigned t, char *str, size_t len)
+{
+ /*
+ * A time delta in kadmin context is a positive number, and there's no
+ * point to it being possibly as large as 2^64 -1, so we use unsigned
+ * instead of a more generally appropriate type for time deltas (which
+ * conceptually can be negative, which in kadmin context there's no need
+ * for).
+ */
+ if (t == 0 || t > INT_MAX)
+ snprintf(str, len, "unlimited");
+ else
+ unparse_time(t, str, len);
+}
+
+/*
+ * parse the delta value in `str', storing result in `*delta'
+ * return 0 if ok, else -1
+ */
+
+int
+str2deltat(const char *str, krb5_deltat *delta)
+{
+ int res;
+
+ if(strcasecmp(str, "unlimited") == 0) {
+ /*
+ * Using zero to mean "unlimited" is unfortunate. We should use
+ * `UINT_MAX'. However, we've had this assumption that zero means
+ * unlimited, so there are HDB entries with present-but-zero max-life
+ * and max-renew-life.
+ *
+ * We could switch to using `UINT_MAX' or `UINT64_MAX' for "unlimited",
+ * but we'd have to continue to treat `0' as special for some time.
+ */
+ *delta = 0;
+ return 0;
+ }
+ res = parse_time(str, "day");
+ if (res < 0)
+ return res;
+ else {
+ *delta = res;
+ return 0;
+ }
+}
+
+/*
+ * try to parse the string in `resp' into a deltad in `value'
+ * `mask' will get the bit `bit' set if a value was given.
+ */
+
+int
+parse_deltat (const char *resp, krb5_deltat *value, int *mask, int bit)
+{
+ krb5_deltat tmp;
+
+ if (str2deltat(resp, &tmp) == 0) {
+ *value = tmp;
+ if (mask)
+ *mask |= bit;
+ return 0;
+ } else if(*resp == '?') {
+ print_time_table (stderr);
+ } else {
+ fprintf (stderr, "Unable to parse time \"%s\"\n", resp);
+ }
+ return -1;
+}
+
+/*
+ * allow the user to edit the deltat in `value'
+ */
+
+int
+edit_deltat (const char *prompt, krb5_deltat *value, int *mask, int bit)
+{
+ char buf[1024], resp[1024];
+
+ if (mask && (*mask & bit))
+ return 0;
+
+ deltat2str(*value, buf, sizeof(buf));
+ for (;;) {
+ if(get_response(prompt, buf, resp, sizeof(resp)) != 0)
+ return 1;
+ if (parse_deltat (resp, value, mask, bit) == 0)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * allow the user to edit `ent'
+ */
+
+void
+set_defaults(kadm5_principal_ent_t ent, int *mask,
+ kadm5_principal_ent_t default_ent, int default_mask)
+{
+ if (default_ent
+ && (default_mask & KADM5_MAX_LIFE)
+ && !(*mask & KADM5_MAX_LIFE))
+ ent->max_life = default_ent->max_life;
+
+ if (default_ent
+ && (default_mask & KADM5_MAX_RLIFE)
+ && !(*mask & KADM5_MAX_RLIFE))
+ ent->max_renewable_life = default_ent->max_renewable_life;
+
+ if (default_ent
+ && (default_mask & KADM5_PRINC_EXPIRE_TIME)
+ && !(*mask & KADM5_PRINC_EXPIRE_TIME))
+ ent->princ_expire_time = default_ent->princ_expire_time;
+
+ if (default_ent
+ && (default_mask & KADM5_PW_EXPIRATION)
+ && !(*mask & KADM5_PW_EXPIRATION))
+ ent->pw_expiration = default_ent->pw_expiration;
+
+ if (default_ent
+ && (default_mask & KADM5_ATTRIBUTES)
+ && !(*mask & KADM5_ATTRIBUTES))
+ ent->attributes = default_ent->attributes & ~KRB5_KDB_DISALLOW_ALL_TIX;
+
+ if (default_ent
+ && (default_mask & KADM5_POLICY)
+ && !(*mask & KADM5_POLICY)) {
+ ent->policy = strdup(default_ent->policy);
+ if (ent->policy == NULL)
+ abort();
+ }
+}
+
+int
+edit_entry(kadm5_principal_ent_t ent, int *mask,
+ kadm5_principal_ent_t default_ent, int default_mask)
+{
+
+ set_defaults(ent, mask, default_ent, default_mask);
+
+ if(edit_deltat ("Max ticket life", &ent->max_life, mask,
+ KADM5_MAX_LIFE) != 0)
+ return 1;
+
+ if(edit_deltat ("Max renewable life", &ent->max_renewable_life, mask,
+ KADM5_MAX_RLIFE) != 0)
+ return 1;
+
+ if(edit_timet ("Principal expiration time", &ent->princ_expire_time, mask,
+ KADM5_PRINC_EXPIRE_TIME) != 0)
+ return 1;
+
+ if(edit_timet ("Password expiration time", &ent->pw_expiration, mask,
+ KADM5_PW_EXPIRATION) != 0)
+ return 1;
+
+ if(edit_attributes ("Attributes", &ent->attributes, mask,
+ KADM5_ATTRIBUTES) != 0)
+ return 1;
+
+ if(edit_policy ("Policy", &ent->policy, mask,
+ KADM5_POLICY) != 0)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Parse the arguments, set the fields in `ent' and the `mask' for the
+ * entries having been set.
+ * Return 1 on failure and 0 on success.
+ */
+
+int
+set_entry(krb5_context contextp,
+ kadm5_principal_ent_t ent,
+ int *mask,
+ const char *max_ticket_life,
+ const char *max_renewable_life,
+ const char *expiration,
+ const char *pw_expiration,
+ const char *attributes,
+ const char *policy)
+{
+ if (max_ticket_life != NULL) {
+ if (parse_deltat (max_ticket_life, &ent->max_life,
+ mask, KADM5_MAX_LIFE)) {
+ krb5_warnx (contextp, "unable to parse `%s'", max_ticket_life);
+ return 1;
+ }
+ }
+ if (max_renewable_life != NULL) {
+ if (parse_deltat (max_renewable_life, &ent->max_renewable_life,
+ mask, KADM5_MAX_RLIFE)) {
+ krb5_warnx (contextp, "unable to parse `%s'", max_renewable_life);
+ return 1;
+ }
+ }
+
+ if (expiration) {
+ if (parse_timet (expiration, &ent->princ_expire_time,
+ mask, KADM5_PRINC_EXPIRE_TIME)) {
+ krb5_warnx (contextp, "unable to parse `%s'", expiration);
+ return 1;
+ }
+ }
+ if (pw_expiration) {
+ if (parse_timet (pw_expiration, &ent->pw_expiration,
+ mask, KADM5_PW_EXPIRATION)) {
+ krb5_warnx (contextp, "unable to parse `%s'", pw_expiration);
+ return 1;
+ }
+ }
+ if (attributes != NULL) {
+ if (parse_attributes (attributes, &ent->attributes,
+ mask, KADM5_ATTRIBUTES)) {
+ krb5_warnx (contextp, "unable to parse `%s'", attributes);
+ return 1;
+ }
+ }
+ if (policy != NULL) {
+ if (parse_policy (policy, &ent->policy,
+ mask, KADM5_POLICY)) {
+ krb5_warnx (contextp, "unable to parse `%s'", attributes);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Does `string' contain any globing characters?
+ */
+
+static int
+is_expression(const char *string)
+{
+ const char *p;
+ int quote = 0;
+
+ for(p = string; *p; p++) {
+ if(quote) {
+ quote = 0;
+ continue;
+ }
+ if(*p == '\\')
+ quote++;
+ else if(strchr("[]*?", *p) != NULL)
+ return 1;
+ }
+ return 0;
+}
+
+struct foreach_principal_data {
+ const char *funcname;
+ int (*func)(krb5_principal, void *);
+ void *data;
+};
+
+static int
+foreach_principal_cb(void *data, const char *p)
+{
+ struct foreach_principal_data *d = data;
+ krb5_principal princ;
+ krb5_error_code ret;
+
+ ret = krb5_parse_name(context, p, &princ);
+ if (ret)
+ return ret;
+
+ ret = d->func(princ, d->data);
+ krb5_free_principal(context, princ);
+ if (ret) {
+ krb5_warn(context, ret, "%s %s", d->funcname, p);
+ krb5_clear_error_message(context);
+ }
+ return ret;
+}
+
+/*
+ * Loop over all principals matching exp. If any of calls to `func'
+ * failes, the first error is returned when all principals are
+ * processed.
+ */
+int
+foreach_principal(const char *exp_str,
+ int (*func)(krb5_principal, void*),
+ const char *funcname,
+ void *data)
+{
+ struct foreach_principal_data d;
+ krb5_error_code ret;
+ krb5_principal p;
+ int is_expr;
+ int go_slow =
+ secure_getenv("KADMIN_USE_GET_PRINCIPALS") != NULL &&
+ *secure_getenv("KADMIN_USE_GET_PRINCIPALS") != '\0';
+
+ /* if this isn't an expression, there is no point in wading
+ through the whole database looking for matches */
+ is_expr = is_expression(exp_str);
+
+ d.funcname = funcname;
+ d.func = func;
+ d.data = data;
+
+ if (is_expr && !go_slow) {
+ ret = kadm5_iter_principals(kadm_handle, exp_str,
+ foreach_principal_cb, &d);
+ if (ret == 0)
+ return 0;
+ if (ret != KADM5_AUTH_LIST) {
+ krb5_warn(context, ret, "kadm5_iter_principals");
+ return ret;
+ }
+ } else if (is_expr) {
+ char **princs = NULL;
+ int count = 0;
+
+ /*
+ * This is just for testing, and maybe in case there are HDB backends
+ * that are not re-entrant (LDAP?).
+ */
+ ret = kadm5_get_principals(kadm_handle, exp_str, &princs, &count);
+ if (ret == 0 && count > 0) {
+ int i;
+
+ for (i = 0; ret == 0 && i < count; i++)
+ ret = foreach_principal_cb(&d, princs[i]);
+ kadm5_free_name_list(kadm_handle, princs, &count);
+ return ret;
+ }
+ if (ret != KADM5_AUTH_LIST) {
+ krb5_warn(context, ret, "kadm5_iter_principals");
+ return ret;
+ }
+ }
+ /* we might be able to perform the requested opreration even
+ if we're not allowed to list principals */
+ ret = krb5_parse_name(context, exp_str, &p);
+ if (ret) {
+ krb5_warn(context, ret, "krb5_parse_name(%s)", exp_str);
+ return ret;
+ }
+ ret = (*func)(p, data);
+ if (ret) {
+ krb5_warn(context, ret, "%s %s", funcname, exp_str);
+ krb5_clear_error_message(context);
+ }
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+/*
+ * prompt with `prompt' and default value `def', and store the reply
+ * in `buf, len'
+ */
+
+#include <setjmp.h>
+
+static jmp_buf jmpbuf;
+
+static void
+interrupt(int sig)
+{
+ longjmp(jmpbuf, 1);
+}
+
+static int
+get_response(const char *prompt, const char *def, char *buf, size_t len)
+{
+ char *p;
+ void (*osig)(int);
+
+ osig = signal(SIGINT, interrupt);
+ if(setjmp(jmpbuf)) {
+ signal(SIGINT, osig);
+ fprintf(stderr, "\n");
+ return 1;
+ }
+
+ fprintf(stderr, "%s [%s]:", prompt, def);
+ if(fgets(buf, len, stdin) == NULL) {
+ int save_errno = errno;
+ if(ferror(stdin))
+ krb5_err(context, 1, save_errno, "<stdin>");
+ signal(SIGINT, osig);
+ return 1;
+ }
+ p = strchr(buf, '\n');
+ if(p)
+ *p = '\0';
+ if(strcmp(buf, "") == 0)
+ strlcpy(buf, def, len);
+ signal(SIGINT, osig);
+ return 0;
+}
+
+/*
+ * return [0, 16) or -1
+ */
+
+static int
+hex2n (char c)
+{
+ static char hexdigits[] = "0123456789abcdef";
+ const char *p;
+
+ p = strchr (hexdigits, tolower((unsigned char)c));
+ if (p == NULL)
+ return -1;
+ else
+ return p - hexdigits;
+}
+
+/*
+ * convert a key in a readable format into a keyblock.
+ * return 0 iff succesful, otherwise `err' should point to an error message
+ */
+
+int
+parse_des_key (const char *key_string, krb5_key_data *key_data,
+ const char **error)
+{
+ const char *p = key_string;
+ unsigned char bits[8];
+ int i;
+
+ if (strlen (key_string) != 16) {
+ *error = "bad length, should be 16 for DES key";
+ return 1;
+ }
+ for (i = 0; i < 8; ++i) {
+ int d1, d2;
+
+ d1 = hex2n(p[2 * i]);
+ d2 = hex2n(p[2 * i + 1]);
+ if (d1 < 0 || d2 < 0) {
+ *error = "non-hex character";
+ return 1;
+ }
+ bits[i] = (d1 << 4) | d2;
+ }
+ for (i = 0; i < 3; ++i) {
+ key_data[i].key_data_ver = 2;
+ key_data[i].key_data_kvno = 0;
+ /* key */
+ key_data[i].key_data_type[0] = ETYPE_DES_CBC_CRC;
+ key_data[i].key_data_length[0] = 8;
+ key_data[i].key_data_contents[0] = malloc(8);
+ if (key_data[i].key_data_contents[0] == NULL) {
+ *error = "malloc";
+ return ENOMEM;
+ }
+ memcpy (key_data[i].key_data_contents[0], bits, 8);
+ /* salt */
+ key_data[i].key_data_type[1] = KRB5_PW_SALT;
+ key_data[i].key_data_length[1] = 0;
+ key_data[i].key_data_contents[1] = NULL;
+ }
+ key_data[0].key_data_type[0] = ETYPE_DES_CBC_MD5;
+ key_data[1].key_data_type[0] = ETYPE_DES_CBC_MD4;
+ return 0;
+}