diff options
Diffstat (limited to 'third_party/heimdal/kadmin')
34 files changed, 11771 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..e7fe58f --- /dev/null +++ b/third_party/heimdal/kadmin/Makefile.am @@ -0,0 +1,98 @@ +# $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 \ + ../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..80d05ec --- /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..4b89ca6 --- /dev/null +++ b/third_party/heimdal/kadmin/ank.c @@ -0,0 +1,525 @@ +/* + * 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); + kadm5_get_principal(kadm_handle, princ_ent, &princ, + KADM5_PRINCIPAL | KADM5_KVNO | KADM5_ATTRIBUTES); + 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..ad14ed2 --- /dev/null +++ b/third_party/heimdal/kadmin/check.c @@ -0,0 +1,264 @@ +/* + * 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(kadm_handle, 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(kadm_handle, &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; + 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) { + printf("%s doesn't exist, are you sure %s is a realm in your database", + 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) { + printf("%s doesn't exist, " + "there is no way to do remote administration", 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) { + printf("%s doesn't exist, " + "there is no way to do change password", 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 doesn't exists) + */ + + 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) { + printf("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; + } + } + + foreach_principal("*", do_check_entry, "check", NULL); + + 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..2f3c1c1 --- /dev/null +++ b/third_party/heimdal/kadmin/cpw.c @@ -0,0 +1,215 @@ +/* + * 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; +}; + +static int +set_random_key (krb5_principal principal, int keepold) +{ + krb5_error_code ret; + int i; + krb5_keyblock *keys; + int num_keys; + + ret = kadm5_randkey_principal_3(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 (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(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 (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(kadm_handle, principal, keepold, 0, NULL, + password); + memset_s(pwbuf, sizeof(pwbuf), 0, sizeof(pwbuf)); + return ret; +} + +static int +set_key_data (krb5_principal principal, krb5_key_data *key_data, int keepold) +{ + krb5_error_code ret; + + ret = kadm5_chpass_principal_with_key_3(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 (principal, e->keepold); + else if (e->random_password) + return set_random_password (principal, e->keepold); + else if (e->key_data) + return set_key_data (principal, e->key_data, e->keepold); + else + return set_password (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; + krb5_key_data key_data[3]; + + 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; + } + data.key_data = key_data; + } + + for(i = 0; i < argc; i++) + ret = foreach_principal(argv[i], do_cpw_entry, "cpw", &data); + + if (data.key_data) { + int16_t dummy; + kadm5_free_key_data (kadm_handle, &dummy, 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..a066f56 --- /dev/null +++ b/third_party/heimdal/kadmin/del.c @@ -0,0 +1,215 @@ +/* + * 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(kadm_handle, principal); +} + +int +del_entry(void *opt, int argc, char **argv) +{ + int i; + krb5_error_code ret = 0; + + for(i = 0; i < argc; i++) { + ret = foreach_principal(argv[i], do_del_entry, "del", NULL); + if (ret) + break; + } + 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; + + for(i = 0; i < argc; i++) { + ret = foreach_principal(argv[i], do_del_ns_entry, "del_ns", NULL); + if (ret) + break; + } + 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..adb2e28 --- /dev/null +++ b/third_party/heimdal/kadmin/ext.c @@ -0,0 +1,215 @@ +/* + * 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; +}; + +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(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(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(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.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; + } + + 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..a884e11 --- /dev/null +++ b/third_party/heimdal/kadmin/get.c @@ -0,0 +1,642 @@ +/* + * 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; + uint32_t n; +}; + +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) +{ + 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]; + krb5_get_pw_salt (context, princ->principal, &def_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; + int ret; + + 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; + int ret; + 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; + int ret; + 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; + + memset(&princ, 0, sizeof(princ)); + ret = kadm5_get_principal(kadm_handle, principal, + &princ, + e->mask | e->extra_mask); + if (ret == 0) { + (e->format)(e, &princ); + kadm5_free_principal_ent(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]; + krb5_error_code ret; + + 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 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, NULL); + 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, argc, argv); + + 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.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); + + 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; + 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..e8a1e8a --- /dev/null +++ b/third_party/heimdal/kadmin/kadmin-commands.in @@ -0,0 +1,728 @@ +/* + * 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.confg in" + } + 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" + } + 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..b0e8529 --- /dev/null +++ b/third_party/heimdal/kadmin/kadmin.1 @@ -0,0 +1,624 @@ +.\" 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 +.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 +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 +When a client requests a service ticket for a service principal +name that is an alias of a principal in a different realm, the +TGS will return a referral to that realm. +This compares favorably to using +.Ar [domain_realm] +entries in the KDC's +.Ar krb5.conf , +but 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 issuing referrals for entire namespaces +of hostnames. +An 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 +.Ar principal [kvno] +.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 principals 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 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. +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 +.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. 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 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, which will set the complete +list of aliases for the principal. +Use the +.Nm add_alias +command instead to add an alias without 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: +.Li new-princ , +.Li support-desmd5 , +.Li pwchange-service , +.Li disallow-client , +.Li disallow-svr , +.Li requires-pw-change , +.Li requires-hw-auth , +.Li requires-pre-auth , +.Li allow-digest , +.Li trusted-for-delegation , +.Li ok-as-delegate , +.Li disallow-all-tix , +.Li disallow-dup-skey , +.Li disallow-proxiable , +.Li disallow-renewable , +.Li disallow-tgt-based , +.Li disallow-forwardable , +.Li disallow-postdated , +.Li no-auth-data-reqd +.Pp +Attributes may be negated with a "-", e.g., +.Pp +kadmin -l modify -a -disallow-proxiable user +.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 +.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. +.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 +.Bd -ragged -offset indent +Writes the Kerberos master key to a file used by the KDC. +.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..d2f5aba --- /dev/null +++ b/third_party/heimdal/kadmin/kadmin.c @@ -0,0 +1,309 @@ +/* + * 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; + } + } + + 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..d76265b --- /dev/null +++ b/third_party/heimdal/kadmin/kadmin_locl.h @@ -0,0 +1,167 @@ +/* + * 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; + +#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..4449506 --- /dev/null +++ b/third_party/heimdal/kadmin/kadmind.c @@ -0,0 +1,222 @@ +/* + * 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" + +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; +static int help_flag; +static int version_flag; +static int debug_flag; +static int readonly_flag; +static char *port_str; +char *realm; + +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 + }, + { + "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 }, + { "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); +} + +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 */ + + kadmind_loop(context, keytab, sfd, readonly_flag); + + return 0; +} 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..7c7b2dd --- /dev/null +++ b/third_party/heimdal/kadmin/mod.c @@ -0,0 +1,826 @@ +/* + * 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; + + 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); +} + +static int +do_mod_entry(krb5_principal principal, void *data) +{ + krb5_error_code ret; + kadm5_principal_ent_rec princ; + int mask = 0; + struct modify_options *e = data; + + memset (&princ, 0, sizeof(princ)); + ret = kadm5_get_principal(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(kadm_handle, &princ, mask); + if(ret) + krb5_warn(context, ret, "kadm5_modify_principal"); + } + + kadm5_free_principal_ent(kadm_handle, &princ); + return ret; +} + +int +mod_entry(struct modify_options *opt, int argc, char **argv) +{ + krb5_error_code ret = 0; + int i; + + for(i = 0; i < argc; i++) { + ret = foreach_principal(argv[i], do_mod_entry, "mod", opt); + if (ret) + break; + } + 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 modify_namespace_options *e = data; + + memset (&princ, 0, sizeof(princ)); + ret = kadm5_get_principal(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(kadm_handle, &princ, mask); + if(ret) + krb5_warn(context, ret, "kadm5_modify_principal"); + } + + kadm5_free_principal_ent(kadm_handle, &princ); + return ret; +} + +int +modify_namespace(struct modify_namespace_options *opt, int argc, char **argv) +{ + krb5_error_code ret = 0; + int i; + + for(i = 0; i < argc; i++) { + ret = foreach_principal(argv[i], do_mod_ns_entry, "mod_ns", opt); + if (ret) + break; + } + 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; + int i; + + for(i = 0; i < argc; i++) { + ret = foreach_principal(argv[i], do_mod_ns_kr, "mod_ns", opt); + if (ret) + break; + } + return ret != 0; + return 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..1ae10f1 --- /dev/null +++ b/third_party/heimdal/kadmin/rpc.c @@ -0,0 +1,1151 @@ +/* + * 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; + } + + 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; + 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..52f2020 --- /dev/null +++ b/third_party/heimdal/kadmin/server.c @@ -0,0 +1,885 @@ +/* + * 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 *); + +static kadm5_ret_t +kadmind_dispatch(void *kadm_handlep, krb5_boolean initial, + krb5_data *in, 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) + goto fail; + + switch(cmd){ + case kadm_get:{ + op = "GET"; + ret = krb5_ret_principal(sp, &princ); + if(ret) + goto fail; + ret = krb5_ret_int32(sp, &mask); + if (ret) + 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) + 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. + */ + 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 = 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 = KADM5_READ_ONLY; + goto fail; + } + ret = kadm5_ret_principal_ent(sp, &ent); + if(ret) + goto fail; + ret = krb5_ret_int32(sp, &mask); + if(ret){ + kadm5_free_principal_ent(kadm_handlep, &ent); + goto fail; + } + ret = krb5_ret_string(sp, &password); + if(ret){ + 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){ + 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) { + 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 = KADM5_READ_ONLY; + goto fail; + } + ret = kadm5_ret_principal_ent(sp, &ent); + if(ret) + goto fail; + ret = krb5_ret_int32(sp, &mask); + if(ret){ + 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){ + 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) { + kadm5_free_principal_ent(contextp, &ent); + goto fail; + } + ret = check_aliases(contextp, &ent, &ent_prev); + kadm5_free_principal_ent(contextp, &ent_prev); + if (ret) { + 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 = KADM5_READ_ONLY; + goto fail; + } + ret = krb5_ret_principal(sp, &princ); + if (ret) + goto fail; + ret = krb5_ret_int32(sp, &kvno); + if (ret == HEIM_ERR_EOF) { + kvno = 0; + } else if (ret) { + 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) + 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 = KADM5_READ_ONLY; + goto fail; + } + ret = krb5_ret_principal(sp, &princ); + if(ret) + goto fail; + ret = krb5_ret_principal(sp, &princ2); + if (ret) + 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) + goto fail; + + ret = kadm5_rename_principal(kadm_handlep, princ, princ2); + ret_sp = krb5_store_int32(sp, ret); + break; + } + case kadm_chpass:{ + krb5_boolean is_self_cpw, allow_self_cpw; + + op = "CHPASS"; + if (readonly) { + 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) + 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) + 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 = 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) + 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 = ERANGE; + goto fail; + } + + key_data = malloc (n_key_data * sizeof(*key_data)); + if (key_data == NULL && n_key_data != 0) { + 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); + 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); + 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 = KADM5_READ_ONLY; + goto fail; + } + ret = krb5_ret_principal(sp, &princ); + if (ret) + 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) + 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) + 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) + goto fail; + + if (n_ks_tuple < 0) { + ret = EOVERFLOW; + goto fail; + } + free(ks_tuple); + if ((ks_tuple = calloc(n_ks_tuple, sizeof (*ks_tuple))) == NULL) { + 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) { + free(ks_tuple); + goto fail; + } + ret = krb5_ret_int32(sp, &ks_tuple[i].ks_salttype); + if (ret != 0) { + 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(sp, privs); + break; + } + case kadm_get_princs:{ + op = "LIST"; + ret = krb5_ret_int32(sp, &tmp); + if(ret) + goto fail; + if(tmp){ + ret = krb5_ret_string(sp, &expression); + if(ret) + 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){ + free(expression); + goto fail; + } + ret = kadm5_get_principals(kadm_handlep, expression, &princs, &n_princs); + free(expression); + ret_sp = krb5_store_int32(rsp, ret); + if (ret == 0) { + int i; + + ret_sp = krb5_store_int32(sp, n_princs); + for (i = 0; ret_sp == 0 && i < n_princs; i++) + ret_sp = krb5_store_string(sp, princs[i]); + kadm5_free_name_list(kadm_handlep, princs, &n_princs); + } + break; + } + default: + krb5_warnx(contextp->context, "%s: UNKNOWN OP %d", client, cmd); + ret_sp = krb5_store_int32(sp, 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, &out, readonly); + if (ret) + krb5_err(contextp, 1, ret, "kadmind_dispatch"); + krb5_data_free(&in); + 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, ¶ms); + if (ret) { + krb5_err(contextp, 1, ret, "krb5_read_priv_message"); + krb5_free_ticket(contextp, ticket); + return; + } + ret = _kadm5_unmarshal_params(contextp, ¶ms, &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..720d9d3 --- /dev/null +++ b/third_party/heimdal/kadmin/util.c @@ -0,0 +1,762 @@ +/* + * 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[] = { + { "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 }, + { "no-auth-data-reqd", KRB5_KDB_NO_AUTH_DATA_REQUIRED }, + { 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) +{ + 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) { + *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; +} + +/* + * 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) +{ + char **princs = NULL; + int num_princs = 0; + int i; + krb5_error_code saved_ret = 0, ret = 0; + krb5_principal princ_ent; + int is_expr; + + /* 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); + if(is_expr) + ret = kadm5_get_principals(kadm_handle, exp_str, &princs, &num_princs); + if(!is_expr || ret == KADM5_AUTH_LIST) { + /* we might be able to perform the requested opreration even + if we're not allowed to list principals */ + num_princs = 1; + princs = malloc(sizeof(*princs)); + if(princs == NULL) + return ENOMEM; + princs[0] = strdup(exp_str); + if(princs[0] == NULL){ + free(princs); + return ENOMEM; + } + } else if(ret) { + krb5_warn(context, ret, "kadm5_get_principals"); + return ret; + } + for(i = 0; i < num_princs; i++) { + ret = krb5_parse_name(context, princs[i], &princ_ent); + if(ret){ + krb5_warn(context, ret, "krb5_parse_name(%s)", princs[i]); + continue; + } + ret = (*func)(princ_ent, data); + if(ret) { + krb5_warn(context, ret, "%s %s", funcname, princs[i]); + krb5_clear_error_message(context); + if (saved_ret == 0) + saved_ret = ret; + } + krb5_free_principal(context, princ_ent); + } + if (ret == 0 && saved_ret != 0) + ret = saved_ret; + kadm5_free_name_list(kadm_handle, princs, &num_princs); + 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; +} |