From 8daa83a594a2e98f39d764422bfbdbc62c9efd44 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:20:00 +0200 Subject: Adding upstream version 2:4.20.0+dfsg. Signed-off-by: Daniel Baumann --- third_party/heimdal/lib/krb5/Makefile.am | 454 +++ third_party/heimdal/lib/krb5/NTMakefile | 542 +++ third_party/heimdal/lib/krb5/acache.c | 1129 ++++++ third_party/heimdal/lib/krb5/acl.c | 293 ++ third_party/heimdal/lib/krb5/add_et_list.c | 54 + third_party/heimdal/lib/krb5/addr_families.c | 1574 ++++++++ third_party/heimdal/lib/krb5/aes-test.c | 1058 +++++ third_party/heimdal/lib/krb5/an2ln_plugin.h | 91 + third_party/heimdal/lib/krb5/aname_to_localname.c | 471 +++ third_party/heimdal/lib/krb5/appdefault.c | 140 + third_party/heimdal/lib/krb5/asn1_glue.c | 162 + third_party/heimdal/lib/krb5/auth_context.c | 621 +++ third_party/heimdal/lib/krb5/authdata.c | 124 + third_party/heimdal/lib/krb5/build_ap_req.c | 68 + third_party/heimdal/lib/krb5/build_auth.c | 267 ++ third_party/heimdal/lib/krb5/cache.c | 2300 +++++++++++ third_party/heimdal/lib/krb5/ccache_plugin.h | 46 + third_party/heimdal/lib/krb5/changepw.c | 860 +++++ third_party/heimdal/lib/krb5/codec.c | 214 ++ third_party/heimdal/lib/krb5/config_file.c | 758 ++++ third_party/heimdal/lib/krb5/constants.c | 67 + third_party/heimdal/lib/krb5/context.c | 1506 ++++++++ third_party/heimdal/lib/krb5/convert_creds.c | 92 + third_party/heimdal/lib/krb5/copy_host_realm.c | 72 + third_party/heimdal/lib/krb5/crc.c | 69 + third_party/heimdal/lib/krb5/creds.c | 282 ++ third_party/heimdal/lib/krb5/crypto-aes-sha1.c | 177 + third_party/heimdal/lib/krb5/crypto-aes-sha2.c | 199 + third_party/heimdal/lib/krb5/crypto-algs.c | 94 + third_party/heimdal/lib/krb5/crypto-arcfour.c | 368 ++ third_party/heimdal/lib/krb5/crypto-des-common.c | 158 + third_party/heimdal/lib/krb5/crypto-des.c | 403 ++ third_party/heimdal/lib/krb5/crypto-des3.c | 292 ++ third_party/heimdal/lib/krb5/crypto-evp.c | 674 ++++ third_party/heimdal/lib/krb5/crypto-null.c | 103 + third_party/heimdal/lib/krb5/crypto-pk.c | 295 ++ third_party/heimdal/lib/krb5/crypto-rand.c | 152 + third_party/heimdal/lib/krb5/crypto-stubs.c | 102 + third_party/heimdal/lib/krb5/crypto.c | 3248 ++++++++++++++++ third_party/heimdal/lib/krb5/crypto.h | 231 ++ third_party/heimdal/lib/krb5/data.c | 228 ++ third_party/heimdal/lib/krb5/db_plugin.c | 41 + third_party/heimdal/lib/krb5/db_plugin.h | 68 + third_party/heimdal/lib/krb5/dcache.c | 854 +++++ third_party/heimdal/lib/krb5/deprecated.c | 725 ++++ third_party/heimdal/lib/krb5/derived-key-test.c | 145 + third_party/heimdal/lib/krb5/digest.c | 1165 ++++++ third_party/heimdal/lib/krb5/dll.c | 76 + third_party/heimdal/lib/krb5/doxygen.c | 700 ++++ third_party/heimdal/lib/krb5/eai_to_heim_errno.c | 118 + third_party/heimdal/lib/krb5/enomem.c | 42 + third_party/heimdal/lib/krb5/error_string.c | 236 ++ third_party/heimdal/lib/krb5/expand_hostname.c | 177 + third_party/heimdal/lib/krb5/expand_path.c | 94 + third_party/heimdal/lib/krb5/fast.c | 963 +++++ third_party/heimdal/lib/krb5/fcache.c | 1693 ++++++++ third_party/heimdal/lib/krb5/free.c | 51 + third_party/heimdal/lib/krb5/free_host_realm.c | 59 + third_party/heimdal/lib/krb5/generate_seq_number.c | 48 + third_party/heimdal/lib/krb5/generate_subkey.c | 73 + third_party/heimdal/lib/krb5/get_addrs.c | 283 ++ third_party/heimdal/lib/krb5/get_cred.c | 2044 ++++++++++ .../heimdal/lib/krb5/get_default_principal.c | 95 + third_party/heimdal/lib/krb5/get_default_realm.c | 80 + third_party/heimdal/lib/krb5/get_for_creds.c | 367 ++ third_party/heimdal/lib/krb5/get_host_realm.c | 308 ++ third_party/heimdal/lib/krb5/get_in_tkt.c | 557 +++ third_party/heimdal/lib/krb5/get_port.c | 52 + third_party/heimdal/lib/krb5/init_creds.c | 482 +++ third_party/heimdal/lib/krb5/init_creds_pw.c | 4052 ++++++++++++++++++++ third_party/heimdal/lib/krb5/k524_err.et | 20 + third_party/heimdal/lib/krb5/k5e1_err.et | 13 + third_party/heimdal/lib/krb5/kcm.c | 1505 ++++++++ third_party/heimdal/lib/krb5/kcm.h | 87 + third_party/heimdal/lib/krb5/kerberos.8 | 115 + third_party/heimdal/lib/krb5/keyblock.c | 203 + third_party/heimdal/lib/krb5/keytab.c | 976 +++++ third_party/heimdal/lib/krb5/keytab_any.c | 260 ++ third_party/heimdal/lib/krb5/keytab_file.c | 856 +++++ third_party/heimdal/lib/krb5/keytab_keyfile.c | 456 +++ third_party/heimdal/lib/krb5/keytab_memory.c | 231 ++ third_party/heimdal/lib/krb5/krb5-plugin.7 | 359 ++ third_party/heimdal/lib/krb5/krb5.conf.5 | 1475 +++++++ third_party/heimdal/lib/krb5/krb5.h | 1073 ++++++ third_party/heimdal/lib/krb5/krb5.moduli | 3 + .../heimdal/lib/krb5/krb524_convert_creds_kdc.3 | 86 + .../heimdal/lib/krb5/krb5_425_conv_principal.3 | 224 ++ third_party/heimdal/lib/krb5/krb5_acl_match_file.3 | 111 + .../heimdal/lib/krb5/krb5_aname_to_localname.3 | 80 + third_party/heimdal/lib/krb5/krb5_appdefault.3 | 88 + third_party/heimdal/lib/krb5/krb5_auth_context.3 | 395 ++ .../heimdal/lib/krb5/krb5_c_make_checksum.3 | 297 ++ third_party/heimdal/lib/krb5/krb5_ccapi.h | 239 ++ .../heimdal/lib/krb5/krb5_check_transited.3 | 106 + .../heimdal/lib/krb5/krb5_create_checksum.3 | 226 ++ third_party/heimdal/lib/krb5/krb5_creds.3 | 119 + third_party/heimdal/lib/krb5/krb5_digest.3 | 260 ++ .../heimdal/lib/krb5/krb5_eai_to_heim_errno.3 | 68 + third_party/heimdal/lib/krb5/krb5_encrypt.3 | 278 ++ third_party/heimdal/lib/krb5/krb5_err.et | 317 ++ third_party/heimdal/lib/krb5/krb5_find_padata.3 | 87 + .../heimdal/lib/krb5/krb5_generate_random_block.3 | 57 + .../heimdal/lib/krb5/krb5_get_all_client_addrs.3 | 74 + .../heimdal/lib/krb5/krb5_get_credentials.3 | 181 + third_party/heimdal/lib/krb5/krb5_get_creds.3 | 173 + .../heimdal/lib/krb5/krb5_get_forwarded_creds.3 | 79 + third_party/heimdal/lib/krb5/krb5_get_in_cred.3 | 274 ++ third_party/heimdal/lib/krb5/krb5_get_init_creds.3 | 403 ++ third_party/heimdal/lib/krb5/krb5_get_krbhst.3 | 86 + third_party/heimdal/lib/krb5/krb5_getportbyname.3 | 67 + third_party/heimdal/lib/krb5/krb5_init_context.3 | 308 ++ third_party/heimdal/lib/krb5/krb5_is_thread_safe.3 | 58 + third_party/heimdal/lib/krb5/krb5_krbhst_init.3 | 174 + third_party/heimdal/lib/krb5/krb5_locl.h | 491 +++ third_party/heimdal/lib/krb5/krb5_mk_req.3 | 187 + third_party/heimdal/lib/krb5/krb5_mk_safe.3 | 82 + third_party/heimdal/lib/krb5/krb5_openlog.3 | 305 ++ third_party/heimdal/lib/krb5/krb5_parse_name.3 | 67 + third_party/heimdal/lib/krb5/krb5_principal.3 | 371 ++ third_party/heimdal/lib/krb5/krb5_rcache.3 | 163 + third_party/heimdal/lib/krb5/krb5_rd_error.3 | 98 + third_party/heimdal/lib/krb5/krb5_rd_safe.3 | 81 + .../heimdal/lib/krb5/krb5_set_default_realm.3 | 164 + third_party/heimdal/lib/krb5/krb5_set_password.3 | 143 + third_party/heimdal/lib/krb5/krb5_string_to_key.3 | 156 + third_party/heimdal/lib/krb5/krb5_timeofday.3 | 118 + .../heimdal/lib/krb5/krb5_verify_init_creds.3 | 103 + third_party/heimdal/lib/krb5/krb5_verify_user.3 | 241 ++ third_party/heimdal/lib/krb5/krb_err.et | 63 + third_party/heimdal/lib/krb5/krbhst-test.c | 109 + third_party/heimdal/lib/krb5/krbhst.c | 1282 +++++++ third_party/heimdal/lib/krb5/krcache.c | 2075 ++++++++++ third_party/heimdal/lib/krb5/kuserok.c | 753 ++++ third_party/heimdal/lib/krb5/kuserok_plugin.h | 91 + third_party/heimdal/lib/krb5/kx509.c | 1323 +++++++ third_party/heimdal/lib/krb5/kx509_err.et | 39 + .../heimdal/lib/krb5/libkrb5-exports.def.in | 901 +++++ third_party/heimdal/lib/krb5/locate_plugin.h | 81 + third_party/heimdal/lib/krb5/log.c | 252 ++ third_party/heimdal/lib/krb5/mcache.c | 623 +++ third_party/heimdal/lib/krb5/misc.c | 111 + third_party/heimdal/lib/krb5/mit_glue.c | 434 +++ third_party/heimdal/lib/krb5/mk_cred.c | 324 ++ third_party/heimdal/lib/krb5/mk_error.c | 117 + third_party/heimdal/lib/krb5/mk_priv.c | 151 + third_party/heimdal/lib/krb5/mk_rep.c | 119 + third_party/heimdal/lib/krb5/mk_req.c | 114 + third_party/heimdal/lib/krb5/mk_req_ext.c | 151 + third_party/heimdal/lib/krb5/mk_safe.c | 139 + third_party/heimdal/lib/krb5/n-fold-test.c | 119 + third_party/heimdal/lib/krb5/n-fold.c | 150 + third_party/heimdal/lib/krb5/net_read.c | 53 + third_party/heimdal/lib/krb5/net_write.c | 118 + third_party/heimdal/lib/krb5/pac.c | 2159 +++++++++++ third_party/heimdal/lib/krb5/padata.c | 62 + third_party/heimdal/lib/krb5/parse-name-test.c | 192 + third_party/heimdal/lib/krb5/pcache.c | 81 + third_party/heimdal/lib/krb5/pkinit-ec.c | 314 ++ third_party/heimdal/lib/krb5/pkinit.c | 2670 +++++++++++++ third_party/heimdal/lib/krb5/plugin.c | 208 + third_party/heimdal/lib/krb5/principal.c | 2220 +++++++++++ third_party/heimdal/lib/krb5/prog_setup.c | 64 + third_party/heimdal/lib/krb5/prompter_posix.c | 72 + third_party/heimdal/lib/krb5/pseudo-random-test.c | 112 + third_party/heimdal/lib/krb5/rd_cred.c | 348 ++ third_party/heimdal/lib/krb5/rd_error.c | 125 + third_party/heimdal/lib/krb5/rd_priv.c | 184 + third_party/heimdal/lib/krb5/rd_rep.c | 121 + third_party/heimdal/lib/krb5/rd_req.c | 1107 ++++++ third_party/heimdal/lib/krb5/rd_safe.c | 214 ++ third_party/heimdal/lib/krb5/read_message.c | 104 + third_party/heimdal/lib/krb5/recvauth.c | 269 ++ third_party/heimdal/lib/krb5/replay.c | 328 ++ third_party/heimdal/lib/krb5/salt-aes-sha1.c | 102 + third_party/heimdal/lib/krb5/salt-aes-sha2.c | 136 + third_party/heimdal/lib/krb5/salt-arcfour.c | 107 + third_party/heimdal/lib/krb5/salt-des.c | 223 ++ third_party/heimdal/lib/krb5/salt-des3.c | 147 + third_party/heimdal/lib/krb5/salt.c | 362 ++ third_party/heimdal/lib/krb5/scache.c | 1602 ++++++++ third_party/heimdal/lib/krb5/send_to_kdc.c | 1357 +++++++ third_party/heimdal/lib/krb5/send_to_kdc_plugin.h | 70 + third_party/heimdal/lib/krb5/sendauth.c | 257 ++ third_party/heimdal/lib/krb5/set_default_realm.c | 85 + third_party/heimdal/lib/krb5/sock_principal.c | 68 + third_party/heimdal/lib/krb5/sp800-108-kdf.c | 100 + third_party/heimdal/lib/krb5/store-int.c | 67 + third_party/heimdal/lib/krb5/store-int.h | 50 + third_party/heimdal/lib/krb5/store-test.c | 117 + third_party/heimdal/lib/krb5/store.c | 2058 ++++++++++ third_party/heimdal/lib/krb5/store_emem.c | 222 ++ third_party/heimdal/lib/krb5/store_fd.c | 200 + third_party/heimdal/lib/krb5/store_mem.c | 212 + third_party/heimdal/lib/krb5/store_sock.c | 163 + third_party/heimdal/lib/krb5/store_stdio.c | 271 ++ third_party/heimdal/lib/krb5/string-to-key-test.c | 140 + third_party/heimdal/lib/krb5/test_acl.c | 118 + third_party/heimdal/lib/krb5/test_addr.c | 239 ++ third_party/heimdal/lib/krb5/test_alname.c | 219 ++ third_party/heimdal/lib/krb5/test_ap-req.c | 228 ++ third_party/heimdal/lib/krb5/test_canon.c | 177 + third_party/heimdal/lib/krb5/test_cc.c | 1218 ++++++ third_party/heimdal/lib/krb5/test_config.c | 246 ++ .../heimdal/lib/krb5/test_config_strings.cfg | 12 + third_party/heimdal/lib/krb5/test_crypto.c | 216 ++ .../heimdal/lib/krb5/test_crypto_wrapping.c | 168 + third_party/heimdal/lib/krb5/test_expand_toks.c | 104 + third_party/heimdal/lib/krb5/test_forward.c | 134 + third_party/heimdal/lib/krb5/test_fx.c | 253 ++ third_party/heimdal/lib/krb5/test_get_addrs.c | 111 + third_party/heimdal/lib/krb5/test_gic.c | 148 + third_party/heimdal/lib/krb5/test_hostname.c | 150 + third_party/heimdal/lib/krb5/test_keytab.c | 291 ++ third_party/heimdal/lib/krb5/test_kuserok.c | 109 + third_party/heimdal/lib/krb5/test_mem.c | 71 + third_party/heimdal/lib/krb5/test_mkforwardable.c | 191 + third_party/heimdal/lib/krb5/test_pac.c | 1270 ++++++ third_party/heimdal/lib/krb5/test_pkinit_dh2key.c | 216 ++ third_party/heimdal/lib/krb5/test_pknistkdf.c | 373 ++ third_party/heimdal/lib/krb5/test_plugin.c | 131 + third_party/heimdal/lib/krb5/test_prf.c | 100 + third_party/heimdal/lib/krb5/test_princ.c | 365 ++ third_party/heimdal/lib/krb5/test_renew.c | 119 + third_party/heimdal/lib/krb5/test_rfc3961.c | 522 +++ third_party/heimdal/lib/krb5/test_set_kvno0.c | 182 + third_party/heimdal/lib/krb5/test_store.c | 364 ++ third_party/heimdal/lib/krb5/test_time.c | 85 + third_party/heimdal/lib/krb5/test_x500.c | 110 + third_party/heimdal/lib/krb5/ticket.c | 967 +++++ third_party/heimdal/lib/krb5/time.c | 138 + third_party/heimdal/lib/krb5/transited.c | 699 ++++ third_party/heimdal/lib/krb5/verify_init.c | 246 ++ .../heimdal/lib/krb5/verify_krb5_conf-version.rc | 36 + third_party/heimdal/lib/krb5/verify_krb5_conf.8 | 95 + third_party/heimdal/lib/krb5/verify_krb5_conf.c | 795 ++++ third_party/heimdal/lib/krb5/verify_user.c | 258 ++ third_party/heimdal/lib/krb5/version-script.map | 892 +++++ third_party/heimdal/lib/krb5/version.c | 39 + third_party/heimdal/lib/krb5/warn.c | 330 ++ third_party/heimdal/lib/krb5/write_message.c | 87 + 240 files changed, 92587 insertions(+) create mode 100644 third_party/heimdal/lib/krb5/Makefile.am create mode 100644 third_party/heimdal/lib/krb5/NTMakefile create mode 100644 third_party/heimdal/lib/krb5/acache.c create mode 100644 third_party/heimdal/lib/krb5/acl.c create mode 100644 third_party/heimdal/lib/krb5/add_et_list.c create mode 100644 third_party/heimdal/lib/krb5/addr_families.c create mode 100644 third_party/heimdal/lib/krb5/aes-test.c create mode 100644 third_party/heimdal/lib/krb5/an2ln_plugin.h create mode 100644 third_party/heimdal/lib/krb5/aname_to_localname.c create mode 100644 third_party/heimdal/lib/krb5/appdefault.c create mode 100644 third_party/heimdal/lib/krb5/asn1_glue.c create mode 100644 third_party/heimdal/lib/krb5/auth_context.c create mode 100644 third_party/heimdal/lib/krb5/authdata.c create mode 100644 third_party/heimdal/lib/krb5/build_ap_req.c create mode 100644 third_party/heimdal/lib/krb5/build_auth.c create mode 100644 third_party/heimdal/lib/krb5/cache.c create mode 100644 third_party/heimdal/lib/krb5/ccache_plugin.h create mode 100644 third_party/heimdal/lib/krb5/changepw.c create mode 100644 third_party/heimdal/lib/krb5/codec.c create mode 100644 third_party/heimdal/lib/krb5/config_file.c create mode 100644 third_party/heimdal/lib/krb5/constants.c create mode 100644 third_party/heimdal/lib/krb5/context.c create mode 100644 third_party/heimdal/lib/krb5/convert_creds.c create mode 100644 third_party/heimdal/lib/krb5/copy_host_realm.c create mode 100644 third_party/heimdal/lib/krb5/crc.c create mode 100644 third_party/heimdal/lib/krb5/creds.c create mode 100644 third_party/heimdal/lib/krb5/crypto-aes-sha1.c create mode 100644 third_party/heimdal/lib/krb5/crypto-aes-sha2.c create mode 100644 third_party/heimdal/lib/krb5/crypto-algs.c create mode 100644 third_party/heimdal/lib/krb5/crypto-arcfour.c create mode 100644 third_party/heimdal/lib/krb5/crypto-des-common.c create mode 100644 third_party/heimdal/lib/krb5/crypto-des.c create mode 100644 third_party/heimdal/lib/krb5/crypto-des3.c create mode 100644 third_party/heimdal/lib/krb5/crypto-evp.c create mode 100644 third_party/heimdal/lib/krb5/crypto-null.c create mode 100644 third_party/heimdal/lib/krb5/crypto-pk.c create mode 100644 third_party/heimdal/lib/krb5/crypto-rand.c create mode 100644 third_party/heimdal/lib/krb5/crypto-stubs.c create mode 100644 third_party/heimdal/lib/krb5/crypto.c create mode 100644 third_party/heimdal/lib/krb5/crypto.h create mode 100644 third_party/heimdal/lib/krb5/data.c create mode 100644 third_party/heimdal/lib/krb5/db_plugin.c create mode 100644 third_party/heimdal/lib/krb5/db_plugin.h create mode 100644 third_party/heimdal/lib/krb5/dcache.c create mode 100644 third_party/heimdal/lib/krb5/deprecated.c create mode 100644 third_party/heimdal/lib/krb5/derived-key-test.c create mode 100644 third_party/heimdal/lib/krb5/digest.c create mode 100644 third_party/heimdal/lib/krb5/dll.c create mode 100644 third_party/heimdal/lib/krb5/doxygen.c create mode 100644 third_party/heimdal/lib/krb5/eai_to_heim_errno.c create mode 100644 third_party/heimdal/lib/krb5/enomem.c create mode 100644 third_party/heimdal/lib/krb5/error_string.c create mode 100644 third_party/heimdal/lib/krb5/expand_hostname.c create mode 100644 third_party/heimdal/lib/krb5/expand_path.c create mode 100644 third_party/heimdal/lib/krb5/fast.c create mode 100644 third_party/heimdal/lib/krb5/fcache.c create mode 100644 third_party/heimdal/lib/krb5/free.c create mode 100644 third_party/heimdal/lib/krb5/free_host_realm.c create mode 100644 third_party/heimdal/lib/krb5/generate_seq_number.c create mode 100644 third_party/heimdal/lib/krb5/generate_subkey.c create mode 100644 third_party/heimdal/lib/krb5/get_addrs.c create mode 100644 third_party/heimdal/lib/krb5/get_cred.c create mode 100644 third_party/heimdal/lib/krb5/get_default_principal.c create mode 100644 third_party/heimdal/lib/krb5/get_default_realm.c create mode 100644 third_party/heimdal/lib/krb5/get_for_creds.c create mode 100644 third_party/heimdal/lib/krb5/get_host_realm.c create mode 100644 third_party/heimdal/lib/krb5/get_in_tkt.c create mode 100644 third_party/heimdal/lib/krb5/get_port.c create mode 100644 third_party/heimdal/lib/krb5/init_creds.c create mode 100644 third_party/heimdal/lib/krb5/init_creds_pw.c create mode 100644 third_party/heimdal/lib/krb5/k524_err.et create mode 100644 third_party/heimdal/lib/krb5/k5e1_err.et create mode 100644 third_party/heimdal/lib/krb5/kcm.c create mode 100644 third_party/heimdal/lib/krb5/kcm.h create mode 100644 third_party/heimdal/lib/krb5/kerberos.8 create mode 100644 third_party/heimdal/lib/krb5/keyblock.c create mode 100644 third_party/heimdal/lib/krb5/keytab.c create mode 100644 third_party/heimdal/lib/krb5/keytab_any.c create mode 100644 third_party/heimdal/lib/krb5/keytab_file.c create mode 100644 third_party/heimdal/lib/krb5/keytab_keyfile.c create mode 100644 third_party/heimdal/lib/krb5/keytab_memory.c create mode 100644 third_party/heimdal/lib/krb5/krb5-plugin.7 create mode 100644 third_party/heimdal/lib/krb5/krb5.conf.5 create mode 100644 third_party/heimdal/lib/krb5/krb5.h create mode 100644 third_party/heimdal/lib/krb5/krb5.moduli create mode 100644 third_party/heimdal/lib/krb5/krb524_convert_creds_kdc.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_425_conv_principal.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_acl_match_file.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_aname_to_localname.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_appdefault.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_auth_context.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_c_make_checksum.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_ccapi.h create mode 100644 third_party/heimdal/lib/krb5/krb5_check_transited.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_create_checksum.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_creds.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_digest.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_eai_to_heim_errno.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_encrypt.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_err.et create mode 100644 third_party/heimdal/lib/krb5/krb5_find_padata.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_generate_random_block.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_all_client_addrs.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_credentials.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_creds.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_forwarded_creds.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_in_cred.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_init_creds.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_get_krbhst.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_getportbyname.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_init_context.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_is_thread_safe.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_krbhst_init.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_locl.h create mode 100644 third_party/heimdal/lib/krb5/krb5_mk_req.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_mk_safe.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_openlog.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_parse_name.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_principal.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_rcache.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_rd_error.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_rd_safe.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_set_default_realm.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_set_password.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_string_to_key.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_timeofday.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_verify_init_creds.3 create mode 100644 third_party/heimdal/lib/krb5/krb5_verify_user.3 create mode 100644 third_party/heimdal/lib/krb5/krb_err.et create mode 100644 third_party/heimdal/lib/krb5/krbhst-test.c create mode 100644 third_party/heimdal/lib/krb5/krbhst.c create mode 100644 third_party/heimdal/lib/krb5/krcache.c create mode 100644 third_party/heimdal/lib/krb5/kuserok.c create mode 100644 third_party/heimdal/lib/krb5/kuserok_plugin.h create mode 100644 third_party/heimdal/lib/krb5/kx509.c create mode 100644 third_party/heimdal/lib/krb5/kx509_err.et create mode 100644 third_party/heimdal/lib/krb5/libkrb5-exports.def.in create mode 100644 third_party/heimdal/lib/krb5/locate_plugin.h create mode 100644 third_party/heimdal/lib/krb5/log.c create mode 100644 third_party/heimdal/lib/krb5/mcache.c create mode 100644 third_party/heimdal/lib/krb5/misc.c create mode 100644 third_party/heimdal/lib/krb5/mit_glue.c create mode 100644 third_party/heimdal/lib/krb5/mk_cred.c create mode 100644 third_party/heimdal/lib/krb5/mk_error.c create mode 100644 third_party/heimdal/lib/krb5/mk_priv.c create mode 100644 third_party/heimdal/lib/krb5/mk_rep.c create mode 100644 third_party/heimdal/lib/krb5/mk_req.c create mode 100644 third_party/heimdal/lib/krb5/mk_req_ext.c create mode 100644 third_party/heimdal/lib/krb5/mk_safe.c create mode 100644 third_party/heimdal/lib/krb5/n-fold-test.c create mode 100644 third_party/heimdal/lib/krb5/n-fold.c create mode 100644 third_party/heimdal/lib/krb5/net_read.c create mode 100644 third_party/heimdal/lib/krb5/net_write.c create mode 100644 third_party/heimdal/lib/krb5/pac.c create mode 100644 third_party/heimdal/lib/krb5/padata.c create mode 100644 third_party/heimdal/lib/krb5/parse-name-test.c create mode 100644 third_party/heimdal/lib/krb5/pcache.c create mode 100644 third_party/heimdal/lib/krb5/pkinit-ec.c create mode 100644 third_party/heimdal/lib/krb5/pkinit.c create mode 100644 third_party/heimdal/lib/krb5/plugin.c create mode 100644 third_party/heimdal/lib/krb5/principal.c create mode 100644 third_party/heimdal/lib/krb5/prog_setup.c create mode 100644 third_party/heimdal/lib/krb5/prompter_posix.c create mode 100644 third_party/heimdal/lib/krb5/pseudo-random-test.c create mode 100644 third_party/heimdal/lib/krb5/rd_cred.c create mode 100644 third_party/heimdal/lib/krb5/rd_error.c create mode 100644 third_party/heimdal/lib/krb5/rd_priv.c create mode 100644 third_party/heimdal/lib/krb5/rd_rep.c create mode 100644 third_party/heimdal/lib/krb5/rd_req.c create mode 100644 third_party/heimdal/lib/krb5/rd_safe.c create mode 100644 third_party/heimdal/lib/krb5/read_message.c create mode 100644 third_party/heimdal/lib/krb5/recvauth.c create mode 100644 third_party/heimdal/lib/krb5/replay.c create mode 100644 third_party/heimdal/lib/krb5/salt-aes-sha1.c create mode 100644 third_party/heimdal/lib/krb5/salt-aes-sha2.c create mode 100644 third_party/heimdal/lib/krb5/salt-arcfour.c create mode 100644 third_party/heimdal/lib/krb5/salt-des.c create mode 100644 third_party/heimdal/lib/krb5/salt-des3.c create mode 100644 third_party/heimdal/lib/krb5/salt.c create mode 100644 third_party/heimdal/lib/krb5/scache.c create mode 100644 third_party/heimdal/lib/krb5/send_to_kdc.c create mode 100644 third_party/heimdal/lib/krb5/send_to_kdc_plugin.h create mode 100644 third_party/heimdal/lib/krb5/sendauth.c create mode 100644 third_party/heimdal/lib/krb5/set_default_realm.c create mode 100644 third_party/heimdal/lib/krb5/sock_principal.c create mode 100755 third_party/heimdal/lib/krb5/sp800-108-kdf.c create mode 100644 third_party/heimdal/lib/krb5/store-int.c create mode 100644 third_party/heimdal/lib/krb5/store-int.h create mode 100644 third_party/heimdal/lib/krb5/store-test.c create mode 100644 third_party/heimdal/lib/krb5/store.c create mode 100644 third_party/heimdal/lib/krb5/store_emem.c create mode 100644 third_party/heimdal/lib/krb5/store_fd.c create mode 100644 third_party/heimdal/lib/krb5/store_mem.c create mode 100644 third_party/heimdal/lib/krb5/store_sock.c create mode 100644 third_party/heimdal/lib/krb5/store_stdio.c create mode 100644 third_party/heimdal/lib/krb5/string-to-key-test.c create mode 100644 third_party/heimdal/lib/krb5/test_acl.c create mode 100644 third_party/heimdal/lib/krb5/test_addr.c create mode 100644 third_party/heimdal/lib/krb5/test_alname.c create mode 100644 third_party/heimdal/lib/krb5/test_ap-req.c create mode 100755 third_party/heimdal/lib/krb5/test_canon.c create mode 100644 third_party/heimdal/lib/krb5/test_cc.c create mode 100644 third_party/heimdal/lib/krb5/test_config.c create mode 100644 third_party/heimdal/lib/krb5/test_config_strings.cfg create mode 100644 third_party/heimdal/lib/krb5/test_crypto.c create mode 100644 third_party/heimdal/lib/krb5/test_crypto_wrapping.c create mode 100644 third_party/heimdal/lib/krb5/test_expand_toks.c create mode 100644 third_party/heimdal/lib/krb5/test_forward.c create mode 100644 third_party/heimdal/lib/krb5/test_fx.c create mode 100644 third_party/heimdal/lib/krb5/test_get_addrs.c create mode 100644 third_party/heimdal/lib/krb5/test_gic.c create mode 100644 third_party/heimdal/lib/krb5/test_hostname.c create mode 100644 third_party/heimdal/lib/krb5/test_keytab.c create mode 100644 third_party/heimdal/lib/krb5/test_kuserok.c create mode 100644 third_party/heimdal/lib/krb5/test_mem.c create mode 100644 third_party/heimdal/lib/krb5/test_mkforwardable.c create mode 100644 third_party/heimdal/lib/krb5/test_pac.c create mode 100644 third_party/heimdal/lib/krb5/test_pkinit_dh2key.c create mode 100644 third_party/heimdal/lib/krb5/test_pknistkdf.c create mode 100644 third_party/heimdal/lib/krb5/test_plugin.c create mode 100644 third_party/heimdal/lib/krb5/test_prf.c create mode 100644 third_party/heimdal/lib/krb5/test_princ.c create mode 100644 third_party/heimdal/lib/krb5/test_renew.c create mode 100644 third_party/heimdal/lib/krb5/test_rfc3961.c create mode 100644 third_party/heimdal/lib/krb5/test_set_kvno0.c create mode 100644 third_party/heimdal/lib/krb5/test_store.c create mode 100644 third_party/heimdal/lib/krb5/test_time.c create mode 100644 third_party/heimdal/lib/krb5/test_x500.c create mode 100644 third_party/heimdal/lib/krb5/ticket.c create mode 100644 third_party/heimdal/lib/krb5/time.c create mode 100644 third_party/heimdal/lib/krb5/transited.c create mode 100644 third_party/heimdal/lib/krb5/verify_init.c create mode 100644 third_party/heimdal/lib/krb5/verify_krb5_conf-version.rc create mode 100644 third_party/heimdal/lib/krb5/verify_krb5_conf.8 create mode 100644 third_party/heimdal/lib/krb5/verify_krb5_conf.c create mode 100644 third_party/heimdal/lib/krb5/verify_user.c create mode 100644 third_party/heimdal/lib/krb5/version-script.map create mode 100644 third_party/heimdal/lib/krb5/version.c create mode 100644 third_party/heimdal/lib/krb5/warn.c create mode 100644 third_party/heimdal/lib/krb5/write_message.c (limited to 'third_party/heimdal/lib/krb5') diff --git a/third_party/heimdal/lib/krb5/Makefile.am b/third_party/heimdal/lib/krb5/Makefile.am new file mode 100644 index 0000000..ecce461 --- /dev/null +++ b/third_party/heimdal/lib/krb5/Makefile.am @@ -0,0 +1,454 @@ +# $Id$ + +include $(top_srcdir)/Makefile.am.common + +WFLAGS += $(WFLAGS_ENUM_CONV) + +AM_CPPFLAGS += -I../com_err -I$(srcdir)/../com_err -I../base -I$(srcdir)/../base $(INCLUDE_sqlite3) $(INCLUDE_libintl) $(INCLUDE_openssl_crypto) + +bin_PROGRAMS = verify_krb5_conf + +noinst_PROGRAMS = \ + krbhst-test \ + test_alname \ + test_crypto \ + test_forward \ + test_get_addrs \ + test_gic \ + test_kuserok \ + test_renew \ + test_rfc3961 + + +noinst_LTLIBRARIES = \ + librfc3961.la + +TESTS = \ + aes-test \ + derived-key-test \ + n-fold-test \ + parse-name-test \ + pseudo-random-test \ + store-test \ + string-to-key-test \ + test_acl \ + test_addr \ + test_cc \ + test_config \ + test_fx \ + test_prf \ + test_store \ + test_crypto_wrapping \ + test_keytab \ + test_mem \ + test_pac \ + test_plugin \ + test_princ \ + test_pkinit_dh2key \ + test_pknistkdf \ + test_time \ + test_expand_toks \ + test_x500 + +check_DATA = test_config_strings.out + +check_PROGRAMS = $(TESTS) test_hostname test_ap-req test_canon test_set_kvno0 \ + test_mkforwardable + +LDADD = libkrb5.la \ + $(LIB_hcrypto) \ + $(top_builddir)/lib/asn1/libasn1.la \ + $(top_builddir)/lib/wind/libwind.la \ + $(LIB_heimbase) $(LIB_roken) + +if HAVE_KEYUTILS +test_cc_LDADD = $(LDADD) -lkeyutils +else +test_cc_LDADD = $(LDADD) +endif + +if PKINIT +LIB_pkinit = ../hx509/libhx509.la +endif + +if have_scc +use_sqlite = $(LIB_sqlite3) +endif + +libkrb5_la_LIBADD = \ + $(top_builddir)/lib/asn1/libasn1.la \ + $(top_builddir)/lib/ipc/libheim-ipcc.la \ + $(top_builddir)/lib/wind/libwind.la \ + $(top_builddir)/lib/base/libheimbase.la \ + $(top_builddir)/lib/hx509/libhx509.la \ + $(LIB_openssl_crypto) \ + $(use_sqlite) \ + $(LIB_com_err) \ + $(LIB_hcrypto) \ + $(LIB_libintl) \ + $(LIBADD_roken) \ + $(PTHREAD_LIBADD) \ + $(LIB_add_key) \ + $(LIB_door_create) + +librfc3961_la_LIBADD = \ + $(top_builddir)/lib/asn1/libasn1.la \ + $(top_builddir)/lib/ipc/libheim-ipcc.la \ + $(top_builddir)/lib/wind/libwind.la \ + $(LIB_pkinit) \ + $(use_sqlite) \ + $(LIB_com_err) \ + $(LIB_hcrypto) \ + $(LIB_libintl) \ + $(LIBADD_roken) \ + $(PTHREAD_LIBADD) \ + $(LIB_add_key) \ + $(LIB_door_create) + +lib_LTLIBRARIES = libkrb5.la + +ERR_FILES = krb5_err.c krb_err.c k524_err.c k5e1_err.c kx509_err.c + +libkrb5_la_CPPFLAGS = \ + -DBUILD_KRB5_LIB \ + $(AM_CPPFLAGS) \ + -DHEIMDAL_LOCALEDIR='"$(localedir)"' + +librfc3961_la_CPPFLAGS = \ + -DBUILD_KRB5_LIB \ + $(AM_CPPFLAGS) \ + -DHEIMDAL_LOCALEDIR='"$(localedir)"' + +dist_libkrb5_la_SOURCES = \ + acache.c \ + acl.c \ + add_et_list.c \ + addr_families.c \ + an2ln_plugin.h \ + aname_to_localname.c \ + appdefault.c \ + asn1_glue.c \ + auth_context.c \ + authdata.c \ + build_ap_req.c \ + build_auth.c \ + cache.c \ + ccache_plugin.h \ + changepw.c \ + codec.c \ + config_file.c \ + convert_creds.c \ + constants.c \ + context.c \ + copy_host_realm.c \ + crc.c \ + creds.c \ + crypto.c \ + crypto.h \ + crypto-aes-sha1.c \ + crypto-aes-sha2.c \ + crypto-algs.c \ + crypto-arcfour.c \ + crypto-des.c \ + crypto-des-common.c \ + crypto-des3.c \ + crypto-evp.c \ + crypto-null.c \ + crypto-pk.c \ + crypto-rand.c \ + doxygen.c \ + data.c \ + db_plugin.c \ + db_plugin.h \ + dcache.c \ + deprecated.c \ + digest.c \ + eai_to_heim_errno.c \ + enomem.c \ + error_string.c \ + expand_hostname.c \ + expand_path.c \ + fast.c \ + fcache.c \ + free.c \ + free_host_realm.c \ + generate_seq_number.c \ + generate_subkey.c \ + get_addrs.c \ + get_cred.c \ + get_default_principal.c \ + get_default_realm.c \ + get_for_creds.c \ + get_host_realm.c \ + get_in_tkt.c \ + get_port.c \ + init_creds.c \ + init_creds_pw.c \ + kcm.c \ + kcm.h \ + keyblock.c \ + keytab.c \ + keytab_any.c \ + keytab_file.c \ + keytab_keyfile.c \ + keytab_memory.c \ + krb5_locl.h \ + krcache.c \ + krbhst.c \ + kuserok.c \ + kuserok_plugin.h \ + kx509.c \ + log.c \ + mcache.c \ + misc.c \ + mk_cred.c \ + mk_error.c \ + mk_priv.c \ + mk_rep.c \ + mk_req.c \ + mk_req_ext.c \ + mk_safe.c \ + mit_glue.c \ + net_read.c \ + net_write.c \ + n-fold.c \ + pac.c \ + padata.c \ + pcache.c \ + pkinit.c \ + pkinit-ec.c \ + principal.c \ + prog_setup.c \ + prompter_posix.c \ + rd_cred.c \ + rd_error.c \ + rd_priv.c \ + rd_rep.c \ + rd_req.c \ + rd_safe.c \ + read_message.c \ + recvauth.c \ + replay.c \ + salt.c \ + salt-aes-sha1.c \ + salt-aes-sha2.c \ + salt-arcfour.c \ + salt-des.c \ + salt-des3.c \ + sp800-108-kdf.c \ + scache.c \ + send_to_kdc.c \ + sendauth.c \ + set_default_realm.c \ + sock_principal.c \ + store.c \ + store-int.c \ + store-int.h \ + store_emem.c \ + store_fd.c \ + store_mem.c \ + store_sock.c \ + store_stdio.c \ + plugin.c \ + ticket.c \ + time.c \ + transited.c \ + verify_init.c \ + verify_user.c \ + version.c \ + warn.c \ + write_message.c + +nodist_libkrb5_la_SOURCES = \ + $(ERR_FILES) + +libkrb5_la_DEPENDENCIES = \ + version-script.map + +libkrb5_la_LDFLAGS = -version-info 26:0:0 +if FRAMEWORK_COREFOUNDATION +libkrb5_la_LDFLAGS += -framework CoreFoundation +endif + +if versionscript +libkrb5_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map +endif + +ALL_OBJECTS = $(libkrb5_la_OBJECTS) +ALL_OBJECTS += $(verify_krb5_conf_OBJECTS) +ALL_OBJECTS += $(librfc3961_la_OBJECTS) +ALL_OBJECTS += $(librfc3961_la_OBJECTS) +ALL_OBJECTS += $(krbhst_test_OBJECTS) +ALL_OBJECTS += $(test_alname_OBJECTS) +ALL_OBJECTS += $(test_crypto_OBJECTS) +ALL_OBJECTS += $(test_forward_OBJECTS) +ALL_OBJECTS += $(test_get_addrs_OBJECTS) +ALL_OBJECTS += $(test_gic_OBJECTS) +ALL_OBJECTS += $(test_kuserok_OBJECTS) +ALL_OBJECTS += $(test_renew_OBJECTS) +ALL_OBJECTS += $(test_rfc3961_OBJECTS) + +$(ALL_OBJECTS): $(srcdir)/krb5-protos.h $(srcdir)/krb5-private.h +$(ALL_OBJECTS): krb5_err.h k524_err.h k5e1_err.h \ + krb_err.h k524_err.h kx509_err.h + +librfc3961_la_SOURCES = \ + crc.c \ + crypto.c \ + crypto.h \ + crypto-aes-sha1.c \ + crypto-aes-sha2.c \ + crypto-algs.c \ + crypto-arcfour.c \ + crypto-des.c \ + crypto-des-common.c \ + crypto-des3.c \ + crypto-evp.c \ + crypto-null.c \ + crypto-pk.c \ + crypto-rand.c \ + crypto-stubs.c \ + data.c \ + enomem.c \ + error_string.c \ + keyblock.c \ + n-fold.c \ + salt.c \ + salt-aes-sha1.c \ + salt-aes-sha2.c \ + salt-arcfour.c \ + salt-des.c \ + salt-des3.c \ + sp800-108-kdf.c \ + store-int.c \ + warn.c + +test_rfc3961_LDADD = \ + librfc3961.la \ + $(top_builddir)/lib/asn1/libasn1.la \ + $(top_builddir)/lib/wind/libwind.la \ + $(LIB_hcrypto) \ + $(LIB_roken) + +if DEVELOPER_MODE +headerdeps = $(dist_libkrb5_la_SOURCES) +endif + +$(srcdir)/krb5-protos.h: $(headerdeps) + @cd $(srcdir) && perl ../../cf/make-proto.pl -E KRB5_LIB -q -P comment -o krb5-protos.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-protos.h + +$(srcdir)/krb5-private.h: $(headerdeps) + @cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p krb5-private.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-private.h + +man_MANS = \ + kerberos.8 \ + krb5.conf.5 \ + krb5-plugin.7 \ + krb524_convert_creds_kdc.3 \ + krb5_425_conv_principal.3 \ + krb5_acl_match_file.3 \ + krb5_aname_to_localname.3 \ + krb5_appdefault.3 \ + krb5_auth_context.3 \ + krb5_c_make_checksum.3 \ + krb5_check_transited.3 \ + krb5_create_checksum.3 \ + krb5_creds.3 \ + krb5_digest.3 \ + krb5_eai_to_heim_errno.3 \ + krb5_encrypt.3 \ + krb5_find_padata.3 \ + krb5_generate_random_block.3 \ + krb5_get_all_client_addrs.3 \ + krb5_get_credentials.3 \ + krb5_get_creds.3 \ + krb5_get_forwarded_creds.3 \ + krb5_get_in_cred.3 \ + krb5_get_init_creds.3 \ + krb5_get_krbhst.3 \ + krb5_getportbyname.3 \ + krb5_init_context.3 \ + krb5_is_thread_safe.3 \ + krb5_krbhst_init.3 \ + krb5_mk_req.3 \ + krb5_mk_safe.3 \ + krb5_openlog.3 \ + krb5_parse_name.3 \ + krb5_principal.3 \ + krb5_rcache.3 \ + krb5_rd_error.3 \ + krb5_rd_safe.3 \ + krb5_set_default_realm.3 \ + krb5_set_password.3 \ + krb5_string_to_key.3 \ + krb5_timeofday.3 \ + krb5_verify_init_creds.3 \ + krb5_verify_user.3 \ + verify_krb5_conf.8 + +dist_include_HEADERS = \ + krb5.h \ + $(srcdir)/krb5-protos.h \ + krb5_ccapi.h + +noinst_HEADERS = $(srcdir)/krb5-private.h + + +nodist_include_HEADERS = krb5_err.h k524_err.h k5e1_err.h kx509_err.h + +# XXX use nobase_include_HEADERS = krb5/locate_plugin.h +krb5dir = $(includedir)/krb5 +krb5_HEADERS = \ + an2ln_plugin.h \ + ccache_plugin.h \ + db_plugin.h \ + kuserok_plugin.h \ + locate_plugin.h \ + send_to_kdc_plugin.h + +build_HEADERZ = \ + $(krb5_HEADERS) \ + krb_err.h + +CLEANFILES = \ + test_config_strings.out \ + test-store-data \ + krb5_err.c krb5_err.h \ + krb_err.c krb_err.h \ + k524_err.c k524_err.h \ + k5e1_err.c k5e1_err.h \ + kx509_err.c kx509_err.h + +$(libkrb5_la_OBJECTS): krb5_err.h krb_err.h k524_err.h k5e1_err.h kx509_err.h + +test_config_strings.out: test_config_strings.cfg + $(CP) $(srcdir)/test_config_strings.cfg test_config_strings.out + +EXTRA_DIST = \ + NTMakefile \ + dll.c \ + libkrb5-exports.def.in \ + verify_krb5_conf-version.rc \ + krb5_err.et \ + krb_err.et \ + k524_err.et \ + k5e1_err.et \ + kx509_err.et \ + $(man_MANS) \ + version-script.map \ + test_config_strings.cfg \ + krb5.moduli + +#sysconf_DATA = krb5.moduli + +# to help stupid solaris make + +krb5_err.h: krb5_err.et + +krb_err.h: krb_err.et + +k524_err.h: k524_err.et + +k5e1_err.h: k5e1_err.et + +kx509_err.h: kx509_err.et diff --git a/third_party/heimdal/lib/krb5/NTMakefile b/third_party/heimdal/lib/krb5/NTMakefile new file mode 100644 index 0000000..993e76f --- /dev/null +++ b/third_party/heimdal/lib/krb5/NTMakefile @@ -0,0 +1,542 @@ +######################################################################## +# +# Copyright (c) 2009 - 2017, Secure Endpoints Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# - Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# - Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +RELDIR=lib\krb5 + +intcflags=-I$(SRCDIR) -I$(SRCDIR)\..\com_err -I$(SRCDIR)\..\base + +!include ../../windows/NTMakefile.w32 + +libkrb5_OBJS = \ + $(OBJ)\acache.obj \ + $(OBJ)\acl.obj \ + $(OBJ)\add_et_list.obj \ + $(OBJ)\addr_families.obj \ + $(OBJ)\aname_to_localname.obj \ + $(OBJ)\appdefault.obj \ + $(OBJ)\asn1_glue.obj \ + $(OBJ)\auth_context.obj \ + $(OBJ)\authdata.obj \ + $(OBJ)\build_ap_req.obj \ + $(OBJ)\build_auth.obj \ + $(OBJ)\cache.obj \ + $(OBJ)\changepw.obj \ + $(OBJ)\codec.obj \ + $(OBJ)\config_file.obj \ + $(OBJ)\constants.obj \ + $(OBJ)\context.obj \ + $(OBJ)\convert_creds.obj \ + $(OBJ)\copy_host_realm.obj \ + $(OBJ)\crc.obj \ + $(OBJ)\creds.obj \ + $(OBJ)\crypto.obj \ + $(OBJ)\crypto-aes-sha1.obj \ + $(OBJ)\crypto-aes-sha2.obj \ + $(OBJ)\crypto-algs.obj \ + $(OBJ)\crypto-arcfour.obj \ + $(OBJ)\crypto-des-common.obj \ + $(OBJ)\crypto-des.obj \ + $(OBJ)\crypto-des3.obj \ + $(OBJ)\crypto-evp.obj \ + $(OBJ)\crypto-null.obj \ + $(OBJ)\crypto-pk.obj \ + $(OBJ)\crypto-rand.obj \ + $(OBJ)\data.obj \ + $(OBJ)\dcache.obj \ + $(OBJ)\db_plugin.obj \ + $(OBJ)\deprecated.obj \ + $(OBJ)\digest.obj \ + $(OBJ)\dll.obj \ + $(OBJ)\eai_to_heim_errno.obj \ + $(OBJ)\enomem.obj \ + $(OBJ)\error_string.obj \ + $(OBJ)\expand_hostname.obj \ + $(OBJ)\expand_path.obj \ + $(OBJ)\fast.obj \ + $(OBJ)\fcache.obj \ + $(OBJ)\free.obj \ + $(OBJ)\free_host_realm.obj \ + $(OBJ)\generate_seq_number.obj \ + $(OBJ)\generate_subkey.obj \ + $(OBJ)\get_addrs.obj \ + $(OBJ)\get_cred.obj \ + $(OBJ)\get_default_principal.obj \ + $(OBJ)\get_default_realm.obj \ + $(OBJ)\get_for_creds.obj \ + $(OBJ)\get_host_realm.obj \ + $(OBJ)\get_in_tkt.obj \ + $(OBJ)\get_port.obj \ + $(OBJ)\init_creds.obj \ + $(OBJ)\init_creds_pw.obj \ + $(OBJ)\kcm.obj \ + $(OBJ)\keyblock.obj \ + $(OBJ)\keytab.obj \ + $(OBJ)\keytab_any.obj \ + $(OBJ)\keytab_file.obj \ + $(OBJ)\keytab_keyfile.obj \ + $(OBJ)\keytab_memory.obj \ + $(OBJ)\krbhst.obj \ + $(OBJ)\kuserok.obj \ + $(OBJ)\kx509.obj \ + $(OBJ)\log.obj \ + $(OBJ)\mcache.obj \ + $(OBJ)\misc.obj \ + $(OBJ)\mit_glue.obj \ + $(OBJ)\mk_cred.obj \ + $(OBJ)\mk_error.obj \ + $(OBJ)\mk_priv.obj \ + $(OBJ)\mk_rep.obj \ + $(OBJ)\mk_req.obj \ + $(OBJ)\mk_req_ext.obj \ + $(OBJ)\mk_safe.obj \ + $(OBJ)\net_read.obj \ + $(OBJ)\net_write.obj \ + $(OBJ)\n-fold.obj \ + $(OBJ)\pac.obj \ + $(OBJ)\padata.obj \ + $(OBJ)\pcache.obj \ + $(OBJ)\pkinit.obj \ + $(OBJ)\pkinit-ec.obj \ + $(OBJ)\plugin.obj \ + $(OBJ)\principal.obj \ + $(OBJ)\prog_setup.obj \ + $(OBJ)\prompter_posix.obj \ + $(OBJ)\rd_cred.obj \ + $(OBJ)\rd_error.obj \ + $(OBJ)\rd_priv.obj \ + $(OBJ)\rd_rep.obj \ + $(OBJ)\rd_req.obj \ + $(OBJ)\rd_safe.obj \ + $(OBJ)\read_message.obj \ + $(OBJ)\recvauth.obj \ + $(OBJ)\replay.obj \ + $(OBJ)\salt-aes-sha1.obj \ + $(OBJ)\salt-aes-sha2.obj \ + $(OBJ)\salt-arcfour.obj \ + $(OBJ)\salt-des.obj \ + $(OBJ)\salt-des3.obj \ + $(OBJ)\salt.obj \ + $(OBJ)\scache.obj \ + $(OBJ)\send_to_kdc.obj \ + $(OBJ)\sendauth.obj \ + $(OBJ)\set_default_realm.obj \ + $(OBJ)\sock_principal.obj \ + $(OBJ)\sp800-108-kdf.obj \ + $(OBJ)\store.obj \ + $(OBJ)\store-int.obj \ + $(OBJ)\store_emem.obj \ + $(OBJ)\store_fd.obj \ + $(OBJ)\store_mem.obj \ + $(OBJ)\store_sock.obj \ + $(OBJ)\store_stdio.obj \ + $(OBJ)\ticket.obj \ + $(OBJ)\time.obj \ + $(OBJ)\transited.obj \ + $(OBJ)\verify_init.obj \ + $(OBJ)\verify_user.obj \ + $(OBJ)\version.obj \ + $(OBJ)\warn.obj \ + $(OBJ)\write_message.obj + +libkrb5_gen_OBJS= \ + $(OBJ)\krb5_err.obj \ + $(OBJ)\krb_err.obj \ + $(OBJ)\k524_err.obj \ + $(OBJ)\k5e1_err.obj + +INCFILES= \ + $(INCDIR)\k524_err.h \ + $(INCDIR)\k5e1_err.h \ + $(INCDIR)\kx509_err.h \ + $(INCDIR)\kcm.h \ + $(INCDIR)\krb_err.h \ + $(INCDIR)\krb5.h \ + $(INCDIR)\krb5_ccapi.h \ + $(INCDIR)\krb5_err.h \ + $(INCDIR)\krb5_locl.h \ + $(INCDIR)\krb5-protos.h \ + $(INCDIR)\krb5-private.h \ + $(INCDIR)\crypto.h \ + $(INCDIR)\an2ln_plugin.h \ + $(INCDIR)\ccache_plugin.h \ + $(INCDIR)\db_plugin.h \ + $(INCDIR)\kuserok_plugin.h \ + $(INCDIR)\locate_plugin.h \ + $(INCDIR)\send_to_kdc_plugin.h + +all:: $(INCFILES) + +clean:: + -$(RM) $(INCFILES) + +dist_libkrb5_la_SOURCES = \ + acache.c \ + acl.c \ + add_et_list.c \ + addr_families.c \ + aname_to_localname.c \ + appdefault.c \ + asn1_glue.c \ + auth_context.c \ + authdata.c \ + build_ap_req.c \ + build_auth.c \ + cache.c \ + changepw.c \ + codec.c \ + config_file.c \ + constants.c \ + context.c \ + copy_host_realm.c \ + crc.c \ + creds.c \ + crypto.c \ + crypto.h \ + crypto-aes-sha1.c \ + crypto-aes-sha2.c \ + crypto-algs.c \ + crypto-arcfour.c \ + crypto-des.c \ + crypto-des-common.c \ + crypto-des3.c \ + crypto-evp.c \ + crypto-pk.c \ + crypto-rand.c \ + db_plugin.c \ + doxygen.c \ + data.c \ + dcache.c \ + deprecated.c \ + digest.c \ + eai_to_heim_errno.c \ + enomem.c \ + error_string.c \ + expand_hostname.c \ + expand_path.c \ + fast.c \ + fcache.c \ + free.c \ + free_host_realm.c \ + generate_seq_number.c \ + generate_subkey.c \ + get_addrs.c \ + get_cred.c \ + get_default_principal.c \ + get_default_realm.c \ + get_for_creds.c \ + get_host_realm.c \ + get_in_tkt.c \ + get_port.c \ + init_creds.c \ + init_creds_pw.c \ + kcm.c \ + kcm.h \ + keyblock.c \ + keytab.c \ + keytab_any.c \ + keytab_file.c \ + keytab_keyfile.c \ + keytab_memory.c \ + krb5_locl.h \ + krbhst.c \ + kuserok.c \ + kx509.c \ + log.c \ + mcache.c \ + misc.c \ + mk_cred.c \ + mk_error.c \ + mk_priv.c \ + mk_rep.c \ + mk_req.c \ + mk_req_ext.c \ + mk_safe.c \ + mit_glue.c \ + net_read.c \ + net_write.c \ + n-fold.c \ + pac.c \ + padata.c \ + pkinit.c \ + pkinit-ec.c \ + plugin.c \ + principal.c \ + prog_setup.c \ + prompter_posix.c \ + rd_cred.c \ + rd_error.c \ + rd_priv.c \ + rd_rep.c \ + rd_req.c \ + rd_safe.c \ + read_message.c \ + recvauth.c \ + replay.c \ + salt.c \ + salt-aes-sha1.c \ + salt-aes-sha2.c \ + salt-arcfour.c \ + salt-des.c \ + salt-des3.c \ + scache.c \ + send_to_kdc.c \ + sendauth.c \ + set_default_realm.c \ + sock_principal.c \ + sp800-108-kdf.c \ + store.c \ + store-int.c \ + store-int.h \ + store_emem.c \ + store_fd.c \ + store_mem.c \ + store_sock.c \ + store_stdio.c \ + pcache.c \ + plugin.c \ + ticket.c \ + time.c \ + transited.c \ + verify_init.c \ + verify_user.c \ + version.c \ + warn.c \ + write_message.c + +$(OBJ)\krb5-protos.h: $(dist_libkrb5_la_SOURCES) + $(PERL) ..\..\cf\make-proto.pl -E KRB5_LIB -q -P remove -o $(OBJ)\krb5-protos.h $(dist_libkrb5_la_SOURCES) || $(RM) -f $(OBJ)\krb5-protos.h + +$(OBJ)\krb5-private.h: $(dist_libkrb5_la_SOURCES) + $(PERL) ..\..\cf\make-proto.pl -q -P remove -p $(OBJ)\krb5-private.h $(dist_libkrb5_la_SOURCES) || $(RM) -f $(OBJ)\krb5-private.h + +$(OBJ)\krb5_err.c $(OBJ)\krb5_err.h: krb5_err.et + cd $(OBJ) + $(BINDIR)\compile_et.exe $(SRCDIR)\krb5_err.et + cd $(SRCDIR) + +$(OBJ)\krb_err.c $(OBJ)\krb_err.h: krb_err.et + cd $(OBJ) + $(BINDIR)\compile_et.exe $(SRCDIR)\krb_err.et + cd $(SRCDIR) + +$(OBJ)\k524_err.c $(OBJ)\k524_err.h: k524_err.et + cd $(OBJ) + $(BINDIR)\compile_et.exe $(SRCDIR)\k524_err.et + cd $(SRCDIR) + +$(OBJ)\k5e1_err.c $(OBJ)\k5e1_err.h: k5e1_err.et + cd $(OBJ) + $(BINDIR)\compile_et.exe $(SRCDIR)\k5e1_err.et + cd $(SRCDIR) + +$(OBJ)\kx509_err.c $(OBJ)\kx509_err.h: kx509_err.et + cd $(OBJ) + $(BINDIR)\compile_et.exe $(SRCDIR)\kx509_err.et + cd $(SRCDIR) + +#---------------------------------------------------------------------- +# libkrb5 + +$(LIBKRB5): $(libkrb5_OBJS) $(libkrb5_gen_OBJS) + $(LIBCON_C) -OUT:$@ $(LIBHEIMBASE) $(LIB_openssl_crypto) @<< +$(libkrb5_OBJS: = +) +$(libkrb5_gen_OBJS: = +) +<< + +all:: $(LIBKRB5) + +clean:: + -$(RM) $(LIBKRB5) + +$(OBJ)\libkrb5-exports.def: libkrb5-exports.def.in $(INCDIR)\config.h + $(CPREPROCESSOUT) libkrb5-exports.def.in > $@ || $(RM) $@ + +all:: $(OBJ)\libkrb5-exports.def + +clean:: + -$(RM) $(OBJ)\libkrb5-exports.def + +#---------------------------------------------------------------------- +# librfc3961 + +librfc3961_OBJS=\ + $(OBJ)\crc.obj \ + $(OBJ)\crypto.obj \ + $(OBJ)\crypto-aes-sha1.obj \ + $(OBJ)\crypto-aes-sha2.obj \ + $(OBJ)\crypto-algs.obj \ + $(OBJ)\crypto-arcfour.obj \ + $(OBJ)\crypto-des.obj \ + $(OBJ)\crypto-des-common.obj \ + $(OBJ)\crypto-des3.obj \ + $(OBJ)\crypto-evp.obj \ + $(OBJ)\crypto-null.obj \ + $(OBJ)\crypto-pk.obj \ + $(OBJ)\crypto-rand.obj \ + $(OBJ)\crypto-stubs.obj \ + $(OBJ)\data.obj \ + $(OBJ)\error_string.obj \ + $(OBJ)\keyblock.obj \ + $(OBJ)\n-fold.obj \ + $(OBJ)\salt.obj \ + $(OBJ)\salt-aes-sha1.obj \ + $(OBJ)\salt-aes-sha2.obj \ + $(OBJ)\salt-arcfour.obj \ + $(OBJ)\salt-des.obj \ + $(OBJ)\salt-des3.obj \ + $(OBJ)\sp800-108-kdf.obj \ + $(OBJ)\store-int.obj \ + $(OBJ)\warn.obj + +$(LIBRFC3961): $(librfc3961_OBJS) + $(LIBCON) + +all:: $(LIBRFC3961) + +clean:: + -$(RM) $(LIBRFC3961) + +#---------------------------------------------------------------------- +# Tools + +all-tools:: $(BINDIR)\verify_krb5_conf.exe + +clean:: + -$(RM) $(BINDIR)\verify_krb5_conf.* + +$(BINDIR)\verify_krb5_conf.exe: $(OBJ)\verify_krb5_conf.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS) $(OBJ)\verify_krb5_conf-version.res + $(EXECONLINK) + $(EXEPREP) + +{}.c{$(OBJ)}.obj:: + $(C2OBJ_P) -DBUILD_KRB5_LIB -DASN1_LIB + +{$(OBJ)}.c{$(OBJ)}.obj:: + $(C2OBJ_P) -DBUILD_KRB5_LIB -DASN1_LIB + +#---------------------------------------------------------------------- +# Tests + +test:: test-binaries test-files test-run + +test_binaries = \ + $(OBJ)\aes-test.exe \ + $(OBJ)\derived-key-test.exe \ + $(OBJ)\krbhst-test.exe \ + $(OBJ)\n-fold-test.exe \ + $(OBJ)\parse-name-test.exe \ + $(OBJ)\pseudo-random-test.exe \ + $(OBJ)\store-test.exe \ + $(OBJ)\string-to-key-test.exe \ + $(OBJ)\test_acl.exe \ + $(OBJ)\test_addr.exe \ + $(OBJ)\test_alname.exe \ + $(OBJ)\test_cc.exe \ + $(OBJ)\test_config.exe \ + $(OBJ)\test_crypto.exe \ + $(OBJ)\test_crypto_wrapping.exe \ + $(OBJ)\test_forward.exe \ + $(OBJ)\test_get_addrs.exe \ + $(OBJ)\test_hostname.exe \ + $(OBJ)\test_keytab.exe \ + $(OBJ)\test_kuserok.exe \ + $(OBJ)\test_mem.exe \ + $(OBJ)\test_pac.exe \ + $(OBJ)\test_pkinit_dh2key.exe \ + $(OBJ)\test_pknistkdf.exe \ + $(OBJ)\test_plugin.exe \ + $(OBJ)\test_prf.exe \ + $(OBJ)\test_princ.exe \ + $(OBJ)\test_renew.exe \ + $(OBJ)\test_store.exe \ + $(OBJ)\test_time.exe \ + +test-binaries: $(test_binaries) $(OBJ)\test_rfc3961.exe + +test-files: $(OBJ)\test_config_strings.out + +$(OBJ)\test_config_strings.out: test_config_strings.cfg + $(CP) $** $@ + +test-run: + cd $(OBJ) + -aes-test.exe + -derived-key-test.exe + -krbhst-test.exe + -n-fold-test.exe + -parse-name-test.exe + -pseudo-random-test.exe + -store-test.exe + -string-to-key-test.exe + -test_acl.exe + -test_addr.exe +# Skip alname due to lack of .k5login and "root" +# -test_alname.exe + -test_cc.exe + -test_config.exe + -test_crypto.exe + -test_crypto_wrapping.exe +# Skip forward due to need for existing hostname +# -test_forward.exe + -test_get_addrs.exe + -test_hostname.exe + -test_keytab.exe +# Skip kuserok requires principal and localname +# -test_kuserok.exe + -test_mem.exe + -test_pac.exe + -test_pkinit_dh2key.exe + -test_pknistkdf.exe + -test_plugin.exe + -test_prf.exe + -test_renew.exe + -test_rfc3961.exe + -test_store.exe + -test_time.exe + cd $(SRCDIR) + +$(test_binaries): $$(@R).obj $(LIBHEIMDAL) $(LIBVERS) $(LIBROKEN) $(LIBHEIMBASE) + $(EXECONLINK) + $(EXEPREP_NODIST) + +$(OBJ)\test_rfc3961.exe: $(OBJ)\test_rfc3961.obj $(LIBRFC3961) $(LIBHEIMDAL) $(LIBVERS) $(LIBCOMERR) $(LIBROKEN) $(LIBHEIMBASE) + $(EXECONLINK) + $(EXEPREP_NODIST) + +$(test_binaries:.exe=.obj): $$(@B).c + $(C2OBJ_C) -Fo$@ -Fd$(@D)\ $** -DBlah + +test-exports: + $(PERL) ..\..\cf\w32-check-exported-symbols.pl --vs version-script.map --def libkrb5-exports.def.in + +test:: test-exports diff --git a/third_party/heimdal/lib/krb5/acache.c b/third_party/heimdal/lib/krb5/acache.c new file mode 100644 index 0000000..72403d7 --- /dev/null +++ b/third_party/heimdal/lib/krb5/acache.c @@ -0,0 +1,1129 @@ +/* + * Copyright (c) 2004 - 2007 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 "krb5_locl.h" +#include + +#ifndef KCM_IS_API_CACHE + +static HEIMDAL_MUTEX acc_mutex = HEIMDAL_MUTEX_INITIALIZER; +static cc_initialize_func init_func; +static void (KRB5_CALLCONV *set_target_uid)(uid_t); +static void (KRB5_CALLCONV *clear_target)(void); + +#ifdef HAVE_DLOPEN +static void *cc_handle; +#endif + +typedef struct krb5_acc { + char *cache_name; + char *cache_subsidiary; + cc_context_t context; + cc_ccache_t ccache; +} krb5_acc; + +static krb5_error_code KRB5_CALLCONV acc_close(krb5_context, krb5_ccache); + +#define ACACHE(X) ((krb5_acc *)(X)->data.data) + +static const struct { + cc_int32 error; + krb5_error_code ret; +} cc_errors[] = { + { ccErrBadName, KRB5_CC_BADNAME }, + { ccErrCredentialsNotFound, KRB5_CC_NOTFOUND }, + { ccErrCCacheNotFound, KRB5_FCC_NOFILE }, + { ccErrContextNotFound, KRB5_CC_NOTFOUND }, + { ccIteratorEnd, KRB5_CC_END }, + { ccErrNoMem, KRB5_CC_NOMEM }, + { ccErrServerUnavailable, KRB5_CC_NOSUPP }, + { ccErrInvalidCCache, KRB5_CC_BADNAME }, + { ccNoError, 0 } +}; + +static krb5_error_code +translate_cc_error(krb5_context context, cc_int32 error) +{ + size_t i; + krb5_clear_error_message(context); + for(i = 0; i < sizeof(cc_errors)/sizeof(cc_errors[0]); i++) + if (cc_errors[i].error == error) + return cc_errors[i].ret; + return KRB5_FCC_INTERNAL; +} + +static krb5_error_code +init_ccapi(krb5_context context) +{ + const char *lib = NULL; +#ifdef HAVE_DLOPEN + char *explib = NULL; +#endif + + HEIMDAL_MUTEX_lock(&acc_mutex); + if (init_func) { + HEIMDAL_MUTEX_unlock(&acc_mutex); + if (context) + krb5_clear_error_message(context); + return 0; + } + + if (context) + lib = krb5_config_get_string(context, NULL, + "libdefaults", "ccapi_library", + NULL); + if (lib == NULL) { +#ifdef __APPLE__ + lib = "/System/Library/Frameworks/Kerberos.framework/Kerberos"; +#elif defined(_WIN32) + lib = "%{LIBDIR}/libkrb5_cc.dll"; +#else + lib = "%{LIBDIR}/libkrb5_cc.so"; +#endif + } + +#ifdef HAVE_DLOPEN + + if (_krb5_expand_path_tokens(context, lib, 0, &explib) == 0) { + cc_handle = dlopen(explib, RTLD_LAZY|RTLD_LOCAL|RTLD_GROUP); + free(explib); + } + + if (cc_handle == NULL) { + HEIMDAL_MUTEX_unlock(&acc_mutex); + krb5_set_error_message(context, KRB5_CC_NOSUPP, + N_("Failed to load API cache module %s", "file"), + lib); + return KRB5_CC_NOSUPP; + } + + init_func = (cc_initialize_func)dlsym(cc_handle, "cc_initialize"); + set_target_uid = (void (KRB5_CALLCONV *)(uid_t)) + dlsym(cc_handle, "krb5_ipc_client_set_target_uid"); + clear_target = (void (KRB5_CALLCONV *)(void)) + dlsym(cc_handle, "krb5_ipc_client_clear_target"); + HEIMDAL_MUTEX_unlock(&acc_mutex); + if (init_func == NULL) { + krb5_set_error_message(context, KRB5_CC_NOSUPP, + N_("Failed to find cc_initialize" + "in %s: %s", "file, error"), lib, dlerror()); + dlclose(cc_handle); + return KRB5_CC_NOSUPP; + } + + return 0; +#else + HEIMDAL_MUTEX_unlock(&acc_mutex); + krb5_set_error_message(context, KRB5_CC_NOSUPP, + N_("no support for shared object", "")); + return KRB5_CC_NOSUPP; +#endif +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_heim_krb5_ipc_client_set_target_uid(uid_t uid) +{ + init_ccapi(NULL); + if (set_target_uid != NULL) + (*set_target_uid)(uid); +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_heim_krb5_ipc_client_clear_target(void) +{ + init_ccapi(NULL); + if (clear_target != NULL) + (*clear_target)(); +} + +static krb5_error_code +make_cred_from_ccred(krb5_context context, + const cc_credentials_v5_t *incred, + krb5_creds *cred) +{ + krb5_error_code ret; + unsigned int i; + + memset(cred, 0, sizeof(*cred)); + + ret = krb5_parse_name(context, incred->client, &cred->client); + if (ret) + goto fail; + + ret = krb5_parse_name(context, incred->server, &cred->server); + if (ret) + goto fail; + + cred->session.keytype = incred->keyblock.type; + cred->session.keyvalue.length = incred->keyblock.length; + cred->session.keyvalue.data = malloc(incred->keyblock.length); + if (cred->session.keyvalue.data == NULL) + goto nomem; + memcpy(cred->session.keyvalue.data, incred->keyblock.data, + incred->keyblock.length); + + cred->times.authtime = incred->authtime; + cred->times.starttime = incred->starttime; + cred->times.endtime = incred->endtime; + cred->times.renew_till = incred->renew_till; + + ret = krb5_data_copy(&cred->ticket, + incred->ticket.data, + incred->ticket.length); + if (ret) + goto nomem; + + ret = krb5_data_copy(&cred->second_ticket, + incred->second_ticket.data, + incred->second_ticket.length); + if (ret) + goto nomem; + + cred->authdata.val = NULL; + cred->authdata.len = 0; + + cred->addresses.val = NULL; + cred->addresses.len = 0; + + for (i = 0; incred->authdata && incred->authdata[i]; i++) + ; + + if (i) { + cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0])); + if (cred->authdata.val == NULL) + goto nomem; + cred->authdata.len = i; + for (i = 0; i < cred->authdata.len; i++) { + cred->authdata.val[i].ad_type = incred->authdata[i]->type; + ret = krb5_data_copy(&cred->authdata.val[i].ad_data, + incred->authdata[i]->data, + incred->authdata[i]->length); + if (ret) + goto nomem; + } + } + + for (i = 0; incred->addresses && incred->addresses[i]; i++) + ; + + if (i) { + cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0])); + if (cred->addresses.val == NULL) + goto nomem; + cred->addresses.len = i; + + for (i = 0; i < cred->addresses.len; i++) { + cred->addresses.val[i].addr_type = incred->addresses[i]->type; + ret = krb5_data_copy(&cred->addresses.val[i].address, + incred->addresses[i]->data, + incred->addresses[i]->length); + if (ret) + goto nomem; + } + } + + cred->flags.i = 0; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE) + cred->flags.b.forwardable = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED) + cred->flags.b.forwarded = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE) + cred->flags.b.proxiable = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY) + cred->flags.b.proxy = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE) + cred->flags.b.may_postdate = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED) + cred->flags.b.postdated = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID) + cred->flags.b.invalid = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE) + cred->flags.b.renewable = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL) + cred->flags.b.initial = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH) + cred->flags.b.pre_authent = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH) + cred->flags.b.hw_authent = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED) + cred->flags.b.transited_policy_checked = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE) + cred->flags.b.ok_as_delegate = 1; + if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS) + cred->flags.b.anonymous = 1; + + return 0; + +nomem: + ret = krb5_enomem(context); + +fail: + krb5_free_cred_contents(context, cred); + return ret; +} + +static void +free_ccred(cc_credentials_v5_t *cred) +{ + int i; + + if (cred->addresses) { + for (i = 0; cred->addresses[i] != 0; i++) { + if (cred->addresses[i]->data) + free(cred->addresses[i]->data); + free(cred->addresses[i]); + } + free(cred->addresses); + } + if (cred->server) + free(cred->server); + if (cred->client) + free(cred->client); + memset(cred, 0, sizeof(*cred)); +} + +static krb5_error_code +make_ccred_from_cred(krb5_context context, + const krb5_creds *incred, + cc_credentials_v5_t *cred) +{ + krb5_error_code ret; + size_t i; + + memset(cred, 0, sizeof(*cred)); + + ret = krb5_unparse_name(context, incred->client, &cred->client); + if (ret) + goto fail; + + ret = krb5_unparse_name(context, incred->server, &cred->server); + if (ret) + goto fail; + + cred->keyblock.type = incred->session.keytype; + cred->keyblock.length = incred->session.keyvalue.length; + cred->keyblock.data = incred->session.keyvalue.data; + + cred->authtime = incred->times.authtime; + cred->starttime = incred->times.starttime; + cred->endtime = incred->times.endtime; + cred->renew_till = incred->times.renew_till; + + cred->ticket.length = incred->ticket.length; + cred->ticket.data = incred->ticket.data; + + cred->second_ticket.length = incred->second_ticket.length; + cred->second_ticket.data = incred->second_ticket.data; + + /* XXX this one should also be filled in */ + cred->authdata = NULL; + + cred->addresses = calloc(incred->addresses.len + 1, + sizeof(cred->addresses[0])); + if (cred->addresses == NULL) { + + ret = ENOMEM; + goto fail; + } + + for (i = 0; i < incred->addresses.len; i++) { + cc_data *addr; + addr = malloc(sizeof(*addr)); + if (addr == NULL) { + ret = ENOMEM; + goto fail; + } + addr->type = incred->addresses.val[i].addr_type; + addr->length = incred->addresses.val[i].address.length; + addr->data = malloc(addr->length); + if (addr->data == NULL) { + free(addr); + ret = ENOMEM; + goto fail; + } + memcpy(addr->data, incred->addresses.val[i].address.data, + addr->length); + cred->addresses[i] = addr; + } + cred->addresses[i] = NULL; + + cred->ticket_flags = 0; + if (incred->flags.b.forwardable) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE; + if (incred->flags.b.forwarded) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED; + if (incred->flags.b.proxiable) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE; + if (incred->flags.b.proxy) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY; + if (incred->flags.b.may_postdate) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE; + if (incred->flags.b.postdated) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED; + if (incred->flags.b.invalid) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID; + if (incred->flags.b.renewable) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE; + if (incred->flags.b.initial) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL; + if (incred->flags.b.pre_authent) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH; + if (incred->flags.b.hw_authent) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH; + if (incred->flags.b.transited_policy_checked) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED; + if (incred->flags.b.ok_as_delegate) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE; + if (incred->flags.b.anonymous) + cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS; + + return 0; + +fail: + free_ccred(cred); + + krb5_clear_error_message(context); + return ret; +} + +static cc_int32 +get_cc_name(krb5_acc *a) +{ + cc_string_t name; + cc_int32 error; + + error = (*a->ccache->func->get_name)(a->ccache, &name); + if (error) + return error; + + a->cache_name = strdup(name->data); + (*name->func->release)(name); + if (a->cache_name == NULL) + return ccErrNoMem; + return ccNoError; +} + + +static krb5_error_code KRB5_CALLCONV +acc_get_name_2(krb5_context context, + krb5_ccache id, + const char **name, + const char **colname, + const char **subsidiary) +{ + krb5_error_code ret = 0; + krb5_acc *a = ACACHE(id); + int32_t error; + + if (name) + *name = NULL; + if (colname) + *colname = NULL; + if (subsidiary) + *subsidiary = NULL; + if (a->cache_subsidiary == NULL) { + krb5_principal principal = NULL; + + ret = _krb5_get_default_principal_local(context, &principal); + if (ret == 0) + ret = krb5_unparse_name(context, principal, &a->cache_subsidiary); + krb5_free_principal(context, principal); + if (ret) + return ret; + } + + if (a->cache_name == NULL) { + error = (*a->context->func->create_new_ccache)(a->context, + cc_credentials_v5, + a->cache_subsidiary, + &a->ccache); + if (error == ccNoError) + error = get_cc_name(a); + if (error != ccNoError) + ret = translate_cc_error(context, error); + } + if (name) + *name = a->cache_name; + if (colname) + *colname = ""; + if (subsidiary) + *subsidiary = a->cache_subsidiary; + return ret; +} + +static krb5_error_code KRB5_CALLCONV +acc_alloc(krb5_context context, krb5_ccache *id) +{ + krb5_error_code ret; + cc_int32 error; + krb5_acc *a; + + ret = init_ccapi(context); + if (ret) + return ret; + + ret = krb5_data_alloc(&(*id)->data, sizeof(*a)); + if (ret) { + krb5_clear_error_message(context); + return ret; + } + + a = ACACHE(*id); + a->cache_subsidiary = NULL; + a->cache_name = NULL; + a->context = NULL; + a->ccache = NULL; + + error = (*init_func)(&a->context, ccapi_version_3, NULL, NULL); + if (error) { + krb5_data_free(&(*id)->data); + return translate_cc_error(context, error); + } + + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_resolve_2(krb5_context context, krb5_ccache *id, const char *res, const char *sub) +{ + krb5_error_code ret; + cc_time_t offset; + cc_int32 error; + krb5_acc *a; + char *s = NULL; + + ret = acc_alloc(context, id); + if (ret) + return ret; + + a = ACACHE(*id); + + if (sub) { + /* + * For API there's no such thing as a collection name, there's only the + * default collection. Though we could perhaps put a CCAPI shared + * object path in the collection name. + * + * So we'll treat (res && !sub) and (!res && sub) as the same cases. + * + * See also the KCM ccache type, where we have similar considerations. + */ + if (asprintf(&s, "%s%s%s", res && *res ? res : "", + res && *res ? ":" : "", sub) == -1 || s == NULL || + (a->cache_subsidiary = strdup(sub)) == NULL) { + acc_close(context, *id); + free(s); + return krb5_enomem(context); + } + res = s; + /* + * XXX With a bit of extra refactoring we could use the collection name + * as the path to the shared object implementing CCAPI... For now we + * ignore the collection name. + */ + } + + error = (*a->context->func->open_ccache)(a->context, res, &a->ccache); + if (error == ccErrCCacheNotFound) { + a->ccache = NULL; + a->cache_name = NULL; + free(s); + return 0; + } + if (error == ccNoError) + error = get_cc_name(a); + if (error != ccNoError) { + acc_close(context, *id); + *id = NULL; + free(s); + return translate_cc_error(context, error); + } + + error = (*a->ccache->func->get_kdc_time_offset)(a->ccache, + cc_credentials_v5, + &offset); + if (error == 0) + context->kdc_sec_offset = offset; + free(s); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_gen_new(krb5_context context, krb5_ccache *id) +{ + return acc_alloc(context, id); +} + +static krb5_error_code KRB5_CALLCONV +acc_initialize(krb5_context context, + krb5_ccache id, + krb5_principal primary_principal) +{ + krb5_acc *a = ACACHE(id); + krb5_error_code ret; + int32_t error; + char *name; + + ret = krb5_unparse_name(context, primary_principal, &name); + if (ret) + return ret; + + if (a->cache_name == NULL) { + error = (*a->context->func->create_new_ccache)(a->context, + cc_credentials_v5, + name, + &a->ccache); + free(name); + if (error == ccNoError) + error = get_cc_name(a); + } else { + cc_credentials_iterator_t iter; + cc_credentials_t ccred; + + error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter); + if (error) { + free(name); + return translate_cc_error(context, error); + } + + while (1) { + error = (*iter->func->next)(iter, &ccred); + if (error) + break; + (*a->ccache->func->remove_credentials)(a->ccache, ccred); + (*ccred->func->release)(ccred); + } + (*iter->func->release)(iter); + + error = (*a->ccache->func->set_principal)(a->ccache, + cc_credentials_v5, + name); + } + + if (error == 0 && context->kdc_sec_offset) + error = (*a->ccache->func->set_kdc_time_offset)(a->ccache, + cc_credentials_v5, + context->kdc_sec_offset); + + return translate_cc_error(context, error); +} + +static krb5_error_code KRB5_CALLCONV +acc_close(krb5_context context, + krb5_ccache id) +{ + krb5_acc *a = ACACHE(id); + + if (a->ccache) { + (*a->ccache->func->release)(a->ccache); + a->ccache = NULL; + } + if (a->cache_name) { + free(a->cache_name); + a->cache_name = NULL; + } + if (a->context) { + (*a->context->func->release)(a->context); + a->context = NULL; + } + krb5_data_free(&id->data); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_destroy(krb5_context context, + krb5_ccache id) +{ + krb5_acc *a = ACACHE(id); + cc_int32 error = 0; + + if (a->ccache) { + error = (*a->ccache->func->destroy)(a->ccache); + a->ccache = NULL; + } + if (a->context) { + error = (a->context->func->release)(a->context); + a->context = NULL; + } + return translate_cc_error(context, error); +} + +static krb5_error_code KRB5_CALLCONV +acc_store_cred(krb5_context context, + krb5_ccache id, + krb5_creds *creds) +{ + krb5_acc *a = ACACHE(id); + cc_credentials_union cred; + cc_credentials_v5_t v5cred; + krb5_error_code ret; + cc_int32 error; + + if (a->ccache == NULL) { + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("No API credential found", "")); + return KRB5_CC_NOTFOUND; + } + + cred.version = cc_credentials_v5; + cred.credentials.credentials_v5 = &v5cred; + + ret = make_ccred_from_cred(context, + creds, + &v5cred); + if (ret) + return ret; + + error = (*a->ccache->func->store_credentials)(a->ccache, &cred); + if (error) + ret = translate_cc_error(context, error); + + free_ccred(&v5cred); + + return ret; +} + +static krb5_error_code KRB5_CALLCONV +acc_get_principal(krb5_context context, + krb5_ccache id, + krb5_principal *principal) +{ + krb5_acc *a = ACACHE(id); + krb5_error_code ret; + int32_t error; + cc_string_t name; + + if (a->ccache == NULL) { + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("No API credential found", "")); + return KRB5_CC_NOTFOUND; + } + + error = (*a->ccache->func->get_principal)(a->ccache, + cc_credentials_v5, + &name); + if (error) + return translate_cc_error(context, error); + + ret = krb5_parse_name(context, name->data, principal); + + (*name->func->release)(name); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +acc_get_first (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + cc_credentials_iterator_t iter; + krb5_acc *a = ACACHE(id); + int32_t error; + + if (a->ccache == NULL) { + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("No API credential found", "")); + return KRB5_CC_NOTFOUND; + } + + error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter); + if (error) { + krb5_clear_error_message(context); + return ENOENT; + } + *cursor = iter; + return 0; +} + + +static krb5_error_code KRB5_CALLCONV +acc_get_next (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *creds) +{ + cc_credentials_iterator_t iter = *cursor; + cc_credentials_t cred; + krb5_error_code ret; + int32_t error; + + while (1) { + error = (*iter->func->next)(iter, &cred); + if (error) + return translate_cc_error(context, error); + if (cred->data->version == cc_credentials_v5) + break; + (*cred->func->release)(cred); + } + + ret = make_cred_from_ccred(context, + cred->data->credentials.credentials_v5, + creds); + (*cred->func->release)(cred); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +acc_end_get (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + cc_credentials_iterator_t iter = *cursor; + (*iter->func->release)(iter); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_remove_cred(krb5_context context, + krb5_ccache id, + krb5_flags which, + krb5_creds *cred) +{ + cc_credentials_iterator_t iter; + krb5_acc *a = ACACHE(id); + cc_credentials_t ccred; + krb5_error_code ret; + cc_int32 error; + char *client, *server; + + if (a->ccache == NULL) { + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("No API credential found", "")); + return KRB5_CC_NOTFOUND; + } + + if (cred->client) { + ret = krb5_unparse_name(context, cred->client, &client); + if (ret) + return ret; + } else + client = NULL; + + ret = krb5_unparse_name(context, cred->server, &server); + if (ret) { + free(client); + return ret; + } + + error = (*a->ccache->func->new_credentials_iterator)(a->ccache, &iter); + if (error) { + free(server); + free(client); + return translate_cc_error(context, error); + } + + ret = KRB5_CC_NOTFOUND; + while (1) { + cc_credentials_v5_t *v5cred; + + error = (*iter->func->next)(iter, &ccred); + if (error) + break; + + if (ccred->data->version != cc_credentials_v5) + goto next; + + v5cred = ccred->data->credentials.credentials_v5; + + if (client && strcmp(v5cred->client, client) != 0) + goto next; + + if (strcmp(v5cred->server, server) != 0) + goto next; + + (*a->ccache->func->remove_credentials)(a->ccache, ccred); + ret = 0; + next: + (*ccred->func->release)(ccred); + } + + (*iter->func->release)(iter); + + if (ret) + krb5_set_error_message(context, ret, + N_("Can't find credential %s in cache", + "principal"), server); + free(server); + free(client); + + return ret; +} + +static krb5_error_code KRB5_CALLCONV +acc_set_flags(krb5_context context, + krb5_ccache id, + krb5_flags flags) +{ + return 0; +} + +static int KRB5_CALLCONV +acc_get_version(krb5_context context, + krb5_ccache id) +{ + return 0; +} + +struct cache_iter { + cc_context_t context; + cc_ccache_iterator_t iter; +}; + +static krb5_error_code KRB5_CALLCONV +acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) +{ + struct cache_iter *iter; + krb5_error_code ret; + cc_int32 error; + + ret = init_ccapi(context); + if (ret) + return ret; + + iter = calloc(1, sizeof(*iter)); + if (iter == NULL) + return krb5_enomem(context); + + error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL); + if (error) { + free(iter); + return translate_cc_error(context, error); + } + + error = (*iter->context->func->new_ccache_iterator)(iter->context, + &iter->iter); + if (error) { + free(iter); + krb5_clear_error_message(context); + return ENOENT; + } + *cursor = iter; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) +{ + struct cache_iter *iter = cursor; + cc_ccache_t cache; + krb5_acc *a; + krb5_error_code ret; + int32_t error; + + error = (*iter->iter->func->next)(iter->iter, &cache); + if (error) + return translate_cc_error(context, error); + + ret = _krb5_cc_allocate(context, &krb5_acc_ops, id); + if (ret) { + (*cache->func->release)(cache); + return ret; + } + + ret = acc_alloc(context, id); + if (ret) { + (*cache->func->release)(cache); + free(*id); + return ret; + } + + a = ACACHE(*id); + a->ccache = cache; + + error = get_cc_name(a); + if (error) { + acc_close(context, *id); + *id = NULL; + return translate_cc_error(context, error); + } + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) +{ + struct cache_iter *iter = cursor; + + (*iter->iter->func->release)(iter->iter); + iter->iter = NULL; + (*iter->context->func->release)(iter->context); + iter->context = NULL; + free(iter); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_move(krb5_context context, krb5_ccache from, krb5_ccache to) +{ + krb5_error_code ret; + krb5_acc *afrom = ACACHE(from); + krb5_acc *ato = ACACHE(to); + int32_t error; + + if (ato->ccache == NULL) { + cc_string_t name; + + error = (*afrom->ccache->func->get_principal)(afrom->ccache, + cc_credentials_v5, + &name); + if (error) + return translate_cc_error(context, error); + + error = (*ato->context->func->create_new_ccache)(ato->context, + cc_credentials_v5, + name->data, + &ato->ccache); + (*name->func->release)(name); + if (error) + return translate_cc_error(context, error); + } + + error = (*ato->ccache->func->move)(afrom->ccache, ato->ccache); + ret = translate_cc_error(context, error); + if (ret == 0) + krb5_cc_destroy(context, from); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +acc_get_default_name(krb5_context context, char **str) +{ + krb5_error_code ret; + cc_context_t cc; + cc_string_t name; + int32_t error; + + ret = init_ccapi(context); + if (ret) + return ret; + + error = (*init_func)(&cc, ccapi_version_3, NULL, NULL); + if (error) + return translate_cc_error(context, error); + + error = (*cc->func->get_default_ccache_name)(cc, &name); + if (error) { + (*cc->func->release)(cc); + return translate_cc_error(context, error); + } + + error = asprintf(str, "API:%s", name->data); + (*name->func->release)(name); + (*cc->func->release)(cc); + + if (error < 0 || *str == NULL) + return krb5_enomem(context); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_set_default(krb5_context context, krb5_ccache id) +{ + krb5_acc *a = ACACHE(id); + cc_int32 error; + + if (a->ccache == NULL) { + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("No API credential found", "")); + return KRB5_CC_NOTFOUND; + } + + error = (*a->ccache->func->set_default)(a->ccache); + if (error) + return translate_cc_error(context, error); + + return 0; +} + +static krb5_error_code KRB5_CALLCONV +acc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) +{ + krb5_acc *a = ACACHE(id); + cc_int32 error; + cc_time_t t; + + if (a->ccache == NULL) { + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("No API credential found", "")); + return KRB5_CC_NOTFOUND; + } + + error = (*a->ccache->func->get_change_time)(a->ccache, &t); + if (error) + return translate_cc_error(context, error); + + *mtime = t; + + return 0; +} + +/** + * Variable containing the API based credential cache implemention. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops = { + KRB5_CC_OPS_VERSION_5, + "API", + NULL, + NULL, + acc_gen_new, + acc_initialize, + acc_destroy, + acc_close, + acc_store_cred, + NULL, /* acc_retrieve */ + acc_get_principal, + acc_get_first, + acc_get_next, + acc_end_get, + acc_remove_cred, + acc_set_flags, + acc_get_version, + acc_get_cache_first, + acc_get_cache_next, + acc_end_cache_get, + acc_move, + acc_get_default_name, + acc_set_default, + acc_lastchange, + NULL, + NULL, + acc_get_name_2, + acc_resolve_2 +}; + +#endif diff --git a/third_party/heimdal/lib/krb5/acl.c b/third_party/heimdal/lib/krb5/acl.c new file mode 100644 index 0000000..d319614 --- /dev/null +++ b/third_party/heimdal/lib/krb5/acl.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2000 - 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. + */ + +#include "krb5_locl.h" +#include + +struct acl_field { + enum { acl_string, acl_fnmatch, acl_retval } type; + union { + const char *cstr; + char **retv; + } u; + struct acl_field *next, **last; +}; + +static void +free_retv(struct acl_field *acl) +{ + while(acl != NULL) { + if (acl->type == acl_retval) { + if (*acl->u.retv) + free(*acl->u.retv); + *acl->u.retv = NULL; + } + acl = acl->next; + } +} + +static void +acl_free_list(struct acl_field *acl, int retv) +{ + struct acl_field *next; + if (retv) + free_retv(acl); + while(acl != NULL) { + next = acl->next; + free(acl); + acl = next; + } +} + +static krb5_error_code +acl_parse_format(krb5_context context, + struct acl_field **acl_ret, + const char *format, + va_list ap) +{ + const char *p; + struct acl_field *acl = NULL, *tmp; + + for(p = format; *p != '\0'; p++) { + tmp = malloc(sizeof(*tmp)); + if(tmp == NULL) { + acl_free_list(acl, 0); + return krb5_enomem(context); + } + if(*p == 's') { + tmp->type = acl_string; + tmp->u.cstr = va_arg(ap, const char*); + } else if(*p == 'f') { + tmp->type = acl_fnmatch; + tmp->u.cstr = va_arg(ap, const char*); + } else if(*p == 'r') { + tmp->type = acl_retval; + tmp->u.retv = va_arg(ap, char **); + *tmp->u.retv = NULL; + } else { + krb5_set_error_message(context, EINVAL, + N_("Unknown format specifier %c while " + "parsing ACL", "specifier"), *p); + acl_free_list(acl, 0); + free(tmp); + return EINVAL; + } + tmp->next = NULL; + if(acl == NULL) + acl = tmp; + else + *acl->last = tmp; + acl->last = &tmp->next; + } + *acl_ret = acl; + return 0; +} + +static krb5_boolean +acl_match_field(krb5_context context, + const char *string, + struct acl_field *field) +{ + if(field->type == acl_string) { + return strcmp(field->u.cstr, string) == 0; + } else if(field->type == acl_fnmatch) { + return !fnmatch(field->u.cstr, string, 0); + } else if(field->type == acl_retval) { + *field->u.retv = strdup(string); + return TRUE; + } + return FALSE; +} + +static krb5_boolean +acl_match_acl(krb5_context context, + struct acl_field *acl, + const char *string) +{ + char buf[256]; + while(strsep_copy(&string, " \t", buf, sizeof(buf)) != -1) { + if(buf[0] == '\0') + continue; /* skip ws */ + if (acl == NULL) + return FALSE; + if(!acl_match_field(context, buf, acl)) { + return FALSE; + } + acl = acl->next; + } + if (acl) + return FALSE; + return TRUE; +} + +/** + * krb5_acl_match_string matches ACL format against a string. + * + * The ACL format has three format specifiers: s, f, and r. Each + * specifier will retrieve one argument from the variable arguments + * for either matching or storing data. The input string is split up + * using " " (space) and "\t" (tab) as a delimiter; multiple and "\t" + * in a row are considered to be the same. + * + * List of format specifiers: + * - s Matches a string using strcmp(3) (case sensitive). + * - f Matches the string with fnmatch(3). Theflags + * argument (the last argument) passed to the fnmatch function is 0. + * - r Returns a copy of the string in the char ** passed in; the copy + * must be freed with free(3). There is no need to free(3) the + * string on error: the function will clean up and set the pointer + * to NULL. + * + * @param context Kerberos 5 context + * @param string string to match with + * @param format format to match + * @param ... parameter to format string + * + * @return Return an error code or 0. + * + * + * @code + * char *s; + * + * ret = krb5_acl_match_string(context, "foo", "s", "foo"); + * if (ret) + * krb5_errx(context, 1, "acl didn't match"); + * ret = krb5_acl_match_string(context, "foo foo baz/kaka", + * "ss", "foo", &s, "foo/\\*"); + * if (ret) { + * // no need to free(s) on error + * assert(s == NULL); + * krb5_errx(context, 1, "acl didn't match"); + * } + * free(s); + * @endcode + * + * @sa krb5_acl_match_file + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_acl_match_string(krb5_context context, + const char *string, + const char *format, + ...) +{ + krb5_error_code ret; + krb5_boolean found; + struct acl_field *acl; + + va_list ap; + va_start(ap, format); + ret = acl_parse_format(context, &acl, format, ap); + va_end(ap); + if(ret) + return ret; + + found = acl_match_acl(context, acl, string); + acl_free_list(acl, !found); + if (found) { + return 0; + } else { + krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); + return EACCES; + } +} + +/** + * krb5_acl_match_file matches ACL format against each line in a file + * using krb5_acl_match_string(). Lines starting with # are treated + * like comments and ignored. + * + * @param context Kerberos 5 context. + * @param file file with acl listed in the file. + * @param format format to match. + * @param ... parameter to format string. + * + * @return Return an error code or 0. + * + * @sa krb5_acl_match_string + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_acl_match_file(krb5_context context, + const char *file, + const char *format, + ...) +{ + krb5_error_code ret; + struct acl_field *acl = NULL; + char buf[256]; + va_list ap; + FILE *f; + krb5_boolean found; + + f = fopen(file, "r"); + if(f == NULL) { + int save_errno = errno; + rk_strerror_r(save_errno, buf, sizeof(buf)); + krb5_set_error_message(context, save_errno, + N_("open(%s): %s", "file, errno"), + file, buf); + return save_errno; + } + rk_cloexec_file(f); + + va_start(ap, format); + ret = acl_parse_format(context, &acl, format, ap); + va_end(ap); + if(ret) { + fclose(f); + return ret; + } + + found = FALSE; + while(fgets(buf, sizeof(buf), f)) { + if(buf[0] == '#') + continue; + if(acl_match_acl(context, acl, buf)) { + found = TRUE; + break; + } + free_retv(acl); + } + + fclose(f); + acl_free_list(acl, !found); + if (found) { + return 0; + } else { + krb5_set_error_message(context, EACCES, N_("ACL did not match", "")); + return EACCES; + } +} diff --git a/third_party/heimdal/lib/krb5/add_et_list.c b/third_party/heimdal/lib/krb5/add_et_list.c new file mode 100644 index 0000000..1a289ee --- /dev/null +++ b/third_party/heimdal/lib/krb5/add_et_list.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1999 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/** + * Add a specified list of error messages to the et list in context. + * Call func (probably a comerr-generated function) with a pointer to + * the current et_list. + * + * @param context A kerberos context. + * @param func The generated com_err et function. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_add_et_list(krb5_context context, void (*func)(struct et_list **)) +{ + return heim_add_et_list(context->hcontext, func); +} diff --git a/third_party/heimdal/lib/krb5/addr_families.c b/third_party/heimdal/lib/krb5/addr_families.c new file mode 100644 index 0000000..7d13211 --- /dev/null +++ b/third_party/heimdal/lib/krb5/addr_families.c @@ -0,0 +1,1574 @@ +/* + * Copyright (c) 1997-2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +struct addr_operations { + int af; + krb5_address_type atype; + size_t max_sockaddr_size; + krb5_error_code (*sockaddr2addr)(const struct sockaddr *, krb5_address *); + krb5_error_code (*sockaddr2port)(const struct sockaddr *, int16_t *); + void (*addr2sockaddr)(const krb5_address *, struct sockaddr *, + krb5_socklen_t *sa_size, int port); + void (*h_addr2sockaddr)(const char *, struct sockaddr *, krb5_socklen_t *, int); + krb5_error_code (*h_addr2addr)(const char *, krb5_address *); + krb5_boolean (*uninteresting)(const struct sockaddr *); + krb5_boolean (*is_loopback)(const struct sockaddr *); + void (*anyaddr)(struct sockaddr *, krb5_socklen_t *, int); + int (*print_addr)(const krb5_address *, char *, size_t); + int (*parse_addr)(krb5_context, const char*, krb5_address *); + int (*order_addr)(krb5_context, const krb5_address*, const krb5_address*); + int (*free_addr)(krb5_context, krb5_address*); + int (*copy_addr)(krb5_context, const krb5_address*, krb5_address*); + int (*mask_boundary)(krb5_context, const krb5_address*, unsigned long, + krb5_address*, krb5_address*); +}; + +/* + * AF_INET - aka IPv4 implementation + */ + +static krb5_error_code +ipv4_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) +{ + const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; + unsigned char buf[4]; + + a->addr_type = KRB5_ADDRESS_INET; + memcpy (buf, &sin4->sin_addr, 4); + return krb5_data_copy(&a->address, buf, 4); +} + +static krb5_error_code +ipv4_sockaddr2port (const struct sockaddr *sa, int16_t *port) +{ + const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; + + *port = sin4->sin_port; + return 0; +} + +static void +ipv4_addr2sockaddr (const krb5_address *a, + struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + struct sockaddr_in tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.sin_family = AF_INET; + memcpy (&tmp.sin_addr, a->address.data, 4); + tmp.sin_port = port; + memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); + *sa_size = sizeof(tmp); +} + +static void +ipv4_h_addr2sockaddr(const char *addr, + struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + struct sockaddr_in tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.sin_family = AF_INET; + tmp.sin_port = port; + tmp.sin_addr = *((const struct in_addr *)addr); + memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); + *sa_size = sizeof(tmp); +} + +static krb5_error_code +ipv4_h_addr2addr (const char *addr, + krb5_address *a) +{ + unsigned char buf[4]; + + a->addr_type = KRB5_ADDRESS_INET; + memcpy(buf, addr, 4); + return krb5_data_copy(&a->address, buf, 4); +} + +/* + * Are there any addresses that should be considered `uninteresting'? + */ + +static krb5_boolean +ipv4_uninteresting (const struct sockaddr *sa) +{ + const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; + + if (sin4->sin_addr.s_addr == INADDR_ANY) + return TRUE; + + return FALSE; +} + +static krb5_boolean +ipv4_is_loopback (const struct sockaddr *sa) +{ + const struct sockaddr_in *sin4 = (const struct sockaddr_in *)sa; + + if ((ntohl(sin4->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET) + return TRUE; + + return FALSE; +} + +static void +ipv4_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port) +{ + struct sockaddr_in tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.sin_family = AF_INET; + tmp.sin_port = port; + tmp.sin_addr.s_addr = INADDR_ANY; + memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); + *sa_size = sizeof(tmp); +} + +static int +ipv4_print_addr (const krb5_address *addr, char *str, size_t len) +{ + struct in_addr ia; + + memcpy (&ia, addr->address.data, 4); + + return snprintf (str, len, "IPv4:%s", inet_ntoa(ia)); +} + +static int +ipv4_parse_addr (krb5_context context, const char *address, krb5_address *addr) +{ + const char *p; + struct in_addr a; + + p = strchr(address, ':'); + if(p) { + p++; + if(strncasecmp(address, "ip:", p - address) != 0 && + strncasecmp(address, "ip4:", p - address) != 0 && + strncasecmp(address, "ipv4:", p - address) != 0 && + strncasecmp(address, "inet:", p - address) != 0) + return -1; + } else + p = address; + if(inet_aton(p, &a) == 0) + return -1; + addr->addr_type = KRB5_ADDRESS_INET; + if(krb5_data_alloc(&addr->address, 4) != 0) + return -1; + _krb5_put_int(addr->address.data, ntohl(a.s_addr), addr->address.length); + return 0; +} + +static int +ipv4_mask_boundary(krb5_context context, const krb5_address *inaddr, + unsigned long len, krb5_address *low, krb5_address *high) +{ + unsigned long ia; + uint32_t l, h, m = 0xffffffff; + + if (len > 32) { + krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, + N_("IPv4 prefix too large (%ld)", "len"), len); + return KRB5_PROG_ATYPE_NOSUPP; + } + m = m << (32 - len); + + _krb5_get_int(inaddr->address.data, &ia, inaddr->address.length); + + l = ia & m; + h = l | ~m; + + low->addr_type = KRB5_ADDRESS_INET; + if(krb5_data_alloc(&low->address, 4) != 0) + return -1; + _krb5_put_int(low->address.data, l, low->address.length); + + high->addr_type = KRB5_ADDRESS_INET; + if(krb5_data_alloc(&high->address, 4) != 0) { + krb5_free_address(context, low); + return -1; + } + _krb5_put_int(high->address.data, h, high->address.length); + + return 0; +} + + +/* + * AF_INET6 - aka IPv6 implementation + */ + +#ifdef HAVE_IPV6 + +static krb5_error_code +ipv6_sockaddr2addr (const struct sockaddr *sa, krb5_address *a) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + + if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) { + unsigned char buf[4]; + + a->addr_type = KRB5_ADDRESS_INET; +#ifndef IN6_ADDR_V6_TO_V4 +#ifdef IN6_EXTRACT_V4ADDR +#define IN6_ADDR_V6_TO_V4(x) (&IN6_EXTRACT_V4ADDR(x)) +#else +#define IN6_ADDR_V6_TO_V4(x) ((const struct in_addr *)&(x)->s6_addr[12]) +#endif +#endif + memcpy (buf, IN6_ADDR_V6_TO_V4(&sin6->sin6_addr), 4); + return krb5_data_copy(&a->address, buf, 4); + } else { + a->addr_type = KRB5_ADDRESS_INET6; + return krb5_data_copy(&a->address, + &sin6->sin6_addr, + sizeof(sin6->sin6_addr)); + } +} + +static krb5_error_code +ipv6_sockaddr2port (const struct sockaddr *sa, int16_t *port) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + + *port = sin6->sin6_port; + return 0; +} + +static void +ipv6_addr2sockaddr (const krb5_address *a, + struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + struct sockaddr_in6 tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.sin6_family = AF_INET6; + memcpy (&tmp.sin6_addr, a->address.data, sizeof(tmp.sin6_addr)); + tmp.sin6_port = port; + memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); + *sa_size = sizeof(tmp); +} + +static void +ipv6_h_addr2sockaddr(const char *addr, + struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + struct sockaddr_in6 tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.sin6_family = AF_INET6; + tmp.sin6_port = port; + tmp.sin6_addr = *((const struct in6_addr *)addr); + memcpy(sa, &tmp, min(sizeof(tmp), *sa_size)); + *sa_size = sizeof(tmp); +} + +static krb5_error_code +ipv6_h_addr2addr (const char *addr, + krb5_address *a) +{ + a->addr_type = KRB5_ADDRESS_INET6; + return krb5_data_copy(&a->address, addr, sizeof(struct in6_addr)); +} + +/* + * + */ + +static krb5_boolean +ipv6_uninteresting (const struct sockaddr *sa) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr; + + return IN6_IS_ADDR_LINKLOCAL(in6) + || IN6_IS_ADDR_V4COMPAT(in6); +} + +static krb5_boolean +ipv6_is_loopback (const struct sockaddr *sa) +{ + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sa; + const struct in6_addr *in6 = (const struct in6_addr *)&sin6->sin6_addr; + + return (IN6_IS_ADDR_LOOPBACK(in6)); +} + +static void +ipv6_anyaddr (struct sockaddr *sa, krb5_socklen_t *sa_size, int port) +{ + struct sockaddr_in6 tmp; + + memset (&tmp, 0, sizeof(tmp)); + tmp.sin6_family = AF_INET6; + tmp.sin6_port = port; + tmp.sin6_addr = in6addr_any; + *sa_size = sizeof(tmp); +} + +static int +ipv6_print_addr (const krb5_address *addr, char *str, size_t len) +{ + char buf[128], buf2[3]; + if(inet_ntop(AF_INET6, addr->address.data, buf, sizeof(buf)) == NULL) + { + /* XXX this is pretty ugly, but better than abort() */ + size_t i; + unsigned char *p = addr->address.data; + buf[0] = '\0'; + for(i = 0; i < addr->address.length; i++) { + snprintf(buf2, sizeof(buf2), "%02x", p[i]); + if(i > 0 && (i & 1) == 0) + strlcat(buf, ":", sizeof(buf)); + strlcat(buf, buf2, sizeof(buf)); + } + } + return snprintf(str, len, "IPv6:%s", buf); +} + +static int +ipv6_parse_addr (krb5_context context, const char *address, krb5_address *addr) +{ + int ret; + struct in6_addr in6; + const char *p; + + p = strchr(address, ':'); + if(p) { + p++; + if(strncasecmp(address, "ip6:", p - address) == 0 || + strncasecmp(address, "ipv6:", p - address) == 0 || + strncasecmp(address, "inet6:", p - address) == 0) + address = p; + } + + ret = inet_pton(AF_INET6, address, &in6.s6_addr); + if(ret == 1) { + addr->addr_type = KRB5_ADDRESS_INET6; + ret = krb5_data_alloc(&addr->address, sizeof(in6.s6_addr)); + if (ret) + return -1; + memcpy(addr->address.data, in6.s6_addr, sizeof(in6.s6_addr)); + return 0; + } + return -1; +} + +static int +ipv6_mask_boundary(krb5_context context, const krb5_address *inaddr, + unsigned long len, krb5_address *low, krb5_address *high) +{ + struct in6_addr addr, laddr, haddr; + uint32_t m; + int i, sub_len; + + if (len > 128) { + krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, + N_("IPv6 prefix too large (%ld)", "length"), len); + return KRB5_PROG_ATYPE_NOSUPP; + } + + if (inaddr->address.length != sizeof(addr)) { + krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, + N_("IPv6 addr bad length", "")); + return KRB5_PROG_ATYPE_NOSUPP; + } + + memcpy(&addr, inaddr->address.data, inaddr->address.length); + + for (i = 0; i < 16; i++) { + sub_len = min(8, len); + + m = 0xff << (8 - sub_len); + + laddr.s6_addr[i] = addr.s6_addr[i] & m; + haddr.s6_addr[i] = (addr.s6_addr[i] & m) | ~m; + + if (len > 8) + len -= 8; + else + len = 0; + } + + low->addr_type = KRB5_ADDRESS_INET6; + if (krb5_data_alloc(&low->address, sizeof(laddr.s6_addr)) != 0) + return -1; + memcpy(low->address.data, laddr.s6_addr, sizeof(laddr.s6_addr)); + + high->addr_type = KRB5_ADDRESS_INET6; + if (krb5_data_alloc(&high->address, sizeof(haddr.s6_addr)) != 0) { + krb5_free_address(context, low); + return -1; + } + memcpy(high->address.data, haddr.s6_addr, sizeof(haddr.s6_addr)); + + return 0; +} + +#endif /* IPv6 */ + +#ifndef HEIMDAL_SMALLER + +/* + * table + */ + +#define KRB5_ADDRESS_ARANGE (-100) + +struct arange { + krb5_address low; + krb5_address high; +}; + +static int +arange_parse_addr (krb5_context context, + const char *address, krb5_address *addr) +{ + char buf[1024], *p; + krb5_address low0, high0; + struct arange *a; + krb5_error_code ret; + + if(strncasecmp(address, "RANGE:", 6) != 0) + return -1; + + address += 6; + + p = strrchr(address, '/'); + if (p) { + krb5_addresses addrmask; + char *q; + long num; + + if (strlcpy(buf, address, sizeof(buf)) > sizeof(buf)) + return -1; + buf[p - address] = '\0'; + ret = krb5_parse_address(context, buf, &addrmask); + if (ret) + return ret; + if(addrmask.len != 1) { + krb5_free_addresses(context, &addrmask); + return -1; + } + + address += p - address + 1; + + num = strtol(address, &q, 10); + if (q == address || *q != '\0' || num < 0) { + krb5_free_addresses(context, &addrmask); + return -1; + } + + ret = krb5_address_prefixlen_boundary(context, &addrmask.val[0], num, + &low0, &high0); + krb5_free_addresses(context, &addrmask); + if (ret) + return ret; + + } else { + krb5_addresses low, high; + + strsep_copy(&address, "-", buf, sizeof(buf)); + ret = krb5_parse_address(context, buf, &low); + if(ret) + return ret; + if(low.len != 1) { + krb5_free_addresses(context, &low); + return -1; + } + + strsep_copy(&address, "-", buf, sizeof(buf)); + ret = krb5_parse_address(context, buf, &high); + if(ret) { + krb5_free_addresses(context, &low); + return ret; + } + + if(high.len != 1 || high.val[0].addr_type != low.val[0].addr_type) { + krb5_free_addresses(context, &low); + krb5_free_addresses(context, &high); + return -1; + } + + ret = krb5_copy_address(context, &high.val[0], &high0); + if (ret == 0) { + ret = krb5_copy_address(context, &low.val[0], &low0); + if (ret) + krb5_free_address(context, &high0); + } + krb5_free_addresses(context, &low); + krb5_free_addresses(context, &high); + if (ret) + return ret; + } + + ret = krb5_data_alloc(&addr->address, sizeof(*a)); + if (ret) { + krb5_free_address(context, &low0); + krb5_free_address(context, &high0); + return ret; + } + + addr->addr_type = KRB5_ADDRESS_ARANGE; + a = addr->address.data; + + if(krb5_address_order(context, &low0, &high0) < 0) { + a->low = low0; + a->high = high0; + } else { + a->low = high0; + a->high = low0; + } + return 0; +} + +static int +arange_free (krb5_context context, krb5_address *addr) +{ + struct arange *a; + a = addr->address.data; + krb5_free_address(context, &a->low); + krb5_free_address(context, &a->high); + krb5_data_free(&addr->address); + return 0; +} + + +static int +arange_copy (krb5_context context, const krb5_address *inaddr, + krb5_address *outaddr) +{ + krb5_error_code ret; + struct arange *i, *o; + + outaddr->addr_type = KRB5_ADDRESS_ARANGE; + ret = krb5_data_alloc(&outaddr->address, sizeof(*o)); + if(ret) + return ret; + i = inaddr->address.data; + o = outaddr->address.data; + ret = krb5_copy_address(context, &i->low, &o->low); + if(ret) { + krb5_data_free(&outaddr->address); + return ret; + } + ret = krb5_copy_address(context, &i->high, &o->high); + if(ret) { + krb5_free_address(context, &o->low); + krb5_data_free(&outaddr->address); + return ret; + } + return 0; +} + +static int +arange_print_addr (const krb5_address *addr, char *str, size_t len) +{ + struct arange *a; + krb5_error_code ret; + size_t l, size, ret_len; + + a = addr->address.data; + + l = strlcpy(str, "RANGE:", len); + ret_len = l; + if (l > len) + l = len; + size = l; + + ret = krb5_print_address (&a->low, str + size, len - size, &l); + if (ret) + return ret; + ret_len += l; + if (len - size > l) + size += l; + else + size = len; + + l = strlcat(str + size, "-", len - size); + ret_len += l; + if (len - size > l) + size += l; + else + size = len; + + ret = krb5_print_address (&a->high, str + size, len - size, &l); + if (ret) + return ret; + ret_len += l; + + return ret_len; +} + +static int +arange_order_addr(krb5_context context, + const krb5_address *addr1, + const krb5_address *addr2) +{ + int tmp1, tmp2, sign; + struct arange *a; + const krb5_address *a2; + + if(addr1->addr_type == KRB5_ADDRESS_ARANGE) { + a = addr1->address.data; + a2 = addr2; + sign = 1; + } else if(addr2->addr_type == KRB5_ADDRESS_ARANGE) { + a = addr2->address.data; + a2 = addr1; + sign = -1; + } else { + abort(); + UNREACHABLE(return 0); + } + + if(a2->addr_type == KRB5_ADDRESS_ARANGE) { + struct arange *b = a2->address.data; + tmp1 = krb5_address_order(context, &a->low, &b->low); + if(tmp1 != 0) + return sign * tmp1; + return sign * krb5_address_order(context, &a->high, &b->high); + } else if(a2->addr_type == a->low.addr_type) { + tmp1 = krb5_address_order(context, &a->low, a2); + if(tmp1 > 0) + return sign; + tmp2 = krb5_address_order(context, &a->high, a2); + if(tmp2 < 0) + return -sign; + return 0; + } else { + return sign * (addr1->addr_type - addr2->addr_type); + } +} + +#endif /* HEIMDAL_SMALLER */ + +static int +addrport_print_addr (const krb5_address *addr, char *str, size_t len) +{ + krb5_error_code ret; + krb5_address addr1, addr2; + uint16_t port = 0; + size_t ret_len = 0, l, size = 0; + krb5_storage *sp; + + sp = krb5_storage_from_data((krb5_data*)rk_UNCONST(&addr->address)); + if (sp == NULL) + return ENOMEM; + + /* for totally obscure reasons, these are not in network byteorder */ + krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE); + + krb5_storage_seek(sp, 2, SEEK_CUR); /* skip first two bytes */ + krb5_ret_address(sp, &addr1); + + krb5_storage_seek(sp, 2, SEEK_CUR); /* skip two bytes */ + krb5_ret_address(sp, &addr2); + krb5_storage_free(sp); + if(addr2.addr_type == KRB5_ADDRESS_IPPORT && addr2.address.length == 2) { + unsigned long value; + _krb5_get_int(addr2.address.data, &value, 2); + port = value; + } + l = strlcpy(str, "ADDRPORT:", len); + ret_len += l; + if (len > l) + size += l; + else + size = len; + + ret = krb5_print_address(&addr1, str + size, len - size, &l); + if (ret) + return ret; + ret_len += l; + if (len - size > l) + size += l; + else + size = len; + + ret = snprintf(str + size, len - size, ",PORT=%u", port); + if (ret < 0) + return EINVAL; + ret_len += ret; + return ret_len; +} + +static const struct addr_operations at[] = { + { + AF_INET, KRB5_ADDRESS_INET, sizeof(struct sockaddr_in), + ipv4_sockaddr2addr, + ipv4_sockaddr2port, + ipv4_addr2sockaddr, + ipv4_h_addr2sockaddr, + ipv4_h_addr2addr, + ipv4_uninteresting, + ipv4_is_loopback, + ipv4_anyaddr, + ipv4_print_addr, + ipv4_parse_addr, + NULL, + NULL, + NULL, + ipv4_mask_boundary + }, +#ifdef HAVE_IPV6 + { + AF_INET6, KRB5_ADDRESS_INET6, sizeof(struct sockaddr_in6), + ipv6_sockaddr2addr, + ipv6_sockaddr2port, + ipv6_addr2sockaddr, + ipv6_h_addr2sockaddr, + ipv6_h_addr2addr, + ipv6_uninteresting, + ipv6_is_loopback, + ipv6_anyaddr, + ipv6_print_addr, + ipv6_parse_addr, + NULL, + NULL, + NULL, + ipv6_mask_boundary + } , +#endif +#ifndef HEIMDAL_SMALLER + /* fake address type */ + { + KRB5_ADDRESS_ARANGE, KRB5_ADDRESS_ARANGE, sizeof(struct arange), + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + arange_print_addr, + arange_parse_addr, + arange_order_addr, + arange_free, + arange_copy, + NULL + }, +#endif + { + KRB5_ADDRESS_ADDRPORT, KRB5_ADDRESS_ADDRPORT, 0, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + addrport_print_addr, + NULL, + NULL, + NULL, + NULL, + NULL + } +}; + +static const size_t num_addrs = sizeof(at) / sizeof(at[0]); + +static size_t max_sockaddr_size = 0; + +/* + * generic functions + */ + +static const struct addr_operations * +find_af(int af) +{ + size_t i; + + for (i = 0; i < num_addrs; i++) { + if (af == at[i].af) + return &at[i]; + } + return NULL; +} + +static const struct addr_operations * +find_atype(krb5_address_type atype) +{ + size_t i; + + for (i = 0; i < num_addrs; i++) { + if (atype == at[i].atype) + return &at[i]; + } + return NULL; +} + +/** + * krb5_sockaddr2address stores a address a "struct sockaddr" sa in + * the krb5_address addr. + * + * @param context a Keberos context + * @param sa a struct sockaddr to extract the address from + * @param addr an Kerberos 5 address to store the address in. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_sockaddr2address (krb5_context context, + const struct sockaddr *sa, krb5_address *addr) +{ + const struct addr_operations *a = find_af(sa->sa_family); + if (a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d not supported", ""), + sa->sa_family); + return KRB5_PROG_ATYPE_NOSUPP; + } + return (*a->sockaddr2addr)(sa, addr); +} + +/** + * krb5_sockaddr2port extracts a port (if possible) from a "struct + * sockaddr. + * + * @param context a Keberos context + * @param sa a struct sockaddr to extract the port from + * @param port a pointer to an int16_t store the port in. + * + * @return Return an error code or 0. Will return + * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_sockaddr2port (krb5_context context, + const struct sockaddr *sa, int16_t *port) +{ + const struct addr_operations *a = find_af(sa->sa_family); + if (a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d not supported", ""), + sa->sa_family); + return KRB5_PROG_ATYPE_NOSUPP; + } + return (*a->sockaddr2port)(sa, port); +} + +/** + * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr + * and port. The argument sa_size should initially contain the size of + * the sa and after the call, it will contain the actual length of the + * address. In case of the sa is too small to fit the whole address, + * the up to *sa_size will be stored, and then *sa_size will be set to + * the required length. + * + * @param context a Keberos context + * @param addr the address to copy the from + * @param sa the struct sockaddr that will be filled in + * @param sa_size pointer to length of sa, and after the call, it will + * contain the actual length of the address. + * @param port set port in sa. + * + * @return Return an error code or 0. Will return + * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_addr2sockaddr (krb5_context context, + const krb5_address *addr, + struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + const struct addr_operations *a = find_atype(addr->addr_type); + + if (a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address type %d not supported", + "krb5_address type"), + addr->addr_type); + return KRB5_PROG_ATYPE_NOSUPP; + } + if (a->addr2sockaddr == NULL) { + krb5_set_error_message (context, + KRB5_PROG_ATYPE_NOSUPP, + N_("Can't convert address type %d to sockaddr", ""), + addr->addr_type); + return KRB5_PROG_ATYPE_NOSUPP; + } + (*a->addr2sockaddr)(addr, sa, sa_size, port); + return 0; +} + +/** + * krb5_max_sockaddr_size returns the max size of the .Li struct + * sockaddr that the Kerberos library will return. + * + * @return Return an size_t of the maximum struct sockaddr. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL +krb5_max_sockaddr_size (void) +{ + if (max_sockaddr_size == 0) { + size_t i; + + for (i = 0; i < num_addrs; i++) + max_sockaddr_size = max(max_sockaddr_size, at[i].max_sockaddr_size); + } + return max_sockaddr_size; +} + +/** + * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the + * kerberos library thinks are uninteresting. One example are link + * local addresses. + * + * @param sa pointer to struct sockaddr that might be interesting. + * + * @return Return a non zero for uninteresting addresses. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_sockaddr_uninteresting(const struct sockaddr *sa) +{ + const struct addr_operations *a = find_af(sa->sa_family); + if (a == NULL || a->uninteresting == NULL) + return TRUE; + return (*a->uninteresting)(sa); +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_sockaddr_is_loopback(const struct sockaddr *sa) +{ + const struct addr_operations *a = find_af(sa->sa_family); + if (a == NULL || a->is_loopback == NULL) + return TRUE; + return (*a->is_loopback)(sa); +} + +/** + * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and + * the "struct hostent" (see gethostbyname(3) ) h_addr_list + * component. The argument sa_size should initially contain the size + * of the sa, and after the call, it will contain the actual length of + * the address. + * + * @param context a Keberos context + * @param af addresses + * @param addr address + * @param sa returned struct sockaddr + * @param sa_size size of sa + * @param port port to set in sa. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_h_addr2sockaddr (krb5_context context, + int af, + const char *addr, struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + const struct addr_operations *a = find_af(af); + if (a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + "Address family %d not supported", af); + return KRB5_PROG_ATYPE_NOSUPP; + } + (*a->h_addr2sockaddr)(addr, sa, sa_size, port); + return 0; +} + +/** + * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception + * that it operates on a krb5_address instead of a struct sockaddr. + * + * @param context a Keberos context + * @param af address family + * @param haddr host address from struct hostent. + * @param addr returned krb5_address. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_h_addr2addr (krb5_context context, + int af, + const char *haddr, krb5_address *addr) +{ + const struct addr_operations *a = find_af(af); + if (a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d not supported", ""), af); + return KRB5_PROG_ATYPE_NOSUPP; + } + return (*a->h_addr2addr)(haddr, addr); +} + +/** + * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to + * bind(2) to. The argument sa_size should initially contain the size + * of the sa, and after the call, it will contain the actual length + * of the address. + * + * @param context a Keberos context + * @param af address family + * @param sa sockaddr + * @param sa_size lenght of sa. + * @param port for to fill into sa. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_anyaddr (krb5_context context, + int af, + struct sockaddr *sa, + krb5_socklen_t *sa_size, + int port) +{ + const struct addr_operations *a = find_af (af); + + if (a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d not supported", ""), af); + return KRB5_PROG_ATYPE_NOSUPP; + } + + (*a->anyaddr)(sa, sa_size, port); + return 0; +} + +/** + * krb5_print_address prints the address in addr to the string string + * that have the length len. If ret_len is not NULL, it will be filled + * with the length of the string if size were unlimited (not including + * the final NUL) . + * + * @param addr address to be printed + * @param str pointer string to print the address into + * @param len length that will fit into area pointed to by "str". + * @param ret_len return length the str. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_print_address (const krb5_address *addr, + char *str, size_t len, size_t *ret_len) +{ + const struct addr_operations *a = find_atype(addr->addr_type); + int ret; + + if (a == NULL || a->print_addr == NULL) { + char *s; + int l; + size_t i; + + s = str; + l = snprintf(s, len, "TYPE_%d:", addr->addr_type); + if (l < 0 || (size_t)l >= len) + return EINVAL; + s += l; + len -= l; + for(i = 0; i < addr->address.length; i++) { + l = snprintf(s, len, "%02x", ((char*)addr->address.data)[i]); + if (l < 0 || (size_t)l >= len) + return EINVAL; + len -= l; + s += l; + } + if(ret_len != NULL) + *ret_len = s - str; + return 0; + } + ret = (*a->print_addr)(addr, str, len); + if (ret < 0) + return EINVAL; + if(ret_len != NULL) + *ret_len = ret; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_parse_address_no_lookup(krb5_context context, + const char *string, + krb5_addresses *addresses) +{ + int i; + + addresses->len = 0; + addresses->val = NULL; + + for(i = 0; i < num_addrs; i++) { + if(at[i].parse_addr) { + krb5_address addr; + if((*at[i].parse_addr)(context, string, &addr) == 0) { + ALLOC_SEQ(addresses, 1); + if (addresses->val == NULL) + return krb5_enomem(context); + addresses->val[0] = addr; + return 0; + } + } + } + + return -1; +} + +/** + * krb5_parse_address returns the resolved hostname in string to the + * krb5_addresses addresses . + * + * @param context a Keberos context + * @param string + * @param addresses + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_parse_address(krb5_context context, + const char *string, + krb5_addresses *addresses) +{ + krb5_error_code ret; + int i, n; + struct addrinfo *ai, *a; + struct addrinfo hint; + int error; + int save_errno; + + addresses->len = 0; + addresses->val = NULL; + + ret = _krb5_parse_address_no_lookup(context, string, addresses); + if (ret == 0 || ret != -1) + return ret; + + /* if not parsed as numeric address, do a name lookup */ + memset(&hint, 0, sizeof(hint)); + hint.ai_family = AF_UNSPEC; + error = getaddrinfo (string, NULL, &hint, &ai); + if (error) { + krb5_error_code ret2; + save_errno = errno; + ret2 = krb5_eai_to_heim_errno(save_errno, error); + krb5_set_error_message (context, ret2, "%s: %s", + string, gai_strerror(error)); + return ret2; + } + + n = 0; + for (a = ai; a != NULL; a = a->ai_next) + ++n; + + ALLOC_SEQ(addresses, n); + if (addresses->val == NULL) { + freeaddrinfo(ai); + return krb5_enomem(context); + } + + addresses->len = 0; + for (a = ai, i = 0; a != NULL; a = a->ai_next) { + if (krb5_sockaddr2address (context, a->ai_addr, &addresses->val[i])) + continue; + if(krb5_address_search(context, &addresses->val[i], addresses)) { + krb5_free_address(context, &addresses->val[i]); + continue; + } + i++; + addresses->len = i; + } + freeaddrinfo (ai); + return 0; +} + +/** + * krb5_address_order compares the addresses addr1 and addr2 so that + * it can be used for sorting addresses. If the addresses are the same + * address krb5_address_order will return 0. Behavies like memcmp(2). + * + * @param context a Keberos context + * @param addr1 krb5_address to compare + * @param addr2 krb5_address to compare + * + * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and + * addr2 is the same address, > 0 if addr2 is "less" then addr1. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_address_order(krb5_context context, + const krb5_address *addr1, + const krb5_address *addr2) +{ + /* this sucks; what if both addresses have order functions, which + should we call? this works for now, though */ + const struct addr_operations *a; + a = find_atype(addr1->addr_type); + if(a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d not supported", ""), + addr1->addr_type); + return KRB5_PROG_ATYPE_NOSUPP; + } + if(a->order_addr != NULL) + return (*a->order_addr)(context, addr1, addr2); + a = find_atype(addr2->addr_type); + if(a == NULL) { + krb5_set_error_message (context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d not supported", ""), + addr2->addr_type); + return KRB5_PROG_ATYPE_NOSUPP; + } + if(a->order_addr != NULL) + return (*a->order_addr)(context, addr1, addr2); + + if(addr1->addr_type != addr2->addr_type) + return addr1->addr_type - addr2->addr_type; + if(addr1->address.length != addr2->address.length) + return addr1->address.length - addr2->address.length; + return memcmp (addr1->address.data, + addr2->address.data, + addr1->address.length); +} + +/** + * krb5_address_compare compares the addresses addr1 and addr2. + * Returns TRUE if the two addresses are the same. + * + * @param context a Keberos context + * @param addr1 address to compare + * @param addr2 address to compare + * + * @return Return an TRUE is the address are the same FALSE if not + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_address_compare(krb5_context context, + const krb5_address *addr1, + const krb5_address *addr2) +{ + return krb5_address_order (context, addr1, addr2) == 0; +} + +/** + * krb5_address_search checks if the address addr is a member of the + * address set list addrlist . + * + * @param context a Keberos context. + * @param addr address to search for. + * @param addrlist list of addresses to look in for addr. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_address_search(krb5_context context, + const krb5_address *addr, + const krb5_addresses *addrlist) +{ + size_t i; + + for (i = 0; i < addrlist->len; ++i) + if (krb5_address_compare (context, addr, &addrlist->val[i])) + return TRUE; + return FALSE; +} + +/** + * krb5_free_address frees the data stored in the address that is + * alloced with any of the krb5_address functions. + * + * @param context a Keberos context + * @param address addresss to be freed. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_address(krb5_context context, + krb5_address *address) +{ + const struct addr_operations *a = find_atype (address->addr_type); + if(a != NULL && a->free_addr != NULL) + return (*a->free_addr)(context, address); + krb5_data_free (&address->address); + memset(address, 0, sizeof(*address)); + return 0; +} + +/** + * krb5_free_addresses frees the data stored in the address that is + * alloced with any of the krb5_address functions. + * + * @param context a Keberos context + * @param addresses addressses to be freed. + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_addresses(krb5_context context, + krb5_addresses *addresses) +{ + free_HostAddresses(addresses); + return 0; +} + +/** + * krb5_copy_address copies the content of address + * inaddr to outaddr. + * + * @param context a Keberos context + * @param inaddr pointer to source address + * @param outaddr pointer to destination address + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_address(krb5_context context, + const krb5_address *inaddr, + krb5_address *outaddr) +{ + const struct addr_operations *a = find_af (inaddr->addr_type); + if(a != NULL && a->copy_addr != NULL) + return (*a->copy_addr)(context, inaddr, outaddr); + return copy_HostAddress(inaddr, outaddr); +} + +/** + * krb5_copy_addresses copies the content of addresses + * inaddr to outaddr. + * + * @param context a Keberos context + * @param inaddr pointer to source addresses + * @param outaddr pointer to destination addresses + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_addresses(krb5_context context, + const krb5_addresses *inaddr, + krb5_addresses *outaddr) +{ + size_t i; + ALLOC_SEQ(outaddr, inaddr->len); + if(inaddr->len > 0 && outaddr->val == NULL) + return krb5_enomem(context); + for(i = 0; i < inaddr->len; i++) + krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]); + return 0; +} + +/** + * krb5_append_addresses adds the set of addresses in source to + * dest. While copying the addresses, duplicates are also sorted out. + * + * @param context a Keberos context + * @param dest destination of copy operation + * @param source adresses that are going to be added to dest + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_append_addresses(krb5_context context, + krb5_addresses *dest, + const krb5_addresses *source) +{ + krb5_address *tmp; + krb5_error_code ret; + size_t i; + if(source->len > 0) { + tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp)); + if (tmp == NULL) + return krb5_enomem(context); + dest->val = tmp; + for(i = 0; i < source->len; i++) { + /* skip duplicates */ + if(krb5_address_search(context, &source->val[i], dest)) + continue; + ret = krb5_copy_address(context, + &source->val[i], + &dest->val[dest->len]); + if(ret) + return ret; + dest->len++; + } + } + return 0; +} + +/** + * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port) + * + * @param context a Keberos context + * @param res built address from addr/port + * @param addr address to use + * @param port port to use + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_make_addrport (krb5_context context, + krb5_address **res, const krb5_address *addr, int16_t port) +{ + krb5_error_code ret; + size_t len = addr->address.length + 2 + 4 * 4; + u_char *p; + + /* XXX Make this assume port == 0 -> port is absent */ + + *res = malloc (sizeof(**res)); + if (*res == NULL) + return krb5_enomem(context); + (*res)->addr_type = KRB5_ADDRESS_ADDRPORT; + ret = krb5_data_alloc (&(*res)->address, len); + if (ret) { + free (*res); + *res = NULL; + return krb5_enomem(context); + } + p = (*res)->address.data; + *p++ = 0; + *p++ = 0; + *p++ = (addr->addr_type ) & 0xFF; + *p++ = (addr->addr_type >> 8) & 0xFF; + + *p++ = (addr->address.length ) & 0xFF; + *p++ = (addr->address.length >> 8) & 0xFF; + *p++ = (addr->address.length >> 16) & 0xFF; + *p++ = (addr->address.length >> 24) & 0xFF; + + memcpy (p, addr->address.data, addr->address.length); + p += addr->address.length; + + *p++ = 0; + *p++ = 0; + *p++ = (KRB5_ADDRESS_IPPORT ) & 0xFF; + *p++ = (KRB5_ADDRESS_IPPORT >> 8) & 0xFF; + + *p++ = (2 ) & 0xFF; + *p++ = (2 >> 8) & 0xFF; + *p++ = (2 >> 16) & 0xFF; + *p++ = (2 >> 24) & 0xFF; + + memcpy (p, &port, 2); + + return 0; +} + +/** + * Calculate the boundary addresses of `inaddr'/`prefixlen' and store + * them in `low' and `high'. + * + * @param context a Keberos context + * @param inaddr address in prefixlen that the bondery searched + * @param prefixlen width of boundery + * @param low lowest address + * @param high highest address + * + * @return Return an error code or 0. + * + * @ingroup krb5_address + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_address_prefixlen_boundary(krb5_context context, + const krb5_address *inaddr, + unsigned long prefixlen, + krb5_address *low, + krb5_address *high) +{ + const struct addr_operations *a = find_atype (inaddr->addr_type); + if(a != NULL && a->mask_boundary != NULL) + return (*a->mask_boundary)(context, inaddr, prefixlen, low, high); + krb5_set_error_message(context, KRB5_PROG_ATYPE_NOSUPP, + N_("Address family %d doesn't support " + "address mask operation", ""), + inaddr->addr_type); + return KRB5_PROG_ATYPE_NOSUPP; +} diff --git a/third_party/heimdal/lib/krb5/aes-test.c b/third_party/heimdal/lib/krb5/aes-test.c new file mode 100644 index 0000000..2d048e4 --- /dev/null +++ b/third_party/heimdal/lib/krb5/aes-test.c @@ -0,0 +1,1058 @@ +/* + * Copyright (c) 2003-2016 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 "krb5_locl.h" +#include +#include +#include + +static int verbose = 0; + +static void +hex_dump_data(const void *data, size_t length) +{ + char *p; + + hex_encode(data, length, &p); + printf("%s\n", p); + free(p); +} + +struct { + char *password; + char *salt; + int saltlen; + int iterations; + krb5_enctype enctype; + size_t keylen; + char *pbkdf2; + char *key; +} keys[] = { + { + "password", + "\x10\xDF\x9D\xD7\x83\xE5\xBC\x8A\xCE\xA1\x73\x0E\x74\x35\x5F\x61" + "ATHENA.MIT.EDUraeburn", + 37, + 32768, + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + 16, + NULL, + "\x08\x9B\xCA\x48\xB1\x05\xEA\x6E\xA7\x7C\xA5\xD2\xF3\x9D\xC5\xE7" + }, + { + "password", + "\x10\xDF\x9D\xD7\x83\xE5\xBC\x8A\xCE\xA1\x73\x0E\x74\x35\x5F\x61" + "ATHENA.MIT.EDUraeburn", + 37, + 32768, + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + 32, + NULL, + "\x45\xBD\x80\x6D\xBF\x6A\x83\x3A\x9C\xFF\xC1\xC9\x45\x89\xA2\x22" + "\x36\x7A\x79\xBC\x21\xC4\x13\x71\x89\x06\xE9\xF5\x78\xA7\x84\x67" + }, + { + "password", "ATHENA.MIT.EDUraeburn", -1, + 1, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01\x56\x5a\x11\x22\xb2\x56\x35\x15", + "\x42\x26\x3c\x6e\x89\xf4\xfc\x28\xb8\xdf\x68\xee\x09\x79\x9f\x15" + }, + { + "password", "ATHENA.MIT.EDUraeburn", -1, + 1, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\xcd\xed\xb5\x28\x1b\xb2\xf8\x01\x56\x5a\x11\x22\xb2\x56\x35\x15" + "\x0a\xd1\xf7\xa0\x4b\xb9\xf3\xa3\x33\xec\xc0\xe2\xe1\xf7\x08\x37", + "\xfe\x69\x7b\x52\xbc\x0d\x3c\xe1\x44\x32\xba\x03\x6a\x92\xe6\x5b" + "\xbb\x52\x28\x09\x90\xa2\xfa\x27\x88\x39\x98\xd7\x2a\xf3\x01\x61" + }, + { + "password", "ATHENA.MIT.EDUraeburn", -1, + 2, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e\x98\x8b\x62\xc7\x3c\xda\x93\x5d", + "\xc6\x51\xbf\x29\xe2\x30\x0a\xc2\x7f\xa4\x69\xd6\x93\xbd\xda\x13" + }, + { + "password", "ATHENA.MIT.EDUraeburn", -1, + 2, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\x01\xdb\xee\x7f\x4a\x9e\x24\x3e\x98\x8b\x62\xc7\x3c\xda\x93\x5d" + "\xa0\x53\x78\xb9\x32\x44\xec\x8f\x48\xa9\x9e\x61\xad\x79\x9d\x86", + "\xa2\xe1\x6d\x16\xb3\x60\x69\xc1\x35\xd5\xe9\xd2\xe2\x5f\x89\x61" + "\x02\x68\x56\x18\xb9\x59\x14\xb4\x67\xc6\x76\x22\x22\x58\x24\xff" + }, + { + "password", "ATHENA.MIT.EDUraeburn", -1, + 1200, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b", + "\x4c\x01\xcd\x46\xd6\x32\xd0\x1e\x6d\xbe\x23\x0a\x01\xed\x64\x2a" + }, + { + "password", "ATHENA.MIT.EDUraeburn", -1, + 1200, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\x5c\x08\xeb\x61\xfd\xf7\x1e\x4e\x4e\xc3\xcf\x6b\xa1\xf5\x51\x2b" + "\xa7\xe5\x2d\xdb\xc5\xe5\x14\x2f\x70\x8a\x31\xe2\xe6\x2b\x1e\x13", + "\x55\xa6\xac\x74\x0a\xd1\x7b\x48\x46\x94\x10\x51\xe1\xe8\xb0\xa7" + "\x54\x8d\x93\xb0\xab\x30\xa8\xbc\x3f\xf1\x62\x80\x38\x2b\x8c\x2a" + }, + { + "password", "\x12\x34\x56\x78\x78\x56\x34\x12", 8, + 5, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\xd1\xda\xa7\x86\x15\xf2\x87\xe6\xa1\xc8\xb1\x20\xd7\x06\x2a\x49", + "\xe9\xb2\x3d\x52\x27\x37\x47\xdd\x5c\x35\xcb\x55\xbe\x61\x9d\x8e" + }, + { + "password", "\x12\x34\x56\x78\x78\x56\x34\x12", 8, + 5, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\xd1\xda\xa7\x86\x15\xf2\x87\xe6\xa1\xc8\xb1\x20\xd7\x06\x2a\x49" + "\x3f\x98\xd2\x03\xe6\xbe\x49\xa6\xad\xf4\xfa\x57\x4b\x6e\x64\xee", + "\x97\xa4\xe7\x86\xbe\x20\xd8\x1a\x38\x2d\x5e\xbc\x96\xd5\x90\x9c" + "\xab\xcd\xad\xc8\x7c\xa4\x8f\x57\x45\x04\x15\x9f\x16\xc3\x6e\x31" + }, + { + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase equals block size", -1, + 1200, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9", + "\x59\xd1\xbb\x78\x9a\x82\x8b\x1a\xa5\x4e\xf9\xc2\x88\x3f\x69\xed" + }, + { + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase equals block size", -1, + 1200, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\x13\x9c\x30\xc0\x96\x6b\xc3\x2b\xa5\x5f\xdb\xf2\x12\x53\x0a\xc9" + "\xc5\xec\x59\xf1\xa4\x52\xf5\xcc\x9a\xd9\x40\xfe\xa0\x59\x8e\xd1", + "\x89\xad\xee\x36\x08\xdb\x8b\xc7\x1f\x1b\xfb\xfe\x45\x94\x86\xb0" + "\x56\x18\xb7\x0c\xba\xe2\x20\x92\x53\x4e\x56\xc5\x53\xba\x4b\x34" + }, + { + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", -1, + 1200, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5\x1b\x10\xe6\xa6\x87\x21\xbe\x61", + "\xcb\x80\x05\xdc\x5f\x90\x17\x9a\x7f\x02\x10\x4c\x00\x18\x75\x1d" + }, + { + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", + "pass phrase exceeds block size", -1, + 1200, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\x9c\xca\xd6\xd4\x68\x77\x0c\xd5\x1b\x10\xe6\xa6\x87\x21\xbe\x61" + "\x1a\x8b\x4d\x28\x26\x01\xdb\x3b\x36\xbe\x92\x46\x91\x5e\xc8\x2a", + "\xd7\x8c\x5c\x9c\xb8\x72\xa8\xc9\xda\xd4\x69\x7f\x0b\xb5\xb2\xd2" + "\x14\x96\xc8\x2b\xeb\x2c\xae\xda\x21\x12\xfc\xee\xa0\x57\x40\x1b" + }, + { + "\xf0\x9d\x84\x9e" /* g-clef */, "EXAMPLE.COMpianist", -1, + 50, + ETYPE_AES128_CTS_HMAC_SHA1_96, 16, + "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43\xa5\xb8\xbb\x27\x6a\x40\x3b\x39", + "\xf1\x49\xc1\xf2\xe1\x54\xa7\x34\x52\xd4\x3e\x7f\xe6\x2a\x56\xe5" + }, + { + "\xf0\x9d\x84\x9e" /* g-clef */, "EXAMPLE.COMpianist", -1, + 50, + ETYPE_AES256_CTS_HMAC_SHA1_96, 32, + "\x6b\x9c\xf2\x6d\x45\x45\x5a\x43\xa5\xb8\xbb\x27\x6a\x40\x3b\x39" + "\xe7\xfe\x37\xa0\xc4\x1e\x02\xc2\x81\xff\x30\x69\xe1\xe9\x4f\x52", + "\x4b\x6d\x98\x39\xf8\x44\x06\xdf\x1f\x09\xcc\x16\x6d\xb4\xb8\x3c" + "\x57\x18\x48\xb7\x84\xa3\xd6\xbd\xc3\x46\x58\x9a\x3e\x39\x3f\x9e" + }, + { + "foo", "", -1, + 0, + ETYPE_ARCFOUR_HMAC_MD5, 16, + NULL, + "\xac\x8e\x65\x7f\x83\xdf\x82\xbe\xea\x5d\x43\xbd\xaf\x78\x00\xcc" + }, + { + "test", "", -1, + 0, + ETYPE_ARCFOUR_HMAC_MD5, 16, + NULL, + "\x0c\xb6\x94\x88\x05\xf7\x97\xbf\x2a\x82\x80\x79\x73\xb8\x95\x37" + } +}; + +static int +string_to_key_test(krb5_context context) +{ + krb5_data password, opaque; + krb5_error_code ret; + krb5_salt salt; + int i, val = 0; + char iter[4]; + + for (i = 0; i < sizeof(keys)/sizeof(keys[0]); i++) { + + password.data = keys[i].password; + password.length = strlen(password.data); + + salt.salttype = KRB5_PW_SALT; + salt.saltvalue.data = keys[i].salt; + if (keys[i].saltlen == -1) + salt.saltvalue.length = strlen(salt.saltvalue.data); + else + salt.saltvalue.length = keys[i].saltlen; + + opaque.data = iter; + opaque.length = sizeof(iter); + _krb5_put_int(iter, keys[i].iterations, 4); + + if (keys[i].pbkdf2) { + unsigned char keyout[32]; + + if (keys[i].keylen > sizeof(keyout)) + abort(); + + PKCS5_PBKDF2_HMAC(password.data, password.length, + salt.saltvalue.data, salt.saltvalue.length, + keys[i].iterations, EVP_sha1(), + keys[i].keylen, keyout); + + if (memcmp(keyout, keys[i].pbkdf2, keys[i].keylen) != 0) { + krb5_warnx(context, "%d: pbkdf2", i); + val = 1; + hex_dump_data(keyout, keys[i].keylen); + continue; + } + + if (verbose) { + printf("PBKDF2:\n"); + hex_dump_data(keyout, keys[i].keylen); + } + } + + { + krb5_keyblock key; + + ret = krb5_string_to_key_data_salt_opaque (context, + keys[i].enctype, + password, + salt, + opaque, + &key); + if (ret) { + krb5_warn(context, ret, "%d: string_to_key_data_salt_opaque", + i); + val = 1; + continue; + } + + if (key.keyvalue.length != keys[i].keylen) { + krb5_warnx(context, "%d: key wrong length (%lu/%lu)", + i, (unsigned long)key.keyvalue.length, + (unsigned long)keys[i].keylen); + val = 1; + continue; + } + + if (memcmp(key.keyvalue.data, keys[i].key, keys[i].keylen) != 0) { + krb5_warnx(context, "%d: key wrong", i); + val = 1; + hex_dump_data(key.keyvalue.data, key.keyvalue.length); + hex_dump_data(keys[i].key, keys[i].keylen); + continue; + } + + if (verbose) { + printf("key:\n"); + hex_dump_data(key.keyvalue.data, key.keyvalue.length); + } + krb5_free_keyblock_contents(context, &key); + } + } + return val; +} + +static int +krb_enc(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_data *cipher, + krb5_data *clear) +{ + krb5_data decrypt; + krb5_error_code ret; + + krb5_data_zero(&decrypt); + + ret = krb5_decrypt(context, + crypto, + usage, + cipher->data, + cipher->length, + &decrypt); + + if (ret) { + krb5_warn(context, ret, "krb5_decrypt"); + return ret; + } + + if (decrypt.length != clear->length || + (decrypt.length && + memcmp(decrypt.data, clear->data, decrypt.length) != 0)) { + krb5_warnx(context, "clear text not same"); + return EINVAL; + } + + krb5_data_free(&decrypt); + + return 0; +} + +static int +krb_enc_iov2(krb5_context context, + krb5_crypto crypto, + unsigned usage, + size_t cipher_len, + krb5_data *clear) +{ + krb5_crypto_iov iov[4]; + krb5_data decrypt; + int ret; + char *p, *q; + size_t len, i; + + p = clear->data; + len = clear->length; + + iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; + krb5_crypto_length(context, crypto, iov[0].flags, &iov[0].data.length); + iov[0].data.data = emalloc(iov[0].data.length); + + iov[1].flags = KRB5_CRYPTO_TYPE_DATA; + iov[1].data.length = len; + iov[1].data.data = emalloc(iov[1].data.length); + memcpy(iov[1].data.data, p, iov[1].data.length); + + /* padding buffer */ + iov[2].flags = KRB5_CRYPTO_TYPE_PADDING; + krb5_crypto_length(context, crypto, KRB5_CRYPTO_TYPE_PADDING, &iov[2].data.length); + iov[2].data.data = emalloc(iov[2].data.length); + + iov[3].flags = KRB5_CRYPTO_TYPE_TRAILER; + krb5_crypto_length(context, crypto, iov[3].flags, &iov[3].data.length); + iov[3].data.data = emalloc(iov[3].data.length); + + ret = krb5_encrypt_iov_ivec(context, crypto, usage, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + errx(1, "encrypt iov failed: %d", ret); + + /* check len */ + for (i = 0, len = 0; i < sizeof(iov)/sizeof(iov[0]); i++) + len += iov[i].data.length; + if (len != cipher_len) + errx(1, "cipher len wrong"); + + /* + * Plain decrypt + */ + + p = q = emalloc(len); + for (i = 0; i < sizeof(iov)/sizeof(iov[0]); i++) { + memcpy(q, iov[i].data.data, iov[i].data.length); + q += iov[i].data.length; + } + + ret = krb5_decrypt(context, crypto, usage, p, len, &decrypt); + if (ret) + krb5_err(context, 1, ret, "krb5_decrypt"); + else + krb5_data_free(&decrypt); + + free(p); + + /* + * Now decrypt use iov + */ + + /* padding turn into data */ + p = q = emalloc(iov[1].data.length + iov[2].data.length); + + memcpy(q, iov[1].data.data, iov[1].data.length); + q += iov[1].data.length; + memcpy(q, iov[2].data.data, iov[2].data.length); + + free(iov[1].data.data); + free(iov[2].data.data); + + iov[1].data.data = p; + iov[1].data.length += iov[2].data.length; + + iov[2].flags = KRB5_CRYPTO_TYPE_EMPTY; + iov[2].data.length = 0; + + ret = krb5_decrypt_iov_ivec(context, crypto, usage, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + free(iov[0].data.data); + free(iov[3].data.data); + + if (ret) + krb5_err(context, 1, ret, "decrypt iov failed: %d", ret); + + if (clear->length != iov[1].data.length) + errx(1, "length incorrect"); + + p = clear->data; + if (memcmp(iov[1].data.data, p, iov[1].data.length) != 0) + errx(1, "iov[1] incorrect"); + + free(iov[1].data.data); + + return 0; +} + + +static int +krb_enc_iov(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_data *cipher, + krb5_data *clear) +{ + krb5_crypto_iov iov[3]; + int ret; + char *p; + size_t len; + + p = cipher->data; + len = cipher->length; + + iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; + krb5_crypto_length(context, crypto, iov[0].flags, &iov[0].data.length); + iov[0].data.data = emalloc(iov[0].data.length); + memcpy(iov[0].data.data, p, iov[0].data.length); + p += iov[0].data.length; + len -= iov[0].data.length; + + iov[1].flags = KRB5_CRYPTO_TYPE_TRAILER; + krb5_crypto_length(context, crypto, iov[1].flags, &iov[1].data.length); + iov[1].data.data = emalloc(iov[1].data.length); + memcpy(iov[1].data.data, p + len - iov[1].data.length, iov[1].data.length); + len -= iov[1].data.length; + + iov[2].flags = KRB5_CRYPTO_TYPE_DATA; + iov[2].data.length = len; + iov[2].data.data = emalloc(len); + memcpy(iov[2].data.data, p, len); + + ret = krb5_decrypt_iov_ivec(context, crypto, usage, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb_enc_iov decrypt iov failed: %d", ret); + + if (clear->length != iov[2].data.length) + errx(1, "length incorrect"); + + p = clear->data; + if (memcmp(iov[2].data.data, p, iov[2].data.length) != 0) + errx(1, "iov[2] incorrect"); + + free(iov[0].data.data); + free(iov[1].data.data); + free(iov[2].data.data); + + + return 0; +} + +static int +krb_checksum_iov(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_data *plain, + krb5_data *verify) +{ + krb5_crypto_iov iov[3]; + int ret; + char *p; + size_t len; + + p = plain->data; + len = plain->length; + + iov[0].flags = KRB5_CRYPTO_TYPE_CHECKSUM; + if (verify) { + iov[0].data = *verify; + } else { + krb5_crypto_length(context, crypto, iov[0].flags, &iov[0].data.length); + iov[0].data.data = emalloc(iov[0].data.length); + } + + iov[1].flags = KRB5_CRYPTO_TYPE_DATA; + iov[1].data.length = len; + iov[1].data.data = p; + + iov[2].flags = KRB5_CRYPTO_TYPE_TRAILER; + krb5_crypto_length(context, crypto, iov[0].flags, &iov[2].data.length); + iov[2].data.data = malloc(iov[2].data.length); + + if (verify == NULL) { + ret = krb5_create_checksum_iov(context, crypto, usage, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_create_checksum_iov failed"); + } + + ret = krb5_verify_checksum_iov(context, crypto, usage, iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_verify_checksum_iov"); + + if (verify == NULL) + free(iov[0].data.data); + free(iov[2].data.data); + + return 0; +} + + +static int +krb_enc_mit(krb5_context context, + krb5_enctype enctype, + krb5_keyblock *key, + unsigned usage, + krb5_data *cipher, + krb5_data *clear) +{ +#ifndef HEIMDAL_SMALLER + krb5_error_code ret; + krb5_enc_data e; + krb5_data decrypt; + size_t len; + + e.kvno = 0; + e.enctype = enctype; + e.ciphertext = *cipher; + + ret = krb5_c_decrypt(context, *key, usage, NULL, &e, &decrypt); + if (ret) + return ret; + + if (decrypt.length != clear->length || + (decrypt.length && + memcmp(decrypt.data, clear->data, decrypt.length) != 0)) { + krb5_warnx(context, "clear text not same"); + return EINVAL; + } + + krb5_data_free(&decrypt); + + ret = krb5_c_encrypt_length(context, enctype, clear->length, &len); + if (ret) + return ret; + + if (len != cipher->length) { + krb5_warnx(context, "c_encrypt_length wrong %lu != %lu", + (unsigned long)len, (unsigned long)cipher->length); + return EINVAL; + } +#endif /* HEIMDAL_SMALLER */ + return 0; +} + +struct { + krb5_enctype enctype; + unsigned usage; + size_t keylen; + void *key; + size_t elen; + void* edata; + size_t plen; + void *pdata; + size_t clen; /* checksum length */ + void *cdata; /* checksum data */ +} krbencs[] = { + { + ETYPE_AES256_CTS_HMAC_SHA1_96, + 7, + 32, + "\x47\x75\x69\x64\x65\x6c\x69\x6e\x65\x73\x20\x74\x6f\x20\x41\x75" + "\x74\x68\x6f\x72\x73\x20\x6f\x66\x20\x49\x6e\x74\x65\x72\x6e\x65", + 44, + "\xcf\x79\x8f\x0d\x76\xf3\xe0\xbe\x8e\x66\x94\x70\xfa\xcc\x9e\x91" + "\xa9\xec\x1c\x5c\x21\xfb\x6e\xef\x1a\x7a\xc8\xc1\xcc\x5a\x95\x24" + "\x6f\x9f\xf4\xd5\xbe\x5d\x59\x97\x44\xd8\x47\xcd", + 16, + "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x2e\x0a", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + 2, + 16, + "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C", + 32, + "\xEF\x85\xFB\x89\x0B\xB8\x47\x2F\x4D\xAB\x20\x39\x4D\xCA\x78\x1D" + "\xAD\x87\x7E\xDA\x39\xD5\x0C\x87\x0C\x0D\x5A\x0A\x8E\x48\xC7\x18", + 0, + "", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + 2, + 16, + "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C", + 38, + "\x84\xD7\xF3\x07\x54\xED\x98\x7B\xAB\x0B\xF3\x50\x6B\xEB\x09\xCF" + "\xB5\x54\x02\xCE\xF7\xE6\x87\x7C\xE9\x9E\x24\x7E\x52\xD1\x6E\xD4" + "\x42\x1D\xFD\xF8\x97\x6C", + 6, + "\x00\x01\x02\x03\x04\x05", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + 2, + 16, + "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C", + 48, + "\x35\x17\xD6\x40\xF5\x0D\xDC\x8A\xD3\x62\x87\x22\xB3\x56\x9D\x2A" + "\xE0\x74\x93\xFA\x82\x63\x25\x40\x80\xEA\x65\xC1\x00\x8E\x8F\xC2" + "\x95\xFB\x48\x52\xE7\xD8\x3E\x1E\x7C\x48\xC3\x7E\xEB\xE6\xB0\xD3", + 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + 2, + 16, + "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C", + 53, + "\x72\x0F\x73\xB1\x8D\x98\x59\xCD\x6C\xCB\x43\x46\x11\x5C\xD3\x36" + "\xC7\x0F\x58\xED\xC0\xC4\x43\x7C\x55\x73\x54\x4C\x31\xC8\x13\xBC" + "\xE1\xE6\xD0\x72\xC1\x86\xB3\x9A\x41\x3C\x2F\x92\xCA\x9B\x83\x34" + "\xA2\x87\xFF\xCB\xFC", + 21, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", + 16, + "\xD7\x83\x67\x18\x66\x43\xD6\x7B\x41\x1C\xBA\x91\x39\xFC\x1D\xEE" + }, + { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + 2, + 32, + "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98" + "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52", + 40, + "\x41\xF5\x3F\xA5\xBF\xE7\x02\x6D\x91\xFA\xF9\xBE\x95\x91\x95\xA0" + "\x58\x70\x72\x73\xA9\x6A\x40\xF0\xA0\x19\x60\x62\x1A\xC6\x12\x74" + "\x8B\x9B\xBF\xBE\x7E\xB4\xCE\x3C", + 0, + "", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + 2, + 32, + "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98" + "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52", + 46, + "\x4E\xD7\xB3\x7C\x2B\xCA\xC8\xF7\x4F\x23\xC1\xCF\x07\xE6\x2B\xC7" + "\xB7\x5F\xB3\xF6\x37\xB9\xF5\x59\xC7\xF6\x64\xF6\x9E\xAB\x7B\x60" + "\x92\x23\x75\x26\xEA\x0D\x1F\x61\xCB\x20\xD6\x9D\x10\xF2", + 6, + "\x00\x01\x02\x03\x04\x05", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + 2, + 32, + "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98" + "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52", + 56, + "\xBC\x47\xFF\xEC\x79\x98\xEB\x91\xE8\x11\x5C\xF8\xD1\x9D\xAC\x4B" + "\xBB\xE2\xE1\x63\xE8\x7D\xD3\x7F\x49\xBE\xCA\x92\x02\x77\x64\xF6" + "\x8C\xF5\x1F\x14\xD7\x98\xC2\x27\x3F\x35\xDF\x57\x4D\x1F\x93\x2E" + "\x40\xC4\xFF\x25\x5B\x36\xA2\x66", + 16, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + 0, + NULL + }, + { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + 2, + 32, + "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98" + "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52", + 61, + "\x40\x01\x3E\x2D\xF5\x8E\x87\x51\x95\x7D\x28\x78\xBC\xD2\xD6\xFE" + "\x10\x1C\xCF\xD5\x56\xCB\x1E\xAE\x79\xDB\x3C\x3E\xE8\x64\x29\xF2" + "\xB2\xA6\x02\xAC\x86\xFE\xF6\xEC\xB6\x47\xD6\x29\x5F\xAE\x07\x7A" + "\x1F\xEB\x51\x75\x08\xD2\xC1\x6B\x41\x92\xE0\x1F\x62", + 21, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14", + 24, + "\x45\xEE\x79\x15\x67\xEE\xFC\xA3\x7F\x4A\xC1\xE0\x22\x2D\xE8\x0D" + "\x43\xC3\xBF\xA0\x66\x99\x67\x2A" + } +}; + +static int +krb_enc_test(krb5_context context) +{ + krb5_error_code ret; + krb5_crypto crypto; + krb5_keyblock kb; + krb5_data cipher, plain; + int i; + + for (i = 0; i < sizeof(krbencs)/sizeof(krbencs[0]); i++) { + + kb.keytype = krbencs[i].enctype; + kb.keyvalue.length = krbencs[i].keylen; + kb.keyvalue.data = krbencs[i].key; + + ret = krb5_crypto_init(context, &kb, krbencs[i].enctype, &crypto); + if (ret) + krb5_err(context, 1, ret, "krb5_crypto_init failed with %d for test %d", + ret, i); + + cipher.length = krbencs[i].elen; + cipher.data = krbencs[i].edata; + plain.length = krbencs[i].plen; + plain.data = krbencs[i].pdata; + + ret = krb_enc(context, crypto, krbencs[i].usage, &cipher, &plain); + + if (ret) + krb5_err(context, 1, ret, "krb_enc failed with %d for test %d", + ret, i); + + ret = krb_enc_iov(context, crypto, krbencs[i].usage, &cipher, &plain); + if (ret) + krb5_err(context, 1, ret, "krb_enc_iov failed with %d for test %d", + ret, i); + + ret = krb_enc_iov2(context, crypto, krbencs[i].usage, + cipher.length, &plain); + if (ret) + krb5_err(context, 1, ret, "krb_enc_iov2 failed with %d for test %d", + ret, i); + + ret = krb_checksum_iov(context, crypto, krbencs[i].usage, &plain, NULL); + if (ret) + krb5_err(context, 1, ret, + "krb_checksum_iov failed with %d for test %d", ret, i); + + if (krbencs[i].cdata) { + krb5_data checksum; + + checksum.length = krbencs[i].clen; + checksum.data = krbencs[i].cdata; + + ret = krb_checksum_iov(context, crypto, krbencs[i].usage, + &plain, &checksum); + if (ret) + krb5_err(context, 1, ret, + "krb_checksum_iov(2) failed with %d for test %d", + ret, i); + } + + krb5_crypto_destroy(context, crypto); + + ret = krb_enc_mit(context, krbencs[i].enctype, &kb, + krbencs[i].usage, &cipher, &plain); + if (ret) + krb5_err(context, 1, ret, "krb_enc_mit failed with %d for test %d", + ret, i); + } + + return 0; +} + +static int +iov_test(krb5_context context, krb5_enctype enctype) +{ + krb5_error_code ret; + krb5_crypto crypto; + krb5_keyblock key; + krb5_data signonly, in, in2; + krb5_crypto_iov iov[6]; + size_t len, i; + unsigned char *base, *p; + + ret = krb5_generate_random_keyblock(context, enctype, &key); + if (ret) + krb5_err(context, 1, ret, "krb5_generate_random_keyblock"); + + ret = krb5_crypto_init(context, &key, 0, &crypto); + if (ret) + krb5_err(context, 1, ret, "krb5_crypto_init"); + + + ret = krb5_crypto_length(context, crypto, KRB5_CRYPTO_TYPE_HEADER, &len); + if (ret) + krb5_err(context, 1, ret, "krb5_crypto_length"); + + signonly.data = "This should be signed"; + signonly.length = strlen(signonly.data); + in.data = "inputdata"; + in.length = strlen(in.data); + + in2.data = "INPUTDATA"; + in2.length = strlen(in2.data); + + + memset(iov, 0, sizeof(iov)); + + iov[0].flags = KRB5_CRYPTO_TYPE_HEADER; + iov[1].flags = KRB5_CRYPTO_TYPE_DATA; + iov[1].data = in; + iov[2].flags = KRB5_CRYPTO_TYPE_SIGN_ONLY; + iov[2].data = signonly; + iov[3].flags = KRB5_CRYPTO_TYPE_EMPTY; + iov[4].flags = KRB5_CRYPTO_TYPE_PADDING; + iov[5].flags = KRB5_CRYPTO_TYPE_TRAILER; + + ret = krb5_crypto_length_iov(context, crypto, iov, + sizeof(iov)/sizeof(iov[0])); + if (ret) + krb5_err(context, 1, ret, "krb5_crypto_length_iov"); + + for (len = 0, i = 0; i < sizeof(iov)/sizeof(iov[0]); i++) { + if (iov[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + continue; + len += iov[i].data.length; + } + + base = emalloc(len); + + /* + * Allocate data for the fields + */ + + for (p = base, i = 0; i < sizeof(iov)/sizeof(iov[0]); i++) { + if (iov[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + continue;; + iov[i].data.data = p; + p += iov[i].data.length; + } + assert(iov[1].data.length == in.length); + memcpy(iov[1].data.data, in.data, iov[1].data.length); + + /* + * Encrypt + */ + + ret = krb5_encrypt_iov_ivec(context, crypto, 7, iov, + sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_encrypt_iov_ivec"); + + /* + * Decrypt + */ + + ret = krb5_decrypt_iov_ivec(context, crypto, 7, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_decrypt_iov_ivec"); + + /* + * Verify data + */ + + if (krb5_data_cmp(&iov[1].data, &in) != 0) + krb5_errx(context, 1, "decrypted data not same"); + + /* + * Free memory + */ + + free(base); + + /* Set up for second try */ + + iov[3].flags = KRB5_CRYPTO_TYPE_DATA; + iov[3].data = in; + + ret = krb5_crypto_length_iov(context, crypto, + iov, sizeof(iov)/sizeof(iov[0])); + if (ret) + krb5_err(context, 1, ret, "krb5_crypto_length_iov"); + + for (len = 0, i = 0; i < sizeof(iov)/sizeof(iov[0]); i++) { + if (iov[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + continue; + len += iov[i].data.length; + } + + base = emalloc(len); + + /* + * Allocate data for the fields + */ + + for (p = base, i = 0; i < sizeof(iov)/sizeof(iov[0]); i++) { + if (iov[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + continue;; + iov[i].data.data = p; + p += iov[i].data.length; + } + assert(iov[1].data.length == in.length); + memcpy(iov[1].data.data, in.data, iov[1].data.length); + + assert(iov[3].data.length == in2.length); + memcpy(iov[3].data.data, in2.data, iov[3].data.length); + + + + /* + * Encrypt + */ + + ret = krb5_encrypt_iov_ivec(context, crypto, 7, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_encrypt_iov_ivec"); + + /* + * Decrypt + */ + + ret = krb5_decrypt_iov_ivec(context, crypto, 7, + iov, sizeof(iov)/sizeof(iov[0]), NULL); + if (ret) + krb5_err(context, 1, ret, "krb5_decrypt_iov_ivec"); + + /* + * Verify data + */ + + if (krb5_data_cmp(&iov[1].data, &in) != 0) + krb5_errx(context, 1, "decrypted data 2.1 not same"); + + if (krb5_data_cmp(&iov[3].data, &in2) != 0) + krb5_errx(context, 1, "decrypted data 2.2 not same"); + + /* + * Free memory + */ + + free(base); + + krb5_crypto_destroy(context, crypto); + + krb5_free_keyblock_contents(context, &key); + + return 0; +} + + + +static int +random_to_key(krb5_context context) +{ + krb5_error_code ret; + krb5_keyblock key; + + ret = krb5_random_to_key(context, + ETYPE_DES3_CBC_SHA1, + "\x21\x39\x04\x58\x6A\xBD\x7F" + "\x21\x39\x04\x58\x6A\xBD\x7F" + "\x21\x39\x04\x58\x6A\xBD\x7F", + 21, + &key); + if (ret){ + krb5_warn(context, ret, "random_to_key"); + return 1; + } + if (key.keyvalue.length != 24) + return 1; + + if (memcmp(key.keyvalue.data, + "\x20\x38\x04\x58\x6b\xbc\x7f\xc7" + "\x20\x38\x04\x58\x6b\xbc\x7f\xc7" + "\x20\x38\x04\x58\x6b\xbc\x7f\xc7", + 24) != 0) + return 1; + + krb5_free_keyblock_contents(context, &key); + + return 0; +} + +int +main(int argc, char **argv) +{ + krb5_error_code ret; + krb5_context context; + int val = 0; + + if (argc > 1 && strcmp(argv[1], "-v") == 0) + verbose = 1; + + ret = krb5_init_context (&context); + if (ret) + errx (1, "krb5_init_context failed: %d", ret); + + val |= string_to_key_test(context); + + val |= krb_enc_test(context); + val |= random_to_key(context); + val |= iov_test(context, KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96); + val |= iov_test(context, KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128); + val |= iov_test(context, KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192); + + if (verbose && val == 0) + printf("all ok\n"); + if (val) + printf("tests failed\n"); + + krb5_free_context(context); + + return val; +} diff --git a/third_party/heimdal/lib/krb5/an2ln_plugin.h b/third_party/heimdal/lib/krb5/an2ln_plugin.h new file mode 100644 index 0000000..b592f23 --- /dev/null +++ b/third_party/heimdal/lib/krb5/an2ln_plugin.h @@ -0,0 +1,91 @@ +/* + * 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 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 HEIMDAL_KRB5_AN2LN_PLUGIN_H +#define HEIMDAL_KRB5_AN2LN_PLUGIN_H 1 + +#include + +#define KRB5_PLUGIN_AN2LN "an2ln" +#define KRB5_PLUGIN_AN2LN_VERSION_0 0 + +typedef krb5_error_code (KRB5_LIB_CALL *set_result_f)(void *, const char *); + +/** @struct krb5plugin_an2ln_ftable_desc + * + * @brief Description of the krb5_aname_to_lname(3) plugin facility. + * + * The krb5_aname_to_lname(3) function is pluggable. The plugin is + * named KRB5_PLUGIN_AN2LN ("an2ln"), with a single minor version, + * KRB5_PLUGIN_AN2LN_VERSION_0 (0). + * + * The plugin for krb5_aname_to_lname(3) consists of a data symbol + * referencing a structure of type krb5plugin_an2ln_ftable, with four + * fields: + * + * @param init Plugin initialization function (see krb5-plugin(7)) + * + * @param minor_version The plugin minor version number (0) + * + * @param fini Plugin finalization function + * + * @param an2ln Plugin aname_to_lname function + * + * The an2ln field is the plugin entry point that performs the + * traditional aname_to_lname operation however the plugin desires. It + * is invoked in no particular order relative to other an2ln plugins, + * but it has a 'rule' argument that indicates which plugin is intended + * to act on the rule. The plugin an2ln function must return + * KRB5_PLUGIN_NO_HANDLE if the rule is not applicable to it. + * + * The plugin an2ln function has the following arguments, in this order: + * + * -# plug_ctx, the context value output by the plugin's init function + * -# context, a krb5_context + * -# rule, the aname_to_lname rule being evaluated (from krb5.conf(5)) + * -# aname, the krb5_principal to be mapped to an lname + * -# set_res_f, a function the plugin must call to set its result + * -# set_res_ctx, the first argument to set_res_f (the second is the result lname string) + * + * @ingroup krb5_support + */ +typedef struct krb5plugin_an2ln_ftable_desc { + HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context); + krb5_error_code (KRB5_LIB_CALL *an2ln)(void *, krb5_context, const char *, + krb5_const_principal, set_result_f, void *); +} krb5plugin_an2ln_ftable; + +#endif /* HEIMDAL_KRB5_AN2LN_PLUGIN_H */ + diff --git a/third_party/heimdal/lib/krb5/aname_to_localname.c b/third_party/heimdal/lib/krb5/aname_to_localname.c new file mode 100644 index 0000000..7c546fb --- /dev/null +++ b/third_party/heimdal/lib/krb5/aname_to_localname.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 1997 - 1999, 2002 - 2003 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" +#include "an2ln_plugin.h" +#include "db_plugin.h" + +#include + +/* Default plugin (DB using binary search of sorted text file) follows */ +static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_init(krb5_context, void **); +static void KRB5_LIB_CALL an2ln_def_plug_fini(void *); +static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_an2ln(void *, krb5_context, const char *, + krb5_const_principal, set_result_f, + void *); + +static const krb5plugin_an2ln_ftable an2ln_def_plug = { + 0, + an2ln_def_plug_init, + an2ln_def_plug_fini, + an2ln_def_plug_an2ln, +}; + +/* Plugin engine code follows */ +struct plctx { + krb5_const_principal aname; + heim_string_t luser; + const char *rule; +}; + +static krb5_error_code KRB5_LIB_CALL +set_res(void *userctx, const char *res) +{ + struct plctx *plctx = userctx; + plctx->luser = heim_string_create(res); + if (plctx->luser == NULL) + return ENOMEM; + return 0; +} + +static krb5_error_code KRB5_LIB_CALL +plcallback(krb5_context context, + const void *plug, void *plugctx, void *userctx) +{ + const krb5plugin_an2ln_ftable *locate = plug; + struct plctx *plctx = userctx; + + if (plctx->luser) + return 0; + + return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx); +} + +static const char *const an2ln_plugin_deps[] = { "krb5", NULL }; + +static const struct heim_plugin_data +an2ln_plugin_data = { + "krb5", + KRB5_PLUGIN_AN2LN, + KRB5_PLUGIN_AN2LN_VERSION_0, + an2ln_plugin_deps, + krb5_get_instance +}; + +static krb5_error_code +an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname, + size_t lnsize, char *lname) +{ + krb5_error_code ret; + struct plctx ctx; + + ctx.rule = rule; + ctx.aname = aname; + ctx.luser = NULL; + + /* + * Order of plugin invocation is non-deterministic, but there should + * really be no more than one plugin that can handle any given kind + * rule, so the effect should be deterministic anyways. + */ + ret = _krb5_plugin_run_f(context, &an2ln_plugin_data, + 0, &ctx, plcallback); + if (ret != 0) { + heim_release(ctx.luser); + return ret; + } + + if (ctx.luser == NULL) + return KRB5_PLUGIN_NO_HANDLE; + + if (strlcpy(lname, heim_string_get_utf8(ctx.luser), lnsize) >= lnsize) + ret = KRB5_CONFIG_NOTENUFSPACE; + + heim_release(ctx.luser); + return ret; +} + +static void +reg_def_plugins_once(void *ctx) +{ + krb5_context context = ctx; + + krb5_plugin_register(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_AN2LN, + &an2ln_def_plug); +} + +static int +princ_realm_is_default(krb5_context context, + krb5_const_principal aname) +{ + krb5_error_code ret; + krb5_realm *lrealms = NULL; + krb5_realm *r; + int valid; + + ret = krb5_get_default_realms(context, &lrealms); + if (ret) + return 0; + + valid = 0; + for (r = lrealms; *r != NULL; ++r) { + if (strcmp (*r, aname->realm) == 0) { + valid = 1; + break; + } + } + krb5_free_host_realm (context, lrealms); + return valid; +} + +/* + * This function implements MIT's auth_to_local_names configuration for + * configuration compatibility. Specifically: + * + * [realms] + * = { + * auth_to_local_names = { + * = + * } + * } + * + * If multiple usernames are configured then the last one is taken. + * + * The configuration can only be expected to hold a relatively small + * number of mappings. For lots of mappings use a DB. + */ +static krb5_error_code +an2ln_local_names(krb5_context context, + krb5_const_principal aname, + size_t lnsize, + char *lname) +{ + krb5_error_code ret; + char *unparsed; + char **values; + char *res; + size_t i; + + if (!princ_realm_is_default(context, aname)) + return KRB5_PLUGIN_NO_HANDLE; + + ret = krb5_unparse_name_flags(context, aname, + KRB5_PRINCIPAL_UNPARSE_NO_REALM, + &unparsed); + if (ret) + return ret; + + ret = KRB5_PLUGIN_NO_HANDLE; + values = krb5_config_get_strings(context, NULL, "realms", aname->realm, + "auth_to_local_names", unparsed, NULL); + free(unparsed); + if (!values) + return ret; + /* Take the last value, just like MIT */ + for (res = NULL, i = 0; values[i]; i++) + res = values[i]; + if (res) { + ret = 0; + if (strlcpy(lname, res, lnsize) >= lnsize) + ret = KRB5_CONFIG_NOTENUFSPACE; + + if (!*res || strcmp(res, ":") == 0) + ret = KRB5_NO_LOCALNAME; + } + + krb5_config_free_strings(values); + return ret; +} + +/* + * Heimdal's default aname2lname mapping. + */ +static krb5_error_code +an2ln_default(krb5_context context, + char *rule, + krb5_const_principal aname, + size_t lnsize, char *lname) +{ + krb5_error_code ret; + const char *res; + int root_princs_ok; + + if (strcmp(rule, "NONE") == 0) + return KRB5_NO_LOCALNAME; + + if (strcmp(rule, "DEFAULT") == 0) + root_princs_ok = 0; + else if (strcmp(rule, "HEIMDAL_DEFAULT") == 0) + root_princs_ok = 1; + else + return KRB5_PLUGIN_NO_HANDLE; + + if (!princ_realm_is_default(context, aname)) + return KRB5_PLUGIN_NO_HANDLE; + + if (aname->name.name_string.len == 1) { + /* + * One component principal names in default realm -> the one + * component is the username. + */ + res = aname->name.name_string.val[0]; + } else if (root_princs_ok && aname->name.name_string.len == 2 && + strcmp (aname->name.name_string.val[1], "root") == 0) { + /* + * Two-component principal names in default realm where the + * first component is "root" -> root IFF the principal is in + * root's .k5login (or whatever krb5_kuserok() does). + */ + krb5_principal rootprinc; + krb5_boolean userok; + + res = "root"; + + ret = krb5_copy_principal(context, aname, &rootprinc); + if (ret) + return ret; + + userok = _krb5_kuserok(context, rootprinc, res, FALSE); + krb5_free_principal(context, rootprinc); + if (!userok) + return KRB5_NO_LOCALNAME; + } else { + return KRB5_PLUGIN_NO_HANDLE; + } + + if (strlcpy(lname, res, lnsize) >= lnsize) + return KRB5_CONFIG_NOTENUFSPACE; + + return 0; +} + +/** + * Map a principal name to a local username. + * + * Returns 0 on success, KRB5_NO_LOCALNAME if no mapping was found, or + * some Kerberos or system error. + * + * Inputs: + * + * @param context A krb5_context + * @param aname A principal name + * @param lnsize The size of the buffer into which the username will be written + * @param lname The buffer into which the username will be written + * + * @ingroup krb5_support + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_aname_to_localname(krb5_context context, + krb5_const_principal aname, + size_t lnsize, + char *lname) +{ + static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT; + krb5_error_code ret; + krb5_realm realm; + size_t i; + char **rules = NULL; + char *rule; + + if (lnsize) + lname[0] = '\0'; + + heim_base_once_f(®_def_plugins, context, reg_def_plugins_once); + + /* Try MIT's auth_to_local_names config first */ + ret = an2ln_local_names(context, aname, lnsize, lname); + if (ret != KRB5_PLUGIN_NO_HANDLE) + return ret; + + ret = krb5_get_default_realm(context, &realm); + if (ret) + return ret; + + rules = krb5_config_get_strings(context, NULL, "realms", realm, + "auth_to_local", NULL); + krb5_xfree(realm); + if (!rules) { + /* Heimdal's default rule */ + ret = an2ln_default(context, "HEIMDAL_DEFAULT", aname, lnsize, lname); + if (ret == KRB5_PLUGIN_NO_HANDLE) + return KRB5_NO_LOCALNAME; + return ret; + } + + /* + * MIT rules. + * + * Note that RULEs and DBs only have white-list functionality, + * thus RULEs and DBs that we don't understand we simply ignore. + * + * This means that plugins that implement black-lists are + * dangerous: if a black-list plugin isn't found, the black-list + * won't be enforced. But black-lists are dangerous anyways. + */ + for (ret = KRB5_PLUGIN_NO_HANDLE, i = 0; rules[i]; i++) { + rule = rules[i]; + + /* Try NONE, DEFAULT, and HEIMDAL_DEFAULT rules */ + ret = an2ln_default(context, rule, aname, lnsize, lname); + if (ret == KRB5_PLUGIN_NO_HANDLE) + /* Try DB, RULE, ... plugins */ + ret = an2ln_plugin(context, rule, aname, lnsize, lname); + + if (ret == 0 && lnsize && !lname[0]) + continue; /* Success but no lname?! lies! */ + else if (ret != KRB5_PLUGIN_NO_HANDLE) + break; + } + + if (ret == KRB5_PLUGIN_NO_HANDLE) { + if (lnsize) + lname[0] = '\0'; + ret = KRB5_NO_LOCALNAME; + } + + krb5_config_free_strings(rules); + return ret; +} + +static krb5_error_code KRB5_LIB_CALL +an2ln_def_plug_init(krb5_context context, void **ctx) +{ + *ctx = NULL; + return 0; +} + +static void KRB5_LIB_CALL +an2ln_def_plug_fini(void *ctx) +{ +} + +static heim_base_once_t sorted_text_db_init_once = HEIM_BASE_ONCE_INIT; + +static void +sorted_text_db_init_f(void *arg) +{ + (void) heim_db_register("sorted-text", NULL, &heim_sorted_text_file_dbtype); +} + +static krb5_error_code KRB5_LIB_CALL +an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context, + const char *rule, + krb5_const_principal aname, + set_result_f set_res_f, void *set_res_ctx) +{ + krb5_error_code ret; + const char *an2ln_db_fname; + heim_db_t dbh = NULL; + heim_dict_t db_options; + heim_data_t k, v; + heim_error_t error; + char *unparsed = NULL; + char *value = NULL; + + _krb5_load_db_plugins(context); + heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f); + + if (strncmp(rule, "DB:", strlen("DB:")) != 0) + return KRB5_PLUGIN_NO_HANDLE; + + an2ln_db_fname = &rule[strlen("DB:")]; + if (!*an2ln_db_fname) + return KRB5_PLUGIN_NO_HANDLE; + + ret = krb5_unparse_name(context, aname, &unparsed); + if (ret) + return ret; + + db_options = heim_dict_create(11); + if (db_options != NULL) + heim_dict_set_value(db_options, HSTR("read-only"), + heim_number_create(1)); + dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error); + heim_release(db_options); + if (dbh == NULL) { + krb5_set_error_message(context, heim_error_get_code(error), + N_("Couldn't open aname2lname-text-db", "")); + ret = KRB5_PLUGIN_NO_HANDLE; + goto cleanup; + } + + /* Binary search; file should be sorted (in C locale) */ + k = heim_data_ref_create(unparsed, strlen(unparsed), NULL); + if (k == NULL) { + ret = krb5_enomem(context); + goto cleanup; + } + v = heim_db_copy_value(dbh, NULL, k, &error); + heim_release(k); + if (v == NULL && error != NULL) { + krb5_set_error_message(context, heim_error_get_code(error), + N_("Lookup in aname2lname-text-db failed", "")); + ret = heim_error_get_code(error); + goto cleanup; + } else if (v == NULL) { + ret = KRB5_PLUGIN_NO_HANDLE; + goto cleanup; + } else { + /* found */ + if (heim_data_get_length(v) == 0) { + krb5_set_error_message(context, ret, + N_("Principal mapped to empty username", "")); + ret = KRB5_NO_LOCALNAME; + goto cleanup; + } + value = strndup(heim_data_get_ptr(v), heim_data_get_length(v)); + heim_release(v); + if (value == NULL) { + ret = krb5_enomem(context); + goto cleanup; + } + ret = set_res_f(set_res_ctx, value); + } + +cleanup: + heim_release(dbh); + free(unparsed); + free(value); + return ret; +} + diff --git a/third_party/heimdal/lib/krb5/appdefault.c b/third_party/heimdal/lib/krb5/appdefault.c new file mode 100644 index 0000000..d4e963d --- /dev/null +++ b/third_party/heimdal/lib/krb5/appdefault.c @@ -0,0 +1,140 @@ +/* + * 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 "krb5_locl.h" + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_appdefault_boolean(krb5_context context, const char *appname, + krb5_const_realm realm, const char *option, + krb5_boolean def_val, krb5_boolean *ret_val) +{ + + if(appname == NULL) + appname = getprogname(); + + def_val = krb5_config_get_bool_default(context, NULL, def_val, + "libdefaults", option, NULL); + if(realm != NULL) + def_val = krb5_config_get_bool_default(context, NULL, def_val, + "realms", realm, option, NULL); + + def_val = krb5_config_get_bool_default(context, NULL, def_val, + "appdefaults", + option, + NULL); + if(realm != NULL) + def_val = krb5_config_get_bool_default(context, NULL, def_val, + "appdefaults", + realm, + option, + NULL); + if(appname != NULL) { + def_val = krb5_config_get_bool_default(context, NULL, def_val, + "appdefaults", + appname, + option, + NULL); + if(realm != NULL) + def_val = krb5_config_get_bool_default(context, NULL, def_val, + "appdefaults", + appname, + realm, + option, + NULL); + } + *ret_val = def_val; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_appdefault_string(krb5_context context, const char *appname, + krb5_const_realm realm, const char *option, + const char *def_val, char **ret_val) +{ + if(appname == NULL) + appname = getprogname(); + + def_val = krb5_config_get_string_default(context, NULL, def_val, + "libdefaults", option, NULL); + if(realm != NULL) + def_val = krb5_config_get_string_default(context, NULL, def_val, + "realms", realm, option, NULL); + + def_val = krb5_config_get_string_default(context, NULL, def_val, + "appdefaults", + option, + NULL); + if(realm != NULL) + def_val = krb5_config_get_string_default(context, NULL, def_val, + "appdefaults", + realm, + option, + NULL); + if(appname != NULL) { + def_val = krb5_config_get_string_default(context, NULL, def_val, + "appdefaults", + appname, + option, + NULL); + if(realm != NULL) + def_val = krb5_config_get_string_default(context, NULL, def_val, + "appdefaults", + appname, + realm, + option, + NULL); + } + if(def_val != NULL) + *ret_val = strdup(def_val); + else + *ret_val = NULL; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_appdefault_time(krb5_context context, const char *appname, + krb5_const_realm realm, const char *option, + time_t def_val, time_t *ret_val) +{ + krb5_deltat t; + char *val; + + krb5_appdefault_string(context, appname, realm, option, NULL, &val); + if (val == NULL) { + *ret_val = def_val; + return; + } + if (krb5_string_to_deltat(val, &t)) + *ret_val = def_val; + else + *ret_val = t; + free(val); +} diff --git a/third_party/heimdal/lib/krb5/asn1_glue.c b/third_party/heimdal/lib/krb5/asn1_glue.c new file mode 100644 index 0000000..16eda2f --- /dev/null +++ b/third_party/heimdal/lib/krb5/asn1_glue.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1997 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * + */ + +#include "krb5_locl.h" + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_principal2principalname(PrincipalName *p, + krb5_const_principal from) +{ + return copy_PrincipalName(&from->name, p); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_principalname2krb5_principal (krb5_context context, + krb5_principal *principal, + const PrincipalName from, + const Realm realm) +{ + krb5_error_code ret; + krb5_principal p; + + p = calloc(1, sizeof(*p)); + if (p == NULL) + return krb5_enomem(context); + ret = copy_PrincipalName(&from, &p->name); + if (ret) { + free(p); + return ret; + } + p->realm = strdup(realm); + if (p->realm == NULL) { + free_PrincipalName(&p->name); + free(p); + return krb5_enomem(context); + } + *principal = p; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_ticket2krb5_principal(krb5_context context, + krb5_principal *principal, + const EncTicketPart *ticket, + const AuthorizationData *authenticator_ad) +{ + krb5_error_code ret; + krb5_principal p = NULL; + + *principal = NULL; + + ret = _krb5_principalname2krb5_principal(context, + &p, + ticket->cname, + ticket->crealm); + if (ret == 0 && + (p->nameattrs = calloc(1, sizeof(p->nameattrs[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) + p->nameattrs->authenticated = 1; + if (ret == 0 && + (p->nameattrs->source = + calloc(1, sizeof(p->nameattrs->source[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) { + p->nameattrs->source->element = + choice_PrincipalNameAttrSrc_enc_ticket_part; + ret = copy_EncTicketPart(ticket, + &p->nameattrs->source->u.enc_ticket_part); + /* NOTE: we don't want to keep a copy of the session key here! */ + if (ret == 0) + der_free_octet_string(&p->nameattrs->source->u.enc_ticket_part.key.keyvalue); + } + if (ret == 0 && authenticator_ad) { + p->nameattrs->authenticator_ad = + calloc(1, sizeof(p->nameattrs->authenticator_ad[0])); + if (p->nameattrs->authenticator_ad == NULL) + ret = krb5_enomem(context); + if (ret == 0) + ret = copy_AuthorizationData(authenticator_ad, + p->nameattrs->authenticator_ad); + } + + if (ret == 0) + *principal = p; + else + krb5_free_principal(context, p); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kdcrep2krb5_principal(krb5_context context, + krb5_principal *principal, + const EncKDCRepPart *kdcrep) +{ + krb5_error_code ret; + krb5_principal p = NULL; + + *principal = NULL; + + ret = _krb5_principalname2krb5_principal(context, + &p, + kdcrep->sname, + kdcrep->srealm); + if (ret == 0 && + (p->nameattrs = calloc(1, sizeof(p->nameattrs[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) + p->nameattrs->authenticated = 1; + if (ret == 0 && + (p->nameattrs->source = + calloc(1, sizeof(p->nameattrs->source[0]))) == NULL) + ret = krb5_enomem(context); + if (ret == 0) { + p->nameattrs->source->element = + choice_PrincipalNameAttrSrc_enc_kdc_rep_part; + ret = copy_EncKDCRepPart(kdcrep, + &p->nameattrs->source->u.enc_kdc_rep_part); + /* NOTE: we don't want to keep a copy of the session key here! */ + if (ret == 0) + der_free_octet_string(&p->nameattrs->source->u.enc_kdc_rep_part.key.keyvalue); + } + + if (ret == 0) + *principal = p; + else + krb5_free_principal(context, p); + return ret; +} diff --git a/third_party/heimdal/lib/krb5/auth_context.c b/third_party/heimdal/lib/krb5/auth_context.c new file mode 100644 index 0000000..8b43b63 --- /dev/null +++ b/third_party/heimdal/lib/krb5/auth_context.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/** + * Allocate and initialize an autentication context. + * + * @param context A kerberos context. + * @param auth_context The authentication context to be initialized. + * + * Use krb5_auth_con_free() to release the memory when done using the context. + * + * @return An krb5 error code, see krb5_get_error_message(). + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_init(krb5_context context, + krb5_auth_context *auth_context) +{ + krb5_auth_context p; + + ALLOC(p, 1); + if (!p) + return krb5_enomem(context); + memset(p, 0, sizeof(*p)); + ALLOC(p->authenticator, 1); + if (!p->authenticator) { + free(p); + return krb5_enomem(context); + } + memset (p->authenticator, 0, sizeof(*p->authenticator)); + p->flags = KRB5_AUTH_CONTEXT_DO_TIME; + + p->local_address = NULL; + p->remote_address = NULL; + p->local_port = 0; + p->remote_port = 0; + p->keytype = KRB5_ENCTYPE_NULL; + p->cksumtype = CKSUMTYPE_NONE; + p->auth_data = NULL; + *auth_context = p; + return 0; +} + +/** + * Deallocate an authentication context previously initialized with + * krb5_auth_con_init(). + * + * @param context A kerberos context. + * @param auth_context The authentication context to be deallocated. + * + * @return An krb5 error code, see krb5_get_error_message(). + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_free(krb5_context context, + krb5_auth_context auth_context) +{ + if (auth_context != NULL) { + if (auth_context->authenticator) + krb5_free_authenticator(context, &auth_context->authenticator); + if(auth_context->local_address){ + free_HostAddress(auth_context->local_address); + free(auth_context->local_address); + } + if(auth_context->remote_address){ + free_HostAddress(auth_context->remote_address); + free(auth_context->remote_address); + } + krb5_free_keyblock(context, auth_context->keyblock); + krb5_free_keyblock(context, auth_context->remote_subkey); + krb5_free_keyblock(context, auth_context->local_subkey); + if (auth_context->auth_data) { + free_AuthorizationData(auth_context->auth_data); + free(auth_context->auth_data); + } + free (auth_context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setflags(krb5_context context, + krb5_auth_context auth_context, + int32_t flags) +{ + auth_context->flags = flags; + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getflags(krb5_context context, + krb5_auth_context auth_context, + int32_t *flags) +{ + *flags = auth_context->flags; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_addflags(krb5_context context, + krb5_auth_context auth_context, + int32_t addflags, + int32_t *flags) +{ + if (flags) + *flags = auth_context->flags; + auth_context->flags |= addflags; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_removeflags(krb5_context context, + krb5_auth_context auth_context, + int32_t removeflags, + int32_t *flags) +{ + if (flags) + *flags = auth_context->flags; + auth_context->flags &= ~removeflags; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setaddrs(krb5_context context, + krb5_auth_context auth_context, + krb5_address *local_addr, + krb5_address *remote_addr) +{ + if (local_addr) { + if (auth_context->local_address) + krb5_free_address (context, auth_context->local_address); + else + if ((auth_context->local_address = malloc(sizeof(krb5_address))) == NULL) + return krb5_enomem(context); + krb5_copy_address(context, local_addr, auth_context->local_address); + } + if (remote_addr) { + if (auth_context->remote_address) + krb5_free_address (context, auth_context->remote_address); + else + if ((auth_context->remote_address = malloc(sizeof(krb5_address))) == NULL) + return krb5_enomem(context); + krb5_copy_address(context, remote_addr, auth_context->remote_address); + } + return 0; +} + +/** + * Update the authentication context \a auth_context with the local + * and remote addresses from socket \a fd, according to \a flags. + * + * @return An krb5 error code, see krb5_get_error_message(). + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_genaddrs(krb5_context context, + krb5_auth_context auth_context, + krb5_socket_t fd, int flags) +{ + krb5_error_code ret; + krb5_address local_k_address, remote_k_address; + krb5_address *lptr = NULL, *rptr = NULL; + struct sockaddr_storage ss_local, ss_remote; + struct sockaddr *local = (struct sockaddr *)&ss_local; + struct sockaddr *remote = (struct sockaddr *)&ss_remote; + socklen_t len; + + if(flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR) { + if (auth_context->local_address == NULL) { + len = sizeof(ss_local); + if(rk_IS_SOCKET_ERROR(getsockname(fd, local, &len))) { + char buf[128]; + ret = rk_SOCK_ERRNO; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "getsockname: %s", buf); + goto out; + } + ret = krb5_sockaddr2address (context, local, &local_k_address); + if(ret) goto out; + if(flags & KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR) { + krb5_sockaddr2port (context, local, &auth_context->local_port); + } else + auth_context->local_port = 0; + lptr = &local_k_address; + } + } + if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR) { + len = sizeof(ss_remote); + if(rk_IS_SOCKET_ERROR(getpeername(fd, remote, &len))) { + char buf[128]; + ret = rk_SOCK_ERRNO; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, "getpeername: %s", buf); + goto out; + } + ret = krb5_sockaddr2address (context, remote, &remote_k_address); + if(ret) goto out; + if(flags & KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR) { + krb5_sockaddr2port (context, remote, &auth_context->remote_port); + } else + auth_context->remote_port = 0; + rptr = &remote_k_address; + } + ret = krb5_auth_con_setaddrs (context, + auth_context, + lptr, + rptr); + out: + if (lptr) + krb5_free_address (context, lptr); + if (rptr) + krb5_free_address (context, rptr); + return ret; + +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setaddrs_from_fd (krb5_context context, + krb5_auth_context auth_context, + void *p_fd) +{ + krb5_socket_t fd = *(krb5_socket_t *)p_fd; + int flags = 0; + if(auth_context->local_address == NULL) + flags |= KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR; + if(auth_context->remote_address == NULL) + flags |= KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR; + return krb5_auth_con_genaddrs(context, auth_context, fd, flags); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getaddrs(krb5_context context, + krb5_auth_context auth_context, + krb5_address **local_addr, + krb5_address **remote_addr) +{ + if(*local_addr) + krb5_free_address (context, *local_addr); + *local_addr = malloc (sizeof(**local_addr)); + if (*local_addr == NULL) + return krb5_enomem(context); + krb5_copy_address(context, + auth_context->local_address, + *local_addr); + + if(*remote_addr) + krb5_free_address (context, *remote_addr); + *remote_addr = malloc (sizeof(**remote_addr)); + if (*remote_addr == NULL) { + krb5_free_address (context, *local_addr); + *local_addr = NULL; + return krb5_enomem(context); + } + krb5_copy_address(context, + auth_context->remote_address, + *remote_addr); + return 0; +} + +/* coverity[+alloc : arg-*2] */ +static krb5_error_code +copy_key(krb5_context context, + krb5_keyblock *in, + krb5_keyblock **out) +{ + *out = NULL; + if (in) + return krb5_copy_keyblock(context, in, out); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock **keyblock) +{ + return copy_key(context, auth_context->keyblock, keyblock); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getlocalsubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock **keyblock) +{ + return copy_key(context, auth_context->local_subkey, keyblock); +} + +/* coverity[+alloc : arg-*2] */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getremotesubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock **keyblock) +{ + return copy_key(context, auth_context->remote_subkey, keyblock); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock *keyblock) +{ + if(auth_context->keyblock) + krb5_free_keyblock(context, auth_context->keyblock); + return copy_key(context, keyblock, &auth_context->keyblock); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setlocalsubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock *keyblock) +{ + if(auth_context->local_subkey) + krb5_free_keyblock(context, auth_context->local_subkey); + return copy_key(context, keyblock, &auth_context->local_subkey); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_generatelocalsubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock *key) +{ + krb5_error_code ret; + krb5_keyblock *subkey; + + ret = krb5_generate_subkey_extended (context, key, + auth_context->keytype, + &subkey); + if(ret) + return ret; + if(auth_context->local_subkey) + krb5_free_keyblock(context, auth_context->local_subkey); + auth_context->local_subkey = subkey; + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setremotesubkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock *keyblock) +{ + if(auth_context->remote_subkey) + krb5_free_keyblock(context, auth_context->remote_subkey); + return copy_key(context, keyblock, &auth_context->remote_subkey); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setcksumtype(krb5_context context, + krb5_auth_context auth_context, + krb5_cksumtype cksumtype) +{ + auth_context->cksumtype = cksumtype; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getcksumtype(krb5_context context, + krb5_auth_context auth_context, + krb5_cksumtype *cksumtype) +{ + *cksumtype = auth_context->cksumtype; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setkeytype (krb5_context context, + krb5_auth_context auth_context, + krb5_keytype keytype) +{ + auth_context->keytype = keytype; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getkeytype (krb5_context context, + krb5_auth_context auth_context, + krb5_keytype *keytype) +{ + *keytype = auth_context->keytype; + return 0; +} + +krb5_error_code +_krb5_add_1auth_data(krb5_context context, + krb5int32 ad_type, krb5_data *ad_data, int critical, + krb5_authdata **dst) +{ + AuthorizationDataElement e; + + e.ad_type = ad_type; + e.ad_data = *ad_data; + + if (!critical) { + AuthorizationData ad; + krb5_error_code ret; + krb5_data ir; + size_t len; + + /* Build an AD-IF-RELEVANT with the new element inside it */ + ad.len = 0; + ad.val = NULL; + ret = add_AuthorizationData(&ad, &e); + + /* Encode the AD-IF-RELEVANT */ + if (ret == 0) + ASN1_MALLOC_ENCODE(AuthorizationData, ir.data, ir.length, &ad, + &len, ret); + if (ret == 0 && ir.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + /* Re-enter to add the encoded AD-IF-RELEVANT */ + ret = _krb5_add_1auth_data(context, KRB5_AUTHDATA_IF_RELEVANT, &ir, 1, + dst); + + free_AuthorizationData(&ad); + krb5_data_free(&ir); + return ret; + } + + if (*dst == NULL) { + ALLOC(*dst, 1); + if (*dst == NULL) + return krb5_enomem(context); + } + return add_AuthorizationData(*dst, &e); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_add_AuthorizationData(krb5_context context, + krb5_auth_context auth_context, + int type, + krb5_data *data) +{ + if (auth_context->auth_data == NULL) { + auth_context->auth_data = calloc(1, sizeof(*auth_context->auth_data)); + if (auth_context->auth_data == NULL) + return krb5_enomem(context); + } + return _krb5_add_1auth_data(context, type, data, 1, + &auth_context->auth_data); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_add_AuthorizationDataIfRelevant(krb5_context context, + krb5_auth_context auth_context, + krb5int32 type, + krb5_data *data) +{ + if (auth_context->auth_data == NULL) { + auth_context->auth_data = calloc(1, sizeof(*auth_context->auth_data)); + if (auth_context->auth_data == NULL) + return krb5_enomem(context); + } + return _krb5_add_1auth_data(context, type, data, 0, + &auth_context->auth_data); +} + + + +#if 0 +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setenctype(krb5_context context, + krb5_auth_context auth_context, + krb5_enctype etype) +{ + if(auth_context->keyblock) + krb5_free_keyblock(context, auth_context->keyblock); + ALLOC(auth_context->keyblock, 1); + if(auth_context->keyblock == NULL) + return krb5_enomem(context); + auth_context->keyblock->keytype = etype; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getenctype(krb5_context context, + krb5_auth_context auth_context, + krb5_enctype *etype) +{ + krb5_abortx(context, "unimplemented krb5_auth_getenctype called"); +} +#endif + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getlocalseqnumber(krb5_context context, + krb5_auth_context auth_context, + int32_t *seqnumber) +{ + *seqnumber = auth_context->local_seqnumber; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setlocalseqnumber (krb5_context context, + krb5_auth_context auth_context, + int32_t seqnumber) +{ + auth_context->local_seqnumber = seqnumber; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getremoteseqnumber(krb5_context context, + krb5_auth_context auth_context, + int32_t *seqnumber) +{ + *seqnumber = auth_context->remote_seqnumber; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setremoteseqnumber (krb5_context context, + krb5_auth_context auth_context, + int32_t seqnumber) +{ + auth_context->remote_seqnumber = seqnumber; + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getauthenticator(krb5_context context, + krb5_auth_context auth_context, + krb5_authenticator *authenticator) +{ + *authenticator = malloc(sizeof(**authenticator)); + if (*authenticator == NULL) + return krb5_enomem(context); + + return copy_Authenticator(auth_context->authenticator, + *authenticator); +} + + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_authenticator(krb5_context context, + krb5_authenticator *authenticator) +{ + free_Authenticator (*authenticator); + free (*authenticator); + *authenticator = NULL; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setuserkey(krb5_context context, + krb5_auth_context auth_context, + krb5_keyblock *keyblock) +{ + if(auth_context->keyblock) + krb5_free_keyblock(context, auth_context->keyblock); + return krb5_copy_keyblock(context, keyblock, &auth_context->keyblock); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_getrcache(krb5_context context, + krb5_auth_context auth_context, + krb5_rcache *rcache) +{ + *rcache = auth_context->rcache; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setrcache(krb5_context context, + krb5_auth_context auth_context, + krb5_rcache rcache) +{ + auth_context->rcache = rcache; + return 0; +} + +#if 0 /* not implemented */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_initivector(krb5_context context, + krb5_auth_context auth_context) +{ + krb5_abortx(context, "unimplemented krb5_auth_con_initivector called"); +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_con_setivector(krb5_context context, + krb5_auth_context auth_context, + krb5_pointer ivector) +{ + krb5_abortx(context, "unimplemented krb5_auth_con_setivector called"); +} + +#endif /* not implemented */ diff --git a/third_party/heimdal/lib/krb5/authdata.c b/third_party/heimdal/lib/krb5/authdata.c new file mode 100644 index 0000000..ac42661 --- /dev/null +++ b/third_party/heimdal/lib/krb5/authdata.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1997-2021 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * Copyright (c) 2021 Isaac Boukris + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* + * Add the AuthorizationData `data´ of `type´ to the last element in + * the sequence of authorization_data in `tkt´ wrapped in an IF_RELEVANT + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_kdc_tkt_add_if_relevant_ad(krb5_context context, + EncTicketPart *tkt, + int type, + const krb5_data *data) +{ + krb5_error_code ret; + size_t size = 0; + + if (tkt->authorization_data == NULL) { + tkt->authorization_data = calloc(1, sizeof(*tkt->authorization_data)); + if (tkt->authorization_data == NULL) { + return krb5_enomem(context); + } + } + + /* add the entry to the last element */ + { + AuthorizationData ad = { 0, NULL }; + AuthorizationDataElement ade; + + ade.ad_type = type; + ade.ad_data = *data; + + ret = add_AuthorizationData(&ad, &ade); + if (ret) { + krb5_set_error_message(context, ret, "add AuthorizationData failed"); + return ret; + } + + ade.ad_type = KRB5_AUTHDATA_IF_RELEVANT; + + ASN1_MALLOC_ENCODE(AuthorizationData, + ade.ad_data.data, ade.ad_data.length, + &ad, &size, ret); + free_AuthorizationData(&ad); + if (ret) { + krb5_set_error_message(context, ret, "ASN.1 encode of " + "AuthorizationData failed"); + return ret; + } + if (ade.ad_data.length != size) + krb5_abortx(context, "internal asn.1 encoder error"); + + ret = add_AuthorizationData(tkt->authorization_data, &ade); + der_free_octet_string(&ade.ad_data); + if (ret) { + krb5_set_error_message(context, ret, "add AuthorizationData failed"); + return ret; + } + } + + return 0; +} + +/* + * Insert a PAC wrapped in AD-IF-RELEVANT container as the first AD element, + * as some clients such as Windows may fail to parse it otherwise. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_kdc_tkt_insert_pac(krb5_context context, + EncTicketPart *tkt, + const krb5_data *data) +{ + AuthorizationDataElement ade; + unsigned int i; + krb5_error_code ret; + + ret = _kdc_tkt_add_if_relevant_ad(context, tkt, KRB5_AUTHDATA_WIN2K_PAC, + data); + if (ret) + return ret; + + heim_assert(tkt->authorization_data->len != 0, "No authorization_data!"); + ade = tkt->authorization_data->val[tkt->authorization_data->len - 1]; + for (i = 0; i < tkt->authorization_data->len - 1; i++) { + tkt->authorization_data->val[i + 1] = tkt->authorization_data->val[i]; + } + tkt->authorization_data->val[0] = ade; + + return 0; +} diff --git a/third_party/heimdal/lib/krb5/build_ap_req.c b/third_party/heimdal/lib/krb5/build_ap_req.c new file mode 100644 index 0000000..cb6f60d --- /dev/null +++ b/third_party/heimdal/lib/krb5/build_ap_req.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_build_ap_req (krb5_context context, + krb5_enctype enctype, + krb5_creds *cred, + krb5_flags ap_options, + krb5_data authenticator, + krb5_data *retdata) +{ + krb5_error_code ret = 0; + AP_REQ ap; + size_t len; + + ap.pvno = 5; + ap.msg_type = krb_ap_req; + memset(&ap.ap_options, 0, sizeof(ap.ap_options)); + ap.ap_options.use_session_key = (ap_options & AP_OPTS_USE_SESSION_KEY) > 0; + ap.ap_options.mutual_required = (ap_options & AP_OPTS_MUTUAL_REQUIRED) > 0; + + ret = decode_Ticket(cred->ticket.data, cred->ticket.length, &ap.ticket, &len); + if (ret) + return ret; + if (cred->ticket.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + ap.authenticator.etype = enctype; + ap.authenticator.kvno = NULL; + ap.authenticator.cipher = authenticator; + + ASN1_MALLOC_ENCODE(AP_REQ, retdata->data, retdata->length, &ap, &len, ret); + if (ret == 0 && retdata->length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + free_AP_REQ(&ap); + return ret; +} diff --git a/third_party/heimdal/lib/krb5/build_auth.c b/third_party/heimdal/lib/krb5/build_auth.c new file mode 100644 index 0000000..3e00125 --- /dev/null +++ b/third_party/heimdal/lib/krb5/build_auth.c @@ -0,0 +1,267 @@ +/* + * Copyright (c) 1997 - 2003 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +static krb5_error_code +add_auth_data(krb5_context context, + AuthorizationData *src, + AuthorizationData **dst) +{ + krb5_error_code ret = 0; + size_t i; + + if (*dst == NULL && + (*dst = calloc(1, sizeof(**dst))) == NULL) + return krb5_enomem(context); + for (i = 0; ret == 0 && i < src->len; i++) + ret = add_AuthorizationData(*dst, &src->val[i]); + return ret; +} + +static krb5_error_code +add_etypelist(krb5_context context, + krb5_authdata *auth_data) +{ + AuthorizationDataElement ade; + EtypeList etypes; + krb5_error_code ret; + krb5_data e; + size_t len = 0; + + ret = _krb5_init_etype(context, KRB5_PDU_NONE, + &etypes.len, &etypes.val, + NULL); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EtypeList, e.data, e.length, &etypes, &len, ret); + if (ret) { + free_EtypeList(&etypes); + return ret; + } + if(e.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + free_EtypeList(&etypes); + + ade.ad_type = KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION; + ade.ad_data = e; + + ret = add_AuthorizationData(auth_data, &ade); + + krb5_data_free(&e); + + return ret; +} + +static krb5_error_code +add_ap_options(krb5_context context, + krb5_authdata *auth_data) +{ + krb5_error_code ret; + AuthorizationDataElement ade; + krb5_boolean require_cb; + uint8_t ap_options[4]; + + require_cb = krb5_config_get_bool_default(context, NULL, FALSE, + "libdefaults", + "client_aware_channel_bindings", + NULL); + + if (!require_cb) + return 0; + + ap_options[0] = (KERB_AP_OPTIONS_CBT >> 0 ) & 0xFF; + ap_options[1] = (KERB_AP_OPTIONS_CBT >> 8 ) & 0xFF; + ap_options[2] = (KERB_AP_OPTIONS_CBT >> 16) & 0xFF; + ap_options[3] = (KERB_AP_OPTIONS_CBT >> 24) & 0xFF; + + ade.ad_type = KRB5_AUTHDATA_AP_OPTIONS; + ade.ad_data.length = sizeof(ap_options); + ade.ad_data.data = ap_options; + + ret = add_AuthorizationData(auth_data, &ade); + + return ret; +} + +static krb5_error_code +make_ap_authdata(krb5_context context, + krb5_authdata **auth_data) +{ + krb5_error_code ret; + AuthorizationData ad; + krb5_data ir; + size_t len; + + ad.len = 0; + ad.val = NULL; + + ret = add_etypelist(context, &ad); + if (ret) + return ret; + + /* + * Windows has a bug and only looks for first occurrence of AD-IF-RELEVANT + * in the AP authenticator when looking for AD-AP-OPTIONS. Make sure to + * bundle it together with etypes. + */ + ret = add_ap_options(context, &ad); + if (ret) { + free_AuthorizationData(&ad); + return ret; + } + + ASN1_MALLOC_ENCODE(AuthorizationData, ir.data, ir.length, &ad, &len, ret); + if (ret) { + free_AuthorizationData(&ad); + return ret; + } + if(ir.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = _krb5_add_1auth_data(context, KRB5_AUTHDATA_IF_RELEVANT, &ir, 1, + auth_data); + + free_AuthorizationData(&ad); + krb5_data_free(&ir); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_build_authenticator (krb5_context context, + krb5_auth_context auth_context, + krb5_enctype enctype, + krb5_creds *cred, + Checksum *cksum, + krb5_data *result, + krb5_key_usage usage) +{ + Authenticator auth; + u_char *buf = NULL; + size_t buf_size; + size_t len = 0; + krb5_error_code ret; + krb5_crypto crypto; + + memset(&auth, 0, sizeof(auth)); + + auth.authenticator_vno = 5; + ret = copy_Realm(&cred->client->realm, &auth.crealm); + if (ret) + goto fail; + ret = copy_PrincipalName(&cred->client->name, &auth.cname); + if (ret) + goto fail; + + krb5_us_timeofday (context, &auth.ctime, &auth.cusec); + + ret = krb5_auth_con_getlocalsubkey(context, auth_context, &auth.subkey); + if(ret) + goto fail; + + if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) { + if(auth_context->local_seqnumber == 0) + krb5_generate_seq_number (context, + &cred->session, + &auth_context->local_seqnumber); + ALLOC(auth.seq_number, 1); + if(auth.seq_number == NULL) { + ret = krb5_enomem(context); + goto fail; + } + *auth.seq_number = auth_context->local_seqnumber; + } else + auth.seq_number = NULL; + auth.authorization_data = NULL; + + if (cksum) { + ALLOC(auth.cksum, 1); + if (auth.cksum == NULL) { + ret = krb5_enomem(context); + goto fail; + } + ret = copy_Checksum(cksum, auth.cksum); + if (ret) + goto fail; + + if (auth.cksum->cksumtype == CKSUMTYPE_GSSAPI) { + /* + * This is not GSS-API specific, we only enable it for + * GSS for now + */ + ret = make_ap_authdata(context, &auth.authorization_data); + if (ret) + goto fail; + } + } + + /* Copy other authz data from auth_context */ + if (auth_context->auth_data) { + ret = add_auth_data(context, auth_context->auth_data, &auth.authorization_data); + if (ret) + goto fail; + } + + /* XXX - Copy more to auth_context? */ + + auth_context->authenticator->ctime = auth.ctime; + auth_context->authenticator->cusec = auth.cusec; + + ASN1_MALLOC_ENCODE(Authenticator, buf, buf_size, &auth, &len, ret); + if (ret) + goto fail; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_crypto_init(context, &cred->session, enctype, &crypto); + if (ret) + goto fail; + ret = krb5_encrypt (context, + crypto, + usage /* KRB5_KU_AP_REQ_AUTH */, + buf, + len, + result); + krb5_crypto_destroy(context, crypto); + + if (ret) + goto fail; + + fail: + free_Authenticator (&auth); + free (buf); + + return ret; +} diff --git a/third_party/heimdal/lib/krb5/cache.c b/third_party/heimdal/lib/krb5/cache.c new file mode 100644 index 0000000..3f16d69 --- /dev/null +++ b/third_party/heimdal/lib/krb5/cache.c @@ -0,0 +1,2300 @@ +/* + * Copyright (c) 1997 - 2008 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 "krb5_locl.h" + +/** + * @page krb5_ccache_intro The credential cache functions + * @section section_krb5_ccache Kerberos credential caches + * + * krb5_ccache structure holds a Kerberos credential cache. + * + * Heimdal support the follow types of credential caches: + * + * - SCC + * Store the credential in a database + * - FILE + * Store the credential in memory + * - MEMORY + * Store the credential in memory + * - API + * A credential cache server based solution for Mac OS X + * - KCM + * A credential cache server based solution for all platforms + * + * @subsection Example + * + * This is a minimalistic version of klist: +@code +#include + +int +main (int argc, char **argv) +{ + krb5_context context; + krb5_cc_cursor cursor; + krb5_error_code ret; + krb5_ccache id; + krb5_creds creds; + + if (krb5_init_context (&context) != 0) + errx(1, "krb5_context"); + + ret = krb5_cc_default (context, &id); + if (ret) + krb5_err(context, 1, ret, "krb5_cc_default"); + + ret = krb5_cc_start_seq_get(context, id, &cursor); + if (ret) + krb5_err(context, 1, ret, "krb5_cc_start_seq_get"); + + while((ret = krb5_cc_next_cred(context, id, &cursor, &creds)) == 0){ + char *principal; + + krb5_unparse_name(context, creds.server, &principal); + printf("principal: %s\\n", principal); + free(principal); + krb5_free_cred_contents (context, &creds); + } + ret = krb5_cc_end_seq_get(context, id, &cursor); + if (ret) + krb5_err(context, 1, ret, "krb5_cc_end_seq_get"); + + krb5_cc_close(context, id); + + krb5_free_context(context); + return 0; +} +* @endcode +*/ + +static const krb5_cc_ops * +cc_get_prefix_ops(krb5_context context, + const char *prefix, + const char **residual); + +/** + * Add a new ccache type with operations `ops', overwriting any + * existing one if `override'. + * + * @param context a Kerberos context + * @param ops type of plugin symbol + * @param override flag to select if the registration is to overide + * an existing ops with the same name. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_register(krb5_context context, + const krb5_cc_ops *ops, + krb5_boolean override) +{ + int i; + + for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) { + if(strcmp(context->cc_ops[i]->prefix, ops->prefix) == 0) { + if(!override) { + krb5_set_error_message(context, + KRB5_CC_TYPE_EXISTS, + N_("cache type %s already exists", "type"), + ops->prefix); + return KRB5_CC_TYPE_EXISTS; + } + break; + } + } + if(i == context->num_cc_ops) { + const krb5_cc_ops **o = realloc(rk_UNCONST(context->cc_ops), + (context->num_cc_ops + 1) * + sizeof(context->cc_ops[0])); + if(o == NULL) { + krb5_set_error_message(context, KRB5_CC_NOMEM, + N_("malloc: out of memory", "")); + return KRB5_CC_NOMEM; + } + context->cc_ops = o; + context->cc_ops[context->num_cc_ops] = NULL; + context->num_cc_ops++; + } + context->cc_ops[i] = ops; + return 0; +} + +/* + * Allocate the memory for a `id' and the that function table to + * `ops'. Returns 0 or and error code. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_cc_allocate(krb5_context context, + const krb5_cc_ops *ops, + krb5_ccache *id) +{ + krb5_ccache p; + + p = calloc(1, sizeof(*p)); + if (p == NULL) { + krb5_set_error_message(context, KRB5_CC_NOMEM, + N_("malloc: out of memory", "")); + return KRB5_CC_NOMEM; + } + p->ops = ops; + *id = p; + + return 0; +} + +/* + * Allocate memory for a new ccache in `id' with operations `ops' + * and name `residual'. Return 0 or an error code. + */ + +static krb5_error_code +allocate_ccache(krb5_context context, + const krb5_cc_ops *ops, + const char *residual, + const char *subsidiary, + krb5_ccache *id) +{ + krb5_error_code ret = 0; + char *exp_residual = NULL; + int filepath; + + filepath = (strcmp("FILE", ops->prefix) == 0 + || strcmp("DIR", ops->prefix) == 0 + || strcmp("SCC", ops->prefix) == 0); + + if (residual) + ret = _krb5_expand_path_tokens(context, residual, filepath, &exp_residual); + if (ret == 0) + ret = _krb5_cc_allocate(context, ops, id); + + if (ret == 0) { + if ((*id)->ops->version < KRB5_CC_OPS_VERSION_5 + || (*id)->ops->resolve_2 == NULL) { + ret = (*id)->ops->resolve(context, id, exp_residual); + } else { + ret = (*id)->ops->resolve_2(context, id, exp_residual, subsidiary); + } + } + if (ret) { + free(*id); + *id = NULL; + } + free(exp_residual); + return ret; +} + + +/** + * Find and allocate a ccache in `id' from the specification in `residual'. + * If the ccache name doesn't contain any colon, interpret it as a file name. + * + * @param context a Kerberos context. + * @param name string name of a credential cache. + * @param id return pointer to a found credential cache. + * + * @return Return 0 or an error code. In case of an error, id is set + * to NULL, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_resolve(krb5_context context, + const char *name, + krb5_ccache *id) +{ + const krb5_cc_ops *ops; + const char *residual = NULL; + + *id = NULL; + + ops = cc_get_prefix_ops(context, name, &residual); + if (ops == NULL) + ops = &krb5_fcc_ops; /* residual will point to name */ + + return allocate_ccache(context, ops, residual, NULL, id); +} + +#ifdef _WIN32 +static const char * +get_default_cc_type_win32(krb5_context context) +{ + krb5_error_code ret; + krb5_ccache id; + + /* + * If the MSLSA ccache type has a principal name, + * use it as the default. + */ + ret = krb5_cc_resolve(context, "MSLSA:", &id); + if (ret == 0) { + krb5_principal princ; + ret = krb5_cc_get_principal(context, id, &princ); + krb5_cc_close(context, id); + if (ret == 0) { + krb5_free_principal(context, princ); + return "MSLSA"; + } + } + + /* + * If the API: ccache can be resolved, + * use it as the default. + */ + ret = krb5_cc_resolve(context, "API:", &id); + if (ret == 0) { + krb5_cc_close(context, id); + return "API"; + } + + return NULL; +} +#endif /* _WIN32 */ + +static const char * +get_default_cc_type(krb5_context context, int simple) +{ + const char *def_ccname; + const char *def_cctype = + krb5_config_get_string_default(context, NULL, + secure_getenv("KRB5CCTYPE"), + "libdefaults", "default_cc_type", NULL); + const char *def_cccol = + krb5_config_get_string(context, NULL, "libdefaults", + "default_cc_collection", NULL); + const krb5_cc_ops *ops; + + if (!simple && (def_ccname = krb5_cc_default_name(context))) { + ops = cc_get_prefix_ops(context, def_ccname, NULL); + if (ops) + return ops->prefix; + } + if (!def_cctype && def_cccol) { + ops = cc_get_prefix_ops(context, def_cccol, NULL); + if (ops) + return ops->prefix; + } +#ifdef _WIN32 + if (def_cctype == NULL) + def_cctype = get_default_cc_type_win32(context); +#endif + if (def_cctype == NULL) + def_cctype = KRB5_DEFAULT_CCTYPE->prefix; + return def_cctype; +} + +/** + * Find and allocate a ccache in `id' for the subsidiary cache named by + * `subsidiary' in the collection named by `collection'. + * + * @param context a Kerberos context. + * @param cctype string name of a credential cache collection type. + * @param collection string name of a credential cache collection. + * @param subsidiary string name of a credential cache in a collection. + * @param id return pointer to a found credential cache. + * + * @return Return 0 or an error code. In case of an error, id is set + * to NULL, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_resolve_sub(krb5_context context, + const char *cctype, + const char *collection, + const char *subsidiary, + krb5_ccache *id) +{ + const krb5_cc_ops *ops = NULL; + + *id = NULL; + + /* Get the cctype from the collection, maybe */ + if (cctype == NULL && collection) + ops = cc_get_prefix_ops(context, collection, &collection); + + if (ops == NULL) + ops = cc_get_prefix_ops(context, get_default_cc_type(context, 0), NULL); + + if (ops == NULL) { + krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, + N_("unknown ccache type %s", ""), cctype); + return KRB5_CC_UNKNOWN_TYPE; + } + + return allocate_ccache(context, ops, collection, subsidiary, id); +} + + +/** + * Find and allocate a ccache in `id' from the specification in `residual', but + * specific to the given principal `principal' by using the principal name as + * the name of a "subsidiary" credentials cache in the collection named by + * `name'. If the ccache name doesn't contain any colon, interpret it as a + * file name. + * + * @param context a Kerberos context. + * @param name string name of a credential cache. + * @param principal principal name of desired credentials. + * @param id return pointer to a found credential cache. + * + * @return Return 0 or an error code. In case of an error, id is set + * to NULL, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_resolve_for(krb5_context context, + const char *cctype, + const char *name, + krb5_const_principal principal, + krb5_ccache *id) +{ + krb5_error_code ret; + char *p, *s; + + *id = NULL; + + ret = krb5_unparse_name(context, principal, &p); + if (ret) + return ret; + /* + * Subsidiary components cannot have various chars in them that are used as + * separators. ':' is used for subsidiary separators in all ccache types + * except FILE, where '+' is used instead because we can't use ':' in file + * paths on Windows and because ':' is not in the POSIX safe set. + */ + for (s = p; *s; s++) { + switch (s[0]) { + case ':': + case '+': + case '/': + case '\\': + s[0] = '-'; + default: break; + } + } + ret = krb5_cc_resolve_sub(context, cctype, name, p, id); + free(p); + return ret; +} + +/** + * Generates a new unique ccache of `type` in `id'. If `type' is NULL, + * the library chooses the default credential cache type. The supplied + * `hint' (that can be NULL) is a string that the credential cache + * type can use to base the name of the credential on, this is to make + * it easier for the user to differentiate the credentials. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_new_unique(krb5_context context, const char *type, + const char *hint, krb5_ccache *id) +{ + const krb5_cc_ops *ops; + krb5_error_code ret; + + if (type == NULL) + type = get_default_cc_type(context, 1); + + ops = krb5_cc_get_prefix_ops(context, type); + if (ops == NULL) { + krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, + "Credential cache type %s is unknown", type); + return KRB5_CC_UNKNOWN_TYPE; + } + + ret = _krb5_cc_allocate(context, ops, id); + if (ret) + return ret; + ret = (*id)->ops->gen_new(context, id); + if (ret) { + free(*id); + *id = NULL; + } + return ret; +} + +/** + * Return the name of the ccache `id' + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_cc_get_name(krb5_context context, + krb5_ccache id) +{ + const char *name = NULL; + + if (id->ops->version < KRB5_CC_OPS_VERSION_5 + || id->ops->get_name_2 == NULL) + return id->ops->get_name(context, id); + + (void) id->ops->get_name_2(context, id, &name, NULL, NULL); + return name; +} + +/** + * Return the name of the ccache collection associated with `id' + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_cc_get_collection(krb5_context context, krb5_ccache id) +{ + const char *name = NULL; + + if (id->ops->version < KRB5_CC_OPS_VERSION_5 + || id->ops->get_name_2 == NULL) + return NULL; + + (void) id->ops->get_name_2(context, id, NULL, &name, NULL); + return name; +} + +/** + * Return the name of the subsidiary ccache of `id' + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_cc_get_subsidiary(krb5_context context, krb5_ccache id) +{ + const char *name = NULL; + + if (id->ops->version >= KRB5_CC_OPS_VERSION_5 + && id->ops->get_name_2 != NULL) + (void) id->ops->get_name_2(context, id, NULL, NULL, &name); + return name; +} + +/** + * Return the type of the ccache `id'. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_cc_get_type(krb5_context context, + krb5_ccache id) +{ + return id->ops->prefix; +} + +/** + * Return the complete resolvable name the cache + + * @param context a Kerberos context + * @param id return pointer to a found credential cache + * @param str the returned name of a credential cache, free with krb5_xfree() + * + * @return Returns 0 or an error (and then *str is set to NULL). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_full_name(krb5_context context, + krb5_ccache id, + char **str) +{ + const char *type, *name; + + *str = NULL; + + type = krb5_cc_get_type(context, id); + if (type == NULL) { + krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, + "cache has no name of type"); + return KRB5_CC_UNKNOWN_TYPE; + } + + name = krb5_cc_get_name(context, id); + if (name == NULL) { + krb5_set_error_message(context, KRB5_CC_BADNAME, + "cache of type %s has no name", type); + return KRB5_CC_BADNAME; + } + + if (asprintf(str, "%s:%s", type, name) == -1) { + *str = NULL; + return krb5_enomem(context); + } + return 0; +} + +/** + * Return krb5_cc_ops of a the ccache `id'. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL +krb5_cc_get_ops(krb5_context context, krb5_ccache id) +{ + return id->ops; +} + +/* + * Expand variables in `str' into `res' + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res) +{ + int filepath; + + filepath = (strncmp("FILE:", str, 5) == 0 + || strncmp("DIR:", str, 4) == 0 + || strncmp("SCC:", str, 4) == 0); + + return _krb5_expand_path_tokens(context, str, filepath, res); +} + +/* + * Return non-zero if envirnoment that will determine default krb5cc + * name has changed. + */ + +static int +environment_changed(krb5_context context) +{ + const char *e; + + /* if the cc name was set, don't change it */ + if (context->default_cc_name_set) + return 0; + + /* XXX performance: always ask KCM/API if default name has changed */ + if (context->default_cc_name && + (strncmp(context->default_cc_name, "KCM:", 4) == 0 || + strncmp(context->default_cc_name, "API:", 4) == 0)) + return 1; + + e = secure_getenv("KRB5CCNAME"); + if (e == NULL) { + if (context->default_cc_name_env) { + free(context->default_cc_name_env); + context->default_cc_name_env = NULL; + return 1; + } + } else { + if (context->default_cc_name_env == NULL) + return 1; + if (strcmp(e, context->default_cc_name_env) != 0) + return 1; + } + return 0; +} + +/** + * Switch the default default credential cache for a specific + * credcache type (and name for some implementations). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_switch(krb5_context context, krb5_ccache id) +{ +#ifdef _WIN32 + _krb5_set_default_cc_name_to_registry(context, id); +#endif + + if (id->ops->version == KRB5_CC_OPS_VERSION_0 + || id->ops->set_default == NULL) + return 0; + + return (*id->ops->set_default)(context, id); +} + +/** + * Return true if the default credential cache support switch + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_cc_support_switch(krb5_context context, const char *type) +{ + const krb5_cc_ops *ops; + + ops = krb5_cc_get_prefix_ops(context, type); + if (ops && ops->version > KRB5_CC_OPS_VERSION_0 && ops->set_default) + return 1; + return FALSE; +} + +/** + * Set the default cc name for `context' to `name'. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_set_default_name(krb5_context context, const char *name) +{ + krb5_error_code ret = 0; + char *p = NULL; + + if (name == NULL) { + const char *e; + + if ((e = secure_getenv("KRB5CCNAME"))) { + if ((p = strdup(e)) == NULL) + return krb5_enomem(context); + + free(context->default_cc_name_env); + context->default_cc_name_env = p; + + if ((p = strdup(e)) == NULL) + return krb5_enomem(context); + + /* + * We're resetting the default ccache name. Recall that we got + * this from the environment, which might change. + */ + context->default_cc_name_set = 0; + } else if ((e = krb5_cc_configured_default_name(context))) { + if ((p = strdup(e)) == NULL) + return krb5_enomem(context); + + /* + * Since $KRB5CCNAME was not set, and since we got the default + * ccache name from configuration, we'll not want + * environment_changed() to return true to avoid re-doing the + * krb5_cc_configured_default_name() call unnecessarily. + * + * XXX Perhaps if we got the ccache name from the registry then + * we'd want to recheck it? If so we might need an indication + * from krb5_cc_configured_default_name() about that! + */ + context->default_cc_name_set = 1; + } + } else { + int filepath = (strncmp("FILE:", name, 5) == 0 || + strncmp("DIR:", name, 4) == 0 || + strncmp("SCC:", name, 4) == 0); + + ret = _krb5_expand_path_tokens(context, name, filepath, &p); + if (ret) + return ret; + + /* + * Since the default ccache name was set explicitly, we won't want + * environment_changed() to return true until the default ccache name + * is reset. + */ + context->default_cc_name_set = 1; + } + + free(context->default_cc_name); + context->default_cc_name = p; + return 0; +} + +/** + * Return a pointer to a context static string containing the default + * ccache name. + * + * @return String to the default credential cache name. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_cc_default_name(krb5_context context) +{ + if (context->default_cc_name == NULL || environment_changed(context)) + krb5_cc_set_default_name(context, NULL); + + return context->default_cc_name; +} + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_cc_configured_default_name(krb5_context context) +{ + krb5_error_code ret = 0; +#ifdef _WIN32 + krb5_ccache id; +#endif + const char *cfg; + char *expanded; + const krb5_cc_ops *ops; + + if (context->configured_default_cc_name) + return context->configured_default_cc_name; + +#ifdef _WIN32 + if ((expanded = _krb5_get_default_cc_name_from_registry(context))) + return context->configured_default_cc_name = expanded; +#endif + + /* If there's a configured default, expand the tokens and use it */ + cfg = krb5_config_get_string(context, NULL, "libdefaults", + "default_cc_name", NULL); + if (cfg == NULL) + cfg = krb5_config_get_string(context, NULL, "libdefaults", + "default_ccache_name", NULL); + if (cfg) { + ret = _krb5_expand_default_cc_name(context, cfg, &expanded); + if (ret) { + krb5_set_error_message(context, ret, + "token expansion failed for %s", cfg); + return NULL; + } + return context->configured_default_cc_name = expanded; + } + + /* Else try a configured default ccache type's default */ + cfg = get_default_cc_type(context, 1); + if ((ops = krb5_cc_get_prefix_ops(context, cfg)) == NULL) { + krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, + "unknown configured credential cache " + "type %s", cfg); + return NULL; + } + + /* The get_default_name() method expands any tokens */ + ret = (*ops->get_default_name)(context, &expanded); + if (ret) { + krb5_set_error_message(context, ret, "failed to find a default " + "ccache for default ccache type %s", cfg); + return NULL; + } + return context->configured_default_cc_name = expanded; +} + +KRB5_LIB_FUNCTION char * KRB5_LIB_CALL +krb5_cccol_get_default_ccname(krb5_context context) +{ + const char *cfg = get_default_cc_type(context, 1); + char *cccol_default_ccname; + const krb5_cc_ops *ops = krb5_cc_get_prefix_ops(context, cfg); + + (void) (*ops->get_default_name)(context, &cccol_default_ccname); + return cccol_default_ccname; +} + +/** + * Open the default ccache in `id'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_default(krb5_context context, + krb5_ccache *id) +{ + const char *p = krb5_cc_default_name(context); + + *id = NULL; + if (p == NULL) + return krb5_enomem(context); + return krb5_cc_resolve(context, p, id); +} + +/** + * Open the named subsidiary cache from the default ccache collection in `id'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_default_sub(krb5_context context, + const char *subsidiary, + krb5_ccache *id) +{ + return krb5_cc_resolve_sub(context, get_default_cc_type(context, 0), NULL, + subsidiary, id); +} + +/** + * Open the default ccache in `id' that corresponds to the given principal. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_default_for(krb5_context context, + krb5_const_principal principal, + krb5_ccache *id) +{ + return krb5_cc_resolve_for(context, get_default_cc_type(context, 0), NULL, + principal, id); +} + +/** + * Create a new ccache in `id' for `primary_principal'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_initialize(krb5_context context, + krb5_ccache id, + krb5_principal primary_principal) +{ + krb5_error_code ret; + + ret = (*id->ops->init)(context, id, primary_principal); + if (ret == 0) { + id->cc_kx509_done = 0; + id->cc_initialized = 1; + id->cc_need_start_realm = 1; + id->cc_start_tgt_stored = 0; + } + return ret; +} + + +/** + * Remove the ccache `id'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_destroy(krb5_context context, + krb5_ccache id) +{ + krb5_error_code ret2 = 0; + krb5_error_code ret; + krb5_data d; + + /* + * Destroy associated hx509 PKIX credential store created by krb5_kx509*(). + */ + if (krb5_cc_get_config(context, id, NULL, "kx509store", &d) == 0) { + char *name; + + if ((name = strndup(d.data, d.length)) == NULL) { + ret2 = krb5_enomem(context); + } else { + hx509_certs certs; + ret = hx509_certs_init(context->hx509ctx, name, 0, NULL, &certs); + if (ret == 0) + ret2 = hx509_certs_destroy(context->hx509ctx, &certs); + else + hx509_certs_free(&certs); + free(name); + } + } + + ret = (*id->ops->destroy)(context, id); + (void) krb5_cc_close(context, id); + return ret ? ret : ret2; +} + +/** + * Stop using the ccache `id' and free the related resources. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_close(krb5_context context, + krb5_ccache id) +{ + krb5_error_code ret; + + if (!id) + return 0; + + /* + * We want to automatically acquire a PKIX credential using kx509. + * + * This can be slow if we're generating an RSA key. Plus it means talking + * to the KDC. + * + * We only want to do this when: + * + * - krb5_cc_initialize() was called on this ccache handle, + * - a start TGT was stored (actually, a cross-realm TGT would do), + * + * and + * + * - we aren't creating a gss_cred_id_t for a delegated credential. + * + * We only have a heuristic for the last condition: that `id' is not a + * MEMORY ccache, which is what's used for delegated credentials. + * + * We really only want to do this when storing a credential in a user's + * default ccache, but we leave it to krb5_kx509() to do that check. + * + * XXX Perhaps we should do what krb5_kx509() does here, and just call + * krb5_kx509_ext() (renamed to krb5_kx509()). Then we wouldn't need + * the delegated cred handle heuristic. + */ + if (id->cc_initialized && id->cc_start_tgt_stored && !id->cc_kx509_done && + strcmp("MEMORY", krb5_cc_get_type(context, id)) != 0) { + krb5_boolean enabled; + + krb5_appdefault_boolean(context, NULL, NULL, "enable_kx509", FALSE, + &enabled); + if (enabled) { + _krb5_debug(context, 2, "attempting to fetch a certificate using " + "kx509"); + ret = krb5_kx509(context, id, NULL); + if (ret) + _krb5_debug(context, 2, "failed to fetch a certificate"); + else + _krb5_debug(context, 2, "fetched a certificate"); + } + } + + ret = (*id->ops->close)(context, id); + free(id); + return ret; +} + +/** + * Store `creds' in the ccache `id'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_store_cred(krb5_context context, + krb5_ccache id, + krb5_creds *creds) +{ + krb5_error_code ret; + krb5_data realm; + const char *cfg = ""; + + /* Automatic cc_config-setting and other actions */ + if (krb5_principal_get_num_comp(context, creds->server) > 1 && + krb5_is_config_principal(context, creds->server)) + cfg = krb5_principal_get_comp_string(context, creds->server, 1); + + if (id->cc_initialized && !id->cc_need_start_realm && + strcmp(cfg, "start_realm") == 0) + return 0; + + ret = (*id->ops->store)(context, id, creds); + if (ret) + return ret; + + if (id->cc_initialized && !id->cc_start_tgt_stored && + id->cc_need_start_realm && + krb5_principal_is_root_krbtgt(context, creds->server)) { + /* Mark the first root TGT's realm as the start realm */ + id->cc_start_tgt_stored = 1; + realm.length = strlen(creds->server->realm); + realm.data = creds->server->realm; + (void) krb5_cc_set_config(context, id, NULL, "start_realm", &realm); + id->cc_need_start_realm = 0; + } else if (id->cc_initialized && id->cc_start_tgt_stored && + !id->cc_kx509_done && strcmp(cfg, "kx509cert") == 0) { + /* + * Do not attempt kx509 at cc close time -- we're copying a ccache and + * we've already got a cert (and private key). + */ + id->cc_kx509_done = 1; + } else if (id->cc_initialized && id->cc_start_tgt_stored && + !id->cc_kx509_done && strcmp(cfg, "kx509_service_status") == 0) { + /* + * Do not attempt kx509 at cc close time -- we're copying a ccache and + * we know the kx509 service is not available. + */ + id->cc_kx509_done = 1; + } else if (id->cc_initialized && strcmp(cfg, "start_realm") == 0) { + /* + * If the caller is storing a start_realm ccconfig, then stop looking + * for root TGTs to mark as the start_realm. + * + * By honoring any start_realm cc config stored, we interop both, with + * ccache implementations that don't preserve insertion order, and + * Kerberos implementations that store this cc config before the TGT. + */ + id->cc_need_start_realm = 0; + } + return ret; +} + +/** + * Retrieve the credential identified by `mcreds' (and `whichfields') + * from `id' in `creds'. 'creds' must be free by the caller using + * krb5_free_cred_contents. + * + * @param context A Kerberos 5 context + * @param id a Kerberos 5 credential cache + * @param whichfields what fields to use for matching credentials, same + * flags as whichfields in krb5_compare_creds() + * @param mcreds template credential to use for comparing + * @param creds returned credential, free with krb5_free_cred_contents() + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_retrieve_cred(krb5_context context, + krb5_ccache id, + krb5_flags whichfields, + const krb5_creds *mcreds, + krb5_creds *creds) +{ + krb5_error_code ret; + krb5_cc_cursor cursor; + + if (id->ops->retrieve != NULL) { + return (*id->ops->retrieve)(context, id, whichfields, + mcreds, creds); + } + + ret = krb5_cc_start_seq_get(context, id, &cursor); + if (ret) + return ret; + while((ret = krb5_cc_next_cred(context, id, &cursor, creds)) == 0){ + if(krb5_compare_creds(context, whichfields, mcreds, creds)){ + ret = 0; + break; + } + krb5_free_cred_contents (context, creds); + } + krb5_cc_end_seq_get(context, id, &cursor); + return ret; +} + +/** + * Return the principal of `id' in `principal'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_principal(krb5_context context, + krb5_ccache id, + krb5_principal *principal) +{ + return (*id->ops->get_princ)(context, id, principal); +} + +/** + * Start iterating over `id', `cursor' is initialized to the + * beginning. Caller must free the cursor with krb5_cc_end_seq_get(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_start_seq_get (krb5_context context, + const krb5_ccache id, + krb5_cc_cursor *cursor) +{ + return (*id->ops->get_first)(context, id, cursor); +} + +/** + * Retrieve the next cred pointed to by (`id', `cursor') in `creds' + * and advance `cursor'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_next_cred (krb5_context context, + const krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *creds) +{ + return (*id->ops->get_next)(context, id, cursor, creds); +} + +/** + * Destroy the cursor `cursor'. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_end_seq_get (krb5_context context, + const krb5_ccache id, + krb5_cc_cursor *cursor) +{ + return (*id->ops->end_get)(context, id, cursor); +} + +/** + * Remove the credential identified by `cred', `which' from `id'. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_remove_cred(krb5_context context, + krb5_ccache id, + krb5_flags which, + krb5_creds *cred) +{ + if(id->ops->remove_cred == NULL) { + krb5_set_error_message(context, + EACCES, + "ccache %s does not support remove_cred", + id->ops->prefix); + return EACCES; /* XXX */ + } + return (*id->ops->remove_cred)(context, id, which, cred); +} + +/** + * Set the flags of `id' to `flags'. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_set_flags(krb5_context context, + krb5_ccache id, + krb5_flags flags) +{ + return (*id->ops->set_flags)(context, id, flags); +} + +/** + * Get the flags of `id', store them in `flags'. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_flags(krb5_context context, + krb5_ccache id, + krb5_flags *flags) +{ + *flags = 0; + return 0; +} + +/** + * Copy the contents of `from' to `to' if the given match function + * return true. + * + * @param context A Kerberos 5 context. + * @param from the cache to copy data from. + * @param to the cache to copy data to. + * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied. + * @param matchctx context passed to match function. + * @param matched set to true if there was a credential that matched, may be NULL. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_copy_match_f(krb5_context context, + const krb5_ccache from, + krb5_ccache to, + krb5_boolean (*match)(krb5_context, void *, const krb5_creds *), + void *matchctx, + unsigned int *matched) +{ + krb5_error_code ret; + krb5_cc_cursor cursor; + krb5_creds cred; + krb5_principal princ; + + if (matched) + *matched = 0; + + ret = krb5_cc_get_principal(context, from, &princ); + if (ret) + return ret; + ret = krb5_cc_initialize(context, to, princ); + if (ret) { + krb5_free_principal(context, princ); + return ret; + } + ret = krb5_cc_start_seq_get(context, from, &cursor); + if (ret) { + krb5_free_principal(context, princ); + return ret; + } + + while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) { + if (match == NULL || (*match)(context, matchctx, &cred)) { + if (matched) + (*matched)++; + ret = krb5_cc_store_cred(context, to, &cred); + if (ret) + break; + } + krb5_free_cred_contents(context, &cred); + } + krb5_cc_end_seq_get(context, from, &cursor); + krb5_free_principal(context, princ); + if (ret == KRB5_CC_END) + ret = 0; + return ret; +} + +/** + * Just like krb5_cc_copy_match_f(), but copy everything. + * + * @ingroup @krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_copy_cache(krb5_context context, + const krb5_ccache from, + krb5_ccache to) +{ + return krb5_cc_copy_match_f(context, from, to, NULL, NULL, NULL); +} + +/** + * Return the version of `id'. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_version(krb5_context context, + const krb5_ccache id) +{ + if(id->ops->get_version) + return (*id->ops->get_version)(context, id); + else + return 0; +} + +/** + * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_cc_clear_mcred(krb5_creds *mcred) +{ + memset(mcred, 0, sizeof(*mcred)); +} + +/** + * Get the cc ops that is registered in `context' to handle the + * prefix. prefix can be a complete credential cache name or a + * prefix, the function will only use part up to the first colon (:) + * if there is one. If prefix the argument is NULL, the default ccache + * implemtation is returned. + * + * @return Returns NULL if ops not found. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL +krb5_cc_get_prefix_ops(krb5_context context, const char *prefix) +{ + return cc_get_prefix_ops(context, prefix, NULL); +} + +/** + * Get the cc ops that is registered in `context' to handle the + * prefix. prefix can be a complete credential cache name or a + * prefix, the function will only use part up to the first colon (:) + * if there is one. If prefix the argument is NULL, the default ccache + * implementation is returned. + * + * If residual is non-NULL, it is set to the residual component of + * prefix (if present) or the prefix itself. + * + * @return Returns NULL if ops not found. + * + * @ingroup krb5_ccache + */ + + +static const krb5_cc_ops * +cc_get_prefix_ops(krb5_context context, + const char *prefix, + const char **residual) +{ + int i; + + if (residual) + *residual = prefix; + + if (prefix == NULL) + return KRB5_DEFAULT_CCTYPE; + + /* Is absolute path? Or UNC path? */ + if (ISPATHSEP(prefix[0])) + return &krb5_fcc_ops; + +#ifdef _WIN32 + /* Is drive letter? */ + if (isalpha((unsigned char)prefix[0]) && prefix[1] == ':') + return &krb5_fcc_ops; +#endif + + for(i = 0; i < context->num_cc_ops && context->cc_ops[i]->prefix; i++) { + size_t prefix_len = strlen(context->cc_ops[i]->prefix); + + if (strncmp(context->cc_ops[i]->prefix, prefix, prefix_len) == 0 && + (prefix[prefix_len] == ':' || prefix[prefix_len] == '\0')) { + if (residual) { + if (prefix[prefix_len] == ':' && prefix[prefix_len + 1] != '\0') + *residual = &prefix[prefix_len + 1]; + else + *residual = NULL; + } + + return context->cc_ops[i]; + } + } + + return NULL; +} + +struct krb5_cc_cache_cursor_data { + const krb5_cc_ops *ops; + krb5_cc_cursor cursor; +}; + +/** + * Start iterating over all caches of specified type. See also + * krb5_cccol_cursor_new(). + + * @param context A Kerberos 5 context + * @param type optional type to iterate over, if NULL, the default cache is used. + * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_cache_get_first (krb5_context context, + const char *type, + krb5_cc_cache_cursor *cursor) +{ + const krb5_cc_ops *ops; + krb5_error_code ret; + + if (type == NULL) + type = krb5_cc_default_name(context); + + ops = krb5_cc_get_prefix_ops(context, type); + if (ops == NULL) { + krb5_set_error_message(context, KRB5_CC_UNKNOWN_TYPE, + "Unknown type \"%s\" when iterating " + "trying to iterate the credential caches", type); + return KRB5_CC_UNKNOWN_TYPE; + } + + if (ops->get_cache_first == NULL) { + krb5_set_error_message(context, KRB5_CC_NOSUPP, + N_("Credential cache type %s doesn't support " + "iterations over caches", "type"), + ops->prefix); + return KRB5_CC_NOSUPP; + } + + *cursor = calloc(1, sizeof(**cursor)); + if (*cursor == NULL) + return krb5_enomem(context); + + (*cursor)->ops = ops; + + ret = ops->get_cache_first(context, &(*cursor)->cursor); + if (ret) { + free(*cursor); + *cursor = NULL; + } + return ret; +} + +/** + * Retrieve the next cache pointed to by (`cursor') in `id' + * and advance `cursor'. + * + * @param context A Kerberos 5 context + * @param cursor the iterator cursor, returned by krb5_cc_cache_get_first() + * @param id next ccache + * + * @return Return 0 or an error code. Returns KRB5_CC_END when the end + * of caches is reached, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_cache_next (krb5_context context, + krb5_cc_cache_cursor cursor, + krb5_ccache *id) +{ + return cursor->ops->get_cache_next(context, cursor->cursor, id); +} + +/** + * Destroy the cursor `cursor'. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_cache_end_seq_get (krb5_context context, + krb5_cc_cache_cursor cursor) +{ + krb5_error_code ret; + ret = cursor->ops->end_cache_get(context, cursor->cursor); + cursor->ops = NULL; + free(cursor); + return ret; +} + +/** + * Search for a matching credential cache that have the + * `principal' as the default principal. On success, `id' needs to be + * freed with krb5_cc_close() or krb5_cc_destroy(). + * + * @param context A Kerberos 5 context + * @param client The principal to search for + * @param id the returned credential cache + * + * @return On failure, error code is returned and `id' is set to NULL. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_cache_match (krb5_context context, + krb5_principal client, + krb5_ccache *id) +{ + krb5_cccol_cursor cursor; + krb5_error_code ret; + krb5_ccache cache = NULL; + krb5_ccache expired_match = NULL; + + *id = NULL; + + ret = krb5_cccol_cursor_new (context, &cursor); + if (ret) + return ret; + + while (krb5_cccol_cursor_next(context, cursor, &cache) == 0 && cache != NULL) { + krb5_principal principal; + krb5_boolean match; + time_t lifetime; + + ret = krb5_cc_get_principal(context, cache, &principal); + if (ret) + goto next; + + if (client->name.name_string.len == 0) + match = (strcmp(client->realm, principal->realm) == 0); + else + match = krb5_principal_compare(context, principal, client); + krb5_free_principal(context, principal); + + if (!match) + goto next; + + if (expired_match == NULL && + (krb5_cc_get_lifetime(context, cache, &lifetime) != 0 || lifetime == 0)) { + expired_match = cache; + cache = NULL; + goto next; + } + break; + + next: + if (cache) + krb5_cc_close(context, cache); + cache = NULL; + } + + krb5_cccol_cursor_free(context, &cursor); + + if (cache == NULL && expired_match) { + cache = expired_match; + expired_match = NULL; + } else if (expired_match) { + krb5_cc_close(context, expired_match); + } else if (cache == NULL) { + char *str; + + (void) krb5_unparse_name(context, client, &str); + krb5_set_error_message(context, KRB5_CC_NOTFOUND, + N_("Principal %s not found in any " + "credential cache", ""), + str ? str : ""); + if (str) + free(str); + return KRB5_CC_NOTFOUND; + } + + *id = cache; + + return 0; +} + +/** + * Move the content from one credential cache to another. The + * operation is an atomic switch. + * + * @param context a Kerberos context + * @param from the credential cache to move the content from + * @param to the credential cache to move the content to + + * @return On sucess, from is destroyed and closed. On failure, error code is + * returned and from and to are both still allocated; see + * krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_move(krb5_context context, krb5_ccache from, krb5_ccache to) +{ + krb5_error_code ret = ENOTSUP; + krb5_principal princ = NULL; + + if (to->ops->move && + strcmp(from->ops->prefix, to->ops->prefix) == 0) { + /* + * NOTE: to->ops->move() is expected to call + * krb5_cc_destroy(context, from) on success. + */ + ret = (*to->ops->move)(context, from, to); + if (ret == 0) + return 0; + if (ret != EXDEV && ret != ENOTSUP && ret != KRB5_CC_NOSUPP && + ret != KRB5_FCC_INTERNAL) + return ret; + /* Fallback to high-level copy */ + } /* Else high-level copy */ + + /* + * Initialize destination, copy the source's contents to the destination, + * then destroy the source on success. + * + * It'd be nice if we could destroy any half-built destination if the copy + * fails, but the interface is not documented as doing so. + */ + ret = krb5_cc_get_principal(context, from, &princ); + if (ret == 0) + ret = krb5_cc_initialize(context, to, princ); + krb5_free_principal(context, princ); + if (ret == 0) + ret = krb5_cc_copy_cache(context, from, to); + if (ret == 0) + krb5_cc_destroy(context, from); + return ret; +} + +#define KRB5_CONF_NAME "krb5_ccache_conf_data" +#define KRB5_REALM_NAME "X-CACHECONF:" + +static krb5_error_code +build_conf_principals(krb5_context context, krb5_ccache id, + krb5_const_principal principal, + const char *name, krb5_creds *cred) +{ + krb5_principal client; + krb5_error_code ret; + char *pname = NULL; + + memset(cred, 0, sizeof(*cred)); + + ret = krb5_cc_get_principal(context, id, &client); + if (ret) + return ret; + + if (principal) { + ret = krb5_unparse_name(context, principal, &pname); + if (ret) + return ret; + } + + ret = krb5_make_principal(context, &cred->server, + KRB5_REALM_NAME, + KRB5_CONF_NAME, name, pname, NULL); + free(pname); + if (ret) { + krb5_free_principal(context, client); + return ret; + } + ret = krb5_copy_principal(context, client, &cred->client); + krb5_free_principal(context, client); + return ret; +} + +/** + * Return TRUE (non zero) if the principal is a configuration + * principal (generated part of krb5_cc_set_config()). Returns FALSE + * (zero) if not a configuration principal. + * + * @param context a Kerberos context + * @param principal principal to check if it a configuration principal + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_is_config_principal(krb5_context context, + krb5_const_principal principal) +{ + if (strcmp(principal->realm, KRB5_REALM_NAME) != 0) + return FALSE; + + if (principal->name.name_string.len == 0 || + strcmp(principal->name.name_string.val[0], KRB5_CONF_NAME) != 0) + return FALSE; + + return TRUE; +} + +/** + * Store some configuration for the credential cache in the cache. + * Existing configuration under the same name is over-written. + * + * @param context a Kerberos context + * @param id the credential cache to store the data for + * @param principal configuration for a specific principal, if + * NULL, global for the whole cache. + * @param name name under which the configuraion is stored. + * @param data data to store, if NULL, configure is removed. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_set_config(krb5_context context, krb5_ccache id, + krb5_const_principal principal, + const char *name, krb5_data *data) +{ + krb5_error_code ret; + krb5_creds cred; + + ret = build_conf_principals(context, id, principal, name, &cred); + if (ret) + goto out; + + /* Remove old configuration */ + ret = krb5_cc_remove_cred(context, id, 0, &cred); + if (ret && ret != KRB5_CC_NOTFOUND && ret != KRB5_CC_NOSUPP && + ret != KRB5_FCC_INTERNAL) + goto out; + + if (data) { + /* not that anyone care when this expire */ + cred.times.authtime = time(NULL); + cred.times.endtime = cred.times.authtime + 3600 * 24 * 30; + + ret = krb5_data_copy(&cred.ticket, data->data, data->length); + if (ret) + goto out; + + ret = krb5_cc_store_cred(context, id, &cred); + } + +out: + krb5_free_cred_contents (context, &cred); + return ret; +} + +/** + * Get some configuration for the credential cache in the cache. + * + * @param context a Kerberos context + * @param id the credential cache to store the data for + * @param principal configuration for a specific principal, if + * NULL, global for the whole cache. + * @param name name under which the configuraion is stored. + * @param data data to fetched, free with krb5_data_free() + * @return 0 on success, KRB5_CC_NOTFOUND or KRB5_CC_END if not found, + * or other system error. + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_config(krb5_context context, krb5_ccache id, + krb5_const_principal principal, + const char *name, krb5_data *data) +{ + krb5_creds mcred, cred; + krb5_error_code ret; + + memset(&cred, 0, sizeof(cred)); + krb5_data_zero(data); + + ret = build_conf_principals(context, id, principal, name, &mcred); + if (ret) + goto out; + + ret = krb5_cc_retrieve_cred(context, id, 0, &mcred, &cred); + if (ret) + goto out; + + ret = krb5_data_copy(data, cred.ticket.data, cred.ticket.length); + +out: + krb5_free_cred_contents (context, &cred); + krb5_free_cred_contents (context, &mcred); + return ret; +} + +/* + * + */ + +struct krb5_cccol_cursor_data { + int idx; + krb5_cc_cache_cursor cursor; +}; + +/** + * Get a new cache interation cursor that will interate over all + * credentials caches independent of type. + * + * @param context a Kerberos context + * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free(). + * + * @return Returns 0 or and error code, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor) +{ + *cursor = calloc(1, sizeof(**cursor)); + if (*cursor == NULL) + return krb5_enomem(context); + (*cursor)->idx = 0; + (*cursor)->cursor = NULL; + + return 0; +} + +/** + * Get next credential cache from the iteration. + * + * @param context A Kerberos 5 context + * @param cursor the iteration cursor + * @param cache the returned cursor, pointer is set to NULL on failure + * and a cache on success. The returned cache needs to be freed + * with krb5_cc_close() or destroyed with krb5_cc_destroy(). + * MIT Kerberos behavies slightly diffrent and sets cache to NULL + * when all caches are iterated over and return 0. + * + * @return Return 0 or and error, KRB5_CC_END is returned at the end + * of iteration. See krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cccol_cursor_next(krb5_context context, krb5_cccol_cursor cursor, + krb5_ccache *cache) +{ + krb5_error_code ret = 0; + + *cache = NULL; + + while (cursor->idx < context->num_cc_ops) { + + if (cursor->cursor == NULL) { + ret = krb5_cc_cache_get_first (context, + context->cc_ops[cursor->idx]->prefix, + &cursor->cursor); + if (ret) { + cursor->idx++; + continue; + } + } + ret = krb5_cc_cache_next(context, cursor->cursor, cache); + if (ret == 0) + break; + + krb5_cc_cache_end_seq_get(context, cursor->cursor); + cursor->cursor = NULL; + if (ret != KRB5_CC_END) + break; + + cursor->idx++; + } + if (cursor->idx >= context->num_cc_ops) { + krb5_set_error_message(context, KRB5_CC_END, + N_("Reached end of credential caches", "")); + return KRB5_CC_END; + } + + return ret; +} + +/** + * End an iteration and free all resources, can be done before end is reached. + * + * @param context A Kerberos 5 context + * @param cursor the iteration cursor to be freed. + * + * @return Return 0 or and error, KRB5_CC_END is returned at the end + * of iteration. See krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cccol_cursor_free(krb5_context context, krb5_cccol_cursor *cursor) +{ + krb5_cccol_cursor c = *cursor; + + *cursor = NULL; + if (c) { + if (c->cursor) + krb5_cc_cache_end_seq_get(context, c->cursor); + free(c); + } + return 0; +} + +/** + * Return the last time the credential cache was modified. + * + * @param context A Kerberos 5 context + * @param id The credential cache to probe + * @param mtime the last modification time, set to 0 on error. + + * @return Return 0 or and error. See krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_last_change_time(krb5_context context, + krb5_ccache id, + krb5_timestamp *mtime) +{ + *mtime = 0; + + if (id->ops->version < KRB5_CC_OPS_VERSION_2 + || id->ops->lastchange == NULL) + return KRB5_CC_NOSUPP; + + return (*id->ops->lastchange)(context, id, mtime); +} + +/** + * Return the last modfication time for a cache collection. The query + * can be limited to a specific cache type. If the function return 0 + * and mtime is 0, there was no credentials in the caches. + * + * @param context A Kerberos 5 context + * @param type The credential cache to probe, if NULL, all type are traversed. + * @param mtime the last modification time, set to 0 on error. + + * @return Return 0 or and error. See krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cccol_last_change_time(krb5_context context, + const char *type, + krb5_timestamp *mtime) +{ + krb5_cccol_cursor cursor; + krb5_error_code ret; + krb5_ccache id; + krb5_timestamp t = 0; + + *mtime = 0; + + ret = krb5_cccol_cursor_new (context, &cursor); + if (ret) + return ret; + + while (krb5_cccol_cursor_next(context, cursor, &id) == 0 && id != NULL) { + + if (type && strcmp(krb5_cc_get_type(context, id), type) != 0) + continue; + + ret = krb5_cc_last_change_time(context, id, &t); + krb5_cc_close(context, id); + if (ret) + continue; + if (t > *mtime) + *mtime = t; + } + + krb5_cccol_cursor_free(context, &cursor); + + return 0; +} +/** + * Return a friendly name on credential cache. Free the result with krb5_xfree(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_friendly_name(krb5_context context, + krb5_ccache id, + char **name) +{ + krb5_error_code ret; + krb5_data data; + + ret = krb5_cc_get_config(context, id, NULL, "FriendlyName", &data); + if (ret) { + krb5_principal principal; + ret = krb5_cc_get_principal(context, id, &principal); + if (ret) + return ret; + ret = krb5_unparse_name(context, principal, name); + krb5_free_principal(context, principal); + } else { + ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data); + krb5_data_free(&data); + if (ret <= 0) + ret = krb5_enomem(context); + else + ret = 0; + } + + return ret; +} + +/** + * Set the friendly name on credential cache. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_set_friendly_name(krb5_context context, + krb5_ccache id, + const char *name) +{ + krb5_data data; + + data.data = rk_UNCONST(name); + data.length = strlen(name); + + return krb5_cc_set_config(context, id, NULL, "FriendlyName", &data); +} + +/** + * Get the lifetime of the initial ticket in the cache + * + * Get the lifetime of the initial ticket in the cache, if the initial + * ticket was not found, the error code KRB5_CC_END is returned. + * + * @param context A Kerberos 5 context. + * @param id a credential cache + * @param t the relative lifetime of the initial ticket + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t) +{ + krb5_data config_start_realm; + char *start_realm; + krb5_cc_cursor cursor; + krb5_error_code ret; + krb5_creds cred; + time_t now, endtime = 0; + + *t = 0; + krb5_timeofday(context, &now); + + ret = krb5_cc_get_config(context, id, NULL, "start_realm", &config_start_realm); + if (ret == 0) { + start_realm = strndup(config_start_realm.data, config_start_realm.length); + krb5_data_free(&config_start_realm); + } else { + krb5_principal client; + + ret = krb5_cc_get_principal(context, id, &client); + if (ret) + return ret; + start_realm = strdup(krb5_principal_get_realm(context, client)); + krb5_free_principal(context, client); + } + if (start_realm == NULL) + return krb5_enomem(context); + + ret = krb5_cc_start_seq_get(context, id, &cursor); + if (ret) { + free(start_realm); + return ret; + } + + while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) { + /** + * If we find the start krbtgt in the cache, use that as the lifespan. + */ + if (krb5_principal_is_root_krbtgt(context, cred.server) && + strcmp(cred.server->realm, start_realm) == 0) { + if (now < cred.times.endtime) + endtime = cred.times.endtime; + krb5_free_cred_contents(context, &cred); + break; + } + /* + * Skip config entries + */ + if (krb5_is_config_principal(context, cred.server)) { + krb5_free_cred_contents(context, &cred); + continue; + } + /** + * If there was no krbtgt, use the shortest lifetime of + * service tickets that have yet to expire. If all + * credentials are expired, krb5_cc_get_lifetime() will fail. + */ + if ((endtime == 0 || cred.times.endtime < endtime) && now < cred.times.endtime) + endtime = cred.times.endtime; + krb5_free_cred_contents(context, &cred); + } + free(start_realm); + + /* if we found an endtime use that */ + if (endtime) { + *t = endtime - now; + ret = 0; + } + + krb5_cc_end_seq_get(context, id, &cursor); + + return ret; +} + +/** + * Set the time offset betwen the client and the KDC + * + * If the backend doesn't support KDC offset, use the context global setting. + * + * @param context A Kerberos 5 context. + * @param id a credential cache + * @param offset the offset in seconds + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset) +{ + if (id->ops->version < KRB5_CC_OPS_VERSION_3 + || id->ops->set_kdc_offset == NULL) { + context->kdc_sec_offset = offset; + context->kdc_usec_offset = 0; + return 0; + } + return (*id->ops->set_kdc_offset)(context, id, offset); +} + +/** + * Get the time offset betwen the client and the KDC + * + * If the backend doesn't support KDC offset, use the context global setting. + * + * @param context A Kerberos 5 context. + * @param id a credential cache + * @param offset the offset in seconds + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *offset) +{ + if (id->ops->version < KRB5_CC_OPS_VERSION_3 + || id->ops->get_kdc_offset == NULL) { + *offset = context->kdc_sec_offset; + return 0; + } + return (*id->ops->get_kdc_offset)(context, id, offset); +} + +#ifdef _WIN32 +#define REGPATH_MIT_KRB5 "SOFTWARE\\MIT\\Kerberos5" + +static char * +_get_default_cc_name_from_registry(krb5_context context, HKEY hkBase) +{ + HKEY hk_k5 = 0; + LONG code; + char *ccname = NULL; + + code = RegOpenKeyEx(hkBase, + REGPATH_MIT_KRB5, + 0, KEY_READ, &hk_k5); + + if (code != ERROR_SUCCESS) + return NULL; + + ccname = heim_parse_reg_value_as_string(context->hcontext, hk_k5, "ccname", + REG_NONE, 0); + + RegCloseKey(hk_k5); + + return ccname; +} + +KRB5_LIB_FUNCTION char * KRB5_LIB_CALL +_krb5_get_default_cc_name_from_registry(krb5_context context) +{ + char *ccname; + + ccname = _get_default_cc_name_from_registry(context, HKEY_CURRENT_USER); + if (ccname == NULL) + ccname = _get_default_cc_name_from_registry(context, + HKEY_LOCAL_MACHINE); + + return ccname; +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +_krb5_set_default_cc_name_to_registry(krb5_context context, krb5_ccache id) +{ + HKEY hk_k5 = 0; + LONG code; + int ret = -1; + char * ccname = NULL; + + code = RegOpenKeyEx(HKEY_CURRENT_USER, + REGPATH_MIT_KRB5, + 0, KEY_READ|KEY_WRITE, &hk_k5); + + if (code != ERROR_SUCCESS) + return -1; + + ret = asprintf(&ccname, "%s:%s", krb5_cc_get_type(context, id), krb5_cc_get_name(context, id)); + if (ret < 0) + goto cleanup; + + ret = heim_store_string_to_reg_value(context->hcontext, hk_k5, "ccname", + REG_SZ, ccname, -1, 0); + + cleanup: + + if (ccname) + free(ccname); + + RegCloseKey(hk_k5); + + return ret; +} +#endif diff --git a/third_party/heimdal/lib/krb5/ccache_plugin.h b/third_party/heimdal/lib/krb5/ccache_plugin.h new file mode 100644 index 0000000..e0fda4c --- /dev/null +++ b/third_party/heimdal/lib/krb5/ccache_plugin.h @@ -0,0 +1,46 @@ +/*********************************************************************** + * 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. + * + **********************************************************************/ + +#ifndef HEIMDAL_KRB5_CCACHE_PLUGIN_H +#define HEIMDAL_KRB5_CCACHE_PLUGIN_H 1 + +#include +#include + +#define KRB5_PLUGIN_CCACHE "ccache_ops" + +krb5_error_code KRB5_CALLCONV +ccache_ops_plugin_load(krb5_context context, + krb5_get_instance_func_t *func, + size_t *n_ftables, + heim_plugin_common_ftable_p **ftables); + +#endif /* HEIMDAL_KRB5_CCACHE_PLUGIN_H */ diff --git a/third_party/heimdal/lib/krb5/changepw.c b/third_party/heimdal/lib/krb5/changepw.c new file mode 100644 index 0000000..22a1f40 --- /dev/null +++ b/third_party/heimdal/lib/krb5/changepw.c @@ -0,0 +1,860 @@ +/* + * 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 "krb5_locl.h" + +#undef __attribute__ +#define __attribute__(X) + + +static void +str2data (krb5_data *d, + const char *fmt, + ...) __attribute__ ((__format__ (__printf__, 2, 3))); + +static void +str2data (krb5_data *d, + const char *fmt, + ...) +{ + va_list args; + char *str; + + va_start(args, fmt); + d->length = vasprintf (&str, fmt, args); + va_end(args); + d->data = str; +} + +/* + * Change password protocol defined by + * draft-ietf-cat-kerb-chg-password-02.txt + * + * Share the response part of the protocol with MS set password + * (RFC3244) + */ + +static krb5_error_code +chgpw_send_request (krb5_context context, + krb5_auth_context *auth_context, + krb5_creds *creds, + krb5_principal targprinc, + int is_stream, + rk_socket_t sock, + const char *passwd, + const char *host) +{ + krb5_error_code ret; + krb5_data ap_req_data; + krb5_data krb_priv_data; + krb5_data passwd_data; + size_t len; + u_char header[6]; + struct iovec iov[3]; + struct msghdr msghdr; + + if (is_stream) + return KRB5_KPASSWD_MALFORMED; + + if (targprinc && + krb5_principal_compare(context, creds->client, targprinc) != TRUE) + return KRB5_KPASSWD_MALFORMED; + + krb5_data_zero (&ap_req_data); + + ret = krb5_mk_req_extended (context, + auth_context, + AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, + NULL, /* in_data */ + creds, + &ap_req_data); + if (ret) + return ret; + + passwd_data.data = rk_UNCONST(passwd); + passwd_data.length = strlen(passwd); + + krb5_data_zero (&krb_priv_data); + + ret = krb5_mk_priv (context, + *auth_context, + &passwd_data, + &krb_priv_data, + NULL); + if (ret) + goto out2; + + len = 6 + ap_req_data.length + krb_priv_data.length; + header[0] = (len >> 8) & 0xFF; + header[1] = (len >> 0) & 0xFF; + header[2] = 0; + header[3] = 1; + header[4] = (ap_req_data.length >> 8) & 0xFF; + header[5] = (ap_req_data.length >> 0) & 0xFF; + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = iov; + msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); +#if 0 + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; +#endif + + iov[0].iov_base = (void*)header; + iov[0].iov_len = 6; + iov[1].iov_base = ap_req_data.data; + iov[1].iov_len = ap_req_data.length; + iov[2].iov_base = krb_priv_data.data; + iov[2].iov_len = krb_priv_data.length; + + if (rk_IS_SOCKET_ERROR( sendmsg (sock, &msghdr, 0) )) { + ret = rk_SOCK_ERRNO; + krb5_set_error_message(context, ret, "sendmsg %s: %s", + host, strerror(ret)); + } + + krb5_data_free (&krb_priv_data); +out2: + krb5_data_free (&ap_req_data); + return ret; +} + +/* + * Set password protocol as defined by RFC3244 -- + * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols + */ + +static krb5_error_code +setpw_send_request (krb5_context context, + krb5_auth_context *auth_context, + krb5_creds *creds, + krb5_principal targprinc, + int is_stream, + rk_socket_t sock, + const char *passwd, + const char *host) +{ + krb5_error_code ret; + krb5_data ap_req_data; + krb5_data krb_priv_data; + krb5_data pwd_data; + ChangePasswdDataMS chpw; + size_t len = 0; + u_char header[4 + 6]; + u_char *p; + struct iovec iov[3]; + struct msghdr msghdr; + + krb5_data_zero (&ap_req_data); + + ret = krb5_mk_req_extended (context, + auth_context, + AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, + NULL, /* in_data */ + creds, + &ap_req_data); + if (ret) + return ret; + + chpw.newpasswd.length = strlen(passwd); + chpw.newpasswd.data = rk_UNCONST(passwd); + if (targprinc) { + chpw.targname = &targprinc->name; + chpw.targrealm = &targprinc->realm; + } else { + chpw.targname = NULL; + chpw.targrealm = NULL; + } + + ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, + &chpw, &len, ret); + if (ret) { + krb5_data_free (&ap_req_data); + return ret; + } + + if(pwd_data.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_mk_priv (context, + *auth_context, + &pwd_data, + &krb_priv_data, + NULL); + if (ret) + goto out2; + + len = 6 + ap_req_data.length + krb_priv_data.length; + p = header; + if (is_stream) { + _krb5_put_int(p, len, 4); + p += 4; + } + *p++ = (len >> 8) & 0xFF; + *p++ = (len >> 0) & 0xFF; + *p++ = 0xff; + *p++ = 0x80; + *p++ = (ap_req_data.length >> 8) & 0xFF; + *p = (ap_req_data.length >> 0) & 0xFF; + + memset(&msghdr, 0, sizeof(msghdr)); + msghdr.msg_name = NULL; + msghdr.msg_namelen = 0; + msghdr.msg_iov = iov; + msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); +#if 0 + msghdr.msg_control = NULL; + msghdr.msg_controllen = 0; +#endif + + iov[0].iov_base = (void*)header; + if (is_stream) + iov[0].iov_len = 10; + else + iov[0].iov_len = 6; + iov[1].iov_base = ap_req_data.data; + iov[1].iov_len = ap_req_data.length; + iov[2].iov_base = krb_priv_data.data; + iov[2].iov_len = krb_priv_data.length; + + if (rk_IS_SOCKET_ERROR( sendmsg (sock, &msghdr, 0) )) { + ret = rk_SOCK_ERRNO; + krb5_set_error_message(context, ret, "sendmsg %s: %s", + host, strerror(ret)); + } + + krb5_data_free (&krb_priv_data); +out2: + krb5_data_free (&ap_req_data); + krb5_data_free (&pwd_data); + return ret; +} + +static krb5_error_code +process_reply (krb5_context context, + krb5_auth_context auth_context, + int is_stream, + rk_socket_t sock, + int *result_code, + krb5_data *result_code_string, + krb5_data *result_string, + const char *host) +{ + krb5_error_code ret; + u_char reply[1024 * 3]; + size_t len; + uint16_t pkt_len, pkt_ver; + krb5_data ap_rep_data; + int save_errno; + + len = 0; + if (is_stream) { + while (len < sizeof(reply)) { + unsigned long size; + + ret = recvfrom (sock, reply + len, sizeof(reply) - len, + 0, NULL, NULL); + if (rk_IS_SOCKET_ERROR(ret)) { + save_errno = rk_SOCK_ERRNO; + krb5_set_error_message(context, save_errno, + "recvfrom %s: %s", + host, strerror(save_errno)); + return save_errno; + } else if (ret == 0) { + krb5_set_error_message(context, 1,"recvfrom timeout %s", host); + return 1; + } + len += ret; + if (len < 4) + continue; + _krb5_get_int(reply, &size, 4); + if (size + 4 < len) + continue; + if (sizeof(reply) - 4 < size) { + krb5_set_error_message(context, ERANGE, "size from server too large %s", host); + return ERANGE; + } + memmove(reply, reply + 4, size); + len = size; + break; + } + if (len == sizeof(reply)) { + krb5_set_error_message(context, ENOMEM, + N_("Message too large from %s", "host"), + host); + return ENOMEM; + } + } else { + ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL); + if (rk_IS_SOCKET_ERROR(ret)) { + save_errno = rk_SOCK_ERRNO; + krb5_set_error_message(context, save_errno, + "recvfrom %s: %s", + host, strerror(save_errno)); + return save_errno; + } + len = ret; + } + + if (len < 6) { + str2data (result_string, "server %s sent to too short message " + "(%llu bytes)", host, (unsigned long long)len); + *result_code = KRB5_KPASSWD_MALFORMED; + return 0; + } + + pkt_len = (reply[0] << 8) | (reply[1]); + pkt_ver = (reply[2] << 8) | (reply[3]); + + if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) { + KRB_ERROR error; + size_t size; + u_char *p; + + memset(&error, 0, sizeof(error)); + + ret = decode_KRB_ERROR(reply, len, &error, &size); + if (ret) + return ret; + + if (error.e_data->length < 2) { + str2data(result_string, "server %s sent too short " + "e_data to print anything usable", host); + free_KRB_ERROR(&error); + *result_code = KRB5_KPASSWD_MALFORMED; + return 0; + } + + p = error.e_data->data; + *result_code = (p[0] << 8) | p[1]; + if (error.e_data->length == 2) + str2data(result_string, "server only sent error code"); + else + krb5_data_copy (result_string, + p + 2, + error.e_data->length - 2); + free_KRB_ERROR(&error); + return 0; + } + + if (pkt_len != len) { + str2data (result_string, "client: wrong len in reply"); + *result_code = KRB5_KPASSWD_MALFORMED; + return 0; + } + if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) { + str2data (result_string, + "client: wrong version number (%d)", pkt_ver); + *result_code = KRB5_KPASSWD_MALFORMED; + return 0; + } + + ap_rep_data.data = reply + 6; + ap_rep_data.length = (reply[4] << 8) | (reply[5]); + + if (len - 6 < ap_rep_data.length) { + str2data (result_string, "client: wrong AP len in reply"); + *result_code = KRB5_KPASSWD_MALFORMED; + return 0; + } + + if (ap_rep_data.length) { + krb5_ap_rep_enc_part *ap_rep; + krb5_data priv_data; + u_char *p; + + priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length; + priv_data.length = len - ap_rep_data.length - 6; + + ret = krb5_rd_rep (context, + auth_context, + &ap_rep_data, + &ap_rep); + if (ret) + return ret; + + krb5_free_ap_rep_enc_part (context, ap_rep); + + ret = krb5_rd_priv (context, + auth_context, + &priv_data, + result_code_string, + NULL); + if (ret) { + krb5_data_free (result_code_string); + return ret; + } + + if (result_code_string->length < 2) { + *result_code = KRB5_KPASSWD_MALFORMED; + str2data (result_string, + "client: bad length in result"); + return 0; + } + + p = result_code_string->data; + + *result_code = (p[0] << 8) | p[1]; + krb5_data_copy (result_string, + (unsigned char*)result_code_string->data + 2, + result_code_string->length - 2); + return 0; + } else { + KRB_ERROR error; + size_t size; + u_char *p; + + ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size); + if (ret) { + return ret; + } + if (error.e_data->length < 2) { + krb5_warnx (context, "too short e_data to print anything usable"); + return 1; /* XXX */ + } + + p = error.e_data->data; + *result_code = (p[0] << 8) | p[1]; + krb5_data_copy (result_string, + p + 2, + error.e_data->length - 2); + return 0; + } +} + + +/* + * change the password using the credentials in `creds' (for the + * principal indicated in them) to `newpw', storing the result of + * the operation in `result_*' and an error code or 0. + */ + +typedef krb5_error_code (*kpwd_send_request) (krb5_context, + krb5_auth_context *, + krb5_creds *, + krb5_principal, + int, + rk_socket_t, + const char *, + const char *); +typedef krb5_error_code (*kpwd_process_reply) (krb5_context, + krb5_auth_context, + int, + rk_socket_t, + int *, + krb5_data *, + krb5_data *, + const char *); + +static const struct kpwd_proc { + const char *name; + int flags; +#define SUPPORT_TCP 1 +#define SUPPORT_UDP 2 + kpwd_send_request send_req; + kpwd_process_reply process_rep; +} procs[] = { + { + "MS set password", + SUPPORT_TCP|SUPPORT_UDP, + setpw_send_request, + process_reply + }, + { + "change password", + SUPPORT_UDP, + chgpw_send_request, + process_reply + }, + { NULL, 0, NULL, NULL } +}; + +/* + * + */ + +static krb5_error_code +change_password_loop (krb5_context context, + krb5_creds *creds, + krb5_principal targprinc, + const char *newpw, + int *result_code, + krb5_data *result_code_string, + krb5_data *result_string, + const struct kpwd_proc *proc) +{ + krb5_error_code ret; + krb5_auth_context auth_context = NULL; + krb5_krbhst_handle handle = NULL; + krb5_krbhst_info *hi; + rk_socket_t sock; + unsigned int i; + int done = 0; + krb5_realm realm; + + if (targprinc) + realm = targprinc->realm; + else + realm = creds->client->realm; + + ret = krb5_auth_con_init (context, &auth_context); + if (ret) + return ret; + + krb5_auth_con_setflags (context, auth_context, + KRB5_AUTH_CONTEXT_DO_SEQUENCE); + + ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle); + if (ret) + goto out; + + while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) { + struct addrinfo *ai, *a; + int is_stream; + + switch (hi->proto) { + case KRB5_KRBHST_UDP: + if ((proc->flags & SUPPORT_UDP) == 0) + continue; + is_stream = 0; + break; + case KRB5_KRBHST_TCP: + if ((proc->flags & SUPPORT_TCP) == 0) + continue; + is_stream = 1; + break; + default: + continue; + } + + ret = krb5_krbhst_get_addrinfo(context, hi, &ai); + if (ret) + continue; + + for (a = ai; !done && a != NULL; a = a->ai_next) { + int replied = 0; + + sock = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol); + if (rk_IS_BAD_SOCKET(sock)) + continue; + rk_cloexec(sock); + + ret = connect(sock, a->ai_addr, a->ai_addrlen); + if (rk_IS_SOCKET_ERROR(ret)) { + rk_closesocket (sock); + goto out; + } + + ret = krb5_auth_con_genaddrs (context, auth_context, sock, + KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); + if (ret) { + rk_closesocket (sock); + goto out; + } + + for (i = 0; !done && i < 5; ++i) { + fd_set fdset; + struct timeval tv; + + if (!replied) { + replied = 0; + + ret = (*proc->send_req) (context, + &auth_context, + creds, + targprinc, + is_stream, + sock, + newpw, + hi->hostname); + if (ret) { + rk_closesocket(sock); + goto out; + } + } + +#ifndef NO_LIMIT_FD_SETSIZE + if (sock >= FD_SETSIZE) { + ret = ERANGE; + krb5_set_error_message(context, ret, + "fd %d too large", sock); + rk_closesocket (sock); + goto out; + } +#endif + + FD_ZERO(&fdset); + FD_SET(sock, &fdset); + tv.tv_usec = 0; + tv.tv_sec = 1 + (1 << i); + + ret = select (sock + 1, &fdset, NULL, NULL, &tv); + if (rk_IS_SOCKET_ERROR(ret) && rk_SOCK_ERRNO != EINTR) { + rk_closesocket(sock); + goto out; + } + if (ret == 1) { + ret = (*proc->process_rep) (context, + auth_context, + is_stream, + sock, + result_code, + result_code_string, + result_string, + hi->hostname); + if (ret == 0) + done = 1; + else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL) + replied = 1; + } else { + ret = KRB5_KDC_UNREACH; + } + } + rk_closesocket (sock); + } + } + + out: + krb5_krbhst_free (context, handle); + krb5_auth_con_free (context, auth_context); + + if (ret == KRB5_KDC_UNREACH) { + krb5_set_error_message(context, + ret, + N_("Unable to reach any changepw server " + " in realm %s", "realm"), realm); + *result_code = KRB5_KPASSWD_HARDERROR; + } + return ret; +} + +#ifndef HEIMDAL_SMALLER + +static const struct kpwd_proc * +find_chpw_proto(const char *name) +{ + const struct kpwd_proc *p; + for (p = procs; p->name != NULL; p++) { + if (strcmp(p->name, name) == 0) + return p; + } + return NULL; +} + +/** + * Deprecated: krb5_change_password() is deprecated, use krb5_set_password(). + * + * @param context a Keberos context + * @param creds + * @param newpw + * @param result_code + * @param result_code_string + * @param result_string + * + * @return On sucess password is changed. + + * @ingroup @krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_change_password (krb5_context context, + krb5_creds *creds, + const char *newpw, + int *result_code, + krb5_data *result_code_string, + krb5_data *result_string) + KRB5_DEPRECATED_FUNCTION("Use krb5_set_password instead") +{ + const struct kpwd_proc *p = find_chpw_proto("change password"); + + *result_code = KRB5_KPASSWD_MALFORMED; + result_code_string->data = result_string->data = NULL; + result_code_string->length = result_string->length = 0; + + if (p == NULL) + return KRB5_KPASSWD_MALFORMED; + + return change_password_loop(context, creds, NULL, newpw, + result_code, result_code_string, + result_string, p); +} +#endif /* HEIMDAL_SMALLER */ + +/** + * Change password using creds. + * + * @param context a Keberos context + * @param creds The initial kadmin/passwd for the principal or an admin principal + * @param newpw The new password to set + * @param targprinc if unset, the client principal from creds is used + * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed. + * @param result_code_string binary message from the server, contains + * at least the result_code. + * @param result_string A message from the kpasswd service or the + * library in human printable form. The string is NUL terminated. + * + * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed. + + * @ingroup @krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_password(krb5_context context, + krb5_creds *creds, + const char *newpw, + krb5_principal targprinc, + int *result_code, + krb5_data *result_code_string, + krb5_data *result_string) +{ + krb5_principal principal = NULL; + krb5_error_code ret = 0; + int i; + + *result_code = KRB5_KPASSWD_MALFORMED; + krb5_data_zero(result_code_string); + krb5_data_zero(result_string); + + if (targprinc == NULL) { + ret = krb5_copy_principal(context, creds->client, &principal); + if (ret) + return ret; + } else + principal = targprinc; + + for (i = 0; procs[i].name != NULL; i++) { + *result_code = 0; + ret = change_password_loop(context, creds, principal, newpw, + result_code, result_code_string, + result_string, + &procs[i]); + if (ret == 0 && *result_code == 0) + break; + } + + if (targprinc == NULL) + krb5_free_principal(context, principal); + return ret; +} + +/* + * + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_password_using_ccache(krb5_context context, + krb5_ccache ccache, + const char *newpw, + krb5_principal targprinc, + int *result_code, + krb5_data *result_code_string, + krb5_data *result_string) +{ + krb5_creds creds, *credsp; + krb5_error_code ret; + krb5_principal principal = NULL; + + *result_code = KRB5_KPASSWD_MALFORMED; + result_code_string->data = result_string->data = NULL; + result_code_string->length = result_string->length = 0; + + memset(&creds, 0, sizeof(creds)); + + if (targprinc == NULL) { + ret = krb5_cc_get_principal(context, ccache, &principal); + if (ret) + return ret; + } else + principal = targprinc; + + ret = krb5_make_principal(context, &creds.server, + krb5_principal_get_realm(context, principal), + "kadmin", "changepw", NULL); + if (ret) + goto out; + + ret = krb5_cc_get_principal(context, ccache, &creds.client); + if (ret) { + krb5_free_principal(context, creds.server); + goto out; + } + + ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp); + krb5_free_principal(context, creds.server); + krb5_free_principal(context, creds.client); + if (ret) + goto out; + + ret = krb5_set_password(context, + credsp, + newpw, + principal, + result_code, + result_code_string, + result_string); + + krb5_free_creds(context, credsp); + + return ret; + out: + if (targprinc == NULL) + krb5_free_principal(context, principal); + return ret; +} + +/* + * + */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_passwd_result_to_string (krb5_context context, + int result) +{ + static const char *strings[] = { + "Success", + "Malformed", + "Hard error", + "Auth error", + "Soft error" , + "Access denied", + "Bad version", + "Initial flag needed" + }; + + if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED) + return "unknown result code"; + else + return strings[result]; +} diff --git a/third_party/heimdal/lib/krb5/codec.c b/third_party/heimdal/lib/krb5/codec.c new file mode 100644 index 0000000..5e754c6 --- /dev/null +++ b/third_party/heimdal/lib/krb5/codec.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 1998 - 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 "krb5_locl.h" + +#ifndef HEIMDAL_SMALLER + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_EncTicketPart (krb5_context context, + const void *data, + size_t length, + EncTicketPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_EncTicketPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_EncTicketPart (krb5_context context, + void *data, + size_t length, + EncTicketPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_EncTicketPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_EncASRepPart (krb5_context context, + const void *data, + size_t length, + EncASRepPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_EncASRepPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_EncASRepPart (krb5_context context, + void *data, + size_t length, + EncASRepPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_EncASRepPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_EncTGSRepPart (krb5_context context, + const void *data, + size_t length, + EncTGSRepPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_EncTGSRepPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_EncTGSRepPart (krb5_context context, + void *data, + size_t length, + EncTGSRepPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_EncTGSRepPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_EncAPRepPart (krb5_context context, + const void *data, + size_t length, + EncAPRepPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_EncAPRepPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_EncAPRepPart (krb5_context context, + void *data, + size_t length, + EncAPRepPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_EncAPRepPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_Authenticator (krb5_context context, + const void *data, + size_t length, + Authenticator *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_Authenticator(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_Authenticator (krb5_context context, + void *data, + size_t length, + Authenticator *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_Authenticator(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_EncKrbCredPart (krb5_context context, + const void *data, + size_t length, + EncKrbCredPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_EncKrbCredPart(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_EncKrbCredPart (krb5_context context, + void *data, + size_t length, + EncKrbCredPart *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_EncKrbCredPart (data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_ETYPE_INFO (krb5_context context, + const void *data, + size_t length, + ETYPE_INFO *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_ETYPE_INFO(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_ETYPE_INFO (krb5_context context, + void *data, + size_t length, + ETYPE_INFO *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_ETYPE_INFO (data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decode_ETYPE_INFO2 (krb5_context context, + const void *data, + size_t length, + ETYPE_INFO2 *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return decode_ETYPE_INFO2(data, length, t, len); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encode_ETYPE_INFO2 (krb5_context context, + void *data, + size_t length, + ETYPE_INFO2 *t, + size_t *len) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return encode_ETYPE_INFO2 (data, length, t, len); +} + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/config_file.c b/third_party/heimdal/lib/krb5/config_file.c new file mode 100644 index 0000000..22eff10 --- /dev/null +++ b/third_party/heimdal/lib/krb5/config_file.c @@ -0,0 +1,758 @@ +/* + * 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 "krb5_locl.h" + +#if defined(HAVE_FRAMEWORK_COREFOUNDATION) +#include +#endif + +/** + * Parse configuration files in the given directory and add the result + * into res. Only files whose names consist only of alphanumeric + * characters, hyphen, and underscore, will be parsed, though files + * ending in ".conf" will also be parsed. + * + * This interface can be used to parse several configuration directories + * into one resulting krb5_config_section by calling it repeatably. + * + * @param context a Kerberos 5 context. + * @param dname a directory name to a Kerberos configuration file + * @param res the returned result, must be free with krb5_free_config_files(). + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_config_parse_dir_multi(krb5_context context, + const char *dname, + krb5_config_section **res) +{ + krb5_error_code ret; + heim_config_section *section = NULL; + + if (res == NULL) + return EINVAL; + + *res = NULL; + + ret = heim_config_parse_dir_multi(context->hcontext, dname, §ion); + if (ret == HEIM_ERR_CONFIG_BADFORMAT) + return KRB5_CONFIG_BADFORMAT; + if (ret) + return ret; + *res = (krb5_config_section *)section; + return 0; +} + +/** + * Parse a configuration file and add the result into res. This + * interface can be used to parse several configuration files into one + * resulting krb5_config_section by calling it repeatably. + * + * @param context a Kerberos 5 context. + * @param fname a file name to a Kerberos configuration file + * @param res the returned result, must be free with krb5_free_config_files(). + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_config_parse_file_multi(krb5_context context, + const char *fname, + krb5_config_section **res) +{ + krb5_error_code ret; + heim_config_section *section = NULL; + + if (res == NULL) + return EINVAL; + + *res = NULL; + + ret = heim_config_parse_file_multi(context->hcontext, fname, §ion); + if (ret == HEIM_ERR_CONFIG_BADFORMAT) + return KRB5_CONFIG_BADFORMAT; + if (ret) + return ret; + *res = (krb5_config_section *)section; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_config_parse_file(krb5_context context, + const char *fname, + krb5_config_section **res) +{ + return krb5_config_parse_file_multi(context, fname, res); +} + +/** + * Free configuration file section, the result of + * krb5_config_parse_file() and krb5_config_parse_file_multi(). + * + * @param context A Kerberos 5 context + * @param s the configuration section to free + * + * @return returns 0 on successes, otherwise an error code, see + * krb5_get_error_message() + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_config_file_free(krb5_context context, krb5_config_section *s) +{ + return heim_config_file_free(context->hcontext, (heim_config_section *)s); +} + +#ifndef HEIMDAL_SMALLER + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_config_copy(krb5_context context, + krb5_config_section *c, + krb5_config_section **res) +{ + krb5_error_code ret; + heim_config_section *section = NULL; + + if (res == NULL) + return EINVAL; + + *res = NULL; + ret = heim_config_copy(context->hcontext, (heim_config_section *)c, §ion); + if (ret) + return ret; + *res = (krb5_config_section *)section; + return 0; +} + +#endif /* HEIMDAL_SMALLER */ + +KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL +_krb5_config_get_next(krb5_context context, + const krb5_config_section *c, + const krb5_config_binding **pointer, + int type, + ...) +{ + const char *ret; + va_list args; + + va_start(args, type); + ret = heim_config_vget_next(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + (const heim_config_binding **)pointer, type, args); + va_end(args); + return ret; +} + +KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL +_krb5_config_vget_next(krb5_context context, + const krb5_config_section *c, + const krb5_config_binding **pointer, + int type, + va_list args) +{ + return heim_config_vget_next(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + (const heim_config_binding **)pointer, type, args); +} + +KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL +_krb5_config_get(krb5_context context, + const krb5_config_section *c, + int type, + ...) +{ + const void *ret; + va_list args; + + va_start(args, type); + ret = heim_config_vget(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + type, args); + va_end(args); + return ret; +} + + +KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL +_krb5_config_vget(krb5_context context, + const krb5_config_section *c, + int type, + va_list args) +{ + return heim_config_vget(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + type, args); +} + +/** + * Get a list of configuration binding list for more processing + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return NULL if configuration list is not found, a list otherwise + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL +krb5_config_get_list(krb5_context context, + const krb5_config_section *c, + ...) +{ + const heim_config_binding *ret; + va_list args; + + va_start(args, c); + ret = heim_config_vget_list(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + args); + va_end(args); + return (const krb5_config_binding *)ret; +} + +/** + * Get a list of configuration binding list for more processing + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return NULL if configuration list is not found, a list otherwise + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL +krb5_config_vget_list(krb5_context context, + const krb5_config_section *c, + va_list args) +{ + const heim_config_binding *ret; + + ret = heim_config_vget_list(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + args); + return (const krb5_config_binding *)ret; +} + +/** + * Returns a "const char *" to a string in the configuration database. + * The string may not be valid after a reload of the configuration + * database so a caller should make a local copy if it needs to keep + * the string. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return NULL if configuration string not found, a string otherwise + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_config_get_string(krb5_context context, + const krb5_config_section *c, + ...) +{ + const char *ret; + va_list args; + + va_start(args, c); + ret = heim_config_vget_string(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + args); + va_end(args); + return ret; +} + +/** + * Like krb5_config_get_string(), but uses a va_list instead of ... + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return NULL if configuration string not found, a string otherwise + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_config_vget_string(krb5_context context, + const krb5_config_section *c, + va_list args) +{ + return heim_config_vget_string(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + args); +} + +/** + * Like krb5_config_vget_string(), but instead of returning NULL, + * instead return a default value. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param args a va_list of arguments + * + * @return a configuration string + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_config_vget_string_default(krb5_context context, + const krb5_config_section *c, + const char *def_value, + va_list args) +{ + return heim_config_vget_string_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, args); +} + +/** + * Like krb5_config_get_string(), but instead of returning NULL, + * instead return a default value. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param ... a list of names, terminated with NULL. + * + * @return a configuration string + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_config_get_string_default(krb5_context context, + const krb5_config_section *c, + const char *def_value, + ...) +{ + const char *ret; + va_list args; + + va_start(args, def_value); + ret = heim_config_vget_string_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, args); + va_end(args); + return ret; +} + +/** + * Get a list of configuration strings, free the result with + * krb5_config_free_strings(). + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return TRUE or FALSE + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL +krb5_config_vget_strings(krb5_context context, + const krb5_config_section *c, + va_list args) +{ + return heim_config_vget_strings(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + args); +} + +/** + * Get a list of configuration strings, free the result with + * krb5_config_free_strings(). + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return TRUE or FALSE + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION char** KRB5_LIB_CALL +krb5_config_get_strings(krb5_context context, + const krb5_config_section *c, + ...) +{ + va_list ap; + char **ret; + va_start(ap, c); + ret = heim_config_vget_strings(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + ap); + va_end(ap); + return ret; +} + +/** + * Free the resulting strings from krb5_config-get_strings() and + * krb5_config_vget_strings(). + * + * @param strings strings to free + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_config_free_strings(char **strings) +{ + heim_config_free_strings(strings); +} + +/** + * Like krb5_config_get_bool_default() but with a va_list list of + * configuration selection. + * + * Configuration value to a boolean value, where yes/true and any + * non-zero number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param args a va_list of arguments + * + * @return TRUE or FALSE + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_config_vget_bool_default(krb5_context context, + const krb5_config_section *c, + krb5_boolean def_value, + va_list args) +{ + return heim_config_vget_bool_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, args); +} + +/** + * krb5_config_get_bool() will convert the configuration + * option value to a boolean value, where yes/true and any non-zero + * number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return TRUE or FALSE + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_config_vget_bool(krb5_context context, + const krb5_config_section *c, + va_list args) +{ + return heim_config_vget_bool_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + FALSE, args); +} + +/** + * krb5_config_get_bool_default() will convert the configuration + * option value to a boolean value, where yes/true and any non-zero + * number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param ... a list of names, terminated with NULL. + * + * @return TRUE or FALSE + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_config_get_bool_default(krb5_context context, + const krb5_config_section *c, + krb5_boolean def_value, + ...) +{ + va_list ap; + krb5_boolean ret; + va_start(ap, def_value); + ret = heim_config_vget_bool_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, ap); + va_end(ap); + return ret; +} + +/** + * Like krb5_config_get_bool() but with a va_list list of + * configuration selection. + * + * Configuration value to a boolean value, where yes/true and any + * non-zero number means TRUE and other value is FALSE. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return TRUE or FALSE + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_config_get_bool (krb5_context context, + const krb5_config_section *c, + ...) +{ + va_list ap; + krb5_boolean ret; + va_start(ap, c); + ret = krb5_config_vget_bool (context, c, ap); + va_end(ap); + return ret; +} + +/** + * Get the time from the configuration file using a relative time. + * + * Like krb5_config_get_time_default() but with a va_list list of + * configuration selection. + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param args a va_list of arguments + * + * @return parsed the time (or def_value on parse error) + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_vget_time_default(krb5_context context, + const krb5_config_section *c, + int def_value, + va_list args) +{ + return heim_config_vget_time_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, args); +} + +/** + * Get the time from the configuration file using a relative time, for example: 1h30s + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param args a va_list of arguments + * + * @return parsed the time or -1 on error + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_vget_time(krb5_context context, + const krb5_config_section *c, + va_list args) +{ + return heim_config_vget_time_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + -1, args); +} + +/** + * Get the time from the configuration file using a relative time, for example: 1h30s + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param def_value the default value to return if no configuration + * found in the database. + * @param ... a list of names, terminated with NULL. + * + * @return parsed the time (or def_value on parse error) + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_get_time_default(krb5_context context, + const krb5_config_section *c, + int def_value, + ...) +{ + va_list ap; + int ret; + va_start(ap, def_value); + ret = heim_config_vget_time_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, ap); + va_end(ap); + return ret; +} + +/** + * Get the time from the configuration file using a relative time, for example: 1h30s + * + * @param context A Kerberos 5 context. + * @param c a configuration section, or NULL to use the section from context + * @param ... a list of names, terminated with NULL. + * + * @return parsed the time or -1 on error + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_get_time(krb5_context context, + const krb5_config_section *c, + ...) +{ + va_list ap; + int ret; + va_start(ap, c); + ret = heim_config_vget_time(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + ap); + va_end(ap); + return ret; +} + + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_vget_int_default(krb5_context context, + const krb5_config_section *c, + int def_value, + va_list args) +{ + return heim_config_vget_int_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, args); +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_vget_int(krb5_context context, + const krb5_config_section *c, + va_list args) +{ + return heim_config_vget_int_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + -1, args); +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_get_int_default(krb5_context context, + const krb5_config_section *c, + int def_value, + ...) +{ + va_list ap; + int ret; + va_start(ap, def_value); + ret = heim_config_vget_int_default(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + def_value, ap); + va_end(ap); + return ret; +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_config_get_int(krb5_context context, + const krb5_config_section *c, + ...) +{ + va_list ap; + int ret; + va_start(ap, c); + ret = heim_config_vget_int(context->hcontext, + (const heim_config_section *)(c ? c : context->cf), + ap); + va_end(ap); + return ret; +} + + +#ifndef HEIMDAL_SMALLER +/** + * Deprecated: configuration files are not strings + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_config_parse_string_multi(krb5_context context, + const char *string, + krb5_config_section **res) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_error_code ret; + heim_config_section *section = NULL; + + if (res == NULL) + return EINVAL; + + *res = NULL; + ret = heim_config_parse_string_multi(context->hcontext, string, §ion); + if (ret == HEIM_ERR_CONFIG_BADFORMAT) + return KRB5_CONFIG_BADFORMAT; + if (ret) + return ret; + *res = (krb5_config_section *)section; + return 0; +} +#endif diff --git a/third_party/heimdal/lib/krb5/constants.c b/third_party/heimdal/lib/krb5/constants.c new file mode 100644 index 0000000..43b8f54 --- /dev/null +++ b/third_party/heimdal/lib/krb5/constants.c @@ -0,0 +1,67 @@ +/* + * 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 "krb5_locl.h" + +KRB5_LIB_VARIABLE const char *const krb5_config_file = +#ifdef KRB5_DEFAULT_CONFIG_FILE +KRB5_DEFAULT_CONFIG_FILE +#else +#ifdef __APPLE__ +"~/Library/Preferences/com.apple.Kerberos.plist" PATH_SEP +"/Library/Preferences/com.apple.Kerberos.plist" PATH_SEP +"~/Library/Preferences/edu.mit.Kerberos" PATH_SEP +"/Library/Preferences/edu.mit.Kerberos" PATH_SEP +#endif /* __APPLE__ */ +"~/.krb5/config" PATH_SEP +SYSCONFDIR "/krb5.conf" PATH_SEP +#ifdef _WIN32 +"%{COMMON_APPDATA}/Kerberos/krb5.conf" PATH_SEP +"%{WINDOWS}/krb5.ini" +#else /* _WIN32 */ +"/etc/krb5.conf" +#endif /* _WIN32 */ +#endif /* KRB5_DEFAULT_CONFIG_FILE */ +; + +KRB5_LIB_VARIABLE const char *const krb5_defkeyname = KEYTAB_DEFAULT; + +KRB5_LIB_VARIABLE const char *const krb5_cc_type_api = "API"; +KRB5_LIB_VARIABLE const char *const krb5_cc_type_file = "FILE"; +KRB5_LIB_VARIABLE const char *const krb5_cc_type_memory = "MEMORY"; +KRB5_LIB_VARIABLE const char *const krb5_cc_type_kcm = "KCM"; +KRB5_LIB_VARIABLE const char *const krb5_cc_type_scc = "SCC"; +KRB5_LIB_VARIABLE const char *const krb5_cc_type_dcc = "DIR"; +KRB5_LIB_VARIABLE const char *const krb5_cc_type_keyring = "KEYRING"; diff --git a/third_party/heimdal/lib/krb5/context.c b/third_party/heimdal/lib/krb5/context.c new file mode 100644 index 0000000..9d03a80 --- /dev/null +++ b/third_party/heimdal/lib/krb5/context.c @@ -0,0 +1,1506 @@ +/* + * Copyright (c) 1997 - 2010 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. + */ + +#undef KRB5_DEPRECATED_FUNCTION +#define KRB5_DEPRECATED_FUNCTION(x) + +#include "krb5_locl.h" +#include +#include + +static void _krb5_init_ets(krb5_context); + +#define INIT_FIELD(C, T, E, D, F) \ + (C)->E = krb5_config_get_ ## T ## _default ((C), NULL, (D), \ + "libdefaults", F, NULL) + +#define INIT_FLAG(C, O, V, D, F) \ + do { \ + if (krb5_config_get_bool_default((C), NULL, (D),"libdefaults", F, NULL)) { \ + (C)->O |= V; \ + } \ + } while(0) + +static krb5_error_code +copy_enctypes(krb5_context context, + const krb5_enctype *in, + krb5_enctype **out); + +/* + * Set the list of etypes `ret_etypes' from the configuration variable + * `name' + */ + +static krb5_error_code +set_etypes (krb5_context context, + const char *name, + krb5_enctype **ret_enctypes) +{ + char **etypes_str; + krb5_enctype *etypes = NULL; + + etypes_str = krb5_config_get_strings(context, NULL, "libdefaults", + name, NULL); + if(etypes_str){ + int i, j, k; + for(i = 0; etypes_str[i]; i++); + etypes = malloc((i+1) * sizeof(*etypes)); + if (etypes == NULL) { + krb5_config_free_strings (etypes_str); + return krb5_enomem(context); + } + for(j = 0, k = 0; j < i; j++) { + krb5_enctype e; + if(krb5_string_to_enctype(context, etypes_str[j], &e) != 0) + continue; + if (krb5_enctype_valid(context, e) != 0) + continue; + etypes[k++] = e; + } + etypes[k] = ETYPE_NULL; + krb5_config_free_strings(etypes_str); + } + *ret_enctypes = etypes; + return 0; +} + +/* + * read variables from the configuration file and set in `context' + */ + +static krb5_error_code +init_context_from_config_file(krb5_context context) +{ + krb5_error_code ret; + const char * tmp; + char **s; + krb5_enctype *tmptypes = NULL; + + INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew"); + INIT_FIELD(context, time, kdc_timeout, 30, "kdc_timeout"); + INIT_FIELD(context, time, host_timeout, 3, "host_timeout"); + INIT_FIELD(context, int, max_retries, 3, "max_retries"); + + INIT_FIELD(context, string, http_proxy, NULL, "http_proxy"); + + ret = krb5_config_get_bool_default(context, NULL, FALSE, + "libdefaults", + "allow_weak_crypto", NULL); + if (ret) { + krb5_enctype_enable(context, ETYPE_DES_CBC_CRC); + krb5_enctype_enable(context, ETYPE_DES_CBC_MD4); + krb5_enctype_enable(context, ETYPE_DES_CBC_MD5); + krb5_enctype_enable(context, ETYPE_DES_CBC_NONE); + krb5_enctype_enable(context, ETYPE_DES_CFB64_NONE); + krb5_enctype_enable(context, ETYPE_DES_PCBC_NONE); + } + + ret = set_etypes (context, "default_etypes", &tmptypes); + if(ret) + return ret; + free(context->etypes); + context->etypes = tmptypes; + + /* The etypes member may change during the lifetime + * of the context. To be able to reset it to + * config value, we keep another copy. + */ + free(context->cfg_etypes); + context->cfg_etypes = NULL; + if (tmptypes) { + ret = copy_enctypes(context, tmptypes, &context->cfg_etypes); + if (ret) + return ret; + } + + ret = set_etypes (context, "default_etypes_des", &tmptypes); + if(ret) + return ret; + free(context->etypes_des); + context->etypes_des = tmptypes; + + ret = set_etypes (context, "default_as_etypes", &tmptypes); + if(ret) + return ret; + free(context->as_etypes); + context->as_etypes = tmptypes; + + ret = set_etypes (context, "default_tgs_etypes", &tmptypes); + if(ret) + return ret; + free(context->tgs_etypes); + context->tgs_etypes = tmptypes; + + ret = set_etypes (context, "permitted_enctypes", &tmptypes); + if(ret) + return ret; + free(context->permitted_enctypes); + context->permitted_enctypes = tmptypes; + + INIT_FIELD(context, string, default_keytab, + KEYTAB_DEFAULT, "default_keytab_name"); + + INIT_FIELD(context, string, default_keytab_modify, + NULL, "default_keytab_modify_name"); + + INIT_FIELD(context, string, time_fmt, + "%Y-%m-%dT%H:%M:%S", "time_format"); + + INIT_FIELD(context, string, date_fmt, + "%Y-%m-%d", "date_format"); + + INIT_FIELD(context, bool, log_utc, + FALSE, "log_utc"); + + context->no_ticket_store = + getenv("KRB5_NO_TICKET_STORE") != NULL; + + /* init dns-proxy slime */ + tmp = krb5_config_get_string(context, NULL, "libdefaults", + "dns_proxy", NULL); + if(tmp) + roken_gethostby_setup(context->http_proxy, tmp); + krb5_free_host_realm (context, context->default_realms); + context->default_realms = NULL; + + { + krb5_addresses addresses; + char **adr, **a; + + krb5_set_extra_addresses(context, NULL); + adr = krb5_config_get_strings(context, NULL, + "libdefaults", + "extra_addresses", + NULL); + memset(&addresses, 0, sizeof(addresses)); + for(a = adr; a && *a; a++) { + ret = krb5_parse_address(context, *a, &addresses); + if (ret == 0) { + krb5_add_extra_addresses(context, &addresses); + krb5_free_addresses(context, &addresses); + } + } + krb5_config_free_strings(adr); + + krb5_set_ignore_addresses(context, NULL); + adr = krb5_config_get_strings(context, NULL, + "libdefaults", + "ignore_addresses", + NULL); + memset(&addresses, 0, sizeof(addresses)); + for(a = adr; a && *a; a++) { + ret = krb5_parse_address(context, *a, &addresses); + if (ret == 0) { + krb5_add_ignore_addresses(context, &addresses); + krb5_free_addresses(context, &addresses); + } + } + krb5_config_free_strings(adr); + } + + INIT_FIELD(context, bool, scan_interfaces, TRUE, "scan_interfaces"); + INIT_FIELD(context, int, fcache_vno, 0, "fcache_version"); + /* prefer dns_lookup_kdc over srv_lookup. */ + INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup"); + INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc"); + INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size"); + INIT_FIELD(context, int, max_msg_size, 1000 * 1024, "maximum_message_size"); + INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname"); + INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac"); + INIT_FLAG(context, flags, KRB5_CTX_F_ENFORCE_OK_AS_DELEGATE, FALSE, "enforce_ok_as_delegate"); + INIT_FLAG(context, flags, KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME, FALSE, "report_canonical_client_name"); + + /* report_canonical_client_name implies check_pac */ + if (context->flags & KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME) + context->flags |= KRB5_CTX_F_CHECK_PAC; + + free(context->default_cc_name); + context->default_cc_name = NULL; + context->default_cc_name_set = 0; + free(context->configured_default_cc_name); + context->configured_default_cc_name = NULL; + + tmp = secure_getenv("KRB5_TRACE"); + if (tmp) + heim_add_debug_dest(context->hcontext, "libkrb5", tmp); + s = krb5_config_get_strings(context, NULL, "logging", "krb5", NULL); + if (s) { + char **p; + + for (p = s; *p; p++) + heim_add_debug_dest(context->hcontext, "libkrb5", *p); + krb5_config_free_strings(s); + } + + tmp = krb5_config_get_string(context, NULL, "libdefaults", + "check-rd-req-server", NULL); + if (tmp == NULL) + tmp = secure_getenv("KRB5_CHECK_RD_REQ_SERVER"); + if(tmp) { + if (strcasecmp(tmp, "ignore") == 0) + context->flags |= KRB5_CTX_F_RD_REQ_IGNORE; + } + ret = krb5_config_get_bool_default(context, NULL, TRUE, + "libdefaults", + "fcache_strict_checking", NULL); + if (ret) + context->flags |= KRB5_CTX_F_FCACHE_STRICT_CHECKING; + + return 0; +} + +static krb5_error_code +cc_ops_register(krb5_context context) +{ + krb5_error_code ret; + + context->cc_ops = NULL; + context->num_cc_ops = 0; + +#ifndef KCM_IS_API_CACHE + ret = krb5_cc_register(context, &krb5_acc_ops, TRUE); + if (ret) + return ret; +#endif + ret = krb5_cc_register(context, &krb5_fcc_ops, TRUE); + if (ret) + return ret; + ret = krb5_cc_register(context, &krb5_dcc_ops, TRUE); + if (ret) + return ret; + ret = krb5_cc_register(context, &krb5_mcc_ops, TRUE); + if (ret) + return ret; +#ifdef HAVE_SCC + ret = krb5_cc_register(context, &krb5_scc_ops, TRUE); + if (ret) + return ret; +#endif +#ifdef HAVE_KCM +#ifdef KCM_IS_API_CACHE + ret = krb5_cc_register(context, &krb5_akcm_ops, TRUE); + if (ret) + return ret; +#endif + ret = krb5_cc_register(context, &krb5_kcm_ops, TRUE); + if (ret) + return ret; +#endif +#if defined(HAVE_KEYUTILS_H) + ret = krb5_cc_register(context, &krb5_krcc_ops, TRUE); + if (ret) + return ret; +#endif + ret = _krb5_load_ccache_plugins(context); + return ret; +} + +static krb5_error_code +cc_ops_copy(krb5_context context, const krb5_context src_context) +{ + const krb5_cc_ops **cc_ops; + + context->cc_ops = NULL; + context->num_cc_ops = 0; + + if (src_context->num_cc_ops == 0) + return 0; + + cc_ops = malloc(sizeof(cc_ops[0]) * src_context->num_cc_ops); + if (cc_ops == NULL) { + krb5_set_error_message(context, KRB5_CC_NOMEM, + N_("malloc: out of memory", "")); + return KRB5_CC_NOMEM; + } + + memcpy(rk_UNCONST(cc_ops), src_context->cc_ops, + sizeof(cc_ops[0]) * src_context->num_cc_ops); + context->cc_ops = cc_ops; + context->num_cc_ops = src_context->num_cc_ops; + + return 0; +} + +static krb5_error_code +kt_ops_register(krb5_context context) +{ + krb5_error_code ret; + + context->num_kt_types = 0; + context->kt_types = NULL; + + ret = krb5_kt_register (context, &krb5_fkt_ops); + if (ret) + return ret; + ret = krb5_kt_register (context, &krb5_wrfkt_ops); + if (ret) + return ret; + ret = krb5_kt_register (context, &krb5_javakt_ops); + if (ret) + return ret; + ret = krb5_kt_register (context, &krb5_mkt_ops); + if (ret) + return ret; +#ifndef HEIMDAL_SMALLER + ret = krb5_kt_register (context, &krb5_akf_ops); + if (ret) + return ret; +#endif + ret = krb5_kt_register (context, &krb5_any_ops); + return ret; +} + +static krb5_error_code +kt_ops_copy(krb5_context context, const krb5_context src_context) +{ + context->num_kt_types = 0; + context->kt_types = NULL; + + if (src_context->num_kt_types == 0) + return 0; + + context->kt_types = malloc(sizeof(context->kt_types[0]) * src_context->num_kt_types); + if (context->kt_types == NULL) + return krb5_enomem(context); + + context->num_kt_types = src_context->num_kt_types; + memcpy(context->kt_types, src_context->kt_types, + sizeof(context->kt_types[0]) * src_context->num_kt_types); + + return 0; +} + +static const char *const sysplugin_dirs[] = { +#ifdef _WIN32 + "$ORIGIN", +#else + "$ORIGIN/../lib/plugin/krb5", +#endif +#ifdef __APPLE__ + LIBDIR "/plugin/krb5", +#ifdef HEIM_PLUGINS_SEARCH_SYSTEM + "/Library/KerberosPlugins/KerberosFrameworkPlugins", + "/System/Library/KerberosPlugins/KerberosFrameworkPlugins", +#endif +#endif + NULL +}; + +static void +init_context_once(void *ctx) +{ + krb5_context context = ctx; + char **dirs; + +#ifdef _WIN32 + dirs = rk_UNCONST(sysplugin_dirs); +#else + dirs = krb5_config_get_strings(context, NULL, "libdefaults", + "plugin_dir", NULL); + if (dirs == NULL) + dirs = rk_UNCONST(sysplugin_dirs); +#endif + + _krb5_load_plugins(context, "krb5", (const char **)dirs); + + if (dirs != rk_UNCONST(sysplugin_dirs)) + krb5_config_free_strings(dirs); + + bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR); +} + +/** + * Initializes the context structure and reads the configuration file + * /etc/krb5.conf. The structure should be freed by calling + * krb5_free_context() when it is no longer being used. + * + * @param context pointer to returned context + * + * @return Returns 0 to indicate success. Otherwise an errno code is + * returned. Failure means either that something bad happened during + * initialization (typically ENOMEM) or that Kerberos should not be + * used ENXIO. If the function returns HEIM_ERR_RANDOM_OFFLINE, the + * random source is not available and later Kerberos calls might fail. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_context(krb5_context *context) +{ + static heim_base_once_t init_context = HEIM_BASE_ONCE_INIT; + krb5_context p; + krb5_error_code ret; + char **files; + uint8_t rnd; + + *context = NULL; + + /** + * krb5_init_context() will get one random byte to make sure our + * random is alive. Assumption is that once the non blocking + * source allows us to pull bytes, its all seeded and allows us to + * pull more bytes. + * + * Most Kerberos users calls krb5_init_context(), so this is + * useful point where we can do the checking. + */ + ret = krb5_generate_random(&rnd, sizeof(rnd)); + if (ret) + return ret; + + p = calloc(1, sizeof(*p)); + if(!p) + return ENOMEM; + + if ((p->hcontext = heim_context_init()) == NULL) { + ret = ENOMEM; + goto out; + } + + if (!issuid()) + p->flags |= KRB5_CTX_F_HOMEDIR_ACCESS; + + ret = krb5_get_default_config_files(&files); + if(ret) + goto out; + ret = krb5_set_config_files(p, files); + krb5_free_config_files(files); + if(ret) + goto out; + + /* done enough to load plugins */ + heim_base_once_f(&init_context, p, init_context_once); + + /* init error tables */ + _krb5_init_ets(p); + ret = cc_ops_register(p); + if (ret) + goto out; + ret = kt_ops_register(p); + if (ret) + goto out; + +#ifdef PKINIT + ret = hx509_context_init(&p->hx509ctx); + if (ret) + goto out; +#endif + if (rk_SOCK_INIT()) + p->flags |= KRB5_CTX_F_SOCKETS_INITIALIZED; + +out: + if (ret) { + krb5_free_context(p); + p = NULL; + } else { + heim_context_set_log_utc(p->hcontext, p->log_utc); + } + *context = p; + return ret; +} + +#ifndef HEIMDAL_SMALLER + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_permitted_enctypes(krb5_context context, + krb5_enctype **etypes) +{ + return krb5_get_default_in_tkt_etypes(context, KRB5_PDU_NONE, etypes); +} + +/* + * + */ + +static krb5_error_code +copy_etypes (krb5_context context, + krb5_enctype *enctypes, + krb5_enctype **ret_enctypes) +{ + unsigned int i; + + for (i = 0; enctypes[i]; i++) + ; + i++; + + *ret_enctypes = malloc(sizeof(enctypes[0]) * i); + if (*ret_enctypes == NULL) + return krb5_enomem(context); + memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * i); + return 0; +} + +/** + * Make a copy for the Kerberos 5 context, the new krb5_context shoud + * be freed with krb5_free_context(). + * + * @param context the Kerberos context to copy + * @param out the copy of the Kerberos, set to NULL error. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_context(krb5_context context, krb5_context *out) +{ + krb5_error_code ret = 0; + krb5_context p; + + *out = NULL; + + p = calloc(1, sizeof(*p)); + if (p == NULL) + return krb5_enomem(context); + + p->cc_ops = NULL; + p->etypes = NULL; + p->kt_types = NULL; + p->cfg_etypes = NULL; + p->etypes_des = NULL; + p->default_realms = NULL; + p->extra_addresses = NULL; + p->ignore_addresses = NULL; + + if ((p->hcontext = heim_context_init()) == NULL) + ret = ENOMEM; + + if (ret == 0) { + heim_context_set_log_utc(p->hcontext, context->log_utc); + ret = _krb5_config_copy(context, context->cf, &p->cf); + } + if (ret == 0) + ret = init_context_from_config_file(p); + if (ret == 0 && context->default_cc_name) { + free(p->default_cc_name); + if ((p->default_cc_name = strdup(context->default_cc_name)) == NULL) + ret = ENOMEM; + } + if (ret == 0 && context->default_cc_name_env) { + free(p->default_cc_name_env); + if ((p->default_cc_name_env = + strdup(context->default_cc_name_env)) == NULL) + ret = ENOMEM; + } + if (ret == 0 && context->configured_default_cc_name) { + free(p->configured_default_cc_name); + if ((p->configured_default_cc_name = + strdup(context->configured_default_cc_name)) == NULL) + ret = ENOMEM; + } + + if (ret == 0 && context->etypes) { + free(p->etypes); + ret = copy_etypes(context, context->etypes, &p->etypes); + } + if (ret == 0 && context->cfg_etypes) { + free(p->cfg_etypes); + ret = copy_etypes(context, context->cfg_etypes, &p->cfg_etypes); + } + if (ret == 0 && context->etypes_des) { + free(p->etypes_des); + ret = copy_etypes(context, context->etypes_des, &p->etypes_des); + } + + if (ret == 0 && context->default_realms) { + krb5_free_host_realm(context, p->default_realms); + ret = krb5_copy_host_realm(context, + context->default_realms, &p->default_realms); + } + + /* XXX should copy */ + if (ret == 0) + _krb5_init_ets(p); + + if (ret == 0) + ret = cc_ops_copy(p, context); + if (ret == 0) + ret = kt_ops_copy(p, context); + if (ret == 0) + ret = krb5_set_extra_addresses(p, context->extra_addresses); + if (ret == 0) + ret = krb5_set_extra_addresses(p, context->ignore_addresses); + if (ret == 0) + ret = _krb5_copy_send_to_kdc_func(p, context); + + if (ret == 0) + *out = p; + else + krb5_free_context(p); + return ret; +} + +#endif + +/** + * Frees the krb5_context allocated by krb5_init_context(). + * + * @param context context to be freed. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_context(krb5_context context) +{ + _krb5_free_name_canon_rules(context, context->name_canon_rules); + free(context->default_cc_name); + free(context->default_cc_name_env); + free(context->configured_default_cc_name); + free(context->etypes); + free(context->cfg_etypes); + free(context->etypes_des); + free(context->permitted_enctypes); + free(context->tgs_etypes); + free(context->as_etypes); + krb5_free_host_realm (context, context->default_realms); + krb5_config_file_free (context, context->cf); + free(rk_UNCONST(context->cc_ops)); + free(context->kt_types); + krb5_clear_error_message(context); + krb5_set_extra_addresses(context, NULL); + krb5_set_ignore_addresses(context, NULL); + krb5_set_send_to_kdc_func(context, NULL, NULL); + +#ifdef PKINIT + hx509_context_free(&context->hx509ctx); +#endif + + if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) { + rk_SOCK_EXIT(); + } + + heim_context_free(&context->hcontext); + memset(context, 0, sizeof(*context)); + free(context); +} + +/** + * Reinit the context from a new set of filenames. + * + * @param context context to add configuration too. + * @param filenames array of filenames, end of list is indicated with a NULL filename. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_config_files(krb5_context context, char **filenames) +{ + krb5_error_code ret; + heim_config_binding *tmp = NULL; + + if ((ret = heim_set_config_files(context->hcontext, filenames, + &tmp))) + return ret; + krb5_config_file_free(context, context->cf); + context->cf = (krb5_config_binding *)tmp; + return init_context_from_config_file(context); +} + +#ifndef HEIMDAL_SMALLER +/** + * Reinit the context from configuration file contents in a C string. + * This should only be used in tests. + * + * @param context context to add configuration too. + * @param config configuration. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_config(krb5_context context, const char *config) +{ + krb5_error_code ret; + krb5_config_binding *tmp = NULL; + + if ((ret = krb5_config_parse_string_multi(context, config, &tmp))) + return ret; +#if 0 + /* with this enabled and if there are no config files, Kerberos is + considererd disabled */ + if (tmp == NULL) + return ENXIO; +#endif + + krb5_config_file_free(context, context->cf); + context->cf = tmp; + ret = init_context_from_config_file(context); + return ret; +} +#endif + +/* + * `pq' isn't free, it's up the the caller + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_prepend_config_files(const char *filelist, char **pq, char ***ret_pp) +{ + return heim_prepend_config_files(filelist, pq, ret_pp); +} + +/** + * Prepend the filename to the global configuration list. + * + * @param filelist a filename to add to the default list of filename + * @param pfilenames return array of filenames, should be freed with krb5_free_config_files(). + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_prepend_config_files_default(const char *filelist, char ***pfilenames) +{ + return heim_prepend_config_files_default(filelist, krb5_config_file, + "KRB5_CONFIG", pfilenames); +} + +/** + * Get the global configuration list. + * + * @param pfilenames return array of filenames, should be freed with krb5_free_config_files(). + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_default_config_files(char ***pfilenames) +{ + if (pfilenames == NULL) + return EINVAL; + return heim_get_default_config_files(krb5_config_file, "KRB5_CONFIG", + pfilenames); +} + +/** + * Free a list of configuration files. + * + * @param filenames list, terminated with a NULL pointer, to be + * freed. NULL is an valid argument. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_config_files(char **filenames) +{ + heim_free_config_files(filenames); +} + +/** + * Returns the list of Kerberos encryption types sorted in order of + * most preferred to least preferred encryption type. Note that some + * encryption types might be disabled, so you need to check with + * krb5_enctype_valid() before using the encryption type. + * + * @return list of enctypes, terminated with ETYPE_NULL. Its a static + * array completed into the Kerberos library so the content doesn't + * need to be freed. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION const krb5_enctype * KRB5_LIB_CALL +krb5_kerberos_enctypes(krb5_context context) +{ + static const krb5_enctype p[] = { + ETYPE_AES256_CTS_HMAC_SHA1_96, + ETYPE_AES128_CTS_HMAC_SHA1_96, + ETYPE_AES256_CTS_HMAC_SHA384_192, + ETYPE_AES128_CTS_HMAC_SHA256_128, + ETYPE_DES3_CBC_SHA1, + ETYPE_ARCFOUR_HMAC_MD5, + ETYPE_NULL + }; + + static const krb5_enctype weak[] = { + ETYPE_AES256_CTS_HMAC_SHA1_96, + ETYPE_AES128_CTS_HMAC_SHA1_96, + ETYPE_AES256_CTS_HMAC_SHA384_192, + ETYPE_AES128_CTS_HMAC_SHA256_128, + ETYPE_DES3_CBC_SHA1, + ETYPE_DES3_CBC_MD5, + ETYPE_ARCFOUR_HMAC_MD5, + ETYPE_DES_CBC_MD5, + ETYPE_DES_CBC_MD4, + ETYPE_DES_CBC_CRC, + ETYPE_NULL + }; + + /* + * if the list of enctypes enabled by "allow_weak_crypto" + * are valid, then return the former default enctype list + * that contained the weak entries. + */ + if (krb5_enctype_valid(context, ETYPE_DES_CBC_CRC) == 0 && + krb5_enctype_valid(context, ETYPE_DES_CBC_MD4) == 0 && + krb5_enctype_valid(context, ETYPE_DES_CBC_MD5) == 0 && + krb5_enctype_valid(context, ETYPE_DES_CBC_NONE) == 0 && + krb5_enctype_valid(context, ETYPE_DES_CFB64_NONE) == 0 && + krb5_enctype_valid(context, ETYPE_DES_PCBC_NONE) == 0) + return weak; + + return p; +} + +/* + * + */ + +static krb5_error_code +copy_enctypes(krb5_context context, + const krb5_enctype *in, + krb5_enctype **out) +{ + krb5_enctype *p = NULL; + size_t m, n; + + for (n = 0; in[n]; n++) + ; + n++; + ALLOC(p, n); + if(p == NULL) + return krb5_enomem(context); + for (n = 0, m = 0; in[n]; n++) { + if (krb5_enctype_valid(context, in[n]) != 0) + continue; + p[m++] = in[n]; + } + p[m] = KRB5_ENCTYPE_NULL; + if (m == 0) { + free(p); + krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, + N_("no valid enctype set", "")); + return KRB5_PROG_ETYPE_NOSUPP; + } + *out = p; + return 0; +} + + +/* + * set `etype' to a malloced list of the default enctypes + */ + +static krb5_error_code +default_etypes(krb5_context context, krb5_enctype **etype) +{ + const krb5_enctype *p = krb5_kerberos_enctypes(context); + return copy_enctypes(context, p, etype); +} + +/** + * Set the default encryption types that will be use in communcation + * with the KDC, clients and servers. + * + * @param context Kerberos 5 context. + * @param etypes Encryption types, array terminated with ETYPE_NULL (0). + * A value of NULL resets the encryption types to the defaults set in the + * configuration file. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_default_in_tkt_etypes(krb5_context context, + const krb5_enctype *etypes) +{ + krb5_error_code ret; + krb5_enctype *p = NULL; + + if(!etypes) { + etypes = context->cfg_etypes; + } + + if(etypes) { + ret = copy_enctypes(context, etypes, &p); + if (ret) + return ret; + } + if(context->etypes) + free(context->etypes); + context->etypes = p; + return 0; +} + +/** + * Get the default encryption types that will be use in communcation + * with the KDC, clients and servers. + * + * @param context Kerberos 5 context. + * @param pdu_type request type (AS, TGS or none) + * @param etypes Encryption types, array terminated with + * ETYPE_NULL(0), caller should free array with krb5_xfree(): + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_default_in_tkt_etypes(krb5_context context, + krb5_pdu pdu_type, + krb5_enctype **etypes) +{ + krb5_enctype *enctypes = NULL; + krb5_error_code ret; + krb5_enctype *p; + + heim_assert(pdu_type == KRB5_PDU_AS_REQUEST || + pdu_type == KRB5_PDU_TGS_REQUEST || + pdu_type == KRB5_PDU_NONE, "unexpected pdu type"); + + if (pdu_type == KRB5_PDU_AS_REQUEST && context->as_etypes != NULL) + enctypes = context->as_etypes; + else if (pdu_type == KRB5_PDU_TGS_REQUEST && context->tgs_etypes != NULL) + enctypes = context->tgs_etypes; + else if (context->etypes != NULL) + enctypes = context->etypes; + + if (enctypes != NULL) { + ret = copy_enctypes(context, enctypes, &p); + if (ret) + return ret; + } else { + ret = default_etypes(context, &p); + if (ret) + return ret; + } + *etypes = p; + return 0; +} + +/** + * Init the built-in ets in the Kerberos library. + * + * @param context kerberos context to add the ets too + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_init_ets(krb5_context context) +{ +} + +static void +_krb5_init_ets(krb5_context context) +{ + heim_add_et_list(context->hcontext, initialize_krb5_error_table_r); + heim_add_et_list(context->hcontext, initialize_asn1_error_table_r); + heim_add_et_list(context->hcontext, initialize_heim_error_table_r); + + heim_add_et_list(context->hcontext, initialize_k524_error_table_r); + heim_add_et_list(context->hcontext, initialize_k5e1_error_table_r); + +#ifdef COM_ERR_BINDDOMAIN_krb5 + bindtextdomain(COM_ERR_BINDDOMAIN_krb5, HEIMDAL_LOCALEDIR); + bindtextdomain(COM_ERR_BINDDOMAIN_asn1, HEIMDAL_LOCALEDIR); + bindtextdomain(COM_ERR_BINDDOMAIN_heim, HEIMDAL_LOCALEDIR); + bindtextdomain(COM_ERR_BINDDOMAIN_k524, HEIMDAL_LOCALEDIR); +#endif + +#ifdef PKINIT + heim_add_et_list(context->hcontext, initialize_hx_error_table_r); +#ifdef COM_ERR_BINDDOMAIN_hx + bindtextdomain(COM_ERR_BINDDOMAIN_hx, HEIMDAL_LOCALEDIR); +#endif +#endif +} + +/** + * Make the kerberos library default to the admin KDC. + * + * @param context Kerberos 5 context. + * @param flag boolean flag to select if the use the admin KDC or not. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_set_use_admin_kdc (krb5_context context, krb5_boolean flag) +{ + context->use_admin_kdc = flag; +} + +/** + * Make the kerberos library default to the admin KDC. + * + * @param context Kerberos 5 context. + * + * @return boolean flag to telling the context will use admin KDC as the default KDC. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_get_use_admin_kdc (krb5_context context) +{ + return context->use_admin_kdc; +} + +/** + * Add extra address to the address list that the library will add to + * the client's address list when communicating with the KDC. + * + * @param context Kerberos 5 context. + * @param addresses addreses to add + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_add_extra_addresses(krb5_context context, krb5_addresses *addresses) +{ + + if(context->extra_addresses) + return krb5_append_addresses(context, + context->extra_addresses, addresses); + else + return krb5_set_extra_addresses(context, addresses); +} + +/** + * Set extra address to the address list that the library will add to + * the client's address list when communicating with the KDC. + * + * @param context Kerberos 5 context. + * @param addresses addreses to set + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses) +{ + if(context->extra_addresses) + krb5_free_addresses(context, context->extra_addresses); + + if(addresses == NULL) { + if(context->extra_addresses != NULL) { + free(context->extra_addresses); + context->extra_addresses = NULL; + } + return 0; + } + if(context->extra_addresses == NULL) { + context->extra_addresses = malloc(sizeof(*context->extra_addresses)); + if (context->extra_addresses == NULL) + return krb5_enomem(context); + } + return krb5_copy_addresses(context, addresses, context->extra_addresses); +} + +/** + * Get extra address to the address list that the library will add to + * the client's address list when communicating with the KDC. + * + * @param context Kerberos 5 context. + * @param addresses addreses to set + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_extra_addresses(krb5_context context, krb5_addresses *addresses) +{ + if(context->extra_addresses == NULL) { + memset(addresses, 0, sizeof(*addresses)); + return 0; + } + return krb5_copy_addresses(context,context->extra_addresses, addresses); +} + +/** + * Add extra addresses to ignore when fetching addresses from the + * underlaying operating system. + * + * @param context Kerberos 5 context. + * @param addresses addreses to ignore + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_add_ignore_addresses(krb5_context context, krb5_addresses *addresses) +{ + + if(context->ignore_addresses) + return krb5_append_addresses(context, + context->ignore_addresses, addresses); + else + return krb5_set_ignore_addresses(context, addresses); +} + +/** + * Set extra addresses to ignore when fetching addresses from the + * underlaying operating system. + * + * @param context Kerberos 5 context. + * @param addresses addreses to ignore + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses) +{ + if(context->ignore_addresses) + krb5_free_addresses(context, context->ignore_addresses); + if(addresses == NULL) { + if(context->ignore_addresses != NULL) { + free(context->ignore_addresses); + context->ignore_addresses = NULL; + } + return 0; + } + if(context->ignore_addresses == NULL) { + context->ignore_addresses = malloc(sizeof(*context->ignore_addresses)); + if (context->ignore_addresses == NULL) + return krb5_enomem(context); + } + return krb5_copy_addresses(context, addresses, context->ignore_addresses); +} + +/** + * Get extra addresses to ignore when fetching addresses from the + * underlaying operating system. + * + * @param context Kerberos 5 context. + * @param addresses list addreses ignored + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_ignore_addresses(krb5_context context, krb5_addresses *addresses) +{ + if(context->ignore_addresses == NULL) { + memset(addresses, 0, sizeof(*addresses)); + return 0; + } + return krb5_copy_addresses(context, context->ignore_addresses, addresses); +} + +/** + * Set version of fcache that the library should use. + * + * @param context Kerberos 5 context. + * @param version version number. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_fcache_version(krb5_context context, int version) +{ + context->fcache_vno = version; + return 0; +} + +/** + * Get version of fcache that the library should use. + * + * @param context Kerberos 5 context. + * @param version version number. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_fcache_version(krb5_context context, int *version) +{ + *version = context->fcache_vno; + return 0; +} + +/** + * Runtime check if the Kerberos library was complied with thread support. + * + * @return TRUE if the library was compiled with thread support, FALSE if not. + * + * @ingroup krb5 + */ + + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_is_thread_safe(void) +{ +#ifdef ENABLE_PTHREAD_SUPPORT + return TRUE; +#else + return FALSE; +#endif +} + +/** + * Set if the library should use DNS to canonicalize hostnames. + * + * @param context Kerberos 5 context. + * @param flag if its dns canonicalizion is used or not. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_set_dns_canonicalize_hostname (krb5_context context, krb5_boolean flag) +{ + if (flag) + context->flags |= KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME; + else + context->flags &= ~KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME; +} + +/** + * Get if the library uses DNS to canonicalize hostnames. + * + * @param context Kerberos 5 context. + * + * @return return non zero if the library uses DNS to canonicalize hostnames. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_get_dns_canonicalize_hostname (krb5_context context) +{ + return (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) ? 1 : 0; +} + +/** + * Get current offset in time to the KDC. + * + * @param context Kerberos 5 context. + * @param sec seconds part of offset. + * @param usec micro seconds part of offset. + * + * @return returns zero + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_kdc_sec_offset (krb5_context context, int32_t *sec, int32_t *usec) +{ + if (sec) + *sec = context->kdc_sec_offset; + if (usec) + *usec = context->kdc_usec_offset; + return 0; +} + +/** + * Set current offset in time to the KDC. + * + * @param context Kerberos 5 context. + * @param sec seconds part of offset. + * @param usec micro seconds part of offset. + * + * @return returns zero + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_kdc_sec_offset (krb5_context context, int32_t sec, int32_t usec) +{ + context->kdc_sec_offset = sec; + if (usec >= 0) + context->kdc_usec_offset = usec; + return 0; +} + +/** + * Get max time skew allowed. + * + * @param context Kerberos 5 context. + * + * @return timeskew in seconds. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL +krb5_get_max_time_skew (krb5_context context) +{ + return context->max_skew; +} + +/** + * Set max time skew allowed. + * + * @param context Kerberos 5 context. + * @param t timeskew in seconds. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_set_max_time_skew (krb5_context context, time_t t) +{ + context->max_skew = t; +} + +/* + * Init encryption types in len, val with etypes. + * + * @param context Kerberos 5 context. + * @param pdu_type type of pdu + * @param len output length of val. + * @param val output array of enctypes. + * @param etypes etypes to set val and len to, if NULL, use default enctypes. + + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_init_etype(krb5_context context, + krb5_pdu pdu_type, + unsigned *len, + krb5_enctype **val, + const krb5_enctype *etypes) +{ + krb5_error_code ret; + + if (etypes == NULL) + ret = krb5_get_default_in_tkt_etypes(context, pdu_type, val); + else + ret = copy_enctypes(context, etypes, val); + if (ret) + return ret; + + if (len) { + *len = 0; + while ((*val)[*len] != KRB5_ENCTYPE_NULL) + (*len)++; + } + return 0; +} + +/* + * Allow homedir access + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +_krb5_homedir_access(krb5_context context) +{ + if (context) + return !!(context->flags & KRB5_CTX_F_HOMEDIR_ACCESS); + return !issuid(); +} + +/** + * Enable and disable home directory access on either the global state + * or the krb5_context state. By calling krb5_set_home_dir_access() + * with context set to NULL, the global state is configured otherwise + * the state for the krb5_context is modified. + * + * For home directory access to be allowed, both the global state and + * the krb5_context state have to be allowed. + * + * @param context a Kerberos 5 context or NULL + * @param allow allow if TRUE home directory + * @return the old value + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_set_home_dir_access(krb5_context context, krb5_boolean allow) +{ + krb5_boolean old = _krb5_homedir_access(context); + + if (context) { + if (allow) + context->flags |= KRB5_CTX_F_HOMEDIR_ACCESS; + else + context->flags &= ~KRB5_CTX_F_HOMEDIR_ACCESS; + heim_context_set_homedir_access(context->hcontext, allow ? 1 : 0); + } + + return old; +} + diff --git a/third_party/heimdal/lib/krb5/convert_creds.c b/third_party/heimdal/lib/krb5/convert_creds.c new file mode 100644 index 0000000..56261b2 --- /dev/null +++ b/third_party/heimdal/lib/krb5/convert_creds.c @@ -0,0 +1,92 @@ +/* + * 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 "krb5_locl.h" + +#ifndef HEIMDAL_SMALLER + +/** + * Convert the v5 credentials in in_cred to v4-dito in v4creds. This + * is done by sending them to the 524 function in the KDC. If + * `in_cred' doesn't contain a DES session key, then a new one is + * gotten from the KDC and stored in the cred cache `ccache'. + * + * @param context Kerberos 5 context. + * @param in_cred the credential to convert + * @param v4creds the converted credential + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5_v4compat + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb524_convert_creds_kdc(krb5_context context, + krb5_creds *in_cred, + struct credentials *v4creds) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_set_error_message(context, EINVAL, + N_("krb524_convert_creds_kdc not supported", "")); + return EINVAL; +} + +/** + * Convert the v5 credentials in in_cred to v4-dito in v4creds, + * check the credential cache ccache before checking with the KDC. + * + * @param context Kerberos 5 context. + * @param ccache credential cache used to check for des-ticket. + * @param in_cred the credential to convert + * @param v4creds the converted credential + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5_v4compat + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb524_convert_creds_kdc_ccache(krb5_context context, + krb5_ccache ccache, + krb5_creds *in_cred, + struct credentials *v4creds) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_set_error_message(context, EINVAL, + N_("krb524_convert_creds_kdc_ccache not supported", "")); + return EINVAL; +} + +#endif diff --git a/third_party/heimdal/lib/krb5/copy_host_realm.c b/third_party/heimdal/lib/krb5/copy_host_realm.c new file mode 100644 index 0000000..f68d0d7 --- /dev/null +++ b/third_party/heimdal/lib/krb5/copy_host_realm.c @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1999 - 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 "krb5_locl.h" + +/** + * Copy the list of realms from `from' to `to'. + * + * @param context Kerberos 5 context. + * @param from list of realms to copy from. + * @param to list of realms to copy to, free list of krb5_free_host_realm(). + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_host_realm(krb5_context context, + const krb5_realm *from, + krb5_realm **to) +{ + unsigned int n, i; + const krb5_realm *p; + + for (n = 1, p = from; *p != NULL; ++p) + ++n; + + *to = calloc (n, sizeof(**to)); + if (*to == NULL) + return krb5_enomem(context); + + for (i = 0, p = from; *p != NULL; ++p, ++i) { + (*to)[i] = strdup(*p); + if ((*to)[i] == NULL) { + krb5_free_host_realm (context, *to); + return krb5_enomem(context); + } + } + return 0; +} diff --git a/third_party/heimdal/lib/krb5/crc.c b/third_party/heimdal/lib/krb5/crc.c new file mode 100644 index 0000000..3090760 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crc.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1997 - 2000 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +static u_long table[256]; + +#define CRC_GEN 0xEDB88320L + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_crc_init_table(void) +{ + static int flag = 0; + unsigned long crc, poly; + unsigned int i, j; + + if(flag) return; + poly = CRC_GEN; + for (i = 0; i < 256; i++) { + crc = i; + for (j = 8; j > 0; j--) { + if (crc & 1) { + crc = (crc >> 1) ^ poly; + } else { + crc >>= 1; + } + } + table[i] = crc; + } + flag = 1; +} + +KRB5_LIB_FUNCTION uint32_t KRB5_LIB_CALL +_krb5_crc_update (const char *p, size_t len, uint32_t res) +{ + while (len--) + res = table[(res ^ *p++) & 0xFF] ^ (res >> 8); + return res & 0xFFFFFFFF; +} diff --git a/third_party/heimdal/lib/krb5/creds.c b/third_party/heimdal/lib/krb5/creds.c new file mode 100644 index 0000000..d62a70a --- /dev/null +++ b/third_party/heimdal/lib/krb5/creds.c @@ -0,0 +1,282 @@ +/* + * 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 "krb5_locl.h" + +/** + * Free content of krb5_creds. + * + * @param context Kerberos 5 context. + * @param c krb5_creds to free. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_cred_contents (krb5_context context, krb5_creds *c) +{ + krb5_free_principal (context, c->client); + c->client = NULL; + krb5_free_principal (context, c->server); + c->server = NULL; + krb5_free_keyblock_contents (context, &c->session); + krb5_data_free (&c->ticket); + krb5_data_free (&c->second_ticket); + free_AuthorizationData (&c->authdata); + krb5_free_addresses (context, &c->addresses); + memset(c, 0, sizeof(*c)); + return 0; +} + +/** + * Copy content of krb5_creds. + * + * @param context Kerberos 5 context. + * @param incred source credential + * @param c destination credential, free with krb5_free_cred_contents(). + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_creds_contents (krb5_context context, + const krb5_creds *incred, + krb5_creds *c) +{ + krb5_error_code ret; + + memset(c, 0, sizeof(*c)); + ret = krb5_copy_principal (context, incred->client, &c->client); + if (ret) + goto fail; + ret = krb5_copy_principal (context, incred->server, &c->server); + if (ret) + goto fail; + ret = krb5_copy_keyblock_contents (context, &incred->session, &c->session); + if (ret) + goto fail; + c->times = incred->times; + ret = krb5_data_copy (&c->ticket, + incred->ticket.data, + incred->ticket.length); + if (ret) + goto fail; + ret = krb5_data_copy (&c->second_ticket, + incred->second_ticket.data, + incred->second_ticket.length); + if (ret) + goto fail; + ret = copy_AuthorizationData(&incred->authdata, &c->authdata); + if (ret) + goto fail; + ret = krb5_copy_addresses (context, + &incred->addresses, + &c->addresses); + if (ret) + goto fail; + c->flags = incred->flags; + return 0; + +fail: + krb5_free_cred_contents (context, c); + return ret; +} + +/** + * Copy krb5_creds. + * + * @param context Kerberos 5 context. + * @param incred source credential + * @param outcred destination credential, free with krb5_free_creds(). + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_creds (krb5_context context, + const krb5_creds *incred, + krb5_creds **outcred) +{ + krb5_creds *c; + + c = calloc(1, sizeof(*c)); + if (c == NULL) + return krb5_enomem(context); + *outcred = c; + return krb5_copy_creds_contents (context, incred, c); +} + +/** + * Free krb5_creds. + * + * @param context Kerberos 5 context. + * @param c krb5_creds to free. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned, see krb5_get_error_message(). + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_creds (krb5_context context, krb5_creds *c) +{ + if (c != NULL) + krb5_free_cred_contents(context, c); + free(c); + return 0; +} + +/* XXX this do not belong here */ +static krb5_boolean +krb5_times_equal(const krb5_times *a, const krb5_times *b) +{ + return a->starttime == b->starttime && + a->authtime == b->authtime && + a->endtime == b->endtime && + a->renew_till == b->renew_till; +} + +/** + * Return TRUE if `mcreds' and `creds' are equal (`whichfields' + * determines what equal means). + * + * + * The following flags, set in whichfields affects the comparison: + * - KRB5_TC_MATCH_SRV_NAMEONLY Consider all realms equal when comparing the service principal. + * - KRB5_TC_MATCH_KEYTYPE Compare enctypes. + * - KRB5_TC_MATCH_FLAGS_EXACT Make sure that the ticket flags are identical. + * - KRB5_TC_MATCH_FLAGS Make sure that all ticket flags set in mcreds are also present in creds . + * - KRB5_TC_MATCH_TIMES_EXACT Compares the ticket times exactly. + * - KRB5_TC_MATCH_TIMES Compares only the expiration times of the creds. + * - KRB5_TC_MATCH_AUTHDATA Compares the authdata fields. + * - KRB5_TC_MATCH_2ND_TKT Compares the second tickets (used by user-to-user authentication). + * - KRB5_TC_MATCH_IS_SKEY Compares the existence of the second ticket. + * + * @param context Kerberos 5 context. + * @param whichfields which fields to compare. + * @param mcreds cred to compare with. + * @param creds cred to compare with. + * + * @return return TRUE if mcred and creds are equal, FALSE if not. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_compare_creds(krb5_context context, krb5_flags whichfields, + const krb5_creds * mcreds, const krb5_creds * creds) +{ + krb5_boolean match = TRUE; + + if (match && mcreds->server) { + if (whichfields & (KRB5_TC_DONT_MATCH_REALM | KRB5_TC_MATCH_SRV_NAMEONLY)) + match = krb5_principal_compare_any_realm (context, mcreds->server, + creds->server); + else + match = krb5_principal_compare (context, mcreds->server, + creds->server); + } + + if (match && mcreds->client) { + if(whichfields & KRB5_TC_DONT_MATCH_REALM) + match = krb5_principal_compare_any_realm (context, mcreds->client, + creds->client); + else + match = krb5_principal_compare (context, mcreds->client, + creds->client); + } + + if (match && (whichfields & KRB5_TC_MATCH_KEYTYPE)) + match = mcreds->session.keytype == creds->session.keytype; + + if (match && (whichfields & KRB5_TC_MATCH_FLAGS_EXACT)) + match = mcreds->flags.i == creds->flags.i; + + if (match && (whichfields & KRB5_TC_MATCH_FLAGS)) + match = (creds->flags.i & mcreds->flags.i) == mcreds->flags.i; + + if (match && (whichfields & KRB5_TC_MATCH_TIMES_EXACT)) + match = krb5_times_equal(&mcreds->times, &creds->times); + + if (match && (whichfields & KRB5_TC_MATCH_TIMES)) + /* compare only expiration times */ + match = (mcreds->times.renew_till <= creds->times.renew_till) && + (mcreds->times.endtime <= creds->times.endtime); + + if (match && (whichfields & KRB5_TC_MATCH_AUTHDATA)) { + unsigned int i; + if(mcreds->authdata.len != creds->authdata.len) + match = FALSE; + else + for(i = 0; match && i < mcreds->authdata.len; i++) + match = (mcreds->authdata.val[i].ad_type == + creds->authdata.val[i].ad_type) && + (krb5_data_cmp(&mcreds->authdata.val[i].ad_data, + &creds->authdata.val[i].ad_data) == 0); + } + if (match && (whichfields & KRB5_TC_MATCH_2ND_TKT)) + match = (krb5_data_cmp(&mcreds->second_ticket, &creds->second_ticket) == 0); + + if (match && (whichfields & KRB5_TC_MATCH_IS_SKEY)) + match = ((mcreds->second_ticket.length == 0) == + (creds->second_ticket.length == 0)); + + return match; +} + +/** + * Returns the ticket flags for the credentials in creds. + * See also krb5_ticket_get_flags(). + * + * @param creds credential to get ticket flags from + * + * @return ticket flags + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL +krb5_creds_get_ticket_flags(krb5_creds *creds) +{ + return TicketFlags2int(creds->flags.b); +} diff --git a/third_party/heimdal/lib/krb5/crypto-aes-sha1.c b/third_party/heimdal/lib/krb5/crypto-aes-sha1.c new file mode 100644 index 0000000..1f3760d --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-aes-sha1.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* + * AES + */ + +static struct _krb5_key_type keytype_aes128_sha1 = { + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96, + "aes-128", + 128, + 16, + sizeof(struct _krb5_evp_schedule), + NULL, + _krb5_evp_schedule, + _krb5_AES_SHA1_salt, + NULL, + _krb5_evp_cleanup, + EVP_aes_128_cbc +}; + +static struct _krb5_key_type keytype_aes256_sha1 = { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96, + "aes-256", + 256, + 32, + sizeof(struct _krb5_evp_schedule), + NULL, + _krb5_evp_schedule, + _krb5_AES_SHA1_salt, + NULL, + _krb5_evp_cleanup, + EVP_aes_256_cbc +}; + +struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128 = { + CKSUMTYPE_HMAC_SHA1_96_AES_128, + "hmac-sha1-96-aes128", + 64, + 12, + F_KEYED | F_CPROOF | F_DERIVED, + _krb5_SP_HMAC_SHA1_checksum, + _krb5_SP_HMAC_SHA1_verify +}; + +struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256 = { + CKSUMTYPE_HMAC_SHA1_96_AES_256, + "hmac-sha1-96-aes256", + 64, + 12, + F_KEYED | F_CPROOF | F_DERIVED, + _krb5_SP_HMAC_SHA1_checksum, + _krb5_SP_HMAC_SHA1_verify +}; + +static krb5_error_code +AES_SHA1_PRF(krb5_context context, + krb5_crypto crypto, + const krb5_data *in, + krb5_data *out) +{ + struct _krb5_checksum_type *ct = crypto->et->checksum; + struct krb5_crypto_iov iov[1]; + krb5_error_code ret; + Checksum result; + krb5_keyblock *derived; + + result.cksumtype = ct->type; + ret = krb5_data_alloc(&result.checksum, ct->checksumsize); + if (ret) { + krb5_set_error_message(context, ret, N_("malloc: out memory", "")); + return ret; + } + + iov[0].data = *in; + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + ret = (*ct->checksum)(context, crypto, NULL, 0, iov, 1, &result); + if (ret) { + krb5_data_free(&result.checksum); + return ret; + } + + if (result.checksum.length < crypto->et->blocksize) + krb5_abortx(context, "internal prf error"); + + derived = NULL; + ret = krb5_derive_key(context, crypto->key.key, + crypto->et->type, "prf", 3, &derived); + if (ret) + krb5_abortx(context, "krb5_derive_key"); + + ret = krb5_data_alloc(out, crypto->et->blocksize); + if (ret) + krb5_abortx(context, "malloc failed"); + + { + const EVP_CIPHER *c = (*crypto->et->keytype->evp)(); + EVP_CIPHER_CTX ctx; + + EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */ + EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1); + EVP_Cipher(&ctx, out->data, result.checksum.data, + crypto->et->blocksize); + EVP_CIPHER_CTX_cleanup(&ctx); + } + + krb5_data_free(&result.checksum); + krb5_free_keyblock(context, derived); + + return ret; +} + +struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1 = { + ETYPE_AES128_CTS_HMAC_SHA1_96, + "aes128-cts-hmac-sha1-96", + "aes128-cts", + 16, + 1, + 16, + &keytype_aes128_sha1, + &_krb5_checksum_sha1, + &_krb5_checksum_hmac_sha1_aes128, + F_DERIVED | F_RFC3961_ENC | F_RFC3961_KDF, + _krb5_evp_encrypt_cts, + _krb5_evp_encrypt_iov_cts, + 16, + AES_SHA1_PRF +}; + +struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1 = { + ETYPE_AES256_CTS_HMAC_SHA1_96, + "aes256-cts-hmac-sha1-96", + "aes256-cts", + 16, + 1, + 16, + &keytype_aes256_sha1, + &_krb5_checksum_sha1, + &_krb5_checksum_hmac_sha1_aes256, + F_DERIVED | F_RFC3961_ENC | F_RFC3961_KDF, + _krb5_evp_encrypt_cts, + _krb5_evp_encrypt_iov_cts, + 16, + AES_SHA1_PRF +}; diff --git a/third_party/heimdal/lib/krb5/crypto-aes-sha2.c b/third_party/heimdal/lib/krb5/crypto-aes-sha2.c new file mode 100644 index 0000000..94ec9a1 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-aes-sha2.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* + * AES HMAC-SHA2 + */ + +krb5_error_code +_krb5_aes_sha2_md_for_enctype(krb5_context context, + krb5_enctype enctype, + const EVP_MD **md) +{ + switch (enctype) { + case ETYPE_AES128_CTS_HMAC_SHA256_128: + *md = EVP_sha256(); + break; + case ETYPE_AES256_CTS_HMAC_SHA384_192: + *md = EVP_sha384(); + break; + default: + return KRB5_PROG_ETYPE_NOSUPP; + break; + } + return 0; +} + +static krb5_error_code +SP_HMAC_SHA2_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *result) +{ + krb5_error_code ret; + const EVP_MD *md; + unsigned char hmac[EVP_MAX_MD_SIZE]; + unsigned int hmaclen = sizeof(hmac); + + ret = _krb5_aes_sha2_md_for_enctype(context, key->key->keytype, &md); + if (ret) + return ret; + + ret = _krb5_evp_hmac_iov(context, crypto, key, iov, niov, hmac, + &hmaclen, md, NULL); + if (ret) + return ret; + + heim_assert(result->checksum.length <= hmaclen, "SHA2 internal error"); + + memcpy(result->checksum.data, hmac, result->checksum.length); + + return 0; +} + +static struct _krb5_key_type keytype_aes128_sha2 = { + KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128, + "aes-128-sha2", + 128, + 16, + sizeof(struct _krb5_evp_schedule), + NULL, + _krb5_evp_schedule, + _krb5_AES_SHA2_salt, + NULL, + _krb5_evp_cleanup, + EVP_aes_128_cbc +}; + +static struct _krb5_key_type keytype_aes256_sha2 = { + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192, + "aes-256-sha2", + 256, + 32, + sizeof(struct _krb5_evp_schedule), + NULL, + _krb5_evp_schedule, + _krb5_AES_SHA2_salt, + NULL, + _krb5_evp_cleanup, + EVP_aes_256_cbc +}; + +struct _krb5_checksum_type _krb5_checksum_hmac_sha256_128_aes128 = { + CKSUMTYPE_HMAC_SHA256_128_AES128, + "hmac-sha256-128-aes128", + 64, + 16, + F_KEYED | F_CPROOF | F_DERIVED, + SP_HMAC_SHA2_checksum, + NULL +}; + +struct _krb5_checksum_type _krb5_checksum_hmac_sha384_192_aes256 = { + CKSUMTYPE_HMAC_SHA384_192_AES256, + "hmac-sha384-192-aes256", + 128, + 24, + F_KEYED | F_CPROOF | F_DERIVED, + SP_HMAC_SHA2_checksum, + NULL +}; + +static krb5_error_code +AES_SHA2_PRF(krb5_context context, + krb5_crypto crypto, + const krb5_data *in, + krb5_data *out) +{ + krb5_error_code ret; + krb5_data label; + const EVP_MD *md = NULL; + + ret = _krb5_aes_sha2_md_for_enctype(context, crypto->et->type, &md); + if (ret) + return ret; + + label.data = "prf"; + label.length = 3; + + ret = krb5_data_alloc(out, EVP_MD_size(md)); + if (ret) + return ret; + + ret = _krb5_SP800_108_HMAC_KDF(context, &crypto->key.key->keyvalue, + &label, in, md, out); + + if (ret) + krb5_data_free(out); + + return ret; +} + +struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha256_128 = { + ETYPE_AES128_CTS_HMAC_SHA256_128, + "aes128-cts-hmac-sha256-128", + "aes128-cts-sha256", + 16, + 1, + 16, + &keytype_aes128_sha2, + NULL, /* should never be called */ + &_krb5_checksum_hmac_sha256_128_aes128, + F_DERIVED | F_ENC_THEN_CKSUM | F_SP800_108_HMAC_KDF, + _krb5_evp_encrypt_cts, + NULL, + 16, + AES_SHA2_PRF +}; + +struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha384_192 = { + ETYPE_AES256_CTS_HMAC_SHA384_192, + "aes256-cts-hmac-sha384-192", + "aes256-cts-sha384", + 16, + 1, + 16, + &keytype_aes256_sha2, + NULL, /* should never be called */ + &_krb5_checksum_hmac_sha384_192_aes256, + F_DERIVED | F_ENC_THEN_CKSUM | F_SP800_108_HMAC_KDF, + _krb5_evp_encrypt_cts, + NULL, + 16, + AES_SHA2_PRF +}; diff --git a/third_party/heimdal/lib/krb5/crypto-algs.c b/third_party/heimdal/lib/krb5/crypto-algs.c new file mode 100644 index 0000000..eb21fce --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-algs.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#ifndef HEIMDAL_SMALLER +#define DES3_OLD_ENCTYPE 1 +#endif + +struct _krb5_checksum_type *_krb5_checksum_types[] = { + &_krb5_checksum_none, +#ifdef HEIM_WEAK_CRYPTO + &_krb5_checksum_crc32, + &_krb5_checksum_rsa_md4, + &_krb5_checksum_rsa_md4_des, + &_krb5_checksum_rsa_md5_des, +#endif +#ifdef DES3_OLD_ENCTYPE + &_krb5_checksum_rsa_md5_des3, +#endif + &_krb5_checksum_rsa_md5, + &_krb5_checksum_sha1, + &_krb5_checksum_hmac_sha1_des3, + &_krb5_checksum_hmac_sha1_aes128, + &_krb5_checksum_hmac_sha1_aes256, + &_krb5_checksum_hmac_sha256_128_aes128, + &_krb5_checksum_hmac_sha384_192_aes256, + &_krb5_checksum_hmac_md5, + &_krb5_checksum_sha256, + &_krb5_checksum_sha384, + &_krb5_checksum_sha512 +}; + +int _krb5_num_checksums + = sizeof(_krb5_checksum_types) / sizeof(_krb5_checksum_types[0]); + +/* + * these should currently be in reverse preference order. + * (only relevant for !F_PSEUDO) */ + +struct _krb5_encryption_type *_krb5_etypes[] = { + &_krb5_enctype_aes256_cts_hmac_sha384_192, + &_krb5_enctype_aes128_cts_hmac_sha256_128, + &_krb5_enctype_aes256_cts_hmac_sha1, + &_krb5_enctype_aes128_cts_hmac_sha1, + &_krb5_enctype_des3_cbc_sha1, + &_krb5_enctype_des3_cbc_none, /* used by the gss-api mech */ + &_krb5_enctype_arcfour_hmac_md5, +#ifdef DES3_OLD_ENCTYPE + &_krb5_enctype_des3_cbc_md5, + &_krb5_enctype_old_des3_cbc_sha1, +#endif +#ifdef HEIM_WEAK_CRYPTO + &_krb5_enctype_des_cbc_md5, + &_krb5_enctype_des_cbc_md4, + &_krb5_enctype_des_cbc_crc, + &_krb5_enctype_des_cbc_none, + &_krb5_enctype_des_cfb64_none, + &_krb5_enctype_des_pcbc_none, +#endif + &_krb5_enctype_null +}; + +int _krb5_num_etypes = sizeof(_krb5_etypes) / sizeof(_krb5_etypes[0]); diff --git a/third_party/heimdal/lib/krb5/crypto-arcfour.c b/third_party/heimdal/lib/krb5/crypto-arcfour.c new file mode 100644 index 0000000..28fc52e --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-arcfour.c @@ -0,0 +1,368 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * ARCFOUR + */ + +#include "krb5_locl.h" + +static struct _krb5_key_type keytype_arcfour = { + KRB5_ENCTYPE_ARCFOUR_HMAC_MD5, + "arcfour", + 128, + 16, + sizeof(struct _krb5_evp_schedule), + NULL, + _krb5_evp_schedule, + _krb5_arcfour_salt, + NULL, + _krb5_evp_cleanup, + EVP_rc4 +}; + +/* + * checksum according to section 5. of draft-brezak-win2k-krb-rc4-hmac-03.txt + */ + +krb5_error_code +_krb5_HMAC_MD5_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *result) +{ + EVP_MD_CTX *m; + struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5); + const char signature[] = "signaturekey"; + Checksum ksign_c; + struct _krb5_key_data ksign; + krb5_keyblock kb; + unsigned char t[4]; + unsigned char tmp[16]; + unsigned char ksign_c_data[16]; + krb5_error_code ret; + int i; + + if (crypto != NULL) { + if (crypto->mdctx == NULL) + crypto->mdctx = EVP_MD_CTX_create(); + if (crypto->mdctx == NULL) + return krb5_enomem(context); + m = crypto->mdctx; + } else + m = EVP_MD_CTX_create(); + + ksign_c.checksum.length = sizeof(ksign_c_data); + ksign_c.checksum.data = ksign_c_data; + ret = _krb5_internal_hmac(context, crypto, c, signature, sizeof(signature), + 0, key, &ksign_c); + if (ret) + goto out; + + ksign.key = &kb; + kb.keyvalue = ksign_c.checksum; + EVP_DigestInit_ex(m, EVP_md5(), NULL); + t[0] = (usage >> 0) & 0xFF; + t[1] = (usage >> 8) & 0xFF; + t[2] = (usage >> 16) & 0xFF; + t[3] = (usage >> 24) & 0xFF; + EVP_DigestUpdate(m, t, 4); + for (i = 0; i < niov; i++) { + if (_krb5_crypto_iov_should_sign(&iov[i])) + EVP_DigestUpdate(m, iov[i].data.data, iov[i].data.length); + } + EVP_DigestFinal_ex (m, tmp, NULL); + + ret = _krb5_internal_hmac(context, crypto, c, tmp, sizeof(tmp), 0, &ksign, result); +out: + if (crypto == NULL) + EVP_MD_CTX_destroy(m); + + return ret; +} + +struct _krb5_checksum_type _krb5_checksum_hmac_md5 = { + CKSUMTYPE_HMAC_MD5, + "hmac-md5", + 64, + 16, + F_KEYED | F_CPROOF, + _krb5_HMAC_MD5_checksum, + NULL +}; + +/* + * section 6 of draft-brezak-win2k-krb-rc4-hmac-03 + * + * warning: not for small children + */ + +static krb5_error_code +ARCFOUR_subencrypt(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + unsigned usage, + void *ivec) +{ + EVP_CIPHER_CTX ctx; + struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5); + Checksum k1_c, k2_c, k3_c, cksum; + struct _krb5_key_data ke; + krb5_keyblock kb; + unsigned char t[4]; + unsigned char *cdata = data; + unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16]; + krb5_error_code ret; + + if (len < 16) { + return KRB5KRB_AP_ERR_INAPP_CKSUM; + } + + t[0] = (usage >> 0) & 0xFF; + t[1] = (usage >> 8) & 0xFF; + t[2] = (usage >> 16) & 0xFF; + t[3] = (usage >> 24) & 0xFF; + + k1_c.checksum.length = sizeof(k1_c_data); + k1_c.checksum.data = k1_c_data; + + ret = _krb5_internal_hmac(context, NULL, c, t, sizeof(t), 0, key, &k1_c); + if (ret) + krb5_abortx(context, "hmac failed"); + + memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data)); + + k2_c.checksum.length = sizeof(k2_c_data); + k2_c.checksum.data = k2_c_data; + + ke.key = &kb; + kb.keyvalue = k2_c.checksum; + + cksum.checksum.length = 16; + cksum.checksum.data = data; + + ret = _krb5_internal_hmac(context, NULL, c, cdata + 16, len - 16, 0, &ke, &cksum); + if (ret) + krb5_abortx(context, "hmac failed"); + + ke.key = &kb; + kb.keyvalue = k1_c.checksum; + + k3_c.checksum.length = sizeof(k3_c_data); + k3_c.checksum.data = k3_c_data; + + ret = _krb5_internal_hmac(context, NULL, c, data, 16, 0, &ke, &k3_c); + if (ret) + krb5_abortx(context, "hmac failed"); + + EVP_CIPHER_CTX_init(&ctx); + + EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 1); + EVP_Cipher(&ctx, cdata + 16, cdata + 16, len - 16); + EVP_CIPHER_CTX_cleanup(&ctx); + + memset_s(k1_c_data, sizeof(k1_c_data), 0, sizeof(k1_c_data)); + memset_s(k2_c_data, sizeof(k2_c_data), 0, sizeof(k2_c_data)); + memset_s(k3_c_data, sizeof(k3_c_data), 0, sizeof(k3_c_data)); + return 0; +} + +static krb5_error_code +ARCFOUR_subdecrypt(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + unsigned usage, + void *ivec) +{ + EVP_CIPHER_CTX ctx; + struct _krb5_checksum_type *c = _krb5_find_checksum (CKSUMTYPE_RSA_MD5); + Checksum k1_c, k2_c, k3_c, cksum; + struct _krb5_key_data ke; + krb5_keyblock kb; + unsigned char t[4]; + unsigned char *cdata = data; + unsigned char k1_c_data[16], k2_c_data[16], k3_c_data[16]; + unsigned char cksum_data[16]; + krb5_error_code ret; + + if (len < 16) { + return KRB5KRB_AP_ERR_INAPP_CKSUM; + } + + t[0] = (usage >> 0) & 0xFF; + t[1] = (usage >> 8) & 0xFF; + t[2] = (usage >> 16) & 0xFF; + t[3] = (usage >> 24) & 0xFF; + + k1_c.checksum.length = sizeof(k1_c_data); + k1_c.checksum.data = k1_c_data; + + ret = _krb5_internal_hmac(context, NULL, c, t, sizeof(t), 0, key, &k1_c); + if (ret) + krb5_abortx(context, "hmac failed"); + + memcpy (k2_c_data, k1_c_data, sizeof(k1_c_data)); + + k2_c.checksum.length = sizeof(k2_c_data); + k2_c.checksum.data = k2_c_data; + + ke.key = &kb; + kb.keyvalue = k1_c.checksum; + + k3_c.checksum.length = sizeof(k3_c_data); + k3_c.checksum.data = k3_c_data; + + ret = _krb5_internal_hmac(context, NULL, c, cdata, 16, 0, &ke, &k3_c); + if (ret) + krb5_abortx(context, "hmac failed"); + + EVP_CIPHER_CTX_init(&ctx); + EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, k3_c.checksum.data, NULL, 0); + EVP_Cipher(&ctx, cdata + 16, cdata + 16, len - 16); + EVP_CIPHER_CTX_cleanup(&ctx); + + ke.key = &kb; + kb.keyvalue = k2_c.checksum; + + cksum.checksum.length = 16; + cksum.checksum.data = cksum_data; + + ret = _krb5_internal_hmac(context, NULL, c, cdata + 16, len - 16, 0, &ke, &cksum); + if (ret) + krb5_abortx(context, "hmac failed"); + + memset_s(k1_c_data, sizeof(k1_c_data), 0, sizeof(k1_c_data)); + memset_s(k2_c_data, sizeof(k2_c_data), 0, sizeof(k2_c_data)); + memset_s(k3_c_data, sizeof(k3_c_data), 0, sizeof(k3_c_data)); + + if (ct_memcmp (cksum.checksum.data, data, 16) != 0) { + krb5_clear_error_message (context); + return KRB5KRB_AP_ERR_BAD_INTEGRITY; + } else { + return 0; + } +} + +/* + * convert the usage numbers used in + * draft-ietf-cat-kerb-key-derivation-00.txt to the ones in + * draft-brezak-win2k-krb-rc4-hmac-04.txt + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_usage2arcfour(krb5_context context, unsigned *usage) +{ + switch (*usage) { + case KRB5_KU_AS_REP_ENC_PART : /* 3 */ + *usage = 8; + return 0; + case KRB5_KU_USAGE_SEAL : /* 22 */ + *usage = 13; + return 0; + case KRB5_KU_USAGE_SIGN : /* 23 */ + *usage = 15; + return 0; + case KRB5_KU_USAGE_SEQ: /* 24 */ + *usage = 0; + return 0; + default : + return 0; + } +} + +static krb5_error_code +ARCFOUR_encrypt(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ivec) +{ + krb5_error_code ret; + unsigned keyusage = usage; + + if((ret = _krb5_usage2arcfour (context, &keyusage)) != 0) + return ret; + + if (encryptp) + return ARCFOUR_subencrypt (context, key, data, len, keyusage, ivec); + else + return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec); +} + +static krb5_error_code +ARCFOUR_prf(krb5_context context, + krb5_crypto crypto, + const krb5_data *in, + krb5_data *out) +{ + struct _krb5_checksum_type *c = _krb5_find_checksum(CKSUMTYPE_SHA1); + krb5_error_code ret; + Checksum res; + + ret = krb5_data_alloc(out, c->checksumsize); + if (ret) + return ret; + + res.checksum.data = out->data; + res.checksum.length = out->length; + + ret = _krb5_internal_hmac(context, crypto, c, in->data, in->length, 0, &crypto->key, &res); + if (ret) + krb5_data_free(out); + return 0; +} + + +struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5 = { + ETYPE_ARCFOUR_HMAC_MD5, + "arcfour-hmac-md5", + "rc4-hmac", + 1, + 1, + 8, + &keytype_arcfour, + &_krb5_checksum_hmac_md5, + &_krb5_checksum_hmac_md5, + F_SPECIAL | F_WEAK | F_OLD, + ARCFOUR_encrypt, + NULL, + 0, + ARCFOUR_prf +}; diff --git a/third_party/heimdal/lib/krb5/crypto-des-common.c b/third_party/heimdal/lib/krb5/crypto-des-common.c new file mode 100644 index 0000000..a8344ae --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-des-common.c @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Functions which are used by both single and triple DES enctypes */ + +#include "krb5_locl.h" + +/* + * A = A xor B. A & B are 8 bytes. + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_xor8(unsigned char *a, const unsigned char *b) +{ + a[0] ^= b[0]; + a[1] ^= b[1]; + a[2] ^= b[2]; + a[3] ^= b[3]; + a[4] ^= b[4]; + a[5] ^= b[5]; + a[6] ^= b[6]; + a[7] ^= b[7]; +} + +#if defined(DES3_OLD_ENCTYPE) || defined(HEIM_WEAK_CRYPTO) +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_des_checksum(krb5_context context, + const EVP_MD *evp_md, + struct _krb5_key_data *key, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *cksum) +{ + struct _krb5_evp_schedule *ctx = key->schedule->data; + EVP_MD_CTX *m; + DES_cblock ivec; + int i; + unsigned char *p = cksum->checksum.data; + + krb5_generate_random_block(p, 8); + + m = EVP_MD_CTX_create(); + if (m == NULL) + return krb5_enomem(context); + + EVP_DigestInit_ex(m, evp_md, NULL); + EVP_DigestUpdate(m, p, 8); + for (i = 0; i < niov; i++) { + if (_krb5_crypto_iov_should_sign(&iov[i])) + EVP_DigestUpdate(m, iov[i].data.data, iov[i].data.length); + } + EVP_DigestFinal_ex (m, p + 8, NULL); + EVP_MD_CTX_destroy(m); + memset_s(&ivec, sizeof(ivec), 0, sizeof(ivec)); + EVP_CipherInit_ex(&ctx->ectx, NULL, NULL, NULL, (void *)&ivec, -1); + EVP_Cipher(&ctx->ectx, p, p, 24); + + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_des_verify(krb5_context context, + const EVP_MD *evp_md, + struct _krb5_key_data *key, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + struct _krb5_evp_schedule *ctx = key->schedule->data; + EVP_MD_CTX *m; + unsigned char tmp[24]; + unsigned char res[16]; + DES_cblock ivec; + krb5_error_code ret = 0; + int i; + + m = EVP_MD_CTX_create(); + if (m == NULL) + return krb5_enomem(context); + + memset_s(&ivec, sizeof(ivec), 0, sizeof(ivec)); + EVP_CipherInit_ex(&ctx->dctx, NULL, NULL, NULL, (void *)&ivec, -1); + EVP_Cipher(&ctx->dctx, tmp, C->checksum.data, 24); + + EVP_DigestInit_ex(m, evp_md, NULL); + EVP_DigestUpdate(m, tmp, 8); /* confounder */ + for (i = 0; i < niov; i++) { + if (_krb5_crypto_iov_should_sign(&iov[i])) + EVP_DigestUpdate(m, iov[i].data.data, iov[i].data.length); + } + EVP_DigestFinal_ex (m, res, NULL); + EVP_MD_CTX_destroy(m); + if(ct_memcmp(res, tmp + 8, sizeof(res)) != 0) { + krb5_clear_error_message (context); + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + } + memset_s(tmp, sizeof(tmp), 0, sizeof(tmp)); + memset_s(res, sizeof(res), 0, sizeof(res)); + return ret; +} + +#endif + +static krb5_error_code +RSA_MD5_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + if (_krb5_evp_digest_iov(crypto, iov, niov, C->checksum.data, + NULL, EVP_md5(), NULL) != 1) + krb5_abortx(context, "md5 checksum failed"); + + return 0; +} + +struct _krb5_checksum_type _krb5_checksum_rsa_md5 = { + CKSUMTYPE_RSA_MD5, + "rsa-md5", + 64, + 16, + F_CPROOF, + RSA_MD5_checksum, + NULL +}; diff --git a/third_party/heimdal/lib/krb5/crypto-des.c b/third_party/heimdal/lib/krb5/crypto-des.c new file mode 100644 index 0000000..c569295 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-des.c @@ -0,0 +1,403 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#ifdef HEIM_WEAK_CRYPTO + + +static void +krb5_DES_random_key(krb5_context context, + krb5_keyblock *key) +{ + DES_cblock *k = key->keyvalue.data; + do { + krb5_generate_random_block(k, sizeof(DES_cblock)); + DES_set_odd_parity(k); + } while(DES_is_weak_key(k)); +} + +static void +krb5_DES_schedule_old(krb5_context context, + struct _krb5_key_type *kt, + struct _krb5_key_data *key) +{ + DES_set_key_unchecked(key->key->keyvalue.data, key->schedule->data); +} + +static void +krb5_DES_random_to_key(krb5_context context, + krb5_keyblock *key, + const void *data, + size_t size) +{ + DES_cblock *k = key->keyvalue.data; + memcpy(k, data, key->keyvalue.length); + DES_set_odd_parity(k); + if(DES_is_weak_key(k)) + _krb5_xor8(*k, (const unsigned char*)"\0\0\0\0\0\0\0\xf0"); +} + +static struct _krb5_key_type keytype_des_old = { + ETYPE_DES_CBC_CRC, + "des-old", + 56, + 8, + sizeof(DES_key_schedule), + krb5_DES_random_key, + krb5_DES_schedule_old, + _krb5_des_salt, + krb5_DES_random_to_key, + NULL, + NULL +}; + +static struct _krb5_key_type keytype_des = { + ETYPE_DES_CBC_CRC, + "des", + 56, + 8, + sizeof(struct _krb5_evp_schedule), + krb5_DES_random_key, + _krb5_evp_schedule, + _krb5_des_salt, + krb5_DES_random_to_key, + _krb5_evp_cleanup, + EVP_des_cbc +}; + +static krb5_error_code +CRC32_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + uint32_t crc = 0; + unsigned char *r = C->checksum.data; + int i; + + _krb5_crc_init_table (); + + for (i = 0; i < niov; i++) { + if (_krb5_crypto_iov_should_sign(&iov[i])) + crc = _krb5_crc_update(iov[i].data.data, iov[i].data.length, crc); + } + + r[0] = crc & 0xff; + r[1] = (crc >> 8) & 0xff; + r[2] = (crc >> 16) & 0xff; + r[3] = (crc >> 24) & 0xff; + return 0; +} + +static krb5_error_code +RSA_MD4_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + if (_krb5_evp_digest_iov(crypto, iov, niov, C->checksum.data, + NULL, EVP_md4(), NULL) != 1) + krb5_abortx(context, "md4 checksum failed"); + return 0; +} + +static krb5_error_code +RSA_MD4_DES_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *cksum) +{ + return _krb5_des_checksum(context, EVP_md4(), key, iov, niov, cksum); +} + +static krb5_error_code +RSA_MD4_DES_verify(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + return _krb5_des_verify(context, EVP_md4(), key, iov, niov, C); +} + +static krb5_error_code +RSA_MD5_DES_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + return _krb5_des_checksum(context, EVP_md5(), key, iov, niov, C); +} + +static krb5_error_code +RSA_MD5_DES_verify(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + return _krb5_des_verify(context, EVP_md5(), key, iov, niov, C); +} + +struct _krb5_checksum_type _krb5_checksum_crc32 = { + CKSUMTYPE_CRC32, + "crc32", + 1, + 4, + 0, + CRC32_checksum, + NULL +}; + +struct _krb5_checksum_type _krb5_checksum_rsa_md4 = { + CKSUMTYPE_RSA_MD4, + "rsa-md4", + 64, + 16, + F_CPROOF, + RSA_MD4_checksum, + NULL +}; + +struct _krb5_checksum_type _krb5_checksum_rsa_md4_des = { + CKSUMTYPE_RSA_MD4_DES, + "rsa-md4-des", + 64, + 24, + F_KEYED | F_CPROOF | F_VARIANT, + RSA_MD4_DES_checksum, + RSA_MD4_DES_verify +}; + +struct _krb5_checksum_type _krb5_checksum_rsa_md5_des = { + CKSUMTYPE_RSA_MD5_DES, + "rsa-md5-des", + 64, + 24, + F_KEYED | F_CPROOF | F_VARIANT, + RSA_MD5_DES_checksum, + RSA_MD5_DES_verify +}; + +static krb5_error_code +evp_des_encrypt_null_ivec(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ignore_ivec) +{ + struct _krb5_evp_schedule *ctx = key->schedule->data; + EVP_CIPHER_CTX *c; + DES_cblock ivec; + memset(&ivec, 0, sizeof(ivec)); + c = encryptp ? &ctx->ectx : &ctx->dctx; + EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1); + EVP_Cipher(c, data, data, len); + return 0; +} + +static krb5_error_code +evp_des_encrypt_key_ivec(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ignore_ivec) +{ + struct _krb5_evp_schedule *ctx = key->schedule->data; + EVP_CIPHER_CTX *c; + DES_cblock ivec; + memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec)); + c = encryptp ? &ctx->ectx : &ctx->dctx; + EVP_CipherInit_ex(c, NULL, NULL, NULL, (void *)&ivec, -1); + EVP_Cipher(c, data, data, len); + return 0; +} + +static krb5_error_code +DES_CFB64_encrypt_null_ivec(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ignore_ivec) +{ + DES_cblock ivec; + int num = 0; + DES_key_schedule *s = key->schedule->data; + memset(&ivec, 0, sizeof(ivec)); + + DES_cfb64_encrypt(data, data, len, s, &ivec, &num, encryptp); + return 0; +} + +static krb5_error_code +DES_PCBC_encrypt_key_ivec(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ignore_ivec) +{ + DES_cblock ivec; + DES_key_schedule *s = key->schedule->data; + memcpy(&ivec, key->key->keyvalue.data, sizeof(ivec)); + + DES_pcbc_encrypt(data, data, len, s, &ivec, encryptp); + return 0; +} + +struct _krb5_encryption_type _krb5_enctype_des_cbc_crc = { + ETYPE_DES_CBC_CRC, + "des-cbc-crc", + NULL, + 8, + 8, + 8, + &keytype_des, + &_krb5_checksum_crc32, + NULL, + F_DISABLED|F_WEAK|F_OLD, + evp_des_encrypt_key_ivec, + NULL, + 0, + NULL +}; + +struct _krb5_encryption_type _krb5_enctype_des_cbc_md4 = { + ETYPE_DES_CBC_MD4, + "des-cbc-md4", + NULL, + 8, + 8, + 8, + &keytype_des, + &_krb5_checksum_rsa_md4, + &_krb5_checksum_rsa_md4_des, + F_DISABLED|F_WEAK|F_OLD, + evp_des_encrypt_null_ivec, + NULL, + 0, + NULL +}; + +struct _krb5_encryption_type _krb5_enctype_des_cbc_md5 = { + ETYPE_DES_CBC_MD5, + "des-cbc-md5", + NULL, + 8, + 8, + 8, + &keytype_des, + &_krb5_checksum_rsa_md5, + &_krb5_checksum_rsa_md5_des, + F_DISABLED|F_WEAK|F_OLD, + evp_des_encrypt_null_ivec, + NULL, + 0, + NULL +}; + +struct _krb5_encryption_type _krb5_enctype_des_cbc_none = { + ETYPE_DES_CBC_NONE, + "des-cbc-none", + NULL, + 8, + 8, + 0, + &keytype_des, + &_krb5_checksum_none, + NULL, + F_PSEUDO|F_DISABLED|F_WEAK|F_OLD, + evp_des_encrypt_null_ivec, + NULL, + 0, + NULL +}; + +struct _krb5_encryption_type _krb5_enctype_des_cfb64_none = { + ETYPE_DES_CFB64_NONE, + "des-cfb64-none", + NULL, + 1, + 1, + 0, + &keytype_des_old, + &_krb5_checksum_none, + NULL, + F_PSEUDO|F_DISABLED|F_WEAK|F_OLD, + DES_CFB64_encrypt_null_ivec, + NULL, + 0, + NULL +}; + +struct _krb5_encryption_type _krb5_enctype_des_pcbc_none = { + ETYPE_DES_PCBC_NONE, + "des-pcbc-none", + NULL, + 8, + 8, + 0, + &keytype_des_old, + &_krb5_checksum_none, + NULL, + F_PSEUDO|F_DISABLED|F_WEAK|F_OLD, + DES_PCBC_encrypt_key_ivec, + NULL, + 0, + NULL +}; +#endif /* HEIM_WEAK_CRYPTO */ diff --git a/third_party/heimdal/lib/krb5/crypto-des3.c b/third_party/heimdal/lib/krb5/crypto-des3.c new file mode 100644 index 0000000..d231921 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-des3.c @@ -0,0 +1,292 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* + * + */ + +static void +DES3_random_key(krb5_context context, + krb5_keyblock *key) +{ + DES_cblock *k = key->keyvalue.data; + do { + krb5_generate_random_block(k, 3 * sizeof(DES_cblock)); + DES_set_odd_parity(&k[0]); + DES_set_odd_parity(&k[1]); + DES_set_odd_parity(&k[2]); + } while(DES_is_weak_key(&k[0]) || + DES_is_weak_key(&k[1]) || + DES_is_weak_key(&k[2])); +} + +static krb5_error_code +DES3_prf(krb5_context context, + krb5_crypto crypto, + const krb5_data *in, + krb5_data *out) +{ + struct _krb5_checksum_type *ct = crypto->et->checksum; + struct krb5_crypto_iov iov[1]; + krb5_error_code ret; + Checksum result; + krb5_keyblock *derived; + + result.cksumtype = ct->type; + ret = krb5_data_alloc(&result.checksum, ct->checksumsize); + if (ret) { + krb5_set_error_message(context, ret, N_("malloc: out memory", "")); + return ret; + } + + iov[0].data = *in; + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + ret = (*ct->checksum)(context, crypto, NULL, 0, iov, 1, &result); + if (ret) { + krb5_data_free(&result.checksum); + return ret; + } + + if (result.checksum.length < crypto->et->blocksize) + krb5_abortx(context, "internal prf error"); + + derived = NULL; + ret = krb5_derive_key(context, crypto->key.key, + crypto->et->type, "prf", 3, &derived); + if (ret) + krb5_abortx(context, "krb5_derive_key"); + + ret = krb5_data_alloc(out, crypto->et->prf_length); + if (ret) + krb5_abortx(context, "malloc failed"); + + { + const EVP_CIPHER *c = (*crypto->et->keytype->evp)(); + EVP_CIPHER_CTX ctx; + + EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */ + EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1); + EVP_Cipher(&ctx, out->data, result.checksum.data, + crypto->et->prf_length); + EVP_CIPHER_CTX_cleanup(&ctx); + } + + krb5_data_free(&result.checksum); + krb5_free_keyblock(context, derived); + + return ret; +} + +#ifdef DES3_OLD_ENCTYPE +static struct _krb5_key_type keytype_des3 = { + ETYPE_OLD_DES3_CBC_SHA1, + "des3", + 168, + 24, + sizeof(struct _krb5_evp_schedule), + DES3_random_key, + _krb5_evp_schedule, + _krb5_des3_salt, + _krb5_DES3_random_to_key, + _krb5_evp_cleanup, + EVP_des_ede3_cbc +}; +#endif + +static struct _krb5_key_type keytype_des3_derived = { + ETYPE_OLD_DES3_CBC_SHA1, + "des3", + 168, + 24, + sizeof(struct _krb5_evp_schedule), + DES3_random_key, + _krb5_evp_schedule, + _krb5_des3_salt_derived, + _krb5_DES3_random_to_key, + _krb5_evp_cleanup, + EVP_des_ede3_cbc +}; + +#ifdef DES3_OLD_ENCTYPE +static krb5_error_code +RSA_MD5_DES3_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + return _krb5_des_checksum(context, EVP_md5(), key, iov, niov, C); +} + +static krb5_error_code +RSA_MD5_DES3_verify(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + return _krb5_des_verify(context, EVP_md5(), key, iov, niov, C); +} + +struct _krb5_checksum_type _krb5_checksum_rsa_md5_des3 = { + CKSUMTYPE_RSA_MD5_DES3, + "rsa-md5-des3", + 64, + 24, + F_KEYED | F_CPROOF | F_VARIANT, + RSA_MD5_DES3_checksum, + RSA_MD5_DES3_verify +}; +#endif + +struct _krb5_checksum_type _krb5_checksum_hmac_sha1_des3 = { + CKSUMTYPE_HMAC_SHA1_DES3, + "hmac-sha1-des3", + 64, + 20, + F_KEYED | F_CPROOF | F_DERIVED, + _krb5_SP_HMAC_SHA1_checksum, + NULL +}; + +#ifdef DES3_OLD_ENCTYPE +struct _krb5_encryption_type _krb5_enctype_des3_cbc_md5 = { + ETYPE_DES3_CBC_MD5, + "des3-cbc-md5", + NULL, + 8, + 8, + 8, + &keytype_des3, + &_krb5_checksum_rsa_md5, + &_krb5_checksum_rsa_md5_des3, + F_OLD, + _krb5_evp_encrypt, + _krb5_evp_encrypt_iov, + 0, + NULL +}; +#endif + +struct _krb5_encryption_type _krb5_enctype_des3_cbc_sha1 = { + ETYPE_DES3_CBC_SHA1, + "des3-cbc-sha1", + NULL, + 8, + 8, + 8, + &keytype_des3_derived, + &_krb5_checksum_sha1, + &_krb5_checksum_hmac_sha1_des3, + F_DERIVED | F_RFC3961_ENC | F_RFC3961_KDF | F_OLD, + _krb5_evp_encrypt, + _krb5_evp_encrypt_iov, + 16, + DES3_prf +}; + +#ifdef DES3_OLD_ENCTYPE +struct _krb5_encryption_type _krb5_enctype_old_des3_cbc_sha1 = { + ETYPE_OLD_DES3_CBC_SHA1, + "old-des3-cbc-sha1", + NULL, + 8, + 8, + 8, + &keytype_des3, + &_krb5_checksum_sha1, + &_krb5_checksum_hmac_sha1_des3, + F_OLD, + _krb5_evp_encrypt, + _krb5_evp_encrypt_iov, + 0, + NULL +}; +#endif + +struct _krb5_encryption_type _krb5_enctype_des3_cbc_none = { + ETYPE_DES3_CBC_NONE, + "des3-cbc-none", + NULL, + 8, + 8, + 0, + &keytype_des3_derived, + &_krb5_checksum_none, + NULL, + F_PSEUDO | F_OLD, + _krb5_evp_encrypt, + _krb5_evp_encrypt_iov, + 0, + NULL +}; + +void +_krb5_DES3_random_to_key(krb5_context context, + krb5_keyblock *key, + const void *data, + size_t size) +{ + unsigned char *x = key->keyvalue.data; + const u_char *q = data; + DES_cblock *k; + int i, j; + + memset(key->keyvalue.data, 0, key->keyvalue.length); + for (i = 0; i < 3; ++i) { + unsigned char foo; + for (j = 0; j < 7; ++j) { + unsigned char b = q[7 * i + j]; + + x[8 * i + j] = b; + } + foo = 0; + for (j = 6; j >= 0; --j) { + foo |= q[7 * i + j] & 1; + foo <<= 1; + } + x[8 * i + 7] = foo; + } + k = key->keyvalue.data; + for (i = 0; i < 3; i++) { + DES_set_odd_parity(&k[i]); + if(DES_is_weak_key(&k[i])) + _krb5_xor8(k[i], (const unsigned char*)"\0\0\0\0\0\0\0\xf0"); + } +} diff --git a/third_party/heimdal/lib/krb5/crypto-evp.c b/third_party/heimdal/lib/krb5/crypto-evp.c new file mode 100644 index 0000000..82237f1 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-evp.c @@ -0,0 +1,674 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +void +_krb5_evp_schedule(krb5_context context, + struct _krb5_key_type *kt, + struct _krb5_key_data *kd) +{ + struct _krb5_evp_schedule *key = kd->schedule->data; + const EVP_CIPHER *c = (*kt->evp)(); + + EVP_CIPHER_CTX_init(&key->ectx); + EVP_CIPHER_CTX_init(&key->dctx); + + EVP_CipherInit_ex(&key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1); + EVP_CipherInit_ex(&key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0); +} + +void +_krb5_evp_cleanup(krb5_context context, struct _krb5_key_data *kd) +{ + struct _krb5_evp_schedule *key = kd->schedule->data; + EVP_CIPHER_CTX_cleanup(&key->ectx); + EVP_CIPHER_CTX_cleanup(&key->dctx); +} + +int +_krb5_evp_digest_iov(krb5_crypto crypto, + const struct krb5_crypto_iov *iov, + int niov, + void *hash, + unsigned int *hsize, + const EVP_MD *md, + ENGINE *engine) +{ + EVP_MD_CTX *ctx; + int ret, i; + krb5_data current = {0,0}; + + if (crypto != NULL) { + if (crypto->mdctx == NULL) + crypto->mdctx = EVP_MD_CTX_create(); + if (crypto->mdctx == NULL) + return 0; + ctx = crypto->mdctx; + } else + ctx = EVP_MD_CTX_create(); + + ret = EVP_DigestInit_ex(ctx, md, engine); + if (ret != 1) + goto out; + + /* Minimize EVP calls by coalescing contiguous iovec elements */ + for (i = 0; i < niov; i++) { + if (_krb5_crypto_iov_should_sign(&iov[i])) { + if (current.data && + (char *)current.data + current.length == iov[i].data.data) { + current.length += iov[i].data.length; + } else { + if (current.data) { + ret = EVP_DigestUpdate(ctx, current.data, current.length); + if (ret != 1) + goto out; + } + current = iov[i].data; + } + } + } + + if (current.data) { + ret = EVP_DigestUpdate(ctx, current.data, current.length); + if (ret != 1) + goto out; + } + + ret = EVP_DigestFinal_ex(ctx, hash, hsize); + +out: + if (crypto == NULL) + EVP_MD_CTX_destroy(ctx); + + return ret; +} + +krb5_error_code +_krb5_evp_hmac_iov(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + const struct krb5_crypto_iov *iov, + int niov, + void *hmac, + unsigned int *hmaclen, + const EVP_MD *md, + ENGINE *engine) +{ + HMAC_CTX *ctx; + krb5_data current = {0, NULL}; + int i; + + if (crypto != NULL) { + if (crypto->hmacctx == NULL) + crypto->hmacctx = HMAC_CTX_new(); + ctx = crypto->hmacctx; + } else { + ctx = HMAC_CTX_new(); + } + if (ctx == NULL) + return krb5_enomem(context); + + if (HMAC_Init_ex(ctx, key->key->keyvalue.data, key->key->keyvalue.length, + md, engine) == 0) { + HMAC_CTX_free(ctx); + return krb5_enomem(context); + } + + for (i = 0; i < niov; i++) { + if (_krb5_crypto_iov_should_sign(&iov[i])) { + if (current.data && + (char *)current.data + current.length == iov[i].data.data) { + current.length += iov[i].data.length; + } else { + if (current.data) + HMAC_Update(ctx, current.data, current.length); + current = iov[i].data; + } + } + } + + if (current.data) + HMAC_Update(ctx, current.data, current.length); + + HMAC_Final(ctx, hmac, hmaclen); + + if (crypto == NULL) + HMAC_CTX_free(ctx); + + return 0; +} + +krb5_error_code +_krb5_evp_encrypt(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ivec) +{ + struct _krb5_evp_schedule *ctx = key->schedule->data; + EVP_CIPHER_CTX *c; + c = encryptp ? &ctx->ectx : &ctx->dctx; + if (ivec == NULL) { + /* alloca ? */ + size_t len2 = EVP_CIPHER_CTX_iv_length(c); + void *loiv = malloc(len2); + if (loiv == NULL) + return krb5_enomem(context); + memset(loiv, 0, len2); + EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1); + free(loiv); + } else + EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1); + EVP_Cipher(c, data, data, len); + return 0; +} + +struct _krb5_evp_iov_cursor +{ + struct krb5_crypto_iov *iov; + int niov; + krb5_data current; + int nextidx; +}; + +static const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 }; + +static inline int +_krb5_evp_iov_should_encrypt(struct krb5_crypto_iov *iov) +{ + return (iov->flags == KRB5_CRYPTO_TYPE_DATA + || iov->flags == KRB5_CRYPTO_TYPE_HEADER + || iov->flags == KRB5_CRYPTO_TYPE_PADDING); +} +/* + * If we have a group of iovecs which have been split up from + * a single common buffer, expand the 'current' iovec out to + * be as large as possible. + */ + +static inline void +_krb5_evp_iov_cursor_expand(struct _krb5_evp_iov_cursor *cursor) +{ + if (cursor->nextidx == cursor->niov) + return; + + while (_krb5_evp_iov_should_encrypt(&cursor->iov[cursor->nextidx])) { + if (cursor->iov[cursor->nextidx].data.length != 0 && + ((char *)cursor->current.data + cursor->current.length + != cursor->iov[cursor->nextidx].data.data)) { + return; + } + cursor->current.length += cursor->iov[cursor->nextidx].data.length; + cursor->nextidx++; + } + + return; +} + +/* Move the cursor along to the start of the next block to be + * encrypted */ +static inline void +_krb5_evp_iov_cursor_nextcrypt(struct _krb5_evp_iov_cursor *cursor) +{ + for (; cursor->nextidx < cursor->niov; cursor->nextidx++) { + if (_krb5_evp_iov_should_encrypt(&cursor->iov[cursor->nextidx]) + && cursor->iov[cursor->nextidx].data.length != 0) { + cursor->current = cursor->iov[cursor->nextidx].data; + cursor->nextidx++; + _krb5_evp_iov_cursor_expand(cursor); + return; + } + } + + cursor->current.length = 0; /* No matches, so we're done here */ +} + +static inline void +_krb5_evp_iov_cursor_init(struct _krb5_evp_iov_cursor *cursor, + struct krb5_crypto_iov *iov, int niov) +{ + memset(cursor, 0, sizeof(struct _krb5_evp_iov_cursor)); + + cursor->iov = iov; + cursor->niov = niov; + cursor->nextidx = 0; + + /* Move along to the first block we're going to be encrypting */ + _krb5_evp_iov_cursor_nextcrypt(cursor); +} + +static inline void +_krb5_evp_iov_cursor_advance(struct _krb5_evp_iov_cursor *cursor, + size_t amount) +{ + while (amount > 0) { + if (cursor->current.length > amount) { + cursor->current.data = (char *)cursor->current.data + amount; + cursor->current.length -= amount; + return; + } + amount -= cursor->current.length; + _krb5_evp_iov_cursor_nextcrypt(cursor); + } +} + +static inline int +_krb5_evp_iov_cursor_done(struct _krb5_evp_iov_cursor *cursor) +{ + return (cursor->nextidx == cursor->niov && cursor->current.length == 0); +} + +/* Fill a memory buffer with data from one or more iovecs. Doesn't + * advance the passed in cursor - use outcursor for the position + * at the end + */ +static inline void +_krb5_evp_iov_cursor_fillbuf(struct _krb5_evp_iov_cursor *cursor, + unsigned char *buf, size_t length, + struct _krb5_evp_iov_cursor *outcursor) +{ + struct _krb5_evp_iov_cursor cursorint; + + cursorint = *cursor; + + while (length > 0 && !_krb5_evp_iov_cursor_done(&cursorint)) { + if (cursorint.current.length > length) { + memcpy(buf, cursorint.current.data, length); + _krb5_evp_iov_cursor_advance(&cursorint, length); + length = 0; + } else { + memcpy(buf, cursorint.current.data, cursorint.current.length); + length -= cursorint.current.length; + buf += cursorint.current.length; + _krb5_evp_iov_cursor_nextcrypt(&cursorint); + } + } + + if (outcursor != NULL) + *outcursor = cursorint; +} + +/* Fill an iovec from a memory buffer. Always advances the cursor to + * the end of the filled region + */ +static inline void +_krb5_evp_iov_cursor_fillvec(struct _krb5_evp_iov_cursor *cursor, + unsigned char *buf, size_t length) +{ + while (length > 0 && !_krb5_evp_iov_cursor_done(cursor)) { + if (cursor->current.length > length) { + memcpy(cursor->current.data, buf, length); + _krb5_evp_iov_cursor_advance(cursor, length); + length = 0; + } else { + memcpy(cursor->current.data, buf, cursor->current.length); + length -= cursor->current.length; + buf += cursor->current.length; + _krb5_evp_iov_cursor_nextcrypt(cursor); + } + } +} + +static size_t +_krb5_evp_iov_cryptlength(struct krb5_crypto_iov *iov, int niov) +{ + int i; + size_t length = 0; + + for (i = 0; i < niov; i++) { + if (_krb5_evp_iov_should_encrypt(&iov[i])) + length += iov[i].data.length; + } + + return length; +} + +int +_krb5_evp_encrypt_iov(krb5_context context, + struct _krb5_key_data *key, + struct krb5_crypto_iov *iov, + int niov, + krb5_boolean encryptp, + int usage, + void *ivec) +{ + size_t blocksize, blockmask, wholeblocks; + struct _krb5_evp_schedule *ctx = key->schedule->data; + unsigned char tmp[EVP_MAX_BLOCK_LENGTH]; + EVP_CIPHER_CTX *c; + struct _krb5_evp_iov_cursor cursor; + + c = encryptp ? &ctx->ectx : &ctx->dctx; + + blocksize = EVP_CIPHER_CTX_block_size(c); + + blockmask = ~(blocksize - 1); + + if (ivec) + EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1); + else + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + + _krb5_evp_iov_cursor_init(&cursor, iov, niov); + + while (!_krb5_evp_iov_cursor_done(&cursor)) { + + /* Number of bytes of data in this iovec that are in whole blocks */ + wholeblocks = cursor.current.length & ~blockmask; + + if (wholeblocks != 0) { + EVP_Cipher(c, cursor.current.data, + cursor.current.data, wholeblocks); + _krb5_evp_iov_cursor_advance(&cursor, wholeblocks); + } + + /* If there's a partial block of data remaining in the current + * iovec, steal enough from subsequent iovecs to form a whole block */ + if (cursor.current.length > 0 && cursor.current.length < blocksize) { + /* Build up a block's worth of data in tmp, leaving the cursor + * pointing at where we started */ + _krb5_evp_iov_cursor_fillbuf(&cursor, tmp, blocksize, NULL); + + EVP_Cipher(c, tmp, tmp, blocksize); + + /* Copy the data in tmp back into the iovecs that it came from, + * advancing the cursor */ + _krb5_evp_iov_cursor_fillvec(&cursor, tmp, blocksize); + } + } + + return 0; +} + +int +_krb5_evp_encrypt_iov_cts(krb5_context context, + struct _krb5_key_data *key, + struct krb5_crypto_iov *iov, + int niov, + krb5_boolean encryptp, + int usage, + void *ivec) +{ + size_t blocksize, blockmask, wholeblocks, length; + size_t remaining, partiallen; + struct _krb5_evp_iov_cursor cursor, lastpos; + struct _krb5_evp_schedule *ctx = key->schedule->data; + unsigned char tmp[EVP_MAX_BLOCK_LENGTH], tmp2[EVP_MAX_BLOCK_LENGTH]; + unsigned char tmp3[EVP_MAX_BLOCK_LENGTH], ivec2[EVP_MAX_BLOCK_LENGTH]; + EVP_CIPHER_CTX *c; + int i; + + c = encryptp ? &ctx->ectx : &ctx->dctx; + + blocksize = EVP_CIPHER_CTX_block_size(c); + blockmask = ~(blocksize - 1); + + length = _krb5_evp_iov_cryptlength(iov, niov); + + if (length < blocksize) { + krb5_set_error_message(context, EINVAL, + "message block too short"); + return EINVAL; + } + + if (length == blocksize) + return _krb5_evp_encrypt_iov(context, key, iov, niov, + encryptp, usage, ivec); + + if (ivec) + EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1); + else + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + + if (encryptp) { + /* On our first pass, we want to process everything but the + * final partial block */ + remaining = ((length - 1) & blockmask); + partiallen = length - remaining; + + memset(&lastpos, 0, sizeof(lastpos)); /* Keep the compiler happy */ + } else { + /* Decryption needs to leave 2 whole blocks and a partial for + * further processing */ + if (length > 2 * blocksize) { + remaining = (((length - 1) / blocksize) * blocksize) - (blocksize*2); + partiallen = length - remaining - (blocksize * 2); + } else { + remaining = 0; + partiallen = length - blocksize; + } + } + + _krb5_evp_iov_cursor_init(&cursor, iov, niov); + while (remaining > 0) { + /* If the iovec has more data than we need, just use it */ + if (cursor.current.length >= remaining) { + EVP_Cipher(c, cursor.current.data, cursor.current.data, remaining); + + if (encryptp) { + /* We've just encrypted the last block of data. Make a copy + * of it (and its location) for the CTS dance, below */ + lastpos = cursor; + _krb5_evp_iov_cursor_advance(&lastpos, remaining - blocksize); + memcpy(ivec2, lastpos.current.data, blocksize); + } + + _krb5_evp_iov_cursor_advance(&cursor, remaining); + remaining = 0; + } else { + /* Use as much as we can, firstly all of the whole blocks */ + wholeblocks = cursor.current.length & blockmask; + + if (wholeblocks > 0) { + EVP_Cipher(c, cursor.current.data, cursor.current.data, + wholeblocks); + _krb5_evp_iov_cursor_advance(&cursor, wholeblocks); + remaining -= wholeblocks; + } + + /* Then, if we have partial data left, steal enough from subsequent + * iovecs to make a whole block */ + if (cursor.current.length > 0 && cursor.current.length < blocksize) { + if (encryptp && remaining == blocksize) + lastpos = cursor; + + _krb5_evp_iov_cursor_fillbuf(&cursor, ivec2, blocksize, NULL); + EVP_Cipher(c, ivec2, ivec2, blocksize); + _krb5_evp_iov_cursor_fillvec(&cursor, ivec2, blocksize); + + remaining -= blocksize; + } + } + } + + /* Encryption */ + if (encryptp) { + /* Copy the partial block into tmp */ + _krb5_evp_iov_cursor_fillbuf(&cursor, tmp, partiallen, NULL); + + /* XOR the final partial block with ivec2 */ + for (i = 0; i < partiallen; i++) + tmp[i] = tmp[i] ^ ivec2[i]; + for (; i < blocksize; i++) + tmp[i] = 0 ^ ivec2[i]; /* XOR 0s if partial block exhausted */ + + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, tmp, tmp, blocksize); + + _krb5_evp_iov_cursor_fillvec(&lastpos, tmp, blocksize); + _krb5_evp_iov_cursor_fillvec(&cursor, ivec2, partiallen); + + if (ivec) + memcpy(ivec, tmp, blocksize); + + return 0; + } + + /* Decryption */ + + /* Make a copy of the 2nd last full ciphertext block in ivec2 before + * decrypting it. If no such block exists, use ivec or zero_ivec */ + if (length <= blocksize * 2) { + if (ivec) + memcpy(ivec2, ivec, blocksize); + else + memcpy(ivec2, zero_ivec, blocksize); + } else { + _krb5_evp_iov_cursor_fillbuf(&cursor, ivec2, blocksize, NULL); + EVP_Cipher(c, tmp, ivec2, blocksize); + _krb5_evp_iov_cursor_fillvec(&cursor, tmp, blocksize); + } + + lastpos = cursor; /* Remember where the last block is */ + _krb5_evp_iov_cursor_fillbuf(&cursor, tmp, blocksize, &cursor); + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, tmp2, tmp, blocksize); /* tmp eventually becomes output ivec */ + + _krb5_evp_iov_cursor_fillbuf(&cursor, tmp3, partiallen, NULL); + + memcpy(tmp3 + partiallen, tmp2 + partiallen, blocksize - partiallen); /* xor 0 */ + for (i = 0; i < partiallen; i++) + tmp2[i] = tmp2[i] ^ tmp3[i]; + + _krb5_evp_iov_cursor_fillvec(&cursor, tmp2, partiallen); + + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, tmp3, tmp3, blocksize); + + for (i = 0; i < blocksize; i++) + tmp3[i] ^= ivec2[i]; + + _krb5_evp_iov_cursor_fillvec(&lastpos, tmp3, blocksize); + + if (ivec) + memcpy(ivec, tmp, blocksize); + + return 0; +} + +krb5_error_code +_krb5_evp_encrypt_cts(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ivec) +{ + size_t i, blocksize; + struct _krb5_evp_schedule *ctx = key->schedule->data; + unsigned char tmp[EVP_MAX_BLOCK_LENGTH], ivec2[EVP_MAX_BLOCK_LENGTH]; + EVP_CIPHER_CTX *c; + unsigned char *p; + + c = encryptp ? &ctx->ectx : &ctx->dctx; + + blocksize = EVP_CIPHER_CTX_block_size(c); + + if (len < blocksize) { + krb5_set_error_message(context, EINVAL, + "message block too short"); + return EINVAL; + } else if (len == blocksize) { + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, data, data, len); + return 0; + } + + if (ivec) + EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1); + else + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + + if (encryptp) { + + p = data; + i = ((len - 1) / blocksize) * blocksize; + EVP_Cipher(c, p, p, i); + p += i - blocksize; + len -= i; + memcpy(ivec2, p, blocksize); + + for (i = 0; i < len; i++) + tmp[i] = p[i + blocksize] ^ ivec2[i]; + for (; i < blocksize; i++) + tmp[i] = 0 ^ ivec2[i]; + + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, p, tmp, blocksize); + + memcpy(p + blocksize, ivec2, len); + if (ivec) + memcpy(ivec, p, blocksize); + } else { + unsigned char tmp2[EVP_MAX_BLOCK_LENGTH], tmp3[EVP_MAX_BLOCK_LENGTH]; + + p = data; + if (len > blocksize * 2) { + /* remove last two blocks and round up, decrypt this with cbc, then do cts dance */ + i = ((((len - blocksize * 2) + blocksize - 1) / blocksize) * blocksize); + memcpy(ivec2, p + i - blocksize, blocksize); + EVP_Cipher(c, p, p, i); + p += i; + len -= i + blocksize; + } else { + if (ivec) + memcpy(ivec2, ivec, blocksize); + else + memcpy(ivec2, zero_ivec, blocksize); + len -= blocksize; + } + + memcpy(tmp, p, blocksize); + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, tmp2, p, blocksize); + + memcpy(tmp3, p + blocksize, len); + memcpy(tmp3 + len, tmp2 + len, blocksize - len); /* xor 0 */ + + for (i = 0; i < len; i++) + p[i + blocksize] = tmp2[i] ^ tmp3[i]; + + EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1); + EVP_Cipher(c, p, tmp3, blocksize); + + for (i = 0; i < blocksize; i++) + p[i] ^= ivec2[i]; + if (ivec) + memcpy(ivec, tmp, blocksize); + } + return 0; +} diff --git a/third_party/heimdal/lib/krb5/crypto-null.c b/third_party/heimdal/lib/krb5/crypto-null.c new file mode 100644 index 0000000..a62a57f --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-null.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#ifndef HEIMDAL_SMALLER +#define DES3_OLD_ENCTYPE 1 +#endif + +static struct _krb5_key_type keytype_null = { + KRB5_ENCTYPE_NULL, + "null", + 0, + 0, + 0, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +static krb5_error_code +NONE_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C) +{ + return 0; +} + +struct _krb5_checksum_type _krb5_checksum_none = { + CKSUMTYPE_NONE, + "none", + 1, + 0, + 0, + NONE_checksum, + NULL +}; + +static krb5_error_code +NULL_encrypt(krb5_context context, + struct _krb5_key_data *key, + void *data, + size_t len, + krb5_boolean encryptp, + int usage, + void *ivec) +{ + return 0; +} + +struct _krb5_encryption_type _krb5_enctype_null = { + ETYPE_NULL, + "null", + NULL, + 1, + 1, + 0, + &keytype_null, + &_krb5_checksum_none, + NULL, + F_DISABLED | F_OLD, + NULL_encrypt, + NULL, + 0, + NULL +}; diff --git a/third_party/heimdal/lib/krb5/crypto-pk.c b/third_party/heimdal/lib/krb5/crypto-pk.c new file mode 100644 index 0000000..24a07cd --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-pk.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#include + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_pk_octetstring2key(krb5_context context, + krb5_enctype type, + const void *dhdata, + size_t dhsize, + const heim_octet_string *c_n, + const heim_octet_string *k_n, + krb5_keyblock *key) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(type); + krb5_error_code ret; + size_t keylen, offset; + void *keydata; + unsigned char counter; + unsigned char shaoutput[SHA_DIGEST_LENGTH]; + EVP_MD_CTX *m; + + if(et == NULL) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + type); + return KRB5_PROG_ETYPE_NOSUPP; + } + keylen = (et->keytype->bits + 7) / 8; + + keydata = malloc(keylen); + if (keydata == NULL) + return krb5_enomem(context); + + m = EVP_MD_CTX_create(); + if (m == NULL) { + free(keydata); + return krb5_enomem(context); + } + + counter = 0; + offset = 0; + do { + + EVP_DigestInit_ex(m, EVP_sha1(), NULL); + EVP_DigestUpdate(m, &counter, 1); + EVP_DigestUpdate(m, dhdata, dhsize); + + if (c_n) + EVP_DigestUpdate(m, c_n->data, c_n->length); + if (k_n) + EVP_DigestUpdate(m, k_n->data, k_n->length); + + EVP_DigestFinal_ex(m, shaoutput, NULL); + + memcpy((unsigned char *)keydata + offset, + shaoutput, + min(keylen - offset, sizeof(shaoutput))); + + offset += sizeof(shaoutput); + counter++; + } while(offset < keylen); + memset_s(shaoutput, sizeof(shaoutput), 0, sizeof(shaoutput)); + + EVP_MD_CTX_destroy(m); + + ret = krb5_random_to_key(context, type, keydata, keylen, key); + memset_s(keydata, sizeof(keylen), 0, sizeof(keylen)); + free(keydata); + return ret; +} + +static krb5_error_code +encode_uvinfo(krb5_context context, krb5_const_principal p, krb5_data *data) +{ + KRB5PrincipalName pn; + krb5_error_code ret; + size_t size = 0; + + pn.principalName = p->name; + pn.realm = p->realm; + + ASN1_MALLOC_ENCODE(KRB5PrincipalName, data->data, data->length, + &pn, &size, ret); + if (ret) { + krb5_data_zero(data); + krb5_set_error_message(context, ret, + N_("Failed to encode KRB5PrincipalName", "")); + return ret; + } + if (data->length != size) + krb5_abortx(context, "asn1 compiler internal error"); + return 0; +} + +static krb5_error_code +encode_otherinfo(krb5_context context, + const AlgorithmIdentifier *ai, + krb5_const_principal client, + krb5_const_principal server, + krb5_enctype enctype, + const krb5_data *as_req, + const krb5_data *pk_as_rep, + const Ticket *ticket, + krb5_data *other) +{ + PkinitSP80056AOtherInfo otherinfo; + PkinitSuppPubInfo pubinfo; + krb5_error_code ret; + krb5_data pub; + size_t size = 0; + + krb5_data_zero(other); + memset(&otherinfo, 0, sizeof(otherinfo)); + memset(&pubinfo, 0, sizeof(pubinfo)); + + pubinfo.enctype = enctype; + pubinfo.as_REQ = *as_req; + pubinfo.pk_as_rep = *pk_as_rep; + pubinfo.ticket = *ticket; + ASN1_MALLOC_ENCODE(PkinitSuppPubInfo, pub.data, pub.length, + &pubinfo, &size, ret); + if (ret) { + krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + return ret; + } + if (pub.length != size) + krb5_abortx(context, "asn1 compiler internal error"); + + ret = encode_uvinfo(context, client, &otherinfo.partyUInfo); + if (ret) { + free(pub.data); + return ret; + } + ret = encode_uvinfo(context, server, &otherinfo.partyVInfo); + if (ret) { + free(otherinfo.partyUInfo.data); + free(pub.data); + return ret; + } + + otherinfo.algorithmID = *ai; + otherinfo.suppPubInfo = &pub; + + ASN1_MALLOC_ENCODE(PkinitSP80056AOtherInfo, other->data, other->length, + &otherinfo, &size, ret); + free(otherinfo.partyUInfo.data); + free(otherinfo.partyVInfo.data); + free(pub.data); + if (ret) { + krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + return ret; + } + if (other->length != size) + krb5_abortx(context, "asn1 compiler internal error"); + + return 0; +} + + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_pk_kdf(krb5_context context, + const struct AlgorithmIdentifier *ai, + const void *dhdata, + size_t dhsize, + krb5_const_principal client, + krb5_const_principal server, + krb5_enctype enctype, + const krb5_data *as_req, + const krb5_data *pk_as_rep, + const Ticket *ticket, + krb5_keyblock *key) +{ + struct _krb5_encryption_type *et; + krb5_error_code ret; + krb5_data other; + size_t keylen, offset; + uint32_t counter; + unsigned char *keydata; + unsigned char shaoutput[SHA512_DIGEST_LENGTH]; + const EVP_MD *md; + EVP_MD_CTX *m; + + if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha1, &ai->algorithm) == 0) { + md = EVP_sha1(); + } else if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha256, &ai->algorithm) == 0) { + md = EVP_sha256(); + } else if (der_heim_oid_cmp(&asn1_oid_id_pkinit_kdf_ah_sha512, &ai->algorithm) == 0) { + md = EVP_sha512(); + } else { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("KDF not supported", "")); + return KRB5_PROG_ETYPE_NOSUPP; + } + if (ai->parameters != NULL && + (ai->parameters->length != 2 || + memcmp(ai->parameters->data, "\x05\x00", 2) != 0)) + { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("kdf params not NULL or the NULL-type", + "")); + return KRB5_PROG_ETYPE_NOSUPP; + } + + et = _krb5_find_enctype(enctype); + if(et == NULL) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + enctype); + return KRB5_PROG_ETYPE_NOSUPP; + } + keylen = (et->keytype->bits + 7) / 8; + + keydata = malloc(keylen); + if (keydata == NULL) + return krb5_enomem(context); + + ret = encode_otherinfo(context, ai, client, server, + enctype, as_req, pk_as_rep, ticket, &other); + if (ret) { + free(keydata); + return ret; + } + + m = EVP_MD_CTX_create(); + if (m == NULL) { + free(keydata); + free(other.data); + return krb5_enomem(context); + } + + offset = 0; + counter = 1; + do { + unsigned char cdata[4]; + + EVP_DigestInit_ex(m, md, NULL); + _krb5_put_int(cdata, counter, 4); + EVP_DigestUpdate(m, cdata, 4); + EVP_DigestUpdate(m, dhdata, dhsize); + EVP_DigestUpdate(m, other.data, other.length); + + EVP_DigestFinal_ex(m, shaoutput, NULL); + + memcpy((unsigned char *)keydata + offset, + shaoutput, + min(keylen - offset, EVP_MD_CTX_size(m))); + + offset += EVP_MD_CTX_size(m); + counter++; + } while(offset < keylen); + memset_s(shaoutput, sizeof(shaoutput), 0, sizeof(shaoutput)); + + EVP_MD_CTX_destroy(m); + free(other.data); + + ret = krb5_random_to_key(context, enctype, keydata, keylen, key); + memset_s(keydata, sizeof(keylen), 0, sizeof(keylen)); + free(keydata); + + return ret; +} diff --git a/third_party/heimdal/lib/krb5/crypto-rand.c b/third_party/heimdal/lib/krb5/crypto-rand.c new file mode 100644 index 0000000..7b126c6 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-rand.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#undef HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE +#define HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE + +#define ENTROPY_NEEDED 128 + +static HEIMDAL_MUTEX crypto_mutex = HEIMDAL_MUTEX_INITIALIZER; + +static int +seed_something(void) +{ +#ifndef NO_RANDFILE + char buf[1024], seedfile[256]; + + /* If there is a seed file, load it. But such a file cannot be trusted, + so use 0 for the entropy estimate */ + if (RAND_file_name(seedfile, sizeof(seedfile))) { + int fd; + fd = open(seedfile, O_RDONLY | O_BINARY | O_CLOEXEC); + if (fd >= 0) { + ssize_t ret; + rk_cloexec(fd); + ret = read(fd, buf, sizeof(buf)); + if (ret > 0) + RAND_add(buf, ret, 0.0); + close(fd); + } else + seedfile[0] = '\0'; + } else + seedfile[0] = '\0'; +#endif + + /* Calling RAND_status() will try to use /dev/urandom if it exists so + we do not have to deal with it. */ + if (RAND_status() != 1) { + /* TODO: Once a Windows CryptoAPI RAND method is defined, we + can use that and failover to another method. */ + } + + if (RAND_status() == 1) { +#ifndef NO_RANDFILE + /* Update the seed file */ + if (seedfile[0]) + RAND_write_file(seedfile); +#endif + + return 0; + } else + return -1; +} + +/** + * Fill buffer buf with len bytes of PRNG randomness that is ok to use + * for key generation, padding and public diclosing the randomness w/o + * disclosing the randomness source. + * + * This function can fail, and callers must check the return value. + * + * @param buf a buffer to fill with randomness + * @param len length of memory that buf points to. + * + * @return return 0 on success or HEIM_ERR_RANDOM_OFFLINE if the + * funcation failed to initialize the randomness source. + * + * @ingroup krb5_crypto + */ + +HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_generate_random(void *buf, size_t len) +{ + static int rng_initialized = 0; + int ret; + + HEIMDAL_MUTEX_lock(&crypto_mutex); + if (!rng_initialized) { + if (seed_something()) { + HEIMDAL_MUTEX_unlock(&crypto_mutex); + return HEIM_ERR_RANDOM_OFFLINE; + } + rng_initialized = 1; + } + if (RAND_bytes(buf, len) <= 0) + ret = HEIM_ERR_RANDOM_OFFLINE; + else + ret = 0; + HEIMDAL_MUTEX_unlock(&crypto_mutex); + + return ret; +} + +/** + * Fill buffer buf with len bytes of PRNG randomness that is ok to use + * for key generation, padding and public diclosing the randomness w/o + * disclosing the randomness source. + * + * This function can NOT fail, instead it will abort() and program will crash. + * + * If this function is called after a successful krb5_init_context(), + * the chance of it failing is low due to that krb5_init_context() + * pulls out some random, and quite commonly the randomness sources + * will not fail once it have started to produce good output, + * /dev/urandom behavies that way. + * + * @param buf a buffer to fill with randomness + * @param len length of memory that buf points to. + * + * @ingroup krb5_crypto + */ + + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_generate_random_block(void *buf, size_t len) +{ + int ret = krb5_generate_random(buf, len); + if (ret) + krb5_abortx(NULL, "Failed to generate random block"); +} diff --git a/third_party/heimdal/lib/krb5/crypto-stubs.c b/third_party/heimdal/lib/krb5/crypto-stubs.c new file mode 100644 index 0000000..5251f88 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto-stubs.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +/* These are stub functions for the standalone RFC3961 crypto library */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_context(krb5_context *context) +{ + krb5_context p; + + *context = NULL; + + /* should have a run_once */ + bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR); + + p = calloc(1, sizeof(*p)); + if(!p) + return ENOMEM; + + *context = p; + return 0; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_context(krb5_context context) +{ + krb5_clear_error_message(context); + + if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) { + rk_SOCK_EXIT(); + } + + memset(context, 0, sizeof(*context)); + free(context); +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +_krb5_homedir_access(krb5_context context) { + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_log(krb5_context context, + krb5_log_facility *fac, + int level, + const char *fmt, + ...) +{ + return 0; +} + +void KRB5_LIB_FUNCTION +_krb5_debug(krb5_context context, + int level, + const char *fmt, + ...) +{ +} + + +/* This function is currently just used to get the location of the EGD + * socket. If we're not using an EGD, then we can just return NULL */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_config_get_string (krb5_context context, + const krb5_config_section *c, + ...) +{ + return NULL; +} diff --git a/third_party/heimdal/lib/krb5/crypto.c b/third_party/heimdal/lib/krb5/crypto.c new file mode 100644 index 0000000..b52f084 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto.c @@ -0,0 +1,3248 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +struct _krb5_key_usage { + unsigned usage; + struct _krb5_key_data key; +}; + + +#ifndef HEIMDAL_SMALLER +#define DES3_OLD_ENCTYPE 1 +#endif + +static krb5_error_code _get_derived_key(krb5_context, krb5_crypto, + unsigned, struct _krb5_key_data**); +static struct _krb5_key_data *_new_derived_key(krb5_crypto crypto, unsigned usage); + +static void free_key_schedule(krb5_context, + struct _krb5_key_data *, + struct _krb5_encryption_type *); + +/* + * Converts etype to a user readable string and sets as a side effect + * the krb5_error_message containing this string. Returns + * KRB5_PROG_ETYPE_NOSUPP in not the conversion of the etype failed in + * which case the error code of the etype convesion is returned. + */ + +static krb5_error_code +unsupported_enctype(krb5_context context, krb5_enctype etype) +{ + krb5_error_code ret; + char *name; + + ret = krb5_enctype_to_string(context, etype, &name); + if (ret) + return ret; + + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("Encryption type %s not supported", ""), + name); + free(name); + return KRB5_PROG_ETYPE_NOSUPP; +} + +/* + * + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_keysize(krb5_context context, + krb5_enctype type, + size_t *keysize) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(type); + if(et == NULL) { + return unsupported_enctype (context, type); + } + *keysize = et->keytype->size; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_keybits(krb5_context context, + krb5_enctype type, + size_t *keybits) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(type); + if(et == NULL) { + return unsupported_enctype (context, type); + } + *keybits = et->keytype->bits; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_generate_random_keyblock(krb5_context context, + krb5_enctype type, + krb5_keyblock *key) +{ + krb5_error_code ret; + struct _krb5_encryption_type *et = _krb5_find_enctype(type); + if(et == NULL) { + return unsupported_enctype (context, type); + } + ret = krb5_data_alloc(&key->keyvalue, et->keytype->size); + if(ret) + return ret; + key->keytype = type; + if(et->keytype->random_key) + (*et->keytype->random_key)(context, key); + else + krb5_generate_random_block(key->keyvalue.data, + key->keyvalue.length); + return 0; +} + +static krb5_error_code +_key_schedule(krb5_context context, + struct _krb5_key_data *key) +{ + krb5_error_code ret; + struct _krb5_encryption_type *et; + struct _krb5_key_type *kt; + + if (key->schedule != NULL) + return 0; + + et = _krb5_find_enctype(key->key->keytype); + + if (et == NULL) { + return unsupported_enctype (context, + key->key->keytype); + } + + kt = et->keytype; + + if(kt->schedule == NULL) + return 0; + ALLOC(key->schedule, 1); + if (key->schedule == NULL) + return krb5_enomem(context); + ret = krb5_data_alloc(key->schedule, kt->schedule_size); + if(ret) { + free(key->schedule); + key->schedule = NULL; + return ret; + } + (*kt->schedule)(context, kt, key); + return 0; +} + +/************************************************************ + * * + ************************************************************/ + +static krb5_error_code +EVP_unkeyed_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *C, + const EVP_MD *md) +{ + if (_krb5_evp_digest_iov(crypto, + iov, niov, + C->checksum.data, NULL, + md, NULL) != 1) + krb5_abortx(context, "unkeyed checksum failed"); + + return 0; +} + +#define EVP_SHA_CHECKSUM(name) \ + \ + static krb5_error_code \ + SHA ## name ##_checksum(krb5_context context, \ + krb5_crypto crypto, \ + struct _krb5_key_data *key, \ + unsigned usage, \ + const struct krb5_crypto_iov *iov, \ + int niov, \ + Checksum *C) \ + { \ + return EVP_unkeyed_checksum(context, crypto, key, \ + usage, iov, niov, \ + C, EVP_sha##name()); \ + } + +EVP_SHA_CHECKSUM(1) +EVP_SHA_CHECKSUM(256) +EVP_SHA_CHECKSUM(384) +EVP_SHA_CHECKSUM(512) + +/* HMAC according to RFC2104 */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_internal_hmac_iov(krb5_context context, + krb5_crypto crypto, + struct _krb5_checksum_type *cm, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + struct _krb5_key_data *keyblock, + Checksum *result) +{ + unsigned char *ipad, *opad; + unsigned char *key; + struct krb5_crypto_iov *working; + size_t key_len; + size_t i; + + ipad = malloc(cm->blocksize); + if (ipad == NULL) + return ENOMEM; + + opad = malloc(cm->blocksize + cm->checksumsize); + if (opad == NULL) { + free(ipad); + return ENOMEM; + } + + working = calloc(niov + 1, sizeof(struct krb5_crypto_iov)); + if (working == NULL) { + free(ipad); + free(opad); + return ENOMEM; + } + + memset(ipad, 0x36, cm->blocksize); + memset(opad, 0x5c, cm->blocksize); + + if(keyblock->key->keyvalue.length > cm->blocksize){ + working[0].data = keyblock->key->keyvalue; + working[0].flags = KRB5_CRYPTO_TYPE_DATA; + (*cm->checksum)(context, + crypto, + keyblock, + usage, + working, + 1, + result); + key = result->checksum.data; + key_len = result->checksum.length; + } else { + key = keyblock->key->keyvalue.data; + key_len = keyblock->key->keyvalue.length; + } + for(i = 0; i < key_len; i++){ + ipad[i] ^= key[i]; + opad[i] ^= key[i]; + } + + working[0].data.data = ipad; + working[0].data.length = cm->blocksize; + working[0].flags = KRB5_CRYPTO_TYPE_DATA; + for (i = 0; i < niov; i++) + working[i + 1] = iov[i]; + + (*cm->checksum)(context, crypto, keyblock, usage, working, niov + 1, result); + memcpy(opad + cm->blocksize, result->checksum.data, + result->checksum.length); + + working[0].data.data = opad; + working[0].data.length = cm->blocksize + cm->checksumsize; + working[0].flags = KRB5_CRYPTO_TYPE_DATA; + (*cm->checksum)(context, crypto, keyblock, usage, working, 1, result); + memset(ipad, 0, cm->blocksize); + free(ipad); + memset(opad, 0, cm->blocksize + cm->checksumsize); + free(opad); + free(working); + + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_internal_hmac(krb5_context context, + krb5_crypto crypto, + struct _krb5_checksum_type *cm, + const void *data, + size_t len, + unsigned usage, + struct _krb5_key_data *keyblock, + Checksum *result) +{ + struct krb5_crypto_iov iov[1]; + + iov[0].data.data = (void *) data; + iov[0].data.length = len; + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + return _krb5_internal_hmac_iov(context, crypto, cm, usage, iov, 1, + keyblock, result); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_hmac(krb5_context context, + krb5_cksumtype cktype, + const void *data, + size_t len, + unsigned usage, + krb5_keyblock *key, + Checksum *result) +{ + struct _krb5_checksum_type *c = _krb5_find_checksum(cktype); + struct _krb5_key_data kd; + + krb5_error_code ret; + + if (c == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + cktype); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + + kd.key = key; + kd.schedule = NULL; + + ret = _krb5_internal_hmac(context, NULL, c, data, len, usage, &kd, result); + + if (kd.schedule) + krb5_free_data(context, kd.schedule); + + return ret; +} + +krb5_error_code +_krb5_SP_HMAC_SHA1_checksum(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *result) +{ + krb5_error_code ret; + unsigned char hmac[EVP_MAX_MD_SIZE]; + unsigned int hmaclen = sizeof(hmac); + + ret = _krb5_evp_hmac_iov(context, crypto, key, iov, niov, hmac, &hmaclen, + EVP_sha1(), NULL); + if (ret) + return ret; + + heim_assert(result->checksum.length <= hmaclen, + "SHA1 checksum too short"); + memcpy(result->checksum.data, hmac, result->checksum.length); + + return 0; +} + +krb5_error_code +_krb5_SP_HMAC_SHA1_verify(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, + int niov, + Checksum *verify) +{ + krb5_error_code ret; + unsigned char hmac[EVP_MAX_MD_SIZE]; + unsigned int hmaclen = sizeof(hmac); + krb5_data data; + + ret = _krb5_evp_hmac_iov(context, crypto, key, iov, niov, hmac, &hmaclen, + EVP_sha1(), NULL); + if (ret) + return ret; + + data.data = hmac; + data.length = min(hmaclen, verify->checksum.length); + + if(krb5_data_ct_cmp(&data, &verify->checksum) != 0) + return KRB5KRB_AP_ERR_BAD_INTEGRITY; + + return 0; +} + +#define SHA_CHECKSUM(name, blocksize, outputsize) \ + struct _krb5_checksum_type _krb5_checksum_sha##name = { \ + CKSUMTYPE_SHA##name, \ + "sha" #name, \ + blocksize, \ + outputsize, \ + F_CPROOF, \ + SHA##name##_checksum, \ + NULL \ + }; + +SHA_CHECKSUM(1, 64, 20); +SHA_CHECKSUM(256, 64, 32); +SHA_CHECKSUM(384, 128, 48); +SHA_CHECKSUM(512, 128, 64); + +KRB5_LIB_FUNCTION struct _krb5_checksum_type * KRB5_LIB_CALL +_krb5_find_checksum(krb5_cksumtype type) +{ + int i; + for(i = 0; i < _krb5_num_checksums; i++) + if(_krb5_checksum_types[i]->type == type) + return _krb5_checksum_types[i]; + return NULL; +} + +static krb5_error_code +get_checksum_key(krb5_context context, + krb5_crypto crypto, + unsigned usage, /* not krb5_key_usage */ + struct _krb5_checksum_type *ct, + struct _krb5_key_data **key) +{ + krb5_error_code ret = 0; + struct _krb5_checksum_type *kct = NULL; + + if (crypto == NULL) { + krb5_set_error_message(context, KRB5_BAD_ENCTYPE, + N_("Checksum type %s is keyed but no " + "crypto context (key) was passed in", ""), + ct->name); + return KRB5_BAD_ENCTYPE; + } + kct = crypto->et->keyed_checksum; + if (kct == NULL || kct->type != ct->type) { + krb5_set_error_message(context, KRB5_BAD_ENCTYPE, + N_("Checksum type %s is keyed, but " + "the key type %s passed didnt have that checksum " + "type as the keyed type", ""), + ct->name, crypto->et->name); + return KRB5_BAD_ENCTYPE; + } + + if(ct->flags & F_DERIVED) + ret = _get_derived_key(context, crypto, usage, key); + else if(ct->flags & F_VARIANT) { + size_t i; + + *key = _new_derived_key(crypto, 0xff/* KRB5_KU_RFC1510_VARIANT */); + if (*key == NULL) + return krb5_enomem(context); + ret = krb5_copy_keyblock(context, crypto->key.key, &(*key)->key); + if(ret) + return ret; + for(i = 0; i < (*key)->key->keyvalue.length; i++) + ((unsigned char*)(*key)->key->keyvalue.data)[i] ^= 0xF0; + } else { + *key = &crypto->key; + } + if(ret == 0) + ret = _key_schedule(context, *key); + return ret; +} + +static krb5_error_code +create_checksum_iov(krb5_context context, + struct _krb5_checksum_type *ct, + krb5_crypto crypto, + unsigned usage, + struct krb5_crypto_iov *iov, + int niov, + krb5_flags flags, + Checksum *result) +{ + krb5_error_code ret; + struct _krb5_key_data *dkey; + + if (ct->flags & F_DISABLED) { + krb5_clear_error_message (context); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + if (ct->flags & F_KEYED) { + ret = get_checksum_key(context, crypto, usage, ct, &dkey); + if (ret) + return ret; + } else if ((flags & KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM) == 0) { + return EINVAL; + } else + dkey = NULL; + + result->cksumtype = ct->type; + + return (*ct->checksum)(context, crypto, dkey, usage, iov, niov, result); +} + +static krb5_error_code +create_checksum (krb5_context context, + struct _krb5_checksum_type *ct, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_flags flags, + Checksum *result) +{ + int ret; + struct krb5_crypto_iov iov[1]; + + ret = krb5_data_alloc(&result->checksum, ct->checksumsize); + if (ret) + return ret; + + iov[0].data.data = data; + iov[0].data.length = len; + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + + return create_checksum_iov(context, ct, crypto, usage, iov, 1, flags, result); +} + +static int +arcfour_checksum_p(struct _krb5_checksum_type *ct, krb5_crypto crypto) +{ + return (ct->type == CKSUMTYPE_HMAC_MD5) && + (crypto->key.key->keytype == KEYTYPE_ARCFOUR); +} + +static inline krb5_flags +crypto_flags(krb5_crypto crypto) +{ + /* If caller didn't specify a key, unkeyed checksums are the only option */ + if (crypto == NULL) + return KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM; + else + return crypto->flags; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_create_checksum(krb5_context context, + krb5_crypto crypto, + krb5_key_usage usage, + int type, + void *data, + size_t len, + Checksum *result) +{ + struct _krb5_checksum_type *ct = NULL; + unsigned keyusage; + + /* type 0 -> pick from crypto */ + if (type) { + ct = _krb5_find_checksum(type); + } else if (crypto) { + ct = crypto->et->keyed_checksum; + if (ct == NULL) + ct = crypto->et->checksum; + } + + if(ct == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + type); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + + if (arcfour_checksum_p(ct, crypto)) { + keyusage = usage; + _krb5_usage2arcfour(context, &keyusage); + } else + keyusage = CHECKSUM_USAGE(usage); + + return create_checksum(context, ct, crypto, keyusage, data, len, + crypto_flags(crypto), result); +} + +static krb5_error_code +verify_checksum_iov(krb5_context context, + krb5_crypto crypto, + unsigned usage, /* not krb5_key_usage */ + struct krb5_crypto_iov *iov, + int niov, + krb5_flags flags, + Checksum *cksum) +{ + krb5_error_code ret; + struct _krb5_key_data *dkey; + Checksum c; + struct _krb5_checksum_type *ct; + + ct = _krb5_find_checksum(cksum->cksumtype); + if (ct == NULL || (ct->flags & F_DISABLED)) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + cksum->cksumtype); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + if(ct->checksumsize != cksum->checksum.length) { + krb5_clear_error_message (context); + krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY, + N_("Decrypt integrity check failed for checksum type %s, " + "length was %u, expected %u", ""), + ct->name, (unsigned)cksum->checksum.length, + (unsigned)ct->checksumsize); + + return KRB5KRB_AP_ERR_BAD_INTEGRITY; /* XXX */ + } + if (ct->flags & F_KEYED) { + ret = get_checksum_key(context, crypto, usage, ct, &dkey); + if (ret) + return ret; + } else if ((flags & KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM) == 0) { + krb5_clear_error_message (context); + krb5_set_error_message(context, KRB5KRB_AP_ERR_INAPP_CKSUM, + N_("Unkeyed checksum type %s provided where keyed " + "checksum was expected", ""), ct->name); + + return KRB5KRB_AP_ERR_INAPP_CKSUM; + } else + dkey = NULL; + + /* + * If checksum have a verify function, lets use that instead of + * calling ->checksum and then compare result. + */ + + if(ct->verify) { + ret = (*ct->verify)(context, crypto, dkey, usage, iov, niov, cksum); + if (ret) + krb5_set_error_message(context, ret, + N_("Decrypt integrity check failed for checksum " + "type %s, key type %s", ""), + ct->name, (crypto != NULL)? crypto->et->name : "(none)"); + return ret; + } + + ret = krb5_data_alloc (&c.checksum, ct->checksumsize); + if (ret) + return ret; + + ret = (*ct->checksum)(context, crypto, dkey, usage, iov, niov, &c); + if (ret) { + krb5_data_free(&c.checksum); + return ret; + } + + if(krb5_data_ct_cmp(&c.checksum, &cksum->checksum) != 0) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + krb5_set_error_message(context, ret, + N_("Decrypt integrity check failed for checksum " + "type %s, key type %s", ""), + ct->name, crypto ? crypto->et->name : "(unkeyed)"); + } else { + ret = 0; + } + krb5_data_free (&c.checksum); + return ret; +} + +static krb5_error_code +verify_checksum(krb5_context context, + krb5_crypto crypto, + unsigned usage, /* not krb5_key_usage */ + void *data, + size_t len, + krb5_flags flags, + Checksum *cksum) +{ + struct krb5_crypto_iov iov[1]; + + iov[0].data.data = data; + iov[0].data.length = len; + iov[0].flags = KRB5_CRYPTO_TYPE_DATA; + + return verify_checksum_iov(context, crypto, usage, iov, 1, flags, cksum); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_verify_checksum(krb5_context context, + krb5_crypto crypto, + krb5_key_usage usage, + void *data, + size_t len, + Checksum *cksum) +{ + struct _krb5_checksum_type *ct; + unsigned keyusage; + + ct = _krb5_find_checksum(cksum->cksumtype); + if(ct == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + cksum->cksumtype); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + + if (arcfour_checksum_p(ct, crypto)) { + keyusage = usage; + _krb5_usage2arcfour(context, &keyusage); + } else + keyusage = CHECKSUM_USAGE(usage); + + return verify_checksum(context, crypto, keyusage, + data, len, crypto_flags(crypto), cksum); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_get_checksum_type(krb5_context context, + krb5_crypto crypto, + krb5_cksumtype *type) +{ + struct _krb5_checksum_type *ct = NULL; + + if (crypto != NULL) { + ct = crypto->et->keyed_checksum; + if (ct == NULL) + ct = crypto->et->checksum; + } + + if (ct == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type not found", "")); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + + *type = ct->type; + + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_checksumsize(krb5_context context, + krb5_cksumtype type, + size_t *size) +{ + struct _krb5_checksum_type *ct = _krb5_find_checksum(type); + if(ct == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + type); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + *size = ct->checksumsize; + return 0; +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_checksum_is_keyed(krb5_context context, + krb5_cksumtype type) +{ + struct _krb5_checksum_type *ct = _krb5_find_checksum(type); + if(ct == NULL) { + if (context) + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + type); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + return ct->flags & F_KEYED; +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_checksum_is_collision_proof(krb5_context context, + krb5_cksumtype type) +{ + struct _krb5_checksum_type *ct = _krb5_find_checksum(type); + if(ct == NULL) { + if (context) + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + type); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + return ct->flags & F_CPROOF; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_checksum_disable(krb5_context context, + krb5_cksumtype type) +{ + struct _krb5_checksum_type *ct = _krb5_find_checksum(type); + if(ct == NULL) { + if (context) + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + type); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + ct->flags |= F_DISABLED; + return 0; +} + +/************************************************************ + * * + ************************************************************/ + +KRB5_LIB_FUNCTION struct _krb5_encryption_type * KRB5_LIB_CALL +_krb5_find_enctype(krb5_enctype type) +{ + int i; + for(i = 0; i < _krb5_num_etypes; i++) + if(_krb5_etypes[i]->type == type) + return _krb5_etypes[i]; + return NULL; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_to_string(krb5_context context, + krb5_enctype etype, + char **string) +{ + struct _krb5_encryption_type *e; + e = _krb5_find_enctype(etype); + if(e == NULL) { + krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + etype); + *string = NULL; + return KRB5_PROG_ETYPE_NOSUPP; + } + *string = strdup(e->name); + if (*string == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_string_to_enctype(krb5_context context, + const char *string, + krb5_enctype *etype) +{ + int i; + for(i = 0; i < _krb5_num_etypes; i++) { + if(strcasecmp(_krb5_etypes[i]->name, string) == 0){ + *etype = _krb5_etypes[i]->type; + return 0; + } + if(_krb5_etypes[i]->alias != NULL && + strcasecmp(_krb5_etypes[i]->alias, string) == 0){ + *etype = _krb5_etypes[i]->type; + return 0; + } + } + krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %s not supported", ""), + string); + return KRB5_PROG_ETYPE_NOSUPP; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_to_keytype(krb5_context context, + krb5_enctype etype, + krb5_keytype *keytype) +{ + struct _krb5_encryption_type *e = _krb5_find_enctype(etype); + if(e == NULL) { + return unsupported_enctype (context, etype); + } + *keytype = (krb5_keytype)e->keytype->type; + return 0; +} + +/** + * Check if a enctype is valid, return 0 if it is. + * + * @param context Kerberos context + * @param etype enctype to check if its valid or not + * + * @return Return an error code for an failure or 0 on success (enctype valid). + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_valid(krb5_context context, + krb5_enctype etype) +{ + struct _krb5_encryption_type *e = _krb5_find_enctype(etype); + if(e && (e->flags & F_DISABLED) == 0) + return 0; + if (context == NULL) + return KRB5_PROG_ETYPE_NOSUPP; + if(e == NULL) { + return unsupported_enctype (context, etype); + } + /* Must be (e->flags & F_DISABLED) */ + krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %s is disabled", ""), + e->name); + return KRB5_PROG_ETYPE_NOSUPP; +} + +/** + * Return the coresponding encryption type for a checksum type. + * + * @param context Kerberos context + * @param ctype The checksum type to get the result enctype for + * @param etype The returned encryption, when the matching etype is + * not found, etype is set to ETYPE_NULL. + * + * @return Return an error code for an failure or 0 on success. + * @ingroup krb5_crypto + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cksumtype_to_enctype(krb5_context context, + krb5_cksumtype ctype, + krb5_enctype *etype) +{ + int i; + + *etype = ETYPE_NULL; + + for(i = 0; i < _krb5_num_etypes; i++) { + if(_krb5_etypes[i]->keyed_checksum && + _krb5_etypes[i]->keyed_checksum->type == ctype) + { + *etype = _krb5_etypes[i]->type; + return 0; + } + } + + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + (int)ctype); + return KRB5_PROG_SUMTYPE_NOSUPP; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cksumtype_valid(krb5_context context, + krb5_cksumtype ctype) +{ + struct _krb5_checksum_type *c = _krb5_find_checksum(ctype); + if (c == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + ctype); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + if (c->flags & F_DISABLED) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %s is disabled", ""), + c->name); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + return 0; +} + +static krb5_boolean +derived_crypto(krb5_context context, + krb5_crypto crypto) +{ + return (crypto->et->flags & F_DERIVED) != 0; +} + +#define CHECKSUMSIZE(C) ((C)->checksumsize) +#define CHECKSUMTYPE(C) ((C)->type) + +static krb5_error_code +encrypt_internal_derived(krb5_context context, + krb5_crypto crypto, + unsigned usage, + const void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + size_t sz, block_sz, checksum_sz, total_sz; + Checksum cksum; + unsigned char *p, *q; + krb5_error_code ret; + struct _krb5_key_data *dkey; + const struct _krb5_encryption_type *et = crypto->et; + + checksum_sz = CHECKSUMSIZE(et->keyed_checksum); + + sz = et->confoundersize + len; + block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */ + total_sz = block_sz + checksum_sz; + p = calloc(1, total_sz); + if (p == NULL) + return krb5_enomem(context); + + q = p; + krb5_generate_random_block(q, et->confoundersize); /* XXX */ + q += et->confoundersize; + memcpy(q, data, len); + + ret = create_checksum(context, + et->keyed_checksum, + crypto, + INTEGRITY_USAGE(usage), + p, + block_sz, + 0, + &cksum); + if(ret == 0 && cksum.checksum.length != checksum_sz) { + free_Checksum (&cksum); + krb5_clear_error_message (context); + ret = KRB5_CRYPTO_INTERNAL; + } + if(ret) + goto fail; + memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length); + free_Checksum (&cksum); + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) + goto fail; + ret = _key_schedule(context, dkey); + if(ret) + goto fail; + ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec); + if (ret) + goto fail; + result->data = p; + result->length = total_sz; + return 0; + fail: + memset(p, 0, total_sz); + free(p); + return ret; +} + +static krb5_error_code +encrypt_internal_enc_then_cksum(krb5_context context, + krb5_crypto crypto, + unsigned usage, + const void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + size_t sz, block_sz, checksum_sz, total_sz; + Checksum cksum; + unsigned char *p, *q, *ivc = NULL; + krb5_error_code ret; + struct _krb5_key_data *dkey; + const struct _krb5_encryption_type *et = crypto->et; + + checksum_sz = CHECKSUMSIZE(et->keyed_checksum); + + sz = et->confoundersize + len; + block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */ + total_sz = block_sz + checksum_sz; + p = calloc(1, total_sz); + if (p == NULL) + return krb5_enomem(context); + + q = p; + krb5_generate_random_block(q, et->confoundersize); /* XXX */ + q += et->confoundersize; + memcpy(q, data, len); + + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) + goto fail; + ret = _key_schedule(context, dkey); + if(ret) + goto fail; + + /* XXX EVP style update API would avoid needing to allocate here */ + ivc = malloc(et->blocksize + block_sz); + if (ivc == NULL) { + ret = krb5_enomem(context); + goto fail; + } + if (ivec) + memcpy(ivc, ivec, et->blocksize); + else + memset(ivc, 0, et->blocksize); + + ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec); + if (ret) + goto fail; + memcpy(&ivc[et->blocksize], p, block_sz); + + ret = create_checksum(context, + et->keyed_checksum, + crypto, + INTEGRITY_USAGE(usage), + ivc, + et->blocksize + block_sz, + 0, + &cksum); + if(ret == 0 && cksum.checksum.length != checksum_sz) { + free_Checksum (&cksum); + krb5_clear_error_message (context); + ret = KRB5_CRYPTO_INTERNAL; + } + if(ret) + goto fail; + memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length); + free_Checksum (&cksum); + result->data = p; + result->length = total_sz; + free(ivc); + return 0; + fail: + memset_s(p, total_sz, 0, total_sz); + free(p); + free(ivc); + return ret; +} + +static krb5_error_code +encrypt_internal(krb5_context context, + krb5_crypto crypto, + const void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + size_t sz, block_sz, checksum_sz; + Checksum cksum; + unsigned char *p, *q; + krb5_error_code ret; + const struct _krb5_encryption_type *et = crypto->et; + + checksum_sz = CHECKSUMSIZE(et->checksum); + + sz = et->confoundersize + checksum_sz + len; + block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */ + p = calloc(1, block_sz); + if (p == NULL) + return krb5_enomem(context); + + q = p; + krb5_generate_random_block(q, et->confoundersize); /* XXX */ + q += et->confoundersize; + memset(q, 0, checksum_sz); + q += checksum_sz; + memcpy(q, data, len); + + ret = create_checksum(context, + et->checksum, + crypto, + 0, + p, + block_sz, + KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM, + &cksum); + if(ret == 0 && cksum.checksum.length != checksum_sz) { + krb5_clear_error_message (context); + free_Checksum(&cksum); + ret = KRB5_CRYPTO_INTERNAL; + } + if(ret) + goto fail; + memcpy(p + et->confoundersize, cksum.checksum.data, cksum.checksum.length); + free_Checksum(&cksum); + ret = _key_schedule(context, &crypto->key); + if(ret) + goto fail; + ret = (*et->encrypt)(context, &crypto->key, p, block_sz, 1, 0, ivec); + if (ret) { + memset(p, 0, block_sz); + free(p); + return ret; + } + result->data = p; + result->length = block_sz; + return 0; + fail: + memset(p, 0, block_sz); + free(p); + return ret; +} + +static krb5_error_code +encrypt_internal_special(krb5_context context, + krb5_crypto crypto, + int usage, + const void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + struct _krb5_encryption_type *et = crypto->et; + size_t cksum_sz = CHECKSUMSIZE(et->checksum); + size_t sz = len + cksum_sz + et->confoundersize; + char *tmp, *p; + krb5_error_code ret; + + tmp = malloc (sz); + if (tmp == NULL) + return krb5_enomem(context); + p = tmp; + memset (p, 0, cksum_sz); + p += cksum_sz; + krb5_generate_random_block(p, et->confoundersize); + p += et->confoundersize; + memcpy (p, data, len); + ret = (*et->encrypt)(context, &crypto->key, tmp, sz, TRUE, usage, ivec); + if (ret) { + memset(tmp, 0, sz); + free(tmp); + return ret; + } + result->data = tmp; + result->length = sz; + return 0; +} + +static krb5_error_code +decrypt_internal_derived(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + size_t checksum_sz; + Checksum cksum; + unsigned char *p; + krb5_error_code ret; + struct _krb5_key_data *dkey; + struct _krb5_encryption_type *et = crypto->et; + unsigned long l; + + checksum_sz = CHECKSUMSIZE(et->keyed_checksum); + if (len < checksum_sz + et->confoundersize) { + krb5_set_error_message(context, KRB5_BAD_MSIZE, + N_("Encrypted data shorter then " + "checksum + confunder", "")); + return KRB5_BAD_MSIZE; + } + + if (((len - checksum_sz) % et->padsize) != 0) { + krb5_clear_error_message(context); + return KRB5_BAD_MSIZE; + } + + p = malloc(len); + if (len != 0 && p == NULL) + return krb5_enomem(context); + memcpy(p, data, len); + + len -= checksum_sz; + + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) { + free(p); + return ret; + } + ret = _key_schedule(context, dkey); + if(ret) { + free(p); + return ret; + } + ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec); + if (ret) { + free(p); + return ret; + } + + cksum.checksum.data = p + len; + cksum.checksum.length = checksum_sz; + cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum); + + ret = verify_checksum(context, + crypto, + INTEGRITY_USAGE(usage), + p, + len, + 0, + &cksum); + if(ret) { + free(p); + return ret; + } + l = len - et->confoundersize; + memmove(p, p + et->confoundersize, l); + result->data = p; + result->length = l; + return 0; +} + +static krb5_error_code +decrypt_internal_enc_then_cksum(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + size_t checksum_sz; + Checksum cksum; + unsigned char *p; + krb5_error_code ret; + struct _krb5_key_data *dkey; + struct _krb5_encryption_type *et = crypto->et; + unsigned long l; + + checksum_sz = CHECKSUMSIZE(et->keyed_checksum); + if (len < checksum_sz + et->confoundersize) { + krb5_set_error_message(context, KRB5_BAD_MSIZE, + N_("Encrypted data shorter then " + "checksum + confunder", "")); + return KRB5_BAD_MSIZE; + } + + if (((len - checksum_sz) % et->padsize) != 0) { + krb5_clear_error_message(context); + return KRB5_BAD_MSIZE; + } + + len -= checksum_sz; + + p = malloc(et->blocksize + len); + if (p == NULL) + return krb5_enomem(context); + + if (ivec) + memcpy(p, ivec, et->blocksize); + else + memset(p, 0, et->blocksize); + memcpy(&p[et->blocksize], data, len); + + cksum.checksum.data = (unsigned char *)data + len; + cksum.checksum.length = checksum_sz; + cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum); + + ret = verify_checksum(context, + crypto, + INTEGRITY_USAGE(usage), + p, + et->blocksize + len, + 0, + &cksum); + if(ret) { + free(p); + return ret; + } + + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) { + free(p); + return ret; + } + ret = _key_schedule(context, dkey); + if(ret) { + free(p); + return ret; + } + ret = (*et->encrypt)(context, dkey, &p[et->blocksize], len, 0, usage, ivec); + if (ret) { + free(p); + return ret; + } + + l = len - et->confoundersize; + memmove(p, p + et->blocksize + et->confoundersize, l); + result->data = p; + result->length = l; + return 0; +} + +static krb5_error_code +decrypt_internal(krb5_context context, + krb5_crypto crypto, + void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + krb5_error_code ret; + unsigned char *p; + Checksum cksum; + size_t checksum_sz, l; + struct _krb5_encryption_type *et = crypto->et; + + if ((len % et->padsize) != 0) { + krb5_clear_error_message(context); + return KRB5_BAD_MSIZE; + } + checksum_sz = CHECKSUMSIZE(et->checksum); + if (len < checksum_sz + et->confoundersize) { + krb5_set_error_message(context, KRB5_BAD_MSIZE, + N_("Encrypted data shorter then " + "checksum + confunder", "")); + return KRB5_BAD_MSIZE; + } + + p = malloc(len); + if (len != 0 && p == NULL) + return krb5_enomem(context); + memcpy(p, data, len); + + ret = _key_schedule(context, &crypto->key); + if(ret) { + free(p); + return ret; + } + ret = (*et->encrypt)(context, &crypto->key, p, len, 0, 0, ivec); + if (ret) { + free(p); + return ret; + } + ret = krb5_data_copy(&cksum.checksum, p + et->confoundersize, checksum_sz); + if(ret) { + free(p); + return ret; + } + memset(p + et->confoundersize, 0, checksum_sz); + cksum.cksumtype = CHECKSUMTYPE(et->checksum); + ret = verify_checksum(context, NULL, 0, p, len, + KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM, &cksum); + free_Checksum(&cksum); + if(ret) { + free(p); + return ret; + } + l = len - et->confoundersize - checksum_sz; + memmove(p, p + et->confoundersize + checksum_sz, l); + result->data = p; + result->length = l; + return 0; +} + +static krb5_error_code +decrypt_internal_special(krb5_context context, + krb5_crypto crypto, + int usage, + void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + struct _krb5_encryption_type *et = crypto->et; + size_t cksum_sz = CHECKSUMSIZE(et->checksum); + size_t sz = len - cksum_sz - et->confoundersize; + unsigned char *p; + krb5_error_code ret; + + if ((len % et->padsize) != 0) { + krb5_clear_error_message(context); + return KRB5_BAD_MSIZE; + } + if (len < cksum_sz + et->confoundersize) { + krb5_set_error_message(context, KRB5_BAD_MSIZE, + N_("Encrypted data shorter then " + "checksum + confunder", "")); + return KRB5_BAD_MSIZE; + } + + p = malloc (len); + if (p == NULL) + return krb5_enomem(context); + memcpy(p, data, len); + + ret = (*et->encrypt)(context, &crypto->key, p, len, FALSE, usage, ivec); + if (ret) { + free(p); + return ret; + } + + memmove (p, p + cksum_sz + et->confoundersize, sz); + result->data = p; + result->length = sz; + return 0; +} + +static krb5_crypto_iov * +iov_find(krb5_crypto_iov *data, size_t num_data, unsigned type) +{ + size_t i; + for (i = 0; i < num_data; i++) + if (data[i].flags == type) + return &data[i]; + return NULL; +} + +static size_t +iov_enc_data_len(krb5_crypto_iov *data, int num_data) +{ + size_t i, len; + + for (len = 0, i = 0; i < num_data; i++) { + if (data[i].flags != KRB5_CRYPTO_TYPE_DATA) + continue; + len += data[i].data.length; + } + + return len; +} + +static size_t +iov_sign_data_len(krb5_crypto_iov *data, int num_data) +{ + size_t i, len; + + for (len = 0, i = 0; i < num_data; i++) { + /* Can't use should_sign, because we must only count data, not + * header/trailer */ + if (data[i].flags == KRB5_CRYPTO_TYPE_DATA || + data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY) + len += data[i].data.length; + } + + return len; +} + +static krb5_error_code +iov_coalesce(krb5_context context, + krb5_data *prefix, + krb5_crypto_iov *data, + int num_data, + krb5_boolean inc_sign_data, + krb5_data *out) +{ + unsigned char *p, *q; + krb5_crypto_iov *hiv, *piv; + size_t len; + unsigned int i; + + hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + + piv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_PADDING); + + len = 0; + if (prefix) + len += prefix->length; + len += hiv->data.length; + if (inc_sign_data) + len += iov_sign_data_len(data, num_data); + else + len += iov_enc_data_len(data, num_data); + if (piv) + len += piv->data.length; + + p = q = malloc(len); + if (p == NULL) + return krb5_enomem(context); + + if (prefix) { + memcpy(q, prefix->data, prefix->length); + q += prefix->length; + } + memcpy(q, hiv->data.data, hiv->data.length); + q += hiv->data.length; + for (i = 0; i < num_data; i++) { + if (data[i].flags == KRB5_CRYPTO_TYPE_DATA || + (inc_sign_data && data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY)) { + memcpy(q, data[i].data.data, data[i].data.length); + q += data[i].data.length; + } + } + if (piv) + memset(q, 0, piv->data.length); + + out->length = len; + out->data = p; + + return 0; +} + +static krb5_error_code +iov_uncoalesce(krb5_context context, + krb5_data *enc_data, + krb5_crypto_iov *data, + int num_data) +{ + unsigned char *q = enc_data->data; + krb5_crypto_iov *hiv, *piv; + unsigned int i; + + hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + + piv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_PADDING); + + memcpy(hiv->data.data, q, hiv->data.length); + q += hiv->data.length; + + for (i = 0; i < num_data; i++) { + if (data[i].flags != KRB5_CRYPTO_TYPE_DATA) + continue; + memcpy(data[i].data.data, q, data[i].data.length); + q += data[i].data.length; + } + if (piv) + memcpy(piv->data.data, q, piv->data.length); + + return 0; +} + +static krb5_error_code +iov_pad_validate(const struct _krb5_encryption_type *et, + krb5_crypto_iov *data, + int num_data, + krb5_crypto_iov **ppiv) +{ + krb5_crypto_iov *piv; + size_t sz, headersz, block_sz, pad_sz, len; + + len = iov_enc_data_len(data, num_data); + + headersz = et->confoundersize; + + sz = headersz + len; + block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */ + + pad_sz = block_sz - sz; + + piv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_PADDING); + /* its ok to have no TYPE_PADDING if there is no padding */ + if (piv == NULL && pad_sz != 0) + return KRB5_BAD_MSIZE; + if (piv) { + if (piv->data.length < pad_sz) + return KRB5_BAD_MSIZE; + piv->data.length = pad_sz; + if (pad_sz) + memset(piv->data.data, 0, pad_sz); + else + piv = NULL; + } + + *ppiv = piv; + return 0; +} + +/** + * Inline encrypt a kerberos message + * + * @param context Kerberos context + * @param crypto Kerberos crypto context + * @param usage Key usage for this buffer + * @param data array of buffers to process + * @param num_data length of array + * @param ivec initial cbc/cts vector + * + * @return Return an error code or 0. + * @ingroup krb5_crypto + * + * Kerberos encrypted data look like this: + * + * 1. KRB5_CRYPTO_TYPE_HEADER + * 2. array [1,...] KRB5_CRYPTO_TYPE_DATA and array [0,...] + * KRB5_CRYPTO_TYPE_SIGN_ONLY in any order, however the receiver + * have to aware of the order. KRB5_CRYPTO_TYPE_SIGN_ONLY is + * commonly used headers and trailers. + * 3. KRB5_CRYPTO_TYPE_PADDING, at least on padsize long if padsize > 1 + * 4. KRB5_CRYPTO_TYPE_TRAILER + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encrypt_iov_ivec(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_crypto_iov *data, + int num_data, + void *ivec) +{ + size_t headersz, trailersz; + Checksum cksum; + krb5_data enc_data, sign_data; + krb5_error_code ret; + struct _krb5_key_data *dkey; + const struct _krb5_encryption_type *et = crypto->et; + krb5_crypto_iov *tiv, *piv, *hiv; + + if (num_data < 0) { + krb5_clear_error_message(context); + return KRB5_CRYPTO_INTERNAL; + } + + if(!derived_crypto(context, crypto)) { + krb5_clear_error_message(context); + return KRB5_CRYPTO_INTERNAL; + } + + krb5_data_zero(&enc_data); + krb5_data_zero(&sign_data); + + headersz = et->confoundersize; + trailersz = CHECKSUMSIZE(et->keyed_checksum); + + /* header */ + hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (hiv == NULL || hiv->data.length != headersz) + return KRB5_BAD_MSIZE; + krb5_generate_random_block(hiv->data.data, hiv->data.length); + + /* padding */ + ret = iov_pad_validate(et, data, num_data, &piv); + if(ret) + goto cleanup; + + /* trailer */ + tiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (tiv == NULL || tiv->data.length != trailersz) { + ret = KRB5_BAD_MSIZE; + goto cleanup; + } + + if (et->flags & F_ENC_THEN_CKSUM) { + unsigned char old_ivec[EVP_MAX_IV_LENGTH]; + krb5_data ivec_data; + + heim_assert(et->blocksize <= sizeof(old_ivec), + "blocksize too big for ivec buffer"); + + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) + goto cleanup; + + ret = _key_schedule(context, dkey); + if(ret) + goto cleanup; + + if (ivec) + memcpy(old_ivec, ivec, et->blocksize); + else + memset(old_ivec, 0, et->blocksize); + + if (et->encrypt_iov != NULL) { + ret = (*et->encrypt_iov)(context, dkey, data, num_data, 1, usage, + ivec); + if (ret) + goto cleanup; + } else { + ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data); + if (ret) + goto cleanup; + + ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length, + 1, usage, ivec); + if (ret) + goto cleanup; + + ret = iov_uncoalesce(context, &enc_data, data, num_data); + if (ret) + goto cleanup; + } + + ivec_data.length = et->blocksize; + ivec_data.data = old_ivec; + + ret = iov_coalesce(context, &ivec_data, data, num_data, TRUE, &sign_data); + if(ret) + goto cleanup; + + ret = create_checksum(context, + et->keyed_checksum, + crypto, + INTEGRITY_USAGE(usage), + sign_data.data, + sign_data.length, + 0, + &cksum); + + if(ret == 0 && cksum.checksum.length != trailersz) { + free_Checksum (&cksum); + krb5_clear_error_message (context); + ret = KRB5_CRYPTO_INTERNAL; + } + if (ret) + goto cleanup; + + /* save cksum at end */ + memcpy(tiv->data.data, cksum.checksum.data, cksum.checksum.length); + free_Checksum (&cksum); + + } else { + cksum.checksum = tiv->data; + ret = create_checksum_iov(context, + et->keyed_checksum, + crypto, + INTEGRITY_USAGE(usage), + data, + num_data, + 0, + &cksum); + if (ret) + goto cleanup; + + /* create_checksum may realloc the derived key space, so any keys + * obtained before it was called may no longer be valid */ + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) + goto cleanup; + + ret = _key_schedule(context, dkey); + if(ret) + goto cleanup; + + if (et->encrypt_iov != NULL) { + ret = (*et->encrypt_iov)(context, dkey, data, num_data, 1, usage, + ivec); + if (ret) + goto cleanup; + } else { + ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data); + if (ret) + goto cleanup; + + ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length, + 1, usage, ivec); + if (ret) + goto cleanup; + + ret = iov_uncoalesce(context, &enc_data, data, num_data); + if (ret) + goto cleanup; + } + } + +cleanup: + if (enc_data.data) { + memset_s(enc_data.data, enc_data.length, 0, enc_data.length); + krb5_data_free(&enc_data); + } + if (sign_data.data) { + memset_s(sign_data.data, sign_data.length, 0, sign_data.length); + krb5_data_free(&sign_data); + } + return ret; +} + +/** + * Inline decrypt a Kerberos message. + * + * @param context Kerberos context + * @param crypto Kerberos crypto context + * @param usage Key usage for this buffer + * @param data array of buffers to process + * @param num_data length of array + * @param ivec initial cbc/cts vector + * + * @return Return an error code or 0. + * @ingroup krb5_crypto + * + * 1. KRB5_CRYPTO_TYPE_HEADER + * 2. one KRB5_CRYPTO_TYPE_DATA and array [0,...] of KRB5_CRYPTO_TYPE_SIGN_ONLY in + * any order, however the receiver have to aware of the + * order. KRB5_CRYPTO_TYPE_SIGN_ONLY is commonly used unencrypoted + * protocol headers and trailers. The output data will be of same + * size as the input data or shorter. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decrypt_iov_ivec(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_crypto_iov *data, + unsigned int num_data, + void *ivec) +{ + Checksum cksum; + krb5_data enc_data, sign_data; + krb5_error_code ret; + struct _krb5_key_data *dkey; + struct _krb5_encryption_type *et = crypto->et; + krb5_crypto_iov *tiv, *hiv; + + if(!derived_crypto(context, crypto)) { + krb5_clear_error_message(context); + return KRB5_CRYPTO_INTERNAL; + } + + /* header */ + hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER); + if (hiv == NULL || hiv->data.length != et->confoundersize) + return KRB5_BAD_MSIZE; + + /* trailer */ + tiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_TRAILER); + if (tiv->data.length != CHECKSUMSIZE(et->keyed_checksum)) + return KRB5_BAD_MSIZE; + + /* padding */ + if ((iov_enc_data_len(data, num_data) % et->padsize) != 0) { + krb5_clear_error_message(context); + return KRB5_BAD_MSIZE; + } + + krb5_data_zero(&enc_data); + krb5_data_zero(&sign_data); + + if (!(et->flags & F_ENC_THEN_CKSUM)) { + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) + goto cleanup; + + ret = _key_schedule(context, dkey); + if(ret) + goto cleanup; + + if (et->encrypt_iov != NULL) { + ret = (*et->encrypt_iov)(context, dkey, data, num_data, + 0, usage, ivec); + if(ret) + goto cleanup; + } else { + ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data); + if(ret) + goto cleanup; + + ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length, + 0, usage, ivec); + if(ret) + goto cleanup; + + ret = iov_uncoalesce(context, &enc_data, data, num_data); + if(ret) + goto cleanup; + } + + cksum.checksum.data = tiv->data.data; + cksum.checksum.length = tiv->data.length; + cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum); + + ret = verify_checksum_iov(context, crypto, INTEGRITY_USAGE(usage), + data, num_data, 0, &cksum); + if(ret) + goto cleanup; + } else { + krb5_data ivec_data; + static const unsigned char zero_ivec[EVP_MAX_IV_LENGTH]; + + heim_assert(et->blocksize <= sizeof(zero_ivec), + "blocksize too big for ivec buffer"); + + ivec_data.length = et->blocksize; + ivec_data.data = ivec ? ivec : rk_UNCONST(zero_ivec); + + ret = iov_coalesce(context, &ivec_data, data, num_data, TRUE, &sign_data); + if(ret) + goto cleanup; + + cksum.checksum.data = tiv->data.data; + cksum.checksum.length = tiv->data.length; + cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum); + + ret = verify_checksum(context, + crypto, + INTEGRITY_USAGE(usage), + sign_data.data, + sign_data.length, + 0, + &cksum); + if(ret) + goto cleanup; + + ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data); + if(ret) + goto cleanup; + + ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey); + if(ret) + goto cleanup; + + ret = _key_schedule(context, dkey); + if(ret) + goto cleanup; + + ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length, + 0, usage, ivec); + if(ret) + goto cleanup; + + ret = iov_uncoalesce(context, &enc_data, data, num_data); + if(ret) + goto cleanup; + } + +cleanup: + if (enc_data.data) { + memset_s(enc_data.data, enc_data.length, 0, enc_data.length); + krb5_data_free(&enc_data); + } + if (sign_data.data) { + memset_s(sign_data.data, sign_data.length, 0, sign_data.length); + krb5_data_free(&sign_data); + } + return ret; +} + +/** + * Create a Kerberos message checksum. + * + * @param context Kerberos context + * @param crypto Kerberos crypto context + * @param usage Key usage for this buffer + * @param data array of buffers to process + * @param num_data length of array + * @param type output data + * + * @return Return an error code or 0. + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_create_checksum_iov(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_crypto_iov *data, + unsigned int num_data, + krb5_cksumtype *type) +{ + Checksum cksum; + krb5_crypto_iov *civ; + struct _krb5_checksum_type *ct; + unsigned keyusage; + krb5_error_code ret; + + civ = iov_find(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM); + if (civ == NULL) + return KRB5_BAD_MSIZE; + + ct = crypto->et->keyed_checksum; + if (ct == NULL) + ct = crypto->et->checksum; + + if(ct == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type not found", "")); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + + if (arcfour_checksum_p(ct, crypto)) { + keyusage = usage; + _krb5_usage2arcfour(context, &keyusage); + } else + keyusage = CHECKSUM_USAGE(usage); + + if (ct->checksumsize > civ->data.length) { + krb5_set_error_message(context, KRB5_BAD_MSIZE, + N_("Checksum larger then input buffer", "")); + return KRB5_BAD_MSIZE; + } + + cksum.checksum = civ->data; + ret = create_checksum_iov(context, ct, crypto, keyusage, + data, num_data, crypto_flags(crypto), &cksum); + + if (ret == 0 && type) + *type = cksum.cksumtype; + + return ret; +} + +/** + * Verify a Kerberos message checksum. + * + * @param context Kerberos context + * @param crypto Kerberos crypto context + * @param usage Key usage for this buffer + * @param data array of buffers to process + * @param num_data length of array + * @param type return checksum type if not NULL + * + * @return Return an error code or 0. + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_verify_checksum_iov(krb5_context context, + krb5_crypto crypto, + unsigned usage, + krb5_crypto_iov *data, + unsigned int num_data, + krb5_cksumtype *type) +{ + struct _krb5_encryption_type *et = crypto->et; + struct _krb5_checksum_type *ct; + Checksum cksum; + krb5_crypto_iov *civ; + krb5_error_code ret; + unsigned keyusage; + + civ = iov_find(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM); + if (civ == NULL) + return KRB5_BAD_MSIZE; + + cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum); + cksum.checksum.length = civ->data.length; + cksum.checksum.data = civ->data.data; + + ct = _krb5_find_checksum(cksum.cksumtype); + if(ct == NULL) { + krb5_set_error_message (context, KRB5_PROG_SUMTYPE_NOSUPP, + N_("checksum type %d not supported", ""), + cksum.cksumtype); + return KRB5_PROG_SUMTYPE_NOSUPP; + } + + if (arcfour_checksum_p(ct, crypto)) { + keyusage = usage; + _krb5_usage2arcfour(context, &keyusage); + } else + keyusage = CHECKSUM_USAGE(usage); + + ret = verify_checksum_iov(context, crypto, keyusage, data, num_data, + crypto_flags(crypto), &cksum); + + if (ret == 0 && type) + *type = cksum.cksumtype; + + return ret; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_length(krb5_context context, + krb5_crypto crypto, + int type, + size_t *len) +{ + if (!derived_crypto(context, crypto)) { + krb5_set_error_message(context, EINVAL, "not a derived crypto"); + return EINVAL; + } + + switch(type) { + case KRB5_CRYPTO_TYPE_EMPTY: + *len = 0; + return 0; + case KRB5_CRYPTO_TYPE_HEADER: + *len = crypto->et->blocksize; + return 0; + case KRB5_CRYPTO_TYPE_DATA: + case KRB5_CRYPTO_TYPE_SIGN_ONLY: + /* len must already been filled in */ + return 0; + case KRB5_CRYPTO_TYPE_PADDING: + if (crypto->et->padsize > 1) + *len = crypto->et->padsize; + else + *len = 0; + return 0; + case KRB5_CRYPTO_TYPE_TRAILER: + if (crypto->et->keyed_checksum) + *len = CHECKSUMSIZE(crypto->et->keyed_checksum); + else + *len = 0; + return 0; + case KRB5_CRYPTO_TYPE_CHECKSUM: + if (crypto->et->keyed_checksum) + *len = CHECKSUMSIZE(crypto->et->keyed_checksum); + else + *len = CHECKSUMSIZE(crypto->et->checksum); + return 0; + } + krb5_set_error_message(context, EINVAL, + "%d not a supported type", type); + return EINVAL; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_length_iov(krb5_context context, + krb5_crypto crypto, + krb5_crypto_iov *data, + unsigned int num_data) +{ + krb5_error_code ret; + size_t i; + + for (i = 0; i < num_data; i++) { + ret = krb5_crypto_length(context, crypto, + data[i].flags, + &data[i].data.length); + if (ret) + return ret; + } + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encrypt_ivec(krb5_context context, + krb5_crypto crypto, + unsigned usage, + const void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + krb5_error_code ret; + + switch (crypto->et->flags & F_CRYPTO_MASK) { + case F_RFC3961_ENC: + ret = encrypt_internal_derived(context, crypto, usage, + data, len, result, ivec); + break; + case F_SPECIAL: + ret = encrypt_internal_special (context, crypto, usage, + data, len, result, ivec); + break; + case F_ENC_THEN_CKSUM: + ret = encrypt_internal_enc_then_cksum(context, crypto, usage, + data, len, result, ivec); + break; + default: + ret = encrypt_internal(context, crypto, data, len, result, ivec); + break; + } + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encrypt(krb5_context context, + krb5_crypto crypto, + unsigned usage, + const void *data, + size_t len, + krb5_data *result) +{ + return krb5_encrypt_ivec(context, crypto, usage, data, len, result, NULL); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_encrypt_EncryptedData(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + int kvno, + EncryptedData *result) +{ + result->etype = CRYPTO_ETYPE(crypto); + if(kvno){ + ALLOC(result->kvno, 1); + *result->kvno = kvno; + }else + result->kvno = NULL; + return krb5_encrypt(context, crypto, usage, data, len, &result->cipher); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decrypt_ivec(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result, + void *ivec) +{ + krb5_error_code ret; + + switch (crypto->et->flags & F_CRYPTO_MASK) { + case F_RFC3961_ENC: + ret = decrypt_internal_derived(context, crypto, usage, + data, len, result, ivec); + break; + case F_SPECIAL: + ret = decrypt_internal_special(context, crypto, usage, + data, len, result, ivec); + break; + case F_ENC_THEN_CKSUM: + ret = decrypt_internal_enc_then_cksum(context, crypto, usage, + data, len, result, ivec); + break; + default: + ret = decrypt_internal(context, crypto, data, len, result, ivec); + break; + } + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decrypt(krb5_context context, + krb5_crypto crypto, + unsigned usage, + void *data, + size_t len, + krb5_data *result) +{ + return krb5_decrypt_ivec (context, crypto, usage, data, len, result, + NULL); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_decrypt_EncryptedData(krb5_context context, + krb5_crypto crypto, + unsigned usage, + const EncryptedData *e, + krb5_data *result) +{ + return krb5_decrypt(context, crypto, usage, + e->cipher.data, e->cipher.length, result); +} + +/************************************************************ + * * + ************************************************************/ + +static krb5_error_code +derive_key_rfc3961(krb5_context context, + struct _krb5_encryption_type *et, + struct _krb5_key_data *key, + const void *constant, + size_t len) +{ + + unsigned char *k = NULL; + unsigned int nblocks = 0, i; + krb5_error_code ret = 0; + struct _krb5_key_type *kt = et->keytype; + + if(et->blocksize * 8 < kt->bits || len != et->blocksize) { + nblocks = (kt->bits + et->blocksize * 8 - 1) / (et->blocksize * 8); + k = malloc(nblocks * et->blocksize); + if(k == NULL) { + ret = krb5_enomem(context); + goto out; + } + ret = _krb5_n_fold(constant, len, k, et->blocksize); + if (ret) { + krb5_enomem(context); + goto out; + } + + for(i = 0; i < nblocks; i++) { + if(i > 0) + memcpy(k + i * et->blocksize, + k + (i - 1) * et->blocksize, + et->blocksize); + ret = (*et->encrypt)(context, key, k + i * et->blocksize, + et->blocksize, 1, 0, NULL); + if (ret) { + krb5_set_error_message(context, ret, N_("encrypt failed", "")); + goto out; + } + } + } else { + /* this case is probably broken, but won't be run anyway */ + void *c = malloc(len); + size_t res_len = (kt->bits + 7) / 8; + + if(len != 0 && c == NULL) { + ret = krb5_enomem(context); + goto out; + } + memcpy(c, constant, len); + ret = (*et->encrypt)(context, key, c, len, 1, 0, NULL); + if (ret) { + free(c); + krb5_set_error_message(context, ret, N_("encrypt failed", "")); + goto out; + } + k = malloc(res_len); + if(res_len != 0 && k == NULL) { + free(c); + ret = krb5_enomem(context); + goto out; + } + ret = _krb5_n_fold(c, len, k, res_len); + free(c); + if (ret) { + krb5_enomem(context); + goto out; + } + } + + if (kt->type == KRB5_ENCTYPE_OLD_DES3_CBC_SHA1) + _krb5_DES3_random_to_key(context, key->key, k, nblocks * et->blocksize); + else + memcpy(key->key->keyvalue.data, k, key->key->keyvalue.length); + + out: + if (k) { + memset_s(k, nblocks * et->blocksize, 0, nblocks * et->blocksize); + free(k); + } + return ret; +} + +static krb5_error_code +derive_key_sp800_hmac(krb5_context context, + struct _krb5_encryption_type *et, + struct _krb5_key_data *key, + const void *constant, + size_t len) +{ + krb5_error_code ret; + struct _krb5_key_type *kt = et->keytype; + krb5_data label; + const EVP_MD *md = NULL; + const unsigned char *c = constant; + size_t key_len; + krb5_data K1; + + ret = _krb5_aes_sha2_md_for_enctype(context, kt->type, &md); + if (ret) + return ret; + + /* + * PRF usage: not handled here (output cannot be longer) + * Integrity usage: truncated hash (half length) + * Encryption usage: base key length + */ + if (len == 5 && (c[4] == 0x99 || c[4] == 0x55)) + key_len = EVP_MD_size(md) / 2; + else + key_len = kt->size; + + ret = krb5_data_alloc(&K1, key_len); + if (ret) + return ret; + + label.data = (void *)constant; + label.length = len; + + ret = _krb5_SP800_108_HMAC_KDF(context, &key->key->keyvalue, + &label, NULL, md, &K1); + if (ret == 0) { + if (key->key->keyvalue.length > key_len) + key->key->keyvalue.length = key_len; + memcpy(key->key->keyvalue.data, K1.data, key_len); + } + + memset_s(K1.data, K1.length, 0, K1.length); + krb5_data_free(&K1); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_derive_key(krb5_context context, + struct _krb5_encryption_type *et, + struct _krb5_key_data *key, + const void *constant, + size_t len) +{ + krb5_error_code ret; + + ret = _key_schedule(context, key); + if(ret) + return ret; + + switch (et->flags & F_KDF_MASK) { + case F_RFC3961_KDF: + ret = derive_key_rfc3961(context, et, key, constant, len); + break; + case F_SP800_108_HMAC_KDF: + ret = derive_key_sp800_hmac(context, et, key, constant, len); + break; + default: + ret = KRB5_CRYPTO_INTERNAL; + krb5_set_error_message(context, ret, + N_("derive_key() called with unknown keytype (%u)", ""), + et->keytype->type); + break; + } + + if (key->schedule) { + free_key_schedule(context, key, et); + key->schedule = NULL; + } + + return ret; +} + +static struct _krb5_key_data * +_new_derived_key(krb5_crypto crypto, unsigned usage) +{ + struct _krb5_key_usage *d = crypto->key_usage; + d = realloc(d, (crypto->num_key_usage + 1) * sizeof(*d)); + if(d == NULL) + return NULL; + crypto->key_usage = d; + d += crypto->num_key_usage++; + memset(d, 0, sizeof(*d)); + d->usage = usage; + return &d->key; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_derive_key(krb5_context context, + const krb5_keyblock *key, + krb5_enctype etype, + const void *constant, + size_t constant_len, + krb5_keyblock **derived_key) +{ + krb5_error_code ret; + struct _krb5_encryption_type *et; + struct _krb5_key_data d; + + *derived_key = NULL; + + et = _krb5_find_enctype (etype); + if (et == NULL) { + return unsupported_enctype (context, etype); + } + + ret = krb5_copy_keyblock(context, key, &d.key); + if (ret) + return ret; + + d.schedule = NULL; + ret = _krb5_derive_key(context, et, &d, constant, constant_len); + if (ret == 0) + ret = krb5_copy_keyblock(context, d.key, derived_key); + _krb5_free_key_data(context, &d, et); + return ret; +} + +static krb5_error_code +_get_derived_key(krb5_context context, + krb5_crypto crypto, + unsigned usage, + struct _krb5_key_data **key) +{ + int i; + struct _krb5_key_data *d; + unsigned char constant[5]; + + *key = NULL; + for(i = 0; i < crypto->num_key_usage; i++) + if(crypto->key_usage[i].usage == usage) { + *key = &crypto->key_usage[i].key; + return 0; + } + d = _new_derived_key(crypto, usage); + if (d == NULL) + return krb5_enomem(context); + *key = d; + krb5_copy_keyblock(context, crypto->key.key, &d->key); + _krb5_put_int(constant, usage, sizeof(constant)); + return _krb5_derive_key(context, crypto->et, d, constant, sizeof(constant)); +} + +/** + * Create a crypto context used for all encryption and signature + * operation. The encryption type to use is taken from the key, but + * can be overridden with the enctype parameter. This can be useful + * for encryptions types which is compatiable (DES for example). + * + * To free the crypto context, use krb5_crypto_destroy(). + * + * @param context Kerberos context + * @param key the key block information with all key data + * @param etype the encryption type + * @param crypto the resulting crypto context + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_init(krb5_context context, + const krb5_keyblock *key, + krb5_enctype etype, + krb5_crypto *crypto) +{ + krb5_error_code ret; + ALLOC(*crypto, 1); + if (*crypto == NULL) + return krb5_enomem(context); + if(etype == ETYPE_NULL) + etype = key->keytype; + (*crypto)->et = _krb5_find_enctype(etype); + if((*crypto)->et == NULL || ((*crypto)->et->flags & F_DISABLED)) { + free(*crypto); + *crypto = NULL; + return unsupported_enctype(context, etype); + } + if((*crypto)->et->keytype->size != key->keyvalue.length) { + free(*crypto); + *crypto = NULL; + krb5_set_error_message (context, KRB5_BAD_KEYSIZE, + "encryption key has bad length"); + return KRB5_BAD_KEYSIZE; + } + ret = krb5_copy_keyblock(context, key, &(*crypto)->key.key); + if(ret) { + free(*crypto); + *crypto = NULL; + return ret; + } + (*crypto)->key.schedule = NULL; + (*crypto)->num_key_usage = 0; + (*crypto)->key_usage = NULL; + (*crypto)->flags = 0; + return 0; +} + +static void +free_key_schedule(krb5_context context, + struct _krb5_key_data *key, + struct _krb5_encryption_type *et) +{ + if (et->keytype->cleanup) + (*et->keytype->cleanup)(context, key); + memset(key->schedule->data, 0, key->schedule->length); + krb5_free_data(context, key->schedule); +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_free_key_data(krb5_context context, struct _krb5_key_data *key, + struct _krb5_encryption_type *et) +{ + krb5_free_keyblock(context, key->key); + if(key->schedule) { + free_key_schedule(context, key, et); + key->schedule = NULL; + } +} + +static void +free_key_usage(krb5_context context, struct _krb5_key_usage *ku, + struct _krb5_encryption_type *et) +{ + _krb5_free_key_data(context, &ku->key, et); +} + +/** + * Free a crypto context created by krb5_crypto_init(). + * + * @param context Kerberos context + * @param crypto crypto context to free + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_destroy(krb5_context context, + krb5_crypto crypto) +{ + int i; + + for(i = 0; i < crypto->num_key_usage; i++) + free_key_usage(context, &crypto->key_usage[i], crypto->et); + free(crypto->key_usage); + _krb5_free_key_data(context, &crypto->key, crypto->et); + + if (crypto->mdctx) + EVP_MD_CTX_destroy(crypto->mdctx); + + if (crypto->hmacctx) + HMAC_CTX_free(crypto->hmacctx); + + free (crypto); + return 0; +} + +/** + * Return the blocksize used algorithm referenced by the crypto context + * + * @param context Kerberos context + * @param crypto crypto context to query + * @param blocksize the resulting blocksize + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_getblocksize(krb5_context context, + krb5_crypto crypto, + size_t *blocksize) +{ + *blocksize = crypto->et->blocksize; + return 0; +} + +/** + * Return the encryption type used by the crypto context + * + * @param context Kerberos context + * @param crypto crypto context to query + * @param enctype the resulting encryption type + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_getenctype(krb5_context context, + krb5_crypto crypto, + krb5_enctype *enctype) +{ + *enctype = crypto->et->type; + return 0; +} + +/** + * Return the padding size used by the crypto context + * + * @param context Kerberos context + * @param crypto crypto context to query + * @param padsize the return padding size + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_getpadsize(krb5_context context, + krb5_crypto crypto, + size_t *padsize) +{ + *padsize = crypto->et->padsize; + return 0; +} + +/** + * Return the confounder size used by the crypto context + * + * @param context Kerberos context + * @param crypto crypto context to query + * @param confoundersize the returned confounder size + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_getconfoundersize(krb5_context context, + krb5_crypto crypto, + size_t *confoundersize) +{ + *confoundersize = crypto->et->confoundersize; + return 0; +} + + +/** + * Disable encryption type + * + * @param context Kerberos 5 context + * @param enctype encryption type to disable + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_disable(krb5_context context, + krb5_enctype enctype) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(enctype); + if(et == NULL) { + if (context) + krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + enctype); + return KRB5_PROG_ETYPE_NOSUPP; + } + et->flags |= F_DISABLED; + return 0; +} + +/** + * Enable encryption type + * + * @param context Kerberos 5 context + * @param enctype encryption type to enable + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enctype_enable(krb5_context context, + krb5_enctype enctype) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(enctype); + if(et == NULL) { + if (context) + krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + enctype); + return KRB5_PROG_ETYPE_NOSUPP; + } + et->flags &= ~F_DISABLED; + return 0; +} + +/** + * Enable or disable all weak encryption types + * + * @param context Kerberos 5 context + * @param enable true to enable, false to disable + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_allow_weak_crypto(krb5_context context, + krb5_boolean enable) +{ + int i; + + for(i = 0; i < _krb5_num_etypes; i++) + if(_krb5_etypes[i]->flags & F_WEAK) { + if(enable) + _krb5_etypes[i]->flags &= ~F_DISABLED; + else + _krb5_etypes[i]->flags |= F_DISABLED; + } + return 0; +} + +/** + * Returns is the encryption is strong or weak + * + * @param context Kerberos 5 context + * @param enctype encryption type to probe + * + * @return Returns true if encryption type is weak or is not supported. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_is_enctype_weak(krb5_context context, krb5_enctype enctype) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(enctype); + if(et == NULL || (et->flags & F_WEAK)) + return TRUE; + return FALSE; +} + +/** + * Returns whether the encryption type is new or old + * + * @param context Kerberos 5 context + * @param enctype encryption type to probe + * + * @return Returns true if encryption type is old or is not supported. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_is_enctype_old(krb5_context context, krb5_enctype enctype) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(enctype); + if (!et || (et->flags & F_OLD)) + return TRUE; + return FALSE; +} + +/** + * Returns whether the encryption type should use randomly generated salts + * + * @param context Kerberos 5 context + * @param enctype encryption type to probe + * + * @return Returns true if generated salts should have random component + * + * @ingroup krb5_crypto + */ +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +_krb5_enctype_requires_random_salt(krb5_context context, + krb5_enctype enctype) +{ + struct _krb5_encryption_type *et; + + et = _krb5_find_enctype (enctype); + + return et && (et->flags & F_SP800_108_HMAC_KDF); +} + +static size_t +wrapped_length (krb5_context context, + krb5_crypto crypto, + size_t data_len) +{ + struct _krb5_encryption_type *et = crypto->et; + size_t padsize = et->padsize; + size_t checksumsize = CHECKSUMSIZE(et->checksum); + size_t res; + + res = et->confoundersize + checksumsize + data_len; + res = (res + padsize - 1) / padsize * padsize; + return res; +} + +static size_t +wrapped_length_dervied (krb5_context context, + krb5_crypto crypto, + size_t data_len) +{ + struct _krb5_encryption_type *et = crypto->et; + size_t padsize = et->padsize; + size_t res; + + res = et->confoundersize + data_len; + res = (res + padsize - 1) / padsize * padsize; + if (et->keyed_checksum) + res += et->keyed_checksum->checksumsize; + else + res += et->checksum->checksumsize; + return res; +} + +/* + * Return the size of an encrypted packet of length `data_len' + */ + +KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL +krb5_get_wrapped_length (krb5_context context, + krb5_crypto crypto, + size_t data_len) +{ + if (derived_crypto (context, crypto)) + return wrapped_length_dervied (context, crypto, data_len); + else + return wrapped_length (context, crypto, data_len); +} + +/* + * Return the size of an encrypted packet of length `data_len' + */ + +static size_t +crypto_overhead (krb5_context context, + krb5_crypto crypto) +{ + struct _krb5_encryption_type *et = crypto->et; + size_t res; + + res = CHECKSUMSIZE(et->checksum); + res += et->confoundersize; + if (et->padsize > 1) + res += et->padsize; + return res; +} + +static size_t +crypto_overhead_dervied (krb5_context context, + krb5_crypto crypto) +{ + struct _krb5_encryption_type *et = crypto->et; + size_t res; + + if (et->keyed_checksum) + res = CHECKSUMSIZE(et->keyed_checksum); + else + res = CHECKSUMSIZE(et->checksum); + res += et->confoundersize; + if (et->padsize > 1) + res += et->padsize; + return res; +} + +KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL +krb5_crypto_overhead (krb5_context context, krb5_crypto crypto) +{ + if (derived_crypto (context, crypto)) + return crypto_overhead_dervied (context, crypto); + else + return crypto_overhead (context, crypto); +} + +/** + * Converts the random bytestring to a protocol key according to + * Kerberos crypto frame work. It may be assumed that all the bits of + * the input string are equally random, even though the entropy + * present in the random source may be limited. + * + * @param context Kerberos 5 context + * @param type the enctype resulting key will be of + * @param data input random data to convert to a key + * @param size size of input random data, at least krb5_enctype_keysize() long + * @param key key, output key, free with krb5_free_keyblock_contents() + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_random_to_key(krb5_context context, + krb5_enctype type, + const void *data, + size_t size, + krb5_keyblock *key) +{ + krb5_error_code ret; + struct _krb5_encryption_type *et = _krb5_find_enctype(type); + if(et == NULL) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + type); + return KRB5_PROG_ETYPE_NOSUPP; + } + if ((et->keytype->bits + 7) / 8 > size) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption key %s needs %d bytes " + "of random to make an encryption key " + "out of it", ""), + et->name, (int)et->keytype->size); + return KRB5_PROG_ETYPE_NOSUPP; + } + ret = krb5_data_alloc(&key->keyvalue, et->keytype->size); + if(ret) + return ret; + key->keytype = type; + if (et->keytype->random_to_key) + (*et->keytype->random_to_key)(context, key, data, size); + else + memcpy(key->keyvalue.data, data, et->keytype->size); + + return 0; +} + + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_prf_length(krb5_context context, + krb5_enctype type, + size_t *length) +{ + struct _krb5_encryption_type *et = _krb5_find_enctype(type); + + if(et == NULL || et->prf_length == 0) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + N_("encryption type %d not supported", ""), + type); + return KRB5_PROG_ETYPE_NOSUPP; + } + + *length = et->prf_length; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_prf(krb5_context context, + const krb5_crypto crypto, + const krb5_data *input, + krb5_data *output) +{ + struct _krb5_encryption_type *et = crypto->et; + + krb5_data_zero(output); + + if(et->prf == NULL) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + "kerberos prf for %s not supported", + et->name); + return KRB5_PROG_ETYPE_NOSUPP; + } + + return (*et->prf)(context, crypto, input, output); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_prfplus(krb5_context context, + const krb5_crypto crypto, + const krb5_data *input, + size_t length, + krb5_data *output) +{ + krb5_error_code ret; + krb5_data input2; + unsigned char i = 1; + unsigned char *p; + + krb5_data_zero(&input2); + krb5_data_zero(output); + + krb5_clear_error_message(context); + + ret = krb5_data_alloc(output, length); + if (ret) goto out; + ret = krb5_data_alloc(&input2, input->length + 1); + if (ret) goto out; + + krb5_clear_error_message(context); + + memcpy(((unsigned char *)input2.data) + 1, input->data, input->length); + + p = output->data; + + while (length) { + krb5_data block; + + ((unsigned char *)input2.data)[0] = i++; + + ret = krb5_crypto_prf(context, crypto, &input2, &block); + if (ret) + goto out; + + if (block.length < length) { + memcpy(p, block.data, block.length); + length -= block.length; + } else { + memcpy(p, block.data, length); + length = 0; + } + p += block.length; + krb5_data_free(&block); + } + + out: + krb5_data_free(&input2); + if (ret) + krb5_data_free(output); + return ret; +} + +/** + * The FX-CF2 key derivation function, used in FAST and preauth framework. + * + * @param context Kerberos 5 context + * @param crypto1 first key to combine + * @param crypto2 second key to combine + * @param pepper1 factor to combine with first key to guarantee uniqueness + * @param pepper2 factor to combine with second key to guarantee uniqueness + * @param enctype the encryption type of the resulting key + * @param res allocated key, free with krb5_free_keyblock_contents() + * + * @return Return an error code or 0. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_crypto_fx_cf2(krb5_context context, + const krb5_crypto crypto1, + const krb5_crypto crypto2, + krb5_data *pepper1, + krb5_data *pepper2, + krb5_enctype enctype, + krb5_keyblock *res) +{ + krb5_error_code ret; + krb5_data os1, os2; + size_t i, keysize; + + memset(res, 0, sizeof(*res)); + krb5_data_zero(&os1); + krb5_data_zero(&os2); + + ret = krb5_enctype_keybits(context, enctype, &keysize); + if (ret) + return ret; + keysize = (keysize + 7) / 8; + + ret = krb5_crypto_prfplus(context, crypto1, pepper1, keysize, &os1); + if (ret) + goto out; + ret = krb5_crypto_prfplus(context, crypto2, pepper2, keysize, &os2); + if (ret) + goto out; + + res->keytype = enctype; + { + unsigned char *p1 = os1.data, *p2 = os2.data; + for (i = 0; i < keysize; i++) + p1[i] ^= p2[i]; + } + ret = krb5_random_to_key(context, enctype, os1.data, keysize, res); + out: + krb5_data_free(&os1); + krb5_data_free(&os2); + + return ret; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_crypto_set_flags(krb5_context context, + krb5_crypto crypto, + krb5_flags flags) +{ + crypto->flags |= flags; +} + +#ifndef HEIMDAL_SMALLER + +/** + * Deprecated: keytypes don't exist, they are really enctypes. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_keytype_to_enctypes (krb5_context context, + krb5_keytype keytype, + unsigned *len, + krb5_enctype **val) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + int i; + unsigned n = 0; + krb5_enctype *ret; + + for (i = _krb5_num_etypes - 1; i >= 0; --i) { + if (_krb5_etypes[i]->keytype->type == keytype + && !(_krb5_etypes[i]->flags & F_PSEUDO) + && krb5_enctype_valid(context, _krb5_etypes[i]->type) == 0) + ++n; + } + if (n == 0) { + krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, + "Keytype has no mapping"); + return KRB5_PROG_KEYTYPE_NOSUPP; + } + + ret = malloc(n * sizeof(*ret)); + if (ret == NULL && n != 0) + return krb5_enomem(context); + n = 0; + for (i = _krb5_num_etypes - 1; i >= 0; --i) { + if (_krb5_etypes[i]->keytype->type == keytype + && !(_krb5_etypes[i]->flags & F_PSEUDO) + && krb5_enctype_valid(context, _krb5_etypes[i]->type) == 0) + ret[n++] = _krb5_etypes[i]->type; + } + *len = n; + *val = ret; + return 0; +} + +/** + * Deprecated: keytypes don't exist, they are really enctypes. + * + * @ingroup krb5_deprecated + */ + +/* if two enctypes have compatible keys */ +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_enctypes_compatible_keys(krb5_context context, + krb5_enctype etype1, + krb5_enctype etype2) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + struct _krb5_encryption_type *e1 = _krb5_find_enctype(etype1); + struct _krb5_encryption_type *e2 = _krb5_find_enctype(etype2); + return e1 != NULL && e2 != NULL && e1->keytype == e2->keytype; +} + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/crypto.h b/third_party/heimdal/lib/krb5/crypto.h new file mode 100644 index 0000000..d02f841 --- /dev/null +++ b/third_party/heimdal/lib/krb5/crypto.h @@ -0,0 +1,231 @@ +/* + * Copyright (c) 1997 - 2016 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. + */ + +#ifndef HEIMDAL_SMALLER +#define DES3_OLD_ENCTYPE 1 +#endif + +struct _krb5_key_data { + krb5_keyblock *key; + krb5_data *schedule; +}; + +struct _krb5_key_usage; + +#define CRYPTO_ETYPE(C) ((C)->et->type) + +/* bits for `flags' below */ +#define F_KEYED 0x0001 /* checksum is keyed */ +#define F_CPROOF 0x0002 /* checksum is collision proof */ +#define F_DERIVED 0x0004 /* uses derived keys */ +#define F_VARIANT 0x0008 /* uses `variant' keys (6.4.3) */ +#define F_PSEUDO 0x0010 /* not a real protocol type */ +#define F_DISABLED 0x0020 /* enctype/checksum disabled */ +#define F_WEAK 0x0040 /* enctype is considered weak */ +#define F_OLD 0x0080 /* enctype is old */ + +#define F_RFC3961_ENC 0x0100 /* RFC3961 simplified profile */ +#define F_SPECIAL 0x0200 /* backwards */ +#define F_ENC_THEN_CKSUM 0x0400 /* checksum is over encrypted data */ +#define F_CRYPTO_MASK 0x0F00 + +#define F_RFC3961_KDF 0x1000 /* RFC3961 KDF */ +#define F_SP800_108_HMAC_KDF 0x2000 /* SP800-108 HMAC KDF */ +#define F_KDF_MASK 0xF000 + +struct salt_type { + krb5_salttype type; + const char *name; + krb5_error_code (*string_to_key)(krb5_context, krb5_enctype, krb5_data, + krb5_salt, krb5_data, krb5_keyblock*); +}; + +struct _krb5_key_type { + krb5_enctype type; + const char *name; + size_t bits; + size_t size; + size_t schedule_size; + void (*random_key)(krb5_context, krb5_keyblock*); + void (*schedule)(krb5_context, struct _krb5_key_type *, struct _krb5_key_data *); + struct salt_type *string_to_key; + void (*random_to_key)(krb5_context, krb5_keyblock*, const void*, size_t); + void (*cleanup)(krb5_context, struct _krb5_key_data *); + const EVP_CIPHER *(*evp)(void); +}; + +struct _krb5_checksum_type { + krb5_cksumtype type; + const char *name; + size_t blocksize; + size_t checksumsize; + unsigned flags; + krb5_error_code (*checksum)(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, int niov, + Checksum *csum); + krb5_error_code (*verify)(krb5_context context, + krb5_crypto crypto, + struct _krb5_key_data *key, + unsigned usage, + const struct krb5_crypto_iov *iov, int niov, + Checksum *csum); +}; + +struct _krb5_encryption_type { + krb5_enctype type; + const char *name; + const char *alias; + size_t blocksize; + size_t padsize; + size_t confoundersize; + struct _krb5_key_type *keytype; + struct _krb5_checksum_type *checksum; + struct _krb5_checksum_type *keyed_checksum; + unsigned flags; + krb5_error_code (*encrypt)(krb5_context context, + struct _krb5_key_data *key, + void *data, size_t len, + krb5_boolean encryptp, + int usage, + void *ivec); + krb5_error_code (*encrypt_iov)(krb5_context context, + struct _krb5_key_data *key, + krb5_crypto_iov *iov, int niov, + krb5_boolean encryptp, + int usage, + void *ivec); + size_t prf_length; + krb5_error_code (*prf)(krb5_context, + krb5_crypto, const krb5_data *, krb5_data *); +}; + +#define ENCRYPTION_USAGE(U) (((uint32_t)(U) << 8) | 0xAA) +#define INTEGRITY_USAGE(U) (((uint32_t)(U) << 8) | 0x55) +#define CHECKSUM_USAGE(U) (((uint32_t)(U) << 8) | 0x99) + +/* Checksums */ + +extern struct _krb5_checksum_type _krb5_checksum_none; +extern struct _krb5_checksum_type _krb5_checksum_crc32; +extern struct _krb5_checksum_type _krb5_checksum_rsa_md4; +extern struct _krb5_checksum_type _krb5_checksum_rsa_md4_des; +extern struct _krb5_checksum_type _krb5_checksum_rsa_md5_des; +extern struct _krb5_checksum_type _krb5_checksum_rsa_md5_des3; +extern struct _krb5_checksum_type _krb5_checksum_rsa_md5; +extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_des3; +extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128; +extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256; +extern struct _krb5_checksum_type _krb5_checksum_hmac_sha256_128_aes128; +extern struct _krb5_checksum_type _krb5_checksum_hmac_sha384_192_aes256; +extern struct _krb5_checksum_type _krb5_checksum_hmac_md5; +extern struct _krb5_checksum_type _krb5_checksum_sha1; +extern struct _krb5_checksum_type _krb5_checksum_sha256; +extern struct _krb5_checksum_type _krb5_checksum_sha384; +extern struct _krb5_checksum_type _krb5_checksum_sha512; + +extern struct _krb5_checksum_type *_krb5_checksum_types[]; +extern int _krb5_num_checksums; + +/* Salts */ + +extern struct salt_type _krb5_AES_SHA1_salt[]; +extern struct salt_type _krb5_AES_SHA2_salt[]; +extern struct salt_type _krb5_arcfour_salt[]; +extern struct salt_type _krb5_des_salt[]; +extern struct salt_type _krb5_des3_salt[]; +extern struct salt_type _krb5_des3_salt_derived[]; + +/* Encryption types */ + +extern struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1; +extern struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1; +extern struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha256_128; +extern struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha384_192; +extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_sha1; +extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_md5; +extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_none; +extern struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5; +extern struct _krb5_encryption_type _krb5_enctype_des_cbc_md5; +extern struct _krb5_encryption_type _krb5_enctype_old_des3_cbc_sha1; +extern struct _krb5_encryption_type _krb5_enctype_des_cbc_crc; +extern struct _krb5_encryption_type _krb5_enctype_des_cbc_md4; +extern struct _krb5_encryption_type _krb5_enctype_des_cbc_md5; +extern struct _krb5_encryption_type _krb5_enctype_des_cbc_none; +extern struct _krb5_encryption_type _krb5_enctype_des_cfb64_none; +extern struct _krb5_encryption_type _krb5_enctype_des_pcbc_none; +extern struct _krb5_encryption_type _krb5_enctype_null; + +extern struct _krb5_encryption_type *_krb5_etypes[]; +extern int _krb5_num_etypes; + +static inline int +_krb5_crypto_iov_should_sign(const struct krb5_crypto_iov *iov) +{ + return (iov->flags == KRB5_CRYPTO_TYPE_DATA + || iov->flags == KRB5_CRYPTO_TYPE_SIGN_ONLY + || iov->flags == KRB5_CRYPTO_TYPE_HEADER + || iov->flags == KRB5_CRYPTO_TYPE_PADDING); +} + +/* NO_HCRYPTO_POLLUTION is defined in pkinit-ec.c. See commentary there. */ +#ifndef NO_HCRYPTO_POLLUTION +/* Interface to the EVP crypto layer provided by hcrypto */ +struct _krb5_evp_schedule { + /* + * Normally we'd say EVP_CIPHER_CTX here, but! this header gets + * included in lib/krb5/pkinit-ec.c + */ + EVP_CIPHER_CTX ectx; + EVP_CIPHER_CTX dctx; +}; + +struct krb5_crypto_data { + struct _krb5_encryption_type *et; + struct _krb5_key_data key; + EVP_MD_CTX *mdctx; + HMAC_CTX *hmacctx; + int num_key_usage; + struct _krb5_key_usage *key_usage; + krb5_flags flags; +}; + +/* + * Allow generation and verification of unkeyed checksums even when + * key material is available. + */ +#define KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM 0x01 + +#endif diff --git a/third_party/heimdal/lib/krb5/data.c b/third_party/heimdal/lib/krb5/data.c new file mode 100644 index 0000000..abfa053 --- /dev/null +++ b/third_party/heimdal/lib/krb5/data.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/** + * Reset the (potentially uninitialized) krb5_data structure. + * + * @param p krb5_data to reset. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_data_zero(krb5_data *p) +{ + p->length = 0; + p->data = NULL; +} + +/** + * Free the content of krb5_data structure, its ok to free a zeroed + * structure (with memset() or krb5_data_zero()). When done, the + * structure will be zeroed. The same function is called + * krb5_free_data_contents() in MIT Kerberos. + * + * @param p krb5_data to free. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_data_free(krb5_data *p) +{ + free(p->data); + krb5_data_zero(p); +} + +/** + * Free krb5_data (and its content). + * + * @param context Kerberos 5 context. + * @param p krb5_data to free. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_data(krb5_context context, + krb5_data *p) +{ + krb5_data_free(p); + free(p); +} + +/** + * Allocate data of and krb5_data. + * + * @param p krb5_data to allocate. + * @param len size to allocate. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_data_alloc(krb5_data *p, int len) +{ + p->data = malloc(len); + if(len && p->data == NULL) + return ENOMEM; + p->length = len; + return 0; +} + +/** + * Grow (or shrink) the content of krb5_data to a new size. + * + * @param p krb5_data to free. + * @param len new size. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_data_realloc(krb5_data *p, int len) +{ + void *tmp; + tmp = realloc(p->data, len); + if(len && !tmp) + return ENOMEM; + p->data = tmp; + p->length = len; + return 0; +} + +/** + * Copy the data of len into the krb5_data. + * + * @param p krb5_data to copy into. + * @param data data to copy.. + * @param len new size. + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_data_copy(krb5_data *p, const void *data, size_t len) +{ + if (len) { + if(krb5_data_alloc(p, len)) + return ENOMEM; + memcpy(p->data, data, len); + } else + p->data = NULL; + p->length = len; + return 0; +} + +/** + * Copy the data into a newly allocated krb5_data. + * + * @param context Kerberos 5 context. + * @param indata the krb5_data data to copy + * @param outdata new krb5_date to copy too. Free with krb5_free_data(). + * + * @return Returns 0 to indicate success. Otherwise an kerberos et + * error code is returned. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_data(krb5_context context, + const krb5_data *indata, + krb5_data **outdata) +{ + krb5_error_code ret; + ALLOC(*outdata, 1); + if(*outdata == NULL) + return krb5_enomem(context); + ret = der_copy_octet_string(indata, *outdata); + if(ret) { + krb5_clear_error_message (context); + free(*outdata); + *outdata = NULL; + } + return ret; +} + +/** + * Compare to data. + * + * @param data1 krb5_data to compare + * @param data2 krb5_data to compare + * + * @return return the same way as memcmp(), useful when sorting. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_data_cmp(const krb5_data *data1, const krb5_data *data2) +{ + size_t len = data1->length < data2->length ? data1->length : data2->length; + int cmp = memcmp(data1->data, data2->data, len); + + if (cmp == 0) + return data1->length - data2->length; + return cmp; +} + +/** + * Compare to data not exposing timing information from the checksum data + * + * @param data1 krb5_data to compare + * @param data2 krb5_data to compare + * + * @return returns zero for same data, otherwise non zero. + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_data_ct_cmp(const krb5_data *data1, const krb5_data *data2) +{ + if (data1->length != data2->length) + return data1->length - data2->length; + return ct_memcmp(data1->data, data2->data, data1->length); +} diff --git a/third_party/heimdal/lib/krb5/db_plugin.c b/third_party/heimdal/lib/krb5/db_plugin.c new file mode 100644 index 0000000..e997d3d --- /dev/null +++ b/third_party/heimdal/lib/krb5/db_plugin.c @@ -0,0 +1,41 @@ +/* + */ + +#include "krb5_locl.h" +#include "db_plugin.h" + +/* Default plugin (DB using binary search of sorted text file) follows */ +static heim_base_once_t db_plugins_once = HEIM_BASE_ONCE_INIT; + +static krb5_error_code KRB5_LIB_CALL +db_plugins_plcallback(krb5_context context, const void *plug, void *plugctx, + void *userctx) +{ + return 0; +} + +static const char *const db_plugin_deps[] = { "krb5", NULL }; + +static const struct heim_plugin_data +db_plugin_data = { + "krb5", + KRB5_PLUGIN_DB, + KRB5_PLUGIN_DB_VERSION_0, + db_plugin_deps, + krb5_get_instance +}; + +static void +db_plugins_init(void *arg) +{ + krb5_context context = arg; + (void)_krb5_plugin_run_f(context, &db_plugin_data, 0, NULL, + db_plugins_plcallback); +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_load_db_plugins(krb5_context context) +{ + heim_base_once_f(&db_plugins_once, context, db_plugins_init); +} + diff --git a/third_party/heimdal/lib/krb5/db_plugin.h b/third_party/heimdal/lib/krb5/db_plugin.h new file mode 100644 index 0000000..ab676d5 --- /dev/null +++ b/third_party/heimdal/lib/krb5/db_plugin.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2011, 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. + */ + +/* $Id$ */ + +#ifndef HEIMDAL_KRB5_DB_PLUGIN_H +#define HEIMDAL_KRB5_DB_PLUGIN_H 1 + +#include + +#define KRB5_PLUGIN_DB "krb5_db_plug" +#define KRB5_PLUGIN_DB_VERSION_0 0 + +/** @struct krb5plugin_db_ftable_desc + * + * @brief Description of the krb5 DB plugin facility. + * + * The krb5_aname_to_lname(3) function's DB rule is pluggable. The + * plugin is named KRB5_PLUGIN_DB ("krb5_db_plug"), with a single minor + * version, KRB5_PLUGIN_DB_VERSION_0 (0). + * + * The plugin consists of a data symbol referencing a structure of type + * krb5plugin_db_ftable_desc, with three fields: + * + * @param init Plugin initialization function (see krb5-plugin(7)) + * + * @param minor_version The plugin minor version number (0) + * + * @param fini Plugin finalization function + * + * The init entry point is expected to call heim_db_register(). The + * fini entry point is expected to do nothing. + * + * @ingroup krb5_support + */ +typedef struct krb5plugin_db_ftable_desc { + HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context); +} krb5plugin_db_ftable; + +#endif /* HEIMDAL_KRB5_DB_PLUGIN_H */ + diff --git a/third_party/heimdal/lib/krb5/dcache.c b/third_party/heimdal/lib/krb5/dcache.c new file mode 100644 index 0000000..77ccda1 --- /dev/null +++ b/third_party/heimdal/lib/krb5/dcache.c @@ -0,0 +1,854 @@ +/* + * Copyright (c) 1997 - 2008 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 "krb5_locl.h" + +typedef struct krb5_dcache{ + krb5_ccache fcache; + char *name; + char *dir; + char *sub; + unsigned int default_candidate:1; +} krb5_dcache; + +#define DCACHE(X) ((krb5_dcache*)(X)->data.data) +#define D2FCACHE(X) ((X)->fcache) + +static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache); +static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **); +static krb5_error_code KRB5_CALLCONV dcc_set_default(krb5_context, krb5_ccache); + +/* + * Make subsidiary filesystem safe by mapping / and : to -. If the subsidiary + * is longer than 128 bytes, then truncate. + * In all cases, "tkt." is prefixed to be compatible with the DIR requirement + * that subsidiary ccache files be named tkt*. + * + * Thus host/foo.bar.baz@BAR.BAZ -> tkt.host-foo.bar.baz@BAR.BAZ. + * + * In particular, no filesystem component separators will be emitted, and . and + * .. will never be traversed. + */ +static krb5_error_code +fs_encode_subsidiary(krb5_context context, + krb5_dcache *dc, + const char *subsidiary, + char **res) +{ + size_t len = strlen(subsidiary); + size_t i; + + *res = NULL; + if (asprintf(res, "tkt.%s", subsidiary) == -1 || *res == NULL) + return krb5_enomem(context); + for (i = sizeof("tkt.") - 1; i < len; i++) { + switch ((*res)[i]) { +#ifdef WIN32 + case '\\': (*res)[0] = '-'; break; +#endif + case '/': (*res)[0] = '-'; break; + case ':': (*res)[0] = '-'; break; + default: break; + } + } + + /* Hopefully this will work on all filesystems */ + if (len > 128 - sizeof("tkt.") - 1) + (*res)[127] = '\0'; + return 0; +} + +static char * +primary_create(krb5_dcache *dc) +{ + char *primary = NULL; + int asprintf_ret = asprintf(&primary, "%s/primary", dc->dir); + if (asprintf_ret == -1 || primary == NULL) { + return NULL; + } + + return primary; +} + +static int +is_filename_cacheish(const char *name) +{ + size_t i; + + if (strncmp(name, "tkt", sizeof("tkt") - 1) != 0) + return 0; + for (i = sizeof("tkt") - 1; name[i]; i++) + if (ISPATHSEP(name[i])) + return 0; + return 1; +} + +static krb5_error_code +set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual) +{ + char *path = NULL, *primary = NULL; + krb5_error_code ret; + struct iovec iov[2]; + size_t len; + int fd = -1; + int asprintf_ret; + + asprintf_ret = asprintf(&path, "%s/primary-XXXXXX", dc->dir); + if (asprintf_ret == -1 || path == NULL) { + return krb5_enomem(context); + } + + fd = mkstemp(path); + if (fd < 0) { + ret = errno; + goto out; + } + rk_cloexec(fd); +#ifndef _WIN32 + if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) { + ret = errno; + goto out; + } +#endif + len = strlen(residual); + + iov[0].iov_base = rk_UNCONST(residual); + iov[0].iov_len = len; + iov[1].iov_base = "\n"; + iov[1].iov_len = 1; + + if (writev(fd, iov, sizeof(iov)/sizeof(iov[0])) != len + 1) { + ret = errno; + goto out; + } + + primary = primary_create(dc); + if (primary == NULL) { + ret = krb5_enomem(context); + goto out; + } + + if (rename(path, primary) < 0) { + ret = errno; + goto out; + } + + close(fd); + fd = -1; + + ret = 0; + out: + if (fd >= 0) { + (void)unlink(path); + close(fd); + } + if (path) + free(path); + if (primary) + free(primary); + + return ret; +} + +static krb5_error_code +get_default_cache(krb5_context context, krb5_dcache *dc, + const char *subsidiary, char **residual) +{ + krb5_error_code ret; + char buf[MAXPATHLEN]; + char *primary = NULL; + FILE *f; + + *residual = NULL; + if (subsidiary) + return fs_encode_subsidiary(context, dc, subsidiary, residual); + + primary = primary_create(dc); + if (primary == NULL) + return krb5_enomem(context); + + f = fopen(primary, "r"); + if (f == NULL) { + if (errno == ENOENT) { + free(primary); + *residual = strdup("tkt"); + if (*residual == NULL) + return krb5_enomem(context); + return 0; + } + ret = errno; + krb5_set_error_message(context, ret, "failed to open %s", primary); + free(primary); + return ret; + } + + if (fgets(buf, sizeof(buf), f) == NULL) { + ret = ferror(f); + fclose(f); + krb5_set_error_message(context, ret, "read file %s", primary); + free(primary); + return ret; + } + fclose(f); + + buf[strcspn(buf, "\r\n")] = '\0'; + + if (!is_filename_cacheish(buf)) { + krb5_set_error_message(context, KRB5_CC_FORMAT, + "name in %s is not a cache (doesn't start with tkt)", primary); + free(primary); + return KRB5_CC_FORMAT; + } + + free(primary); + + *residual = strdup(buf); + if (*residual == NULL) + return krb5_enomem(context); + + return 0; +} + + + +static krb5_error_code KRB5_CALLCONV +dcc_get_name_2(krb5_context context, + krb5_ccache id, + const char **name, + const char **dir, + const char **sub) +{ + krb5_dcache *dc = DCACHE(id); + + if (name) + *name = dc->name; + if (dir) + *dir = dc->dir; + if (sub) + *sub = dc->sub; + return 0; +} + + +static krb5_error_code +verify_directory(krb5_context context, const char *path) +{ + struct stat sb; + + if (!path[0]) { + krb5_set_error_message(context, EINVAL, + N_("DIR empty directory component", "")); + return EINVAL; + } + + /* XXX should use mkdirx_np() */ + if (rk_mkdir(path, S_IRWXU) == 0) + return 0; + + if (stat(path, &sb) != 0) { + if (errno == ENOENT) { + krb5_set_error_message(context, ENOENT, + N_("DIR directory %s doesn't exists", ""), path); + return ENOENT; + } else { + krb5_set_error_message(context, errno, + N_("DIR directory %s is bad: %s", ""), path, strerror(errno)); + return errno; + } + } + if (!S_ISDIR(sb.st_mode)) { + krb5_set_error_message(context, KRB5_CC_BADNAME, + N_("DIR directory %s is not a directory", ""), path); + return KRB5_CC_BADNAME; + } + + return 0; +} + +static void +dcc_release(krb5_context context, krb5_dcache *dc) +{ + if (dc->fcache) + krb5_cc_close(context, dc->fcache); + free(dc->sub); + free(dc->dir); + free(dc->name); + memset(dc, 0, sizeof(*dc)); + free(dc); +} + +static krb5_error_code +get_default_dir(krb5_context context, char **res) +{ + krb5_error_code ret; + char *s; + + if ((ret = dcc_get_default_name(context, &s))) + return ret; + if (strncmp(s, "DIR:", sizeof("DIR:") - 1) != 0) { + *res = s; + s = NULL; + } else if ((*res = strdup(s + sizeof("DIR:") - 1)) == NULL) { + ret = krb5_enomem(context); + } + free(s); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +dcc_resolve_2(krb5_context context, + krb5_ccache *id, + const char *res, + const char *sub) +{ + krb5_error_code ret; + krb5_dcache *dc = NULL; + char *filename = NULL; + size_t len; + int has_pathsep = 0; + + if (sub) { + /* + * Here `res' has the directory name (or, if NULL, refers to the + * default DIR cccol), and `sub' has the "subsidiary" name, to which + * we'll prefix "tkt." (though we will insist only on "tkt" later). + */ + if ((dc = calloc(1, sizeof(*dc))) == NULL || + asprintf(&dc->sub, "tkt.%s", sub) == -1 || dc->sub == NULL) { + free(dc); + return krb5_enomem(context); + } + if (res && res[0] && (dc->dir = strdup(res)) == NULL) { + free(dc->sub); + free(dc); + return krb5_enomem(context); + } else if ((!res || !res[0]) && (ret = get_default_dir(context, &dc->dir))) { + free(dc->sub); + free(dc); + return ret; + } + } else { + const char *p; + int is_drive_letter_colon = 0; + + /* + * Here `res' has whatever string followed "DIR:", and we need to parse + * it into `dc->dir' and `dc->sub'. + * + * Conventions we support for DIR cache naming: + * + * - DIR:path:NAME ---> FILE:path/tktNAME + * - DIR::path/tktNAME ---> FILE:path/tktNAME + * - DIR::NAME ---> FILE:${default_DIR_cccol_path}/tktNAME + * \-> FILE:/tmp/krb5cc_${uid}_dir/tktNAME + * - DIR:path ---> FILE:path/$(cat primary) or FILE:path/tkt + * + */ + + if (res == NULL || *res == '\0' || (res[0] == ':' && res[1] == '\0')) { + /* XXX Why not? */ + krb5_set_error_message(context, KRB5_CC_FORMAT, + N_("\"DIR:\" is not a valid ccache name", "")); + return KRB5_CC_FORMAT; + } + +#ifdef WIN32 + has_pathsep = strchr(res, '\\') != NULL; +#endif + has_pathsep |= strchr(res, '/') != NULL; + + if ((dc = calloc(1, sizeof(*dc))) == NULL) + return krb5_enomem(context); + + p = strrchr(res, ':'); +#ifdef WIN32 + is_drive_letter_colon = + p && ((res[0] == ':' && res[1] != ':' && p - res == 2) || + (res[0] != ':' && p - res == 1)); +#endif + + if (res[0] != ':' && p && !is_drive_letter_colon) { + /* DIR:path:NAME */ + if ((dc->dir = strndup(res, (p - res))) == NULL || + asprintf(&dc->sub, "tkt.%s", p + 1) < 0 || dc->sub == NULL) { + dcc_release(context, dc); + return krb5_enomem(context); + } + } else if (res[0] == ':' && has_pathsep) { + char *q; + + /* DIR::path/tktNAME (the "tkt" must be there; we'll check) */ + if ((dc->dir = strdup(&res[1])) == NULL) { + dcc_release(context, dc); + return krb5_enomem(context); + } +#ifdef _WIN32 + q = strrchr(dc->dir, '\\'); + if (q == NULL || ((p = strrchr(dc->dir, '/')) && q < p)) +#endif + q = strrchr(dc->dir, '/'); + *q++ = '\0'; + if ((dc->sub = strdup(q)) == NULL) { + dcc_release(context, dc); + return krb5_enomem(context); + } + } else if (res[0] == ':') { + /* DIR::NAME -- no path component separators in NAME */ + if ((ret = get_default_dir(context, &dc->dir))) { + dcc_release(context, dc); + return ret; + } + if (asprintf(&dc->sub, "tkt.%s", res + 1) < 0 || dc->sub == NULL) { + dcc_release(context, dc); + return krb5_enomem(context); + } + } else { + /* DIR:path */ + if ((dc->dir = strdup(res)) == NULL) { + dcc_release(context, dc); + return krb5_enomem(context); + } + + if ((ret = get_default_cache(context, dc, NULL, &dc->sub))) { + dcc_release(context, dc); + return ret; + } + } + } + + /* Strip off extra slashes on the end */ + for (len = strlen(dc->dir); + len && ISPATHSEP(dc->dir[len - 1]); + len--) + dc->dir[len - 1] = '\0'; + + /* If we got here then `dc->dir' and `dc->sub' must both be set */ + + if ((ret = verify_directory(context, dc->dir))) { + dcc_release(context, dc); + return ret; + } + if (!is_filename_cacheish(dc->sub)) { + krb5_set_error_message(context, KRB5_CC_FORMAT, + N_("Name %s is not a cache " + "(doesn't start with tkt)", ""), dc->sub); + dcc_release(context, dc); + return KRB5_CC_FORMAT; + } + if (asprintf(&dc->name, ":%s/%s", dc->dir, dc->sub) == -1 || + dc->name == NULL || + asprintf(&filename, "FILE%s", dc->name) == -1 || filename == NULL) { + dcc_release(context, dc); + return krb5_enomem(context); + } + + ret = krb5_cc_resolve(context, filename, &dc->fcache); + free(filename); + if (ret) { + dcc_release(context, dc); + return ret; + } + + dc->default_candidate = 1; + (*id)->data.data = dc; + (*id)->data.length = sizeof(*dc); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +dcc_gen_new(krb5_context context, krb5_ccache *id) +{ + krb5_error_code ret; + char *def_dir = NULL; + char *name = NULL; + int fd = -1; + + ret = get_default_dir(context, &def_dir); + if (ret == 0) + ret = verify_directory(context, def_dir); + if (ret == 0 && + (asprintf(&name, "DIR::%s/tktXXXXXX", def_dir) == -1 || name == NULL)) + ret = krb5_enomem(context); + if (ret == 0 && (fd = mkstemp(name + sizeof("DIR::") - 1)) == -1) + ret = errno; + if (ret == 0) + ret = dcc_resolve_2(context, id, name + sizeof("DIR:") - 1, NULL); + + free(def_dir); + free(name); + if (fd != -1) + close(fd); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +dcc_initialize(krb5_context context, + krb5_ccache id, + krb5_principal primary_principal) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_initialize(context, D2FCACHE(dc), primary_principal); +} + +static krb5_error_code KRB5_CALLCONV +dcc_close(krb5_context context, + krb5_ccache id) +{ + krb5_dcache *dc = DCACHE(id); + krb5_principal p = NULL; + struct stat st; + char *primary = NULL; + + /* + * If there's no default cache, but we're closing one, and the one we're + * closing has been initialized, then make it the default. This makes the + * first cache created the default. + * + * FIXME We should check if `D2FCACHE(dc)' has live credentials. + */ + if (dc->default_candidate && D2FCACHE(dc) && + krb5_cc_get_principal(context, D2FCACHE(dc), &p) == 0 && + (primary = primary_create(dc)) && + (stat(primary, &st) == -1 || !S_ISREG(st.st_mode) || st.st_size == 0)) + dcc_set_default(context, id); + krb5_free_principal(context, p); + free(primary); + dcc_release(context, DCACHE(id)); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +dcc_destroy(krb5_context context, + krb5_ccache id) +{ + krb5_dcache *dc = DCACHE(id); + krb5_ccache fcache = D2FCACHE(dc); + dc->fcache = NULL; + return krb5_cc_destroy(context, fcache); +} + +static krb5_error_code KRB5_CALLCONV +dcc_store_cred(krb5_context context, + krb5_ccache id, + krb5_creds *creds) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_store_cred(context, D2FCACHE(dc), creds); +} + +static krb5_error_code KRB5_CALLCONV +dcc_get_principal(krb5_context context, + krb5_ccache id, + krb5_principal *principal) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_get_principal(context, D2FCACHE(dc), principal); +} + +static krb5_error_code KRB5_CALLCONV +dcc_get_first (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_start_seq_get(context, D2FCACHE(dc), cursor); +} + +static krb5_error_code KRB5_CALLCONV +dcc_get_next (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *creds) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_next_cred(context, D2FCACHE(dc), cursor, creds); +} + +static krb5_error_code KRB5_CALLCONV +dcc_end_get (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_end_seq_get(context, D2FCACHE(dc), cursor); +} + +static krb5_error_code KRB5_CALLCONV +dcc_remove_cred(krb5_context context, + krb5_ccache id, + krb5_flags which, + krb5_creds *cred) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_remove_cred(context, D2FCACHE(dc), which, cred); +} + +static krb5_error_code KRB5_CALLCONV +dcc_set_flags(krb5_context context, + krb5_ccache id, + krb5_flags flags) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_set_flags(context, D2FCACHE(dc), flags); +} + +static int KRB5_CALLCONV +dcc_get_version(krb5_context context, + krb5_ccache id) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_get_version(context, D2FCACHE(dc)); +} + +struct dcache_iter { + char *primary; + krb5_dcache *dc; + DIR *d; + unsigned int first:1; +}; + +static krb5_error_code KRB5_CALLCONV +dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) +{ + struct dcache_iter *iter = NULL; + const char *name = krb5_cc_default_name(context); + size_t len; + char *p; + + *cursor = NULL; + + if (strncmp(name, "DIR:", sizeof("DIR:") - 1) != 0) { + krb5_set_error_message(context, KRB5_CC_FORMAT, + N_("Can't list DIR caches unless its the default type", "")); + return KRB5_CC_FORMAT; + } + + if ((iter = calloc(1, sizeof(*iter))) == NULL || + (iter->dc = calloc(1, sizeof(iter->dc[0]))) == NULL || + (iter->dc->dir = strdup(name + sizeof("DIR:") - 1)) == NULL) { + if (iter) + free(iter->dc); + free(iter); + return krb5_enomem(context); + } + iter->first = 1; + p = strrchr(iter->dc->dir, ':'); +#ifdef WIN32 + if (p == iter->dc->dir + 1) + p = NULL; +#endif + if (p) + *p = '\0'; + + /* Strip off extra slashes on the end */ + for (len = strlen(iter->dc->dir); + len && ISPATHSEP(iter->dc->dir[len - 1]); + len--) { + iter->dc->dir[len - 1] = '\0'; + } + + if ((iter->d = opendir(iter->dc->dir)) == NULL) { + krb5_set_error_message(context, KRB5_CC_FORMAT, + N_("Can't open DIR %s: %s", ""), + iter->dc->dir, strerror(errno)); + free(iter->dc->dir); + free(iter->dc); + free(iter); + return KRB5_CC_FORMAT; + } + + *cursor = iter; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) +{ + struct dcache_iter *iter = cursor; + krb5_error_code ret; + struct stat st; + struct dirent *dentry; + char *p = NULL; + + *id = NULL; + if (iter == NULL) + return krb5_einval(context, 2); + + /* Emit primary subsidiary first */ + if (iter->first && + get_default_cache(context, iter->dc, NULL, &iter->primary) == 0 && + iter->primary && is_filename_cacheish(iter->primary)) { + iter->first = 0; + ret = KRB5_CC_END; + if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, iter->primary) > -1 && p != NULL && + stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode)) + ret = krb5_cc_resolve(context, p, id); + if (p == NULL) + return krb5_enomem(context); + free(p); + if (ret == 0) + return ret; + p = NULL; + } + + iter->first = 0; + for (dentry = readdir(iter->d); dentry; dentry = readdir(iter->d)) { + if (!is_filename_cacheish(dentry->d_name) || + (iter->primary && strcmp(dentry->d_name, iter->primary) == 0)) + continue; + p = NULL; + ret = KRB5_CC_END; + if (asprintf(&p, "FILE:%s/%s", iter->dc->dir, dentry->d_name) > -1 && + p != NULL && + stat(p + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode)) + ret = krb5_cc_resolve(context, p, id); + free(p); + if (p == NULL) + return krb5_enomem(context); + if (ret == 0) + return ret; + } + return KRB5_CC_END; +} + +static krb5_error_code KRB5_CALLCONV +dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) +{ + struct dcache_iter *iter = cursor; + + if (iter == NULL) + return krb5_einval(context, 2); + + (void) closedir(iter->d); + free(iter->dc->dir); + free(iter->dc); + free(iter->primary); + free(iter); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) +{ + krb5_dcache *dcfrom = DCACHE(from); + krb5_dcache *dcto = DCACHE(to); + + dcfrom->default_candidate = 0; + dcto->default_candidate = 1; + return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto)); +} + +static krb5_error_code KRB5_CALLCONV +dcc_get_default_name(krb5_context context, char **str) +{ + const char *def_cc_colname = + krb5_config_get_string_default(context, NULL, KRB5_DEFAULT_CCNAME_DIR, + "libdefaults", "default_cc_collection", + NULL); + + /* [libdefaults] default_cc_collection is for testing */ + if (strncmp(def_cc_colname, "DIR:", sizeof("DIR:") - 1) != 0) + def_cc_colname = KRB5_DEFAULT_CCNAME_DIR; + return _krb5_expand_default_cc_name(context, def_cc_colname, str); +} + +static krb5_error_code KRB5_CALLCONV +dcc_set_default(krb5_context context, krb5_ccache id) +{ + krb5_dcache *dc = DCACHE(id); + + if (dc->sub == NULL) + return ENOENT; + return set_default_cache(context, dc, dc->sub); +} + +static krb5_error_code KRB5_CALLCONV +dcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_last_change_time(context, D2FCACHE(dc), mtime); +} + +static krb5_error_code KRB5_CALLCONV +dcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_set_kdc_offset(context, D2FCACHE(dc), kdc_offset); +} + +static krb5_error_code KRB5_CALLCONV +dcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) +{ + krb5_dcache *dc = DCACHE(id); + return krb5_cc_get_kdc_offset(context, D2FCACHE(dc), kdc_offset); +} + + +/** + * Variable containing the DIR based credential cache implemention. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops = { + KRB5_CC_OPS_VERSION_5, + "DIR", + NULL, + NULL, + dcc_gen_new, + dcc_initialize, + dcc_destroy, + dcc_close, + dcc_store_cred, + NULL, /* dcc_retrieve */ + dcc_get_principal, + dcc_get_first, + dcc_get_next, + dcc_end_get, + dcc_remove_cred, + dcc_set_flags, + dcc_get_version, + dcc_get_cache_first, + dcc_get_cache_next, + dcc_end_cache_get, + dcc_move, + dcc_get_default_name, + dcc_set_default, + dcc_lastchange, + dcc_set_kdc_offset, + dcc_get_kdc_offset, + dcc_get_name_2, + dcc_resolve_2 +}; diff --git a/third_party/heimdal/lib/krb5/deprecated.c b/third_party/heimdal/lib/krb5/deprecated.c new file mode 100644 index 0000000..172f089 --- /dev/null +++ b/third_party/heimdal/lib/krb5/deprecated.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 1997 - 2009 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. + */ + +#ifdef __GNUC__ +/* For some GCCs there's no way to shut them up about deprecated functions */ +#define KRB5_DEPRECATED_FUNCTION(x) +#endif + +#include "krb5_locl.h" + + +#undef __attribute__ +#define __attribute__(x) + +#ifndef HEIMDAL_SMALLER + +/** + * Same as krb5_data_free(). MIT compat. + * + * Deprecated: use krb5_data_free(). + * + * @param context Kerberos 5 context. + * @param data krb5_data to free. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_data_contents(krb5_context context, krb5_data *data) + KRB5_DEPRECATED_FUNCTION("Use krb5_data_free instead") +{ + krb5_data_free(data); +} + +/** + * Deprecated: keytypes doesn't exists, they are really enctypes. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_keytype_to_enctypes_default (krb5_context context, + krb5_keytype keytype, + unsigned *len, + krb5_enctype **val) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + unsigned int i, n; + krb5_enctype *ret; + + if (keytype != (krb5_keytype)KEYTYPE_DES || context->etypes_des == NULL) + return krb5_keytype_to_enctypes (context, keytype, len, val); + + for (n = 0; context->etypes_des[n]; ++n) + ; + ret = malloc (n * sizeof(*ret)); + if (ret == NULL && n != 0) + return krb5_enomem(context); + for (i = 0; i < n; ++i) + ret[i] = context->etypes_des[i]; + *len = n; + *val = ret; + return 0; +} + + +static struct { + const char *name; + krb5_keytype type; +} keys[] = { + { "null", KRB5_ENCTYPE_NULL }, + { "des", KRB5_ENCTYPE_DES_CBC_CRC }, + { "des3", KRB5_ENCTYPE_OLD_DES3_CBC_SHA1 }, + { "aes-128", KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 }, + { "aes-256", KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 }, + { "arcfour", KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 }, + { "arcfour-56", KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56 } +}; + +static int num_keys = sizeof(keys) / sizeof(keys[0]); + +/** + * Deprecated: keytypes doesn't exists, they are really enctypes in + * most cases, use krb5_enctype_to_string(). + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_keytype_to_string(krb5_context context, + krb5_keytype keytype, + char **string) + KRB5_DEPRECATED_FUNCTION("Use krb5_enctype_to_string instead") +{ + const char *name = NULL; + int i; + + for(i = 0; i < num_keys; i++) { + if(keys[i].type == keytype) { + name = keys[i].name; + break; + } + } + + if(i >= num_keys) { + krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, + "key type %d not supported", keytype); + return KRB5_PROG_KEYTYPE_NOSUPP; + } + *string = strdup(name); + if (*string == NULL) + return krb5_enomem(context); + return 0; +} + +/** + * Deprecated: keytypes doesn't exists, they are really enctypes in + * most cases, use krb5_string_to_enctype(). + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_string_to_keytype(krb5_context context, + const char *string, + krb5_keytype *keytype) + KRB5_DEPRECATED_FUNCTION("Use krb5_string_to_enctype instead") +{ + char *end; + int i; + + for(i = 0; i < num_keys; i++) + if(strcasecmp(keys[i].name, string) == 0){ + *keytype = keys[i].type; + return 0; + } + + /* check if the enctype is a number */ + *keytype = strtol(string, &end, 0); + if(*end == '\0' && *keytype != 0) { + if (krb5_enctype_valid(context, *keytype) == 0) + return 0; + } + + krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP, + "key type %s not supported", string); + return KRB5_PROG_KEYTYPE_NOSUPP; +} + +/** + * Deprecated: use krb5_get_init_creds() and friends. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_CALLCONV +krb5_password_key_proc (krb5_context context, + krb5_enctype type, + krb5_salt salt, + krb5_const_pointer keyseed, + krb5_keyblock **key) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_error_code ret; + const char *password = (const char *)keyseed; + char buf[BUFSIZ]; + + *key = malloc (sizeof (**key)); + if (*key == NULL) + return krb5_enomem(context); + if (password == NULL) { + if(UI_UTIL_read_pw_string (buf, sizeof(buf), "Password: ", 0)) { + free (*key); + krb5_clear_error_message(context); + return KRB5_LIBOS_PWDINTR; + } + password = buf; + } + ret = krb5_string_to_key_salt (context, type, password, salt, *key); + memset_s(buf, sizeof(buf), 0, sizeof(buf)); + return ret; +} + +/** + * Deprecated: use krb5_get_init_creds() and friends. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_in_tkt_with_password (krb5_context context, + krb5_flags options, + krb5_addresses *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *pre_auth_types, + const char *password, + krb5_ccache ccache, + krb5_creds *creds, + krb5_kdc_rep *ret_as_reply) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + return krb5_get_in_tkt (context, + options, + addrs, + etypes, + pre_auth_types, + krb5_password_key_proc, + password, + NULL, + NULL, + creds, + ccache, + ret_as_reply); +} + +static krb5_error_code KRB5_CALLCONV +krb5_skey_key_proc (krb5_context context, + krb5_enctype type, + krb5_salt salt, + krb5_const_pointer keyseed, + krb5_keyblock **key) +{ + return krb5_copy_keyblock (context, keyseed, key); +} + +/** + * Deprecated: use krb5_get_init_creds() and friends. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_in_tkt_with_skey (krb5_context context, + krb5_flags options, + krb5_addresses *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *pre_auth_types, + const krb5_keyblock *key, + krb5_ccache ccache, + krb5_creds *creds, + krb5_kdc_rep *ret_as_reply) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + if(key == NULL) + return krb5_get_in_tkt_with_keytab (context, + options, + addrs, + etypes, + pre_auth_types, + NULL, + ccache, + creds, + ret_as_reply); + else + return krb5_get_in_tkt (context, + options, + addrs, + etypes, + pre_auth_types, + krb5_skey_key_proc, + key, + NULL, + NULL, + creds, + ccache, + ret_as_reply); +} + +/** + * Deprecated: use krb5_get_init_creds() and friends. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_CALLCONV +krb5_keytab_key_proc (krb5_context context, + krb5_enctype enctype, + krb5_salt salt, + krb5_const_pointer keyseed, + krb5_keyblock **key) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); + krb5_keytab keytab = args->keytab; + krb5_principal principal = args->principal; + krb5_error_code ret; + krb5_keytab real_keytab; + krb5_keytab_entry entry; + + if(keytab == NULL) + krb5_kt_default(context, &real_keytab); + else + real_keytab = keytab; + + ret = krb5_kt_get_entry (context, real_keytab, principal, + 0, enctype, &entry); + if (ret == 0) { + ret = krb5_copy_keyblock (context, &entry.keyblock, key); + krb5_kt_free_entry(context, &entry); + } + + if (keytab == NULL) + krb5_kt_close (context, real_keytab); + return ret; +} + +/** + * Deprecated: use krb5_get_init_creds() and friends. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_in_tkt_with_keytab (krb5_context context, + krb5_flags options, + krb5_addresses *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *pre_auth_types, + krb5_keytab keytab, + krb5_ccache ccache, + krb5_creds *creds, + krb5_kdc_rep *ret_as_reply) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_keytab_key_proc_args a; + + a.principal = creds->client; + a.keytab = keytab; + + return krb5_get_in_tkt (context, + options, + addrs, + etypes, + pre_auth_types, + krb5_keytab_key_proc, + &a, + NULL, + NULL, + creds, + ccache, + ret_as_reply); +} + +/** + * Generate a new ccache of type `ops' in `id'. + * + * Deprecated: use krb5_cc_new_unique() instead. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_ccache + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_cc_gen_new(krb5_context context, + const krb5_cc_ops *ops, + krb5_ccache *id) + KRB5_DEPRECATED_FUNCTION("Use krb5_cc_new_unique instead") +{ + return krb5_cc_new_unique(context, ops->prefix, NULL, id); +} + +/** + * Deprecated: use krb5_principal_get_realm() + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_realm * KRB5_LIB_CALL +krb5_princ_realm(krb5_context context, + krb5_principal principal) + KRB5_DEPRECATED_FUNCTION("Use krb5_principal_get_realm instead") +{ + return &principal->realm; +} + + +/** + * Deprecated: use krb5_principal_set_realm() + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_princ_set_realm(krb5_context context, + krb5_principal principal, + krb5_realm *realm) + KRB5_DEPRECATED_FUNCTION("Use krb5_principal_set_realm instead") +{ + principal->realm = *realm; +} + +/** + * Deprecated: use krb5_free_cred_contents() + * + * @ingroup krb5_deprecated + */ + +/* keep this for compatibility with older code */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_creds_contents (krb5_context context, krb5_creds *c) + KRB5_DEPRECATED_FUNCTION("Use krb5_free_cred_contents instead") +{ + return krb5_free_cred_contents (context, c); +} + +/** + * Free the error message returned by krb5_get_error_string(). + * + * Deprecated: use krb5_free_error_message() + * + * @param context Kerberos context + * @param str error message to free + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_error_string(krb5_context context, char *str) + KRB5_DEPRECATED_FUNCTION("Use krb5_free_error_message instead") +{ + krb5_free_error_message(context, str); +} + +/** + * Set the error message returned by krb5_get_error_string(). + * + * Deprecated: use krb5_set_error_message() + * + * @param context Kerberos context + * @param fmt error message to free + * + * @return Return an error code or 0. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_error_string(krb5_context context, const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 2, 3))) + KRB5_DEPRECATED_FUNCTION("Use krb5_set_error_message instead") +{ + va_list ap; + + va_start(ap, fmt); + krb5_vset_error_message (context, 0, fmt, ap); + va_end(ap); + return 0; +} + +/** + * Set the error message returned by krb5_get_error_string(). + * + * Deprecated: use krb5_vset_error_message() + * + * @param context Kerberos context + * @param fmt error message to free + * @param args variable argument list vector + * + * @return Return an error code or 0. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_vset_error_string(krb5_context context, const char *fmt, va_list args) + __attribute__ ((__format__ (__printf__, 2, 0))) + KRB5_DEPRECATED_FUNCTION("Use krb5_vset_error_message instead") +{ + krb5_vset_error_message(context, 0, fmt, args); + return 0; +} + +/** + * Clear the error message returned by krb5_get_error_string(). + * + * Deprecated: use krb5_clear_error_message() + * + * @param context Kerberos context + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_clear_error_string(krb5_context context) + KRB5_DEPRECATED_FUNCTION("Use krb5_clear_error_message instead") +{ + krb5_clear_error_message(context); +} + +/** + * Deprecated: use krb5_get_credentials_with_flags(). + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_cred_from_kdc_opt(krb5_context context, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_creds **out_creds, + krb5_creds ***ret_tgts, + krb5_flags flags) + KRB5_DEPRECATED_FUNCTION("Use krb5_get_credentials_with_flags instead") +{ + krb5_kdc_flags f; + f.i = flags; + return _krb5_get_cred_kdc_any(context, f, ccache, NULL, + in_creds, NULL, NULL, + out_creds, ret_tgts); +} + +/** + * Deprecated: use krb5_get_credentials_with_flags(). + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_cred_from_kdc(krb5_context context, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_creds **out_creds, + krb5_creds ***ret_tgts) + KRB5_DEPRECATED_FUNCTION("Use krb5_get_credentials_with_flags instead") +{ + return krb5_get_cred_from_kdc_opt(context, ccache, + in_creds, out_creds, ret_tgts, 0); +} + +/** + * Deprecated: use krb5_xfree(). + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_unparsed_name(krb5_context context, char *str) + KRB5_DEPRECATED_FUNCTION("Use krb5_xfree instead") +{ + krb5_xfree(str); +} + +/** + * Deprecated: use krb5_generate_subkey_extended() + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_generate_subkey(krb5_context context, + const krb5_keyblock *key, + krb5_keyblock **subkey) + KRB5_DEPRECATED_FUNCTION("Use krb5_generate_subkey_extended instead") +{ + return krb5_generate_subkey_extended(context, key, ETYPE_NULL, subkey); +} + +/** + * Deprecated: use krb5_auth_con_getremoteseqnumber() + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_auth_getremoteseqnumber(krb5_context context, + krb5_auth_context auth_context, + int32_t *seqnumber) + KRB5_DEPRECATED_FUNCTION("Use krb5_auth_con_getremoteseqnumber instead") +{ + *seqnumber = auth_context->remote_seqnumber; + return 0; +} + +/** + * Return the error message in context. On error or no error string, + * the function returns NULL. + * + * @param context Kerberos 5 context + * + * @return an error string, needs to be freed with + * krb5_free_error_message(). The functions return NULL on error. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_get_error_string(krb5_context context) + KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead") +{ + return heim_get_error_string(context->hcontext); +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_have_error_string(krb5_context context) + KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead") +{ + return heim_have_error_string(context->hcontext); +} + +struct send_to_kdc { + krb5_send_to_kdc_func func; + void *data; +}; + +/* + * Send the data `send' to one host from `handle` and get back the reply + * in `receive'. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_sendto (krb5_context context, + const krb5_data *send_data, + krb5_krbhst_handle handle, + krb5_data *receive) +{ + krb5_error_code ret; + krb5_sendto_ctx ctx; + + ret = krb5_sendto_ctx_alloc(context, &ctx); + if (ret) + return ret; + _krb5_sendto_ctx_set_krb5hst(context, ctx, handle); + + ret = krb5_sendto_context(context, ctx, send_data, (char *)_krb5_krbhst_get_realm(handle), receive); + krb5_sendto_ctx_free(context, ctx); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_sendto_kdc(krb5_context context, + const krb5_data *send_data, + const krb5_realm *realm, + krb5_data *receive) +{ + return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_sendto_kdc_flags(krb5_context context, + const krb5_data *send_data, + const krb5_realm *realm, + krb5_data *receive, + int flags) +{ + krb5_error_code ret; + krb5_sendto_ctx ctx; + + ret = krb5_sendto_ctx_alloc(context, &ctx); + if (ret) + return ret; + krb5_sendto_ctx_add_flags(ctx, flags); + krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL); + + ret = krb5_sendto_context(context, ctx, send_data, *realm, receive); + krb5_sendto_ctx_free(context, ctx); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_set_send_to_kdc_func(krb5_context context, + krb5_send_to_kdc_func func, + void *data) +{ + free(context->send_to_kdc); + if (func == NULL) { + context->send_to_kdc = NULL; + return 0; + } + + context->send_to_kdc = malloc(sizeof(*context->send_to_kdc)); + if (context->send_to_kdc == NULL) { + krb5_set_error_message(context, ENOMEM, + N_("malloc: out of memory", "")); + return ENOMEM; + } + + context->send_to_kdc->func = func; + context->send_to_kdc->data = data; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to) +{ + if (context->send_to_kdc) + return krb5_set_send_to_kdc_func(to, + context->send_to_kdc->func, + context->send_to_kdc->data); + else + return krb5_set_send_to_kdc_func(to, NULL, NULL); +} + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/derived-key-test.c b/third_party/heimdal/lib/krb5/derived-key-test.c new file mode 100644 index 0000000..0e92764 --- /dev/null +++ b/third_party/heimdal/lib/krb5/derived-key-test.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 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 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 "krb5_locl.h" +#include + +enum { MAXSIZE = 32 }; + +static struct testcase { + krb5_enctype enctype; + unsigned char constant[MAXSIZE]; + size_t constant_len; + unsigned char key[MAXSIZE]; + unsigned char res[MAXSIZE]; +} tests[] = { + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0x55}, 5, + {0xdc, 0xe0, 0x6b, 0x1f, 0x64, 0xc8, 0x57, 0xa1, 0x1c, 0x3d, 0xb5, 0x7c, 0x51, 0x89, 0x9b, 0x2c, 0xc1, 0x79, 0x10, 0x08, 0xce, 0x97, 0x3b, 0x92}, + {0x92, 0x51, 0x79, 0xd0, 0x45, 0x91, 0xa7, 0x9b, 0x5d, 0x31, 0x92, 0xc4, 0xa7, 0xe9, 0xc2, 0x89, 0xb0, 0x49, 0xc7, 0x1f, 0x6e, 0xe6, 0x04, 0xcd}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0xaa}, 5, + {0x5e, 0x13, 0xd3, 0x1c, 0x70, 0xef, 0x76, 0x57, 0x46, 0x57, 0x85, 0x31, 0xcb, 0x51, 0xc1, 0x5b, 0xf1, 0x1c, 0xa8, 0x2c, 0x97, 0xce, 0xe9, 0xf2}, + {0x9e, 0x58, 0xe5, 0xa1, 0x46, 0xd9, 0x94, 0x2a, 0x10, 0x1c, 0x46, 0x98, 0x45, 0xd6, 0x7a, 0x20, 0xe3, 0xc4, 0x25, 0x9e, 0xd9, 0x13, 0xf2, 0x07}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0x55}, 5, + {0x98, 0xe6, 0xfd, 0x8a, 0x04, 0xa4, 0xb6, 0x85, 0x9b, 0x75, 0xa1, 0x76, 0x54, 0x0b, 0x97, 0x52, 0xba, 0xd3, 0xec, 0xd6, 0x10, 0xa2, 0x52, 0xbc}, + {0x13, 0xfe, 0xf8, 0x0d, 0x76, 0x3e, 0x94, 0xec, 0x6d, 0x13, 0xfd, 0x2c, 0xa1, 0xd0, 0x85, 0x07, 0x02, 0x49, 0xda, 0xd3, 0x98, 0x08, 0xea, 0xbf}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0xaa}, 5, + {0x62, 0x2a, 0xec, 0x25, 0xa2, 0xfe, 0x2c, 0xad, 0x70, 0x94, 0x68, 0x0b, 0x7c, 0x64, 0x94, 0x02, 0x80, 0x08, 0x4c, 0x1a, 0x7c, 0xec, 0x92, 0xb5}, + {0xf8, 0xdf, 0xbf, 0x04, 0xb0, 0x97, 0xe6, 0xd9, 0xdc, 0x07, 0x02, 0x68, 0x6b, 0xcb, 0x34, 0x89, 0xd9, 0x1f, 0xd9, 0xa4, 0x51, 0x6b, 0x70, 0x3e}}, + {ETYPE_DES3_CBC_SHA1, {0x6b, 0x65, 0x72, 0x62, 0x65, 0x72, 0x6f, 0x73}, 8, + {0xd3, 0xf8, 0x29, 0x8c, 0xcb, 0x16, 0x64, 0x38, 0xdc, 0xb9, 0xb9, 0x3e, 0xe5, 0xa7, 0x62, 0x92, 0x86, 0xa4, 0x91, 0xf8, 0x38, 0xf8, 0x02, 0xfb}, + {0x23, 0x70, 0xda, 0x57, 0x5d, 0x2a, 0x3d, 0xa8, 0x64, 0xce, 0xbf, 0xdc, 0x52, 0x04, 0xd5, 0x6d, 0xf7, 0x79, 0xa7, 0xdf, 0x43, 0xd9, 0xda, 0x43}}, + {ETYPE_DES3_CBC_SHA1, {0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65}, 7, + {0xb5, 0x5e, 0x98, 0x34, 0x67, 0xe5, 0x51, 0xb3, 0xe5, 0xd0, 0xe5, 0xb6, 0xc8, 0x0d, 0x45, 0x76, 0x94, 0x23, 0xa8, 0x73, 0xdc, 0x62, 0xb3, 0x0e}, + {0x01, 0x26, 0x38, 0x8a, 0xad, 0xc8, 0x1a, 0x1f, 0x2a, 0x62, 0xbc, 0x45, 0xf8, 0xd5, 0xc1, 0x91, 0x51, 0xba, 0xcd, 0xd5, 0xcb, 0x79, 0x8a, 0x3e}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0x55}, 5, + {0xc1, 0x08, 0x16, 0x49, 0xad, 0xa7, 0x43, 0x62, 0xe6, 0xa1, 0x45, 0x9d, 0x01, 0xdf, 0xd3, 0x0d, 0x67, 0xc2, 0x23, 0x4c, 0x94, 0x07, 0x04, 0xda}, + {0x34, 0x80, 0x57, 0xec, 0x98, 0xfd, 0xc4, 0x80, 0x16, 0x16, 0x1c, 0x2a, 0x4c, 0x7a, 0x94, 0x3e, 0x92, 0xae, 0x49, 0x2c, 0x98, 0x91, 0x75, 0xf7}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0xaa}, 5, + {0x5d, 0x15, 0x4a, 0xf2, 0x38, 0xf4, 0x67, 0x13, 0x15, 0x57, 0x19, 0xd5, 0x5e, 0x2f, 0x1f, 0x79, 0x0d, 0xd6, 0x61, 0xf2, 0x79, 0xa7, 0x91, 0x7c}, + {0xa8, 0x80, 0x8a, 0xc2, 0x67, 0xda, 0xda, 0x3d, 0xcb, 0xe9, 0xa7, 0xc8, 0x46, 0x26, 0xfb, 0xc7, 0x61, 0xc2, 0x94, 0xb0, 0x13, 0x15, 0xe5, 0xc1}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0x55}, 5, + {0x79, 0x85, 0x62, 0xe0, 0x49, 0x85, 0x2f, 0x57, 0xdc, 0x8c, 0x34, 0x3b, 0xa1, 0x7f, 0x2c, 0xa1, 0xd9, 0x73, 0x94, 0xef, 0xc8, 0xad, 0xc4, 0x43}, + {0xc8, 0x13, 0xf8, 0x8a, 0x3b, 0xe3, 0xb3, 0x34, 0xf7, 0x54, 0x25, 0xce, 0x91, 0x75, 0xfb, 0xe3, 0xc8, 0x49, 0x3b, 0x89, 0xc8, 0x70, 0x3b, 0x49}}, + {ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0xaa}, 5, + {0x26, 0xdc, 0xe3, 0x34, 0xb5, 0x45, 0x29, 0x2f, 0x2f, 0xea, 0xb9, 0xa8, 0x70, 0x1a, 0x89, 0xa4, 0xb9, 0x9e, 0xb9, 0x94, 0x2c, 0xec, 0xd0, 0x16}, + {0xf4, 0x8f, 0xfd, 0x6e, 0x83, 0xf8, 0x3e, 0x73, 0x54, 0xe6, 0x94, 0xfd, 0x25, 0x2c, 0xf8, 0x3b, 0xfe, 0x58, 0xf7, 0xd5, 0xba, 0x37, 0xec, 0x5d}}, + {ETYPE_AES128_CTS_HMAC_SHA256_128, {0x00, 0x00, 0x00, 0x02, 0x99}, 5, + {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C}, + {0xB3, 0x1A, 0x01, 0x8A, 0x48, 0xF5, 0x47, 0x76, 0xF4, 0x03, 0xE9, 0xA3, 0x96, 0x32, 0x5D, 0xC3}}, + {ETYPE_AES128_CTS_HMAC_SHA256_128, {0x00, 0x00, 0x00, 0x02, 0xAA}, 5, + {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C}, + {0x9B, 0x19, 0x7D, 0xD1, 0xE8, 0xC5, 0x60, 0x9D, 0x6E, 0x67, 0xC3, 0xE3, 0x7C, 0x62, 0xC7, 0x2E}}, + {ETYPE_AES128_CTS_HMAC_SHA256_128, {0x00, 0x00, 0x00, 0x02, 0x55}, 5, + {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C}, + {0x9F, 0xDA, 0x0E, 0x56, 0xAB, 0x2D, 0x85, 0xE1, 0x56, 0x9A, 0x68, 0x86, 0x96, 0xC2, 0x6A, 0x6C}}, + {ETYPE_AES256_CTS_HMAC_SHA384_192, {0x00, 0x00, 0x00, 0x02, 0x99}, 5, + {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98, + 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52}, + {0xEF, 0x57, 0x18, 0xBE, 0x86, 0xCC, 0x84, 0x96, 0x3D, 0x8B, 0xBB, 0x50, 0x31, 0xE9, 0xF5, 0xC4, + 0xBA, 0x41, 0xF2, 0x8F, 0xAF, 0x69, 0xE7, 0x3D }}, + {ETYPE_AES256_CTS_HMAC_SHA384_192, {0x00, 0x00, 0x00, 0x02, 0xAA}, 5, + {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98, + 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52}, + {0x56, 0xAB, 0x22, 0xBE, 0xE6, 0x3D, 0x82, 0xD7, 0xBC, 0x52, 0x27, 0xF6, 0x77, 0x3F, 0x8E, 0xA7, + 0xA5, 0xEB, 0x1C, 0x82, 0x51, 0x60, 0xC3, 0x83, 0x12, 0x98, 0x0C, 0x44, 0x2E, 0x5C, 0x7E, 0x49}}, + {ETYPE_AES256_CTS_HMAC_SHA384_192, {0x00, 0x00, 0x00, 0x02, 0x55}, 5, + {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98, + 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52}, + {0x69, 0xB1, 0x65, 0x14, 0xE3, 0xCD, 0x8E, 0x56, 0xB8, 0x20, 0x10, 0xD5, 0xC7, 0x30, 0x12, 0xB6, + 0x22, 0xC4, 0xD0, 0x0F, 0xFC, 0x23, 0xED, 0x1F}}, + {0, {0}, 0, {0}, {0}} +}; + +int +main(int argc, char **argv) +{ + struct testcase *t; + krb5_context context; + krb5_error_code ret; + int val = 0; + + ret = krb5_init_context (&context); + if (ret) + errx (1, "krb5_init_context failed: %d", ret); + + for (t = tests; t->enctype != 0; ++t) { + krb5_keyblock key; + krb5_keyblock *dkey; + + key.keytype = t->enctype; + krb5_enctype_keysize(context, t->enctype, &key.keyvalue.length); + key.keyvalue.data = t->key; + + ret = krb5_derive_key(context, &key, t->enctype, t->constant, + t->constant_len, &dkey); + if (ret) + krb5_err (context, 1, ret, "krb5_derive_key"); + if (memcmp (dkey->keyvalue.data, t->res, dkey->keyvalue.length) != 0) { + const unsigned char *p = dkey->keyvalue.data; + int i; + + printf ("derive_key failed (enctype %d)\n", t->enctype); + printf ("should be: "); + for (i = 0; i < dkey->keyvalue.length; ++i) + printf ("%02x", t->res[i]); + printf ("\nresult was: "); + for (i = 0; i < dkey->keyvalue.length; ++i) + printf ("%02x", p[i]); + printf ("\n"); + val = 1; + } + krb5_free_keyblock(context, dkey); + } + krb5_free_context(context); + + return val; +} diff --git a/third_party/heimdal/lib/krb5/digest.c b/third_party/heimdal/lib/krb5/digest.c new file mode 100644 index 0000000..cc37c6d --- /dev/null +++ b/third_party/heimdal/lib/krb5/digest.c @@ -0,0 +1,1165 @@ +/* + * 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 the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" +#include "digest_asn1.h" + +#ifndef HEIMDAL_SMALLER + +struct krb5_digest_data { + char *cbtype; + char *cbbinding; + + DigestInit init; + DigestInitReply initReply; + DigestRequest request; + DigestResponse response; +}; + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_alloc(krb5_context context, krb5_digest *digest) +{ + krb5_digest d; + + d = calloc(1, sizeof(*d)); + if (d == NULL) { + *digest = NULL; + return krb5_enomem(context); + } + *digest = d; + + return 0; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_digest_free(krb5_digest digest) +{ + if (digest == NULL) + return; + free_DigestInit(&digest->init); + free_DigestInitReply(&digest->initReply); + free_DigestRequest(&digest->request); + free_DigestResponse(&digest->response); + memset(digest, 0, sizeof(*digest)); + free(digest); + return; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_server_cb(krb5_context context, + krb5_digest digest, + const char *type, + const char *binding) +{ + if (digest->init.channel) { + krb5_set_error_message(context, EINVAL, + N_("server channel binding already set", "")); + return EINVAL; + } + digest->init.channel = calloc(1, sizeof(*digest->init.channel)); + if (digest->init.channel == NULL) + goto error; + + digest->init.channel->cb_type = strdup(type); + if (digest->init.channel->cb_type == NULL) + goto error; + + digest->init.channel->cb_binding = strdup(binding); + if (digest->init.channel->cb_binding == NULL) + goto error; + return 0; + error: + if (digest->init.channel) { + free(digest->init.channel->cb_type); + free(digest->init.channel->cb_binding); + free(digest->init.channel); + digest->init.channel = NULL; + } + return krb5_enomem(context); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_type(krb5_context context, + krb5_digest digest, + const char *type) +{ + if (digest->init.type) { + krb5_set_error_message(context, EINVAL, "client type already set"); + return EINVAL; + } + digest->init.type = strdup(type); + if (digest->init.type == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_hostname(krb5_context context, + krb5_digest digest, + const char *hostname) +{ + if (digest->init.hostname) { + krb5_set_error_message(context, EINVAL, "server hostname already set"); + return EINVAL; + } + digest->init.hostname = malloc(sizeof(*digest->init.hostname)); + if (digest->init.hostname == NULL) + return krb5_enomem(context); + *digest->init.hostname = strdup(hostname); + if (*digest->init.hostname == NULL) { + free(digest->init.hostname); + digest->init.hostname = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_digest_get_server_nonce(krb5_context context, + krb5_digest digest) +{ + return digest->initReply.nonce; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_server_nonce(krb5_context context, + krb5_digest digest, + const char *nonce) +{ + if (digest->request.serverNonce) { + krb5_set_error_message(context, EINVAL, N_("nonce already set", "")); + return EINVAL; + } + digest->request.serverNonce = strdup(nonce); + if (digest->request.serverNonce == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_digest_get_opaque(krb5_context context, + krb5_digest digest) +{ + return digest->initReply.opaque; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_opaque(krb5_context context, + krb5_digest digest, + const char *opaque) +{ + if (digest->request.opaque) { + krb5_set_error_message(context, EINVAL, "opaque already set"); + return EINVAL; + } + digest->request.opaque = strdup(opaque); + if (digest->request.opaque == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_digest_get_identifier(krb5_context context, + krb5_digest digest) +{ + if (digest->initReply.identifier == NULL) + return NULL; + return *digest->initReply.identifier; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_identifier(krb5_context context, + krb5_digest digest, + const char *id) +{ + if (digest->request.identifier) { + krb5_set_error_message(context, EINVAL, N_("identifier already set", "")); + return EINVAL; + } + digest->request.identifier = calloc(1, sizeof(*digest->request.identifier)); + if (digest->request.identifier == NULL) + return krb5_enomem(context); + *digest->request.identifier = strdup(id); + if (*digest->request.identifier == NULL) { + free(digest->request.identifier); + digest->request.identifier = NULL; + return krb5_enomem(context); + } + return 0; +} + +static krb5_error_code +digest_request(krb5_context context, + krb5_realm realm, + krb5_ccache ccache, + krb5_key_usage usage, + const DigestReqInner *ireq, + DigestRepInner *irep) +{ + DigestREQ req; + DigestREP rep; + krb5_error_code ret; + krb5_data data, data2; + size_t size = 0; + krb5_crypto crypto = NULL; + krb5_auth_context ac = NULL; + krb5_principal principal = NULL; + krb5_ccache id = NULL; + krb5_realm r = NULL; + + krb5_data_zero(&data); + krb5_data_zero(&data2); + memset(&req, 0, sizeof(req)); + memset(&rep, 0, sizeof(rep)); + + if (ccache == NULL) { + ret = krb5_cc_default(context, &id); + if (ret) + goto out; + } else + id = ccache; + + if (realm == NULL) { + ret = krb5_get_default_realm(context, &r); + if (ret) + goto out; + } else + r = realm; + + /* + * + */ + + ret = krb5_make_principal(context, &principal, + r, KRB5_DIGEST_NAME, r, NULL); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(DigestReqInner, data.data, data.length, + ireq, &size, ret); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to encode digest inner request", "")); + goto out; + } + if (size != data.length) + krb5_abortx(context, "ASN.1 internal encoder error"); + + ret = krb5_mk_req_exact(context, &ac, + AP_OPTS_USE_SUBKEY|AP_OPTS_MUTUAL_REQUIRED, + principal, NULL, id, &req.apReq); + if (ret) + goto out; + + { + krb5_keyblock *key; + + ret = krb5_auth_con_getlocalsubkey(context, ac, &key); + if (ret) + goto out; + if (key == NULL) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("Digest failed to get local subkey", "")); + goto out; + } + + ret = krb5_crypto_init(context, key, 0, &crypto); + krb5_free_keyblock (context, key); + if (ret) + goto out; + } + + ret = krb5_encrypt_EncryptedData(context, crypto, usage, + data.data, data.length, 0, + &req.innerReq); + if (ret) + goto out; + + krb5_data_free(&data); + + ASN1_MALLOC_ENCODE(DigestREQ, data.data, data.length, + &req, &size, ret); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to encode DigestREQest", "")); + goto out; + } + if (size != data.length) + krb5_abortx(context, "ASN.1 internal encoder error"); + + ret = krb5_sendto_kdc(context, &data, &r, &data2); + if (ret) + goto out; + + ret = decode_DigestREP(data2.data, data2.length, &rep, NULL); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to parse digest response", "")); + goto out; + } + + { + krb5_ap_rep_enc_part *repl; + + ret = krb5_rd_rep(context, ac, &rep.apRep, &repl); + if (ret) + goto out; + + krb5_free_ap_rep_enc_part(context, repl); + } + { + krb5_keyblock *key; + + ret = krb5_auth_con_getremotesubkey(context, ac, &key); + if (ret) + goto out; + if (key == NULL) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("Digest reply has no remote subkey", "")); + goto out; + } + + krb5_crypto_destroy(context, crypto); + ret = krb5_crypto_init(context, key, 0, &crypto); + krb5_free_keyblock (context, key); + if (ret) + goto out; + } + + krb5_data_free(&data); + ret = krb5_decrypt_EncryptedData(context, crypto, usage, + &rep.innerRep, &data); + if (ret) + goto out; + + ret = decode_DigestRepInner(data.data, data.length, irep, NULL); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to decode digest inner reply", "")); + goto out; + } + + out: + if (ccache == NULL && id) + krb5_cc_close(context, id); + if (realm == NULL && r) + free(r); + if (crypto) + krb5_crypto_destroy(context, crypto); + if (ac) + krb5_auth_con_free(context, ac); + if (principal) + krb5_free_principal(context, principal); + + krb5_data_free(&data); + krb5_data_free(&data2); + + free_DigestREQ(&req); + free_DigestREP(&rep); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_init_request(krb5_context context, + krb5_digest digest, + krb5_realm realm, + krb5_ccache ccache) +{ + DigestReqInner ireq; + DigestRepInner irep; + krb5_error_code ret; + + memset(&ireq, 0, sizeof(ireq)); + memset(&irep, 0, sizeof(irep)); + + if (digest->init.type == NULL) { + krb5_set_error_message(context, EINVAL, + N_("Type missing from init req", "")); + return EINVAL; + } + + ireq.element = choice_DigestReqInner_init; + ireq.u.init = digest->init; + + ret = digest_request(context, realm, ccache, + KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); + if (ret) + goto out; + + if (irep.element == choice_DigestRepInner_error) { + ret = irep.u.error.code; + krb5_set_error_message(context, ret, N_("Digest init error: %s", ""), + irep.u.error.reason); + goto out; + } + + if (irep.element != choice_DigestRepInner_initReply) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("digest reply not an initReply", "")); + goto out; + } + + ret = copy_DigestInitReply(&irep.u.initReply, &digest->initReply); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to copy initReply", "")); + goto out; + } + + out: + free_DigestRepInner(&irep); + + return ret; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_client_nonce(krb5_context context, + krb5_digest digest, + const char *nonce) +{ + if (digest->request.clientNonce) { + krb5_set_error_message(context, EINVAL, + N_("clientNonce already set", "")); + return EINVAL; + } + digest->request.clientNonce = + calloc(1, sizeof(*digest->request.clientNonce)); + if (digest->request.clientNonce == NULL) + return krb5_enomem(context); + *digest->request.clientNonce = strdup(nonce); + if (*digest->request.clientNonce == NULL) { + free(digest->request.clientNonce); + digest->request.clientNonce = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_digest(krb5_context context, + krb5_digest digest, + const char *dgst) +{ + if (digest->request.digest) { + krb5_set_error_message(context, EINVAL, + N_("digest already set", "")); + return EINVAL; + } + digest->request.digest = strdup(dgst); + if (digest->request.digest == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_username(krb5_context context, + krb5_digest digest, + const char *username) +{ + if (digest->request.username) { + krb5_set_error_message(context, EINVAL, "username already set"); + return EINVAL; + } + digest->request.username = strdup(username); + if (digest->request.username == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_authid(krb5_context context, + krb5_digest digest, + const char *authid) +{ + if (digest->request.authid) { + krb5_set_error_message(context, EINVAL, "authid already set"); + return EINVAL; + } + digest->request.authid = malloc(sizeof(*digest->request.authid)); + if (digest->request.authid == NULL) + return krb5_enomem(context); + *digest->request.authid = strdup(authid); + if (*digest->request.authid == NULL) { + free(digest->request.authid); + digest->request.authid = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_authentication_user(krb5_context context, + krb5_digest digest, + krb5_principal authentication_user) +{ + krb5_error_code ret; + + if (digest->request.authentication_user) { + krb5_set_error_message(context, EINVAL, + N_("authentication_user already set", "")); + return EINVAL; + } + ret = krb5_copy_principal(context, + authentication_user, + &digest->request.authentication_user); + if (ret) + return ret; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_realm(krb5_context context, + krb5_digest digest, + const char *realm) +{ + if (digest->request.realm) { + krb5_set_error_message(context, EINVAL, "realm already set"); + return EINVAL; + } + digest->request.realm = malloc(sizeof(*digest->request.realm)); + if (digest->request.realm == NULL) + return krb5_enomem(context); + *digest->request.realm = strdup(realm); + if (*digest->request.realm == NULL) { + free(digest->request.realm); + digest->request.realm = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_method(krb5_context context, + krb5_digest digest, + const char *method) +{ + if (digest->request.method) { + krb5_set_error_message(context, EINVAL, + N_("method already set", "")); + return EINVAL; + } + digest->request.method = malloc(sizeof(*digest->request.method)); + if (digest->request.method == NULL) + return krb5_enomem(context); + *digest->request.method = strdup(method); + if (*digest->request.method == NULL) { + free(digest->request.method); + digest->request.method = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_uri(krb5_context context, + krb5_digest digest, + const char *uri) +{ + if (digest->request.uri) { + krb5_set_error_message(context, EINVAL, N_("uri already set", "")); + return EINVAL; + } + digest->request.uri = malloc(sizeof(*digest->request.uri)); + if (digest->request.uri == NULL) + return krb5_enomem(context); + *digest->request.uri = strdup(uri); + if (*digest->request.uri == NULL) { + free(digest->request.uri); + digest->request.uri = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_nonceCount(krb5_context context, + krb5_digest digest, + const char *nonce_count) +{ + if (digest->request.nonceCount) { + krb5_set_error_message(context, EINVAL, + N_("nonceCount already set", "")); + return EINVAL; + } + digest->request.nonceCount = + malloc(sizeof(*digest->request.nonceCount)); + if (digest->request.nonceCount == NULL) + return krb5_enomem(context); + *digest->request.nonceCount = strdup(nonce_count); + if (*digest->request.nonceCount == NULL) { + free(digest->request.nonceCount); + digest->request.nonceCount = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_set_qop(krb5_context context, + krb5_digest digest, + const char *qop) +{ + if (digest->request.qop) { + krb5_set_error_message(context, EINVAL, "qop already set"); + return EINVAL; + } + digest->request.qop = malloc(sizeof(*digest->request.qop)); + if (digest->request.qop == NULL) + return krb5_enomem(context); + *digest->request.qop = strdup(qop); + if (*digest->request.qop == NULL) { + free(digest->request.qop); + digest->request.qop = NULL; + return krb5_enomem(context); + } + return 0; +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_digest_set_responseData(krb5_context context, + krb5_digest digest, + const char *response) +{ + digest->request.responseData = strdup(response); + if (digest->request.responseData == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_request(krb5_context context, + krb5_digest digest, + krb5_realm realm, + krb5_ccache ccache) +{ + DigestReqInner ireq; + DigestRepInner irep; + krb5_error_code ret; + + memset(&ireq, 0, sizeof(ireq)); + memset(&irep, 0, sizeof(irep)); + + ireq.element = choice_DigestReqInner_digestRequest; + ireq.u.digestRequest = digest->request; + + if (digest->request.type == NULL) { + if (digest->init.type == NULL) { + krb5_set_error_message(context, EINVAL, + N_("Type missing from req", "")); + return EINVAL; + } + ireq.u.digestRequest.type = digest->init.type; + } + + if (ireq.u.digestRequest.digest == NULL) { + static char md5[] = "md5"; + ireq.u.digestRequest.digest = md5; + } + + ret = digest_request(context, realm, ccache, + KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); + if (ret) + return ret; + + if (irep.element == choice_DigestRepInner_error) { + ret = irep.u.error.code; + krb5_set_error_message(context, ret, + N_("Digest response error: %s", ""), + irep.u.error.reason); + goto out; + } + + if (irep.element != choice_DigestRepInner_response) { + krb5_set_error_message(context, EINVAL, + N_("digest reply not an DigestResponse", "")); + ret = EINVAL; + goto out; + } + + ret = copy_DigestResponse(&irep.u.response, &digest->response); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to copy initReply,", "")); + goto out; + } + + out: + free_DigestRepInner(&irep); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_digest_rep_get_status(krb5_context context, + krb5_digest digest) +{ + return digest->response.success ? TRUE : FALSE; +} + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_digest_get_rsp(krb5_context context, + krb5_digest digest) +{ + if (digest->response.rsp == NULL) + return NULL; + return *digest->response.rsp; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_get_tickets(krb5_context context, + krb5_digest digest, + Ticket **tickets) +{ + *tickets = NULL; + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_get_client_binding(krb5_context context, + krb5_digest digest, + char **type, + char **binding) +{ + if (digest->response.channel) { + *type = strdup(digest->response.channel->cb_type); + *binding = strdup(digest->response.channel->cb_binding); + if (*type == NULL || *binding == NULL) { + free(*type); + free(*binding); + return krb5_enomem(context); + } + } else { + *type = NULL; + *binding = NULL; + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_get_session_key(krb5_context context, + krb5_digest digest, + krb5_data *data) +{ + krb5_error_code ret; + + krb5_data_zero(data); + if (digest->response.session_key == NULL) + return 0; + ret = der_copy_octet_string(digest->response.session_key, data); + if (ret) + krb5_clear_error_message(context); + + return ret; +} + +struct krb5_ntlm_data { + NTLMInit init; + NTLMInitReply initReply; + NTLMRequest request; + NTLMResponse response; +}; + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_alloc(krb5_context context, + krb5_ntlm *ntlm) +{ + *ntlm = calloc(1, sizeof(**ntlm)); + if (*ntlm == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_free(krb5_context context, krb5_ntlm ntlm) +{ + free_NTLMInit(&ntlm->init); + free_NTLMInitReply(&ntlm->initReply); + free_NTLMRequest(&ntlm->request); + free_NTLMResponse(&ntlm->response); + memset(ntlm, 0, sizeof(*ntlm)); + free(ntlm); + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_init_request(krb5_context context, + krb5_ntlm ntlm, + krb5_realm realm, + krb5_ccache ccache, + uint32_t flags, + const char *hostname, + const char *domainname) +{ + DigestReqInner ireq; + DigestRepInner irep; + krb5_error_code ret; + + memset(&ireq, 0, sizeof(ireq)); + memset(&irep, 0, sizeof(irep)); + + ntlm->init.flags = flags; + if (hostname) { + ALLOC(ntlm->init.hostname, 1); + *ntlm->init.hostname = strdup(hostname); + } + if (domainname) { + ALLOC(ntlm->init.domain, 1); + *ntlm->init.domain = strdup(domainname); + } + + ireq.element = choice_DigestReqInner_ntlmInit; + ireq.u.ntlmInit = ntlm->init; + + ret = digest_request(context, realm, ccache, + KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); + if (ret) + goto out; + + if (irep.element == choice_DigestRepInner_error) { + ret = irep.u.error.code; + krb5_set_error_message(context, ret, N_("Digest init error: %s", ""), + irep.u.error.reason); + goto out; + } + + if (irep.element != choice_DigestRepInner_ntlmInitReply) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("ntlm reply not an initReply", "")); + goto out; + } + + ret = copy_NTLMInitReply(&irep.u.ntlmInitReply, &ntlm->initReply); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to copy initReply", "")); + goto out; + } + + out: + free_DigestRepInner(&irep); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_init_get_flags(krb5_context context, + krb5_ntlm ntlm, + uint32_t *flags) +{ + *flags = ntlm->initReply.flags; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_init_get_challenge(krb5_context context, + krb5_ntlm ntlm, + krb5_data *challenge) +{ + krb5_error_code ret; + + ret = der_copy_octet_string(&ntlm->initReply.challenge, challenge); + if (ret) + krb5_clear_error_message(context); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_init_get_opaque(krb5_context context, + krb5_ntlm ntlm, + krb5_data *opaque) +{ + krb5_error_code ret; + + ret = der_copy_octet_string(&ntlm->initReply.opaque, opaque); + if (ret) + krb5_clear_error_message(context); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_init_get_targetname(krb5_context context, + krb5_ntlm ntlm, + char **name) +{ + *name = strdup(ntlm->initReply.targetname); + if (*name == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_init_get_targetinfo(krb5_context context, + krb5_ntlm ntlm, + krb5_data *data) +{ + krb5_error_code ret; + + if (ntlm->initReply.targetinfo == NULL) { + krb5_data_zero(data); + return 0; + } + + ret = krb5_data_copy(data, + ntlm->initReply.targetinfo->data, + ntlm->initReply.targetinfo->length); + if (ret) { + krb5_clear_error_message(context); + return ret; + } + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_request(krb5_context context, + krb5_ntlm ntlm, + krb5_realm realm, + krb5_ccache ccache) +{ + DigestReqInner ireq; + DigestRepInner irep; + krb5_error_code ret; + + memset(&ireq, 0, sizeof(ireq)); + memset(&irep, 0, sizeof(irep)); + + ireq.element = choice_DigestReqInner_ntlmRequest; + ireq.u.ntlmRequest = ntlm->request; + + ret = digest_request(context, realm, ccache, + KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); + if (ret) + return ret; + + if (irep.element == choice_DigestRepInner_error) { + ret = irep.u.error.code; + krb5_set_error_message(context, ret, + N_("NTLM response error: %s", ""), + irep.u.error.reason); + goto out; + } + + if (irep.element != choice_DigestRepInner_ntlmResponse) { + ret = EINVAL; + krb5_set_error_message(context, ret, + N_("NTLM reply not an NTLMResponse", "")); + goto out; + } + + ret = copy_NTLMResponse(&irep.u.ntlmResponse, &ntlm->response); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to copy NTLMResponse", "")); + goto out; + } + + out: + free_DigestRepInner(&irep); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_flags(krb5_context context, + krb5_ntlm ntlm, + uint32_t flags) +{ + ntlm->request.flags = flags; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_username(krb5_context context, + krb5_ntlm ntlm, + const char *username) +{ + ntlm->request.username = strdup(username); + if (ntlm->request.username == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_targetname(krb5_context context, + krb5_ntlm ntlm, + const char *targetname) +{ + ntlm->request.targetname = strdup(targetname); + if (ntlm->request.targetname == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_lm(krb5_context context, + krb5_ntlm ntlm, + void *hash, size_t len) +{ + ntlm->request.lm.data = malloc(len); + if (ntlm->request.lm.data == NULL && len != 0) + return krb5_enomem(context); + ntlm->request.lm.length = len; + memcpy(ntlm->request.lm.data, hash, len); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_ntlm(krb5_context context, + krb5_ntlm ntlm, + void *hash, size_t len) +{ + ntlm->request.ntlm.data = malloc(len); + if (ntlm->request.ntlm.data == NULL && len != 0) + return krb5_enomem(context); + ntlm->request.ntlm.length = len; + memcpy(ntlm->request.ntlm.data, hash, len); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_opaque(krb5_context context, + krb5_ntlm ntlm, + krb5_data *opaque) +{ + ntlm->request.opaque.data = malloc(opaque->length); + if (ntlm->request.opaque.data == NULL && opaque->length != 0) + return krb5_enomem(context); + ntlm->request.opaque.length = opaque->length; + memcpy(ntlm->request.opaque.data, opaque->data, opaque->length); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_req_set_session(krb5_context context, + krb5_ntlm ntlm, + void *sessionkey, size_t length) +{ + ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey)); + if (ntlm->request.sessionkey == NULL) + return krb5_enomem(context); + ntlm->request.sessionkey->data = malloc(length); + if (ntlm->request.sessionkey->data == NULL && length != 0) + return krb5_enomem(context); + memcpy(ntlm->request.sessionkey->data, sessionkey, length); + ntlm->request.sessionkey->length = length; + return 0; +} + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_ntlm_rep_get_status(krb5_context context, + krb5_ntlm ntlm) +{ + return ntlm->response.success ? TRUE : FALSE; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_ntlm_rep_get_sessionkey(krb5_context context, + krb5_ntlm ntlm, + krb5_data *data) +{ + if (ntlm->response.sessionkey == NULL) { + krb5_set_error_message(context, EINVAL, + N_("no ntlm session key", "")); + return EINVAL; + } + krb5_clear_error_message(context); + return krb5_data_copy(data, + ntlm->response.sessionkey->data, + ntlm->response.sessionkey->length); +} + +/** + * Get the supported/allowed mechanism for this principal. + * + * @param context A Keberos context. + * @param realm The realm of the KDC. + * @param ccache The credential cache to use when talking to the KDC. + * @param flags The supported mechanism. + * + * @return Return an error code or 0. + * + * @ingroup krb5_digest + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_digest_probe(krb5_context context, + krb5_realm realm, + krb5_ccache ccache, + unsigned *flags) +{ + DigestReqInner ireq; + DigestRepInner irep; + krb5_error_code ret; + + memset(&ireq, 0, sizeof(ireq)); + memset(&irep, 0, sizeof(irep)); + + ireq.element = choice_DigestReqInner_supportedMechs; + + ret = digest_request(context, realm, ccache, + KRB5_KU_DIGEST_ENCRYPT, &ireq, &irep); + if (ret) + goto out; + + if (irep.element == choice_DigestRepInner_error) { + ret = irep.u.error.code; + krb5_set_error_message(context, ret, "Digest probe error: %s", + irep.u.error.reason); + goto out; + } + + if (irep.element != choice_DigestRepInner_supportedMechs) { + ret = EINVAL; + krb5_set_error_message(context, ret, "Digest reply not an probe"); + goto out; + } + + *flags = DigestTypes2int(irep.u.supportedMechs); + + out: + free_DigestRepInner(&irep); + + return ret; +} + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/dll.c b/third_party/heimdal/lib/krb5/dll.c new file mode 100644 index 0000000..80d9fa4 --- /dev/null +++ b/third_party/heimdal/lib/krb5/dll.c @@ -0,0 +1,76 @@ +/*********************************************************************** + * 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. + * + **********************************************************************/ + +#include + +extern void heim_w32_service_thread_detach(void *); + +HINSTANCE _krb5_hInstance = NULL; + +#if NTDDI_VERSION >= NTDDI_VISTA +extern BOOL WINAPI +_hc_w32crypto_DllMain(HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved); +#endif + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, + DWORD fdwReason, + LPVOID lpvReserved) +{ +#if NTDDI_VERSION >= NTDDI_VISTA + BOOL ret; + + ret = _hc_w32crypto_DllMain(hinstDLL, fdwReason, lpvReserved); + if (!ret) + return ret; +#endif + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + + _krb5_hInstance = hinstDLL; + return TRUE; + + case DLL_PROCESS_DETACH: + return FALSE; + + case DLL_THREAD_ATTACH: + return FALSE; + + case DLL_THREAD_DETACH: + heim_w32_service_thread_detach(NULL); + return FALSE; + } + + return FALSE; +} + diff --git a/third_party/heimdal/lib/krb5/doxygen.c b/third_party/heimdal/lib/krb5/doxygen.c new file mode 100644 index 0000000..e9266c9 --- /dev/null +++ b/third_party/heimdal/lib/krb5/doxygen.c @@ -0,0 +1,700 @@ +/* + * Copyright (c) 2007-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 "krb5_locl.h" + +/** + * + */ + +/*! @mainpage Heimdal Kerberos 5 library + * + * @section intro Introduction + * + * Heimdal libkrb5 library is a implementation of the Kerberos + * protocol. + * + * Kerberos is a system for authenticating users and services on a + * network. It is built upon the assumption that the network is + * ``unsafe''. For example, data sent over the network can be + * eavesdropped and altered, and addresses can also be faked. + * Therefore they cannot be used for authentication purposes. + * + * + * - @ref krb5_introduction + * - @ref krb5_principal_intro + * - @ref krb5_ccache_intro + * - @ref krb5_keytab_intro + * + * If you want to know more about the file formats that is used by + * Heimdal, please see: @ref krb5_fileformats + * + * The project web page: http://www.h5l.org/ + * + */ + +/** @defgroup krb5 Heimdal Kerberos 5 library */ +/** @defgroup krb5_address Heimdal Kerberos 5 address functions */ +/** @defgroup krb5_principal Heimdal Kerberos 5 principal functions */ +/** @defgroup krb5_ccache Heimdal Kerberos 5 credential cache functions */ +/** @defgroup krb5_crypto Heimdal Kerberos 5 cryptography functions */ +/** @defgroup krb5_credential Heimdal Kerberos 5 credential handing functions */ +/** @defgroup krb5_deprecated Heimdal Kerberos 5 deprecated functions */ +/** @defgroup krb5_digest Heimdal Kerberos 5 digest service */ +/** @defgroup krb5_error Heimdal Kerberos 5 error reporting functions */ +/** @defgroup krb5_keytab Heimdal Kerberos 5 keytab handling functions */ +/** @defgroup krb5_ticket Heimdal Kerberos 5 ticket functions */ +/** @defgroup krb5_pac Heimdal Kerberos 5 PAC handling functions */ +/** @defgroup krb5_v4compat Heimdal Kerberos 4 compatiblity functions */ +/** @defgroup krb5_storage Heimdal Kerberos 5 storage functions */ +/** @defgroup krb5_support Heimdal Kerberos 5 support functions */ +/** @defgroup krb5_auth Heimdal Kerberos 5 authentication functions */ + + +/** + * @page krb5_introduction Introduction to the Kerberos 5 API + * @section api_overview Kerberos 5 API Overview + * + * All functions are documented in manual pages. This section tries + * to give an overview of the major components used in Kerberos + * library, and point to where to look for a specific function. + * + * @subsection intro_krb5_context Kerberos context + * + * A kerberos context (krb5_context) holds all per thread state. All + * global variables that are context specific are stored in this + * structure, including default encryption types, credential cache + * (for example, a ticket file), and default realms. + * + * The internals of the structure should never be accessed directly, + * functions exist for extracting information. + * + * See the manual page for krb5_init_context() how to create a context + * and module @ref krb5 for more information about the functions. + * + * @subsection intro_krb5_auth_context Kerberos authentication context + * + * Kerberos authentication context (krb5_auth_context) holds all + * context related to an authenticated connection, in a similar way to + * the kerberos context that holds the context for the thread or + * process. + * + * The krb5_auth_context is used by various functions that are + * directly related to authentication between the + * server/client. Example of data that this structure contains are + * various flags, addresses of client and server, port numbers, + * keyblocks (and subkeys), sequence numbers, replay cache, and + * checksum types. + * + * @subsection intro_krb5_principal Kerberos principal + * + * The Kerberos principal is the structure that identifies a user or + * service in Kerberos. The structure that holds the principal is the + * krb5_principal. There are function to extract the realm and + * elements of the principal, but most applications have no reason to + * inspect the content of the structure. + * + * The are several ways to create a principal (with different degree of + * portability), and one way to free it. + * + * See also the page @ref krb5_principal_intro for more information and also + * module @ref krb5_principal. + * + * @subsection intro_krb5_ccache Credential cache + * + * A credential cache holds the tickets for a user. A given user can + * have several credential caches, one for each realm where the user + * have the initial tickets (the first krbtgt). + * + * The credential cache data can be stored internally in different + * way, each of them for different proposes. File credential (FILE) + * caches and processes based (KCM) caches are for permanent + * storage. While memory caches (MEMORY) are local caches to the local + * process. + * + * Caches are opened with krb5_cc_resolve() or created with + * krb5_cc_new_unique(). + * + * If the cache needs to be opened again (using krb5_cc_resolve()) + * krb5_cc_close() will close the handle, but not the remove the + * cache. krb5_cc_destroy() will zero out the cache, remove the cache + * so it can no longer be referenced. + * + * See also @ref krb5_ccache_intro and @ref krb5_ccache . + * + * @subsection intro_krb5_error_code Kerberos errors + * + * Kerberos errors are based on the com_err library. All error codes are + * 32-bit signed numbers, the first 24 bits define what subsystem the + * error originates from, and last 8 bits are 255 error codes within the + * library. Each error code have fixed string associated with it. For + * example, the error-code -1765328383 have the symbolic name + * KRB5KDC_ERR_NAME_EXP, and associated error string ``Client's entry in + * database has expired''. + * + * This is a great improvement compared to just getting one of the unix + * error-codes back. However, Heimdal have an extention to pass back + * customised errors messages. Instead of getting ``Key table entry not + * found'', the user might back ``failed to find + * host/host.example.com\@EXAMLE.COM(kvno 3) in keytab /etc/krb5.keytab + * (des-cbc-crc)''. This improves the chance that the user find the + * cause of the error so you should use the customised error message + * whenever it's available. + * + * See also module @ref krb5_error . + * + * + * @subsection intro_krb5_keytab Keytab management + * + * A keytab is a storage for locally stored keys. Heimdal includes keytab + * support for Kerberos 5 keytabs, Kerberos 4 srvtab, AFS-KeyFile's, + * and for storing keys in memory. + * + * Keytabs are used for servers and long-running services. + * + * See also @ref krb5_keytab_intro and @ref krb5_keytab . + * + * @subsection intro_krb5_crypto Kerberos crypto + * + * Heimdal includes a implementation of the Kerberos crypto framework, + * all crypto operations. To create a crypto context call krb5_crypto_init(). + * + * See also module @ref krb5_crypto . + * + * @section kerberos5_client Walkthrough of a sample Kerberos 5 client + * + * This example contains parts of a sample TCP Kerberos 5 clients, if you + * want a real working client, please look in appl/test directory in + * the Heimdal distribution. + * + * All Kerberos error-codes that are returned from kerberos functions in + * this program are passed to krb5_err, that will print a + * descriptive text of the error code and exit. Graphical programs can + * convert error-code to a human readable error-string with the + * krb5_get_error_message() function. + * + * Note that you should not use any Kerberos function before + * krb5_init_context() have completed successfully. That is the + * reason err() is used when krb5_init_context() fails. + * + * First the client needs to call krb5_init_context to initialise + * the Kerberos 5 library. This is only needed once per thread + * in the program. If the function returns a non-zero value it indicates + * that either the Kerberos implementation is failing or it's disabled on + * this host. + * + * @code + * #include + * + * int + * main(int argc, char **argv) + * { + * krb5_context context; + * + * if (krb5_init_context(&context)) + * errx (1, "krb5_context"); + * @endcode + * + * Now the client wants to connect to the host at the other end. The + * preferred way of doing this is using getaddrinfo (for + * operating system that have this function implemented), since getaddrinfo + * is neutral to the address type and can use any protocol that is available. + * + * @code + * struct addrinfo *ai, *a; + * struct addrinfo hints; + * int error; + * + * memset (&hints, 0, sizeof(hints)); + * hints.ai_socktype = SOCK_STREAM; + * hints.ai_protocol = IPPROTO_TCP; + * + * error = getaddrinfo (hostname, "pop3", &hints, &ai); + * if (error) + * errx (1, "%s: %s", hostname, gai_strerror(error)); + * + * for (a = ai; a != NULL; a = a->ai_next) { + * int s; + * + * s = socket (a->ai_family, a->ai_socktype, a->ai_protocol); + * if (s < 0) + * continue; + * if (connect (s, a->ai_addr, a->ai_addrlen) < 0) { + * warn ("connect(%s)", hostname); + * close (s); + * continue; + * } + * freeaddrinfo (ai); + * ai = NULL; + * } + * if (ai) { + * freeaddrinfo (ai); + * errx ("failed to contact %s", hostname); + * } + * @endcode + * + * Before authenticating, an authentication context needs to be + * created. This context keeps all information for one (to be) authenticated + * connection (see krb5_auth_context). + * + * @code + * status = krb5_auth_con_init (context, &auth_context); + * if (status) + * krb5_err (context, 1, status, "krb5_auth_con_init"); + * @endcode + * + * For setting the address in the authentication there is a help function + * krb5_auth_con_setaddrs_from_fd() that does everything that is needed + * when given a connected file descriptor to the socket. + * + * @code + * status = krb5_auth_con_setaddrs_from_fd (context, + * auth_context, + * &sock); + * if (status) + * krb5_err (context, 1, status, + * "krb5_auth_con_setaddrs_from_fd"); + * @endcode + * + * The next step is to build a server principal for the service we want + * to connect to. (See also krb5_sname_to_principal().) + * + * @code + * status = krb5_sname_to_principal (context, + * hostname, + * service, + * KRB5_NT_SRV_HST, + * &server); + * if (status) + * krb5_err (context, 1, status, "krb5_sname_to_principal"); + * @endcode + * + * The client principal is not passed to krb5_sendauth() + * function, this causes the krb5_sendauth() function to try to figure it + * out itself. + * + * The server program is using the function krb5_recvauth() to + * receive the Kerberos 5 authenticator. + * + * In this case, mutual authentication will be tried. That means that the server + * will authenticate to the client. Using mutual authentication + * is required to avoid man-in-the-middle attacks, since it enables the user to + * verify that they are talking to the right server (a server that knows the key). + * + * If you are using a non-blocking socket you will need to do all work of + * krb5_sendauth() yourself. Basically you need to send over the + * authenticator from krb5_mk_req() and, in case of mutual + * authentication, verifying the result from the server with + * krb5_rd_rep(). + * + * @code + * status = krb5_sendauth (context, + * &auth_context, + * &sock, + * VERSION, + * NULL, + * server, + * AP_OPTS_MUTUAL_REQUIRED, + * NULL, + * NULL, + * NULL, + * NULL, + * NULL, + * NULL); + * if (status) + * krb5_err (context, 1, status, "krb5_sendauth"); + * @endcode + * + * Once authentication has been performed, it is time to send some + * data. First we create a krb5_data structure, then we sign it with + * krb5_mk_safe() using the auth_context that contains the + * session-key that was exchanged in the + * krb5_sendauth()/krb5_recvauth() authentication + * sequence. + * + * @code + * data.data = "hej"; + * data.length = 3; + * + * krb5_data_zero (&packet); + * + * status = krb5_mk_safe (context, + * auth_context, + * &data, + * &packet, + * NULL); + * if (status) + * krb5_err (context, 1, status, "krb5_mk_safe"); + * @endcode + * + * And send it over the network. + * + * @code + * len = packet.length; + * net_len = htonl(len); + * + * if (krb5_net_write (context, &sock, &net_len, 4) != 4) + * err (1, "krb5_net_write"); + * if (krb5_net_write (context, &sock, packet.data, len) != len) + * err (1, "krb5_net_write"); + * @endcode + * + * To send encrypted (and signed) data krb5_mk_priv() should be + * used instead. krb5_mk_priv() works the same way as + * krb5_mk_safe(), with the exception that it encrypts the data + * in addition to signing it. + * + * @code + * data.data = "hemligt"; + * data.length = 7; + * + * krb5_data_free (&packet); + * + * status = krb5_mk_priv (context, + * auth_context, + * &data, + * &packet, + * NULL); + * if (status) + * krb5_err (context, 1, status, "krb5_mk_priv"); + * @endcode + * + * And send it over the network. + * + * @code + * len = packet.length; + * net_len = htonl(len); + * + * if (krb5_net_write (context, &sock, &net_len, 4) != 4) + * err (1, "krb5_net_write"); + * if (krb5_net_write (context, &sock, packet.data, len) != len) + * err (1, "krb5_net_write"); + * + * @endcode + * + * The server is using krb5_rd_safe() and + * krb5_rd_priv() to verify the signature and decrypt the packet. + * + * @section intro_krb5_verify_user Validating a password in an application + * + * See the manual page for krb5_verify_user(). + * + * @section mit_differences API differences to MIT Kerberos + * + * This section is somewhat disorganised, but so far there is no overall + * structure to the differences, though some of the have their root in + * that Heimdal uses an ASN.1 compiler and MIT doesn't. + * + * @subsection mit_krb5_principal Principal and realms + * + * Heimdal stores the realm as a krb5_realm, that is a char *. + * MIT Kerberos uses a krb5_data to store a realm. + * + * In Heimdal krb5_principal doesn't contain the component + * name_type; it's instead stored in component + * name.name_type. To get and set the nametype in Heimdal, use + * krb5_principal_get_type() and + * krb5_principal_set_type(). + * + * For more information about principal and realms, see + * krb5_principal. + * + * @subsection mit_krb5_error_code Error messages + * + * To get the error string, Heimdal uses + * krb5_get_error_message(). This is to return custom error messages + * (like ``Can't find host/datan.example.com\@CODE.COM in + * /etc/krb5.conf.'' instead of a ``Key table entry not found'' that + * error_message returns. + * + * Heimdal uses a threadsafe(r) version of the com_err interface; the + * global com_err table isn't initialised. Then + * error_message returns quite a boring error string (just + * the error code itself). + * + * + */ + +/** + * + * + * @page krb5_fileformats File formats + * + * @section fileformats File formats + * + * This section documents the diffrent file formats that are used in + * Heimdal and other Kerberos implementations. + * + * @subsection file_keytab keytab + * + * The keytab binary format is not a standard format. The format has + * evolved and may continue to. It is however understood by several + * Kerberos implementations including Heimdal, MIT, Sun's Java ktab and + * are created by the ktpass.exe utility from Windows. So it has + * established itself as the defacto format for storing Kerberos keys. + * + * The following C-like structure definitions illustrate the MIT keytab + * file format. All values are in network byte order. All text is ASCII. + * + * @code + * keytab { + * uint16_t file_format_version; # 0x502 + * keytab_entry entries[*]; + * }; + * + * keytab_entry { + * int32_t size; + * uint16_t num_components; # subtract 1 if version 0x501 + * counted_octet_string realm; + * counted_octet_string components[num_components]; + * uint32_t name_type; # not present if version 0x501 + * uint32_t timestamp; + * uint8_t vno8; + * keyblock key; + * uint32_t vno; #only present if >= 4 bytes left in entry + * uint32_t flags; #only present if >= 4 bytes left in entry + * }; + * + * counted_octet_string { + * uint16_t length; + * uint8_t data[length]; + * }; + * + * keyblock { + * uint16_t type; + * counted_octet_string; + * }; + * @endcode + * + * All numbers are stored in network byteorder (big endian) format. + * + * The keytab file format begins with the 16 bit file_format_version which + * at the time this document was authored is 0x502. The format of older + * keytabs is described at the end of this document. + * + * The file_format_version is immediately followed by an array of + * keytab_entry structures which are prefixed with a 32 bit size indicating + * the number of bytes that follow in the entry. Note that the size should be + * evaluated as signed. This is because a negative value indicates that the + * entry is in fact empty (e.g. it has been deleted) and that the negative + * value of that negative value (which is of course a positive value) is + * the offset to the next keytab_entry. Based on these size values alone + * the entire keytab file can be traversed. + * + * The size is followed by a 16 bit num_components field indicating the + * number of counted_octet_string components in the components array. + * + * The num_components field is followed by a counted_octet_string + * representing the realm of the principal. + * + * A counted_octet_string is simply an array of bytes prefixed with a 16 + * bit length. For the realm and name components, the counted_octet_string + * bytes are ASCII encoded text with no zero terminator. + * + * Following the realm is the components array that represents the name of + * the principal. The text of these components may be joined with slashs + * to construct the typical SPN representation. For example, the service + * principal HTTP/www.foo.net\@FOO.NET would consist of name components + * "HTTP" followed by "www.foo.net". + * + * Following the components array is the 32 bit name_type (e.g. 1 is + * KRB5_NT_PRINCIPAL, 2 is KRB5_NT_SRV_INST, 5 is KRB5_NT_UID, etc). In + * practice the name_type is almost certainly 1 meaning KRB5_NT_PRINCIPAL. + * + * The 32 bit timestamp indicates the time the key was established for that + * principal. The value represents the number of seconds since Jan 1, 1970. + * + * The 8 bit vno8 field is the version number of the key. This value is + * overridden by the 32 bit vno field if it is present. The vno8 field is + * filled with the lower 8 bits of the 32 bit protocol kvno field. + * + * The keyblock structure consists of a 16 bit value indicating the + * encryption type and is a counted_octet_string containing the key. The + * encryption type is the same as the Kerberos standard (e.g. 3 is + * des-cbc-md5, 23 is arcfour-hmac-md5, etc). + * + * The last field of the keytab_entry structure is optional. If the size of + * the keytab_entry indicates that there are at least 4 bytes remaining, + * a 32 bit value representing the key version number is present. This + * value supersedes the 8 bit vno8 value preceeding the keyblock. + * + * Older keytabs with a file_format_version of 0x501 are different in + * three ways: + * + * - All integers are in host byte order [1]. + * - The num_components field is 1 too large (i.e. after decoding, decrement by 1). + * - The 32 bit name_type field is not present. + * + * [1] The file_format_version field should really be treated as two + * separate 8 bit quantities representing the major and minor version + * number respectively. + * + * @subsection file_hdb_dump Heimdal database dump file + * + * Format of the Heimdal text dump file as of Heimdal 0.6.3: + * + * Each line in the dump file is one entry in the database. + * + * Each field of a line is separated by one or more spaces, with the + * exception of fields consisting of principals containing spaces, where + * space can be quoted with \ and \ is quoted by \. + * + * Fields and their types are: + * + * @code + * Quoted principal (quote character is \) [string] + * Keys [keys] + * Created by [event] + * Modified by [event optional] + * Valid start time [time optional] + * Valid end time [time optional] + * Password end valid time [time optional] + * Max lifetime of ticket [time optional] + * Max renew time of ticket [integer optional] + * Flags [hdb flags] + * Generation number [generation optional] + * Extensions [extentions optional] + * @endcode + * + * Fields following these silently are ignored. + * + * All optional fields will be skipped if they fail to parse (or comprise + * the optional field marker of "-", w/o quotes). + * + * Example: + * + * @code + * fred\@CODE.COM 27:1:16:e8b4c8fc7e60b9e641dcf4cff3f08a701d982a2f89ba373733d26ca59ba6c789666f6b8bfcf169412bb1e5dceb9b33cda29f3412:-:1:3:4498a933881178c744f4232172dcd774c64e81fa6d05ecdf643a7e390624a0ebf3c7407a:-:1:2:b01934b13eb795d76f3a80717d469639b4da0cfb644161340ef44fdeb375e54d684dbb85:-:1:1:ea8e16d8078bf60c781da90f508d4deccba70595258b9d31888d33987cd31af0c9cced2e:- 20020415130120:admin\@CODE.COM 20041221112428:fred\@CODE.COM - - - 86400 604800 126 20020415130120:793707:28 - + * @endcode + * + * Encoding of types are as follows: + * + * - keys + * + * @code + * kvno:[masterkvno:keytype:keydata:salt]{zero or more separated by :} + * @endcode + * + * kvno is the key version number. + * + * keydata is hex-encoded + * + * masterkvno is the kvno of the database master key. If this field is + * empty, the kadmin load and merge operations will encrypt the key data + * with the master key if there is one. Otherwise the key data will be + * imported asis. + * + * salt is encoded as "-" (no/default salt) or + * + * @code + * salt-type / + * salt-type / "string" + * salt-type / hex-encoded-data + * @endcode + * + * keytype is the protocol enctype number; see enum ENCTYPE in + * include/krb5_asn1.h for values. + * + * Example: + * @code + * 27:1:16:e8b4c8fc7e60b9e641dcf4cff3f08a701d982a2f89ba373733d26ca59ba6c789666f6b8bfcf169412bb1e5dceb9b33cda29f3412:-:1:3:4498a933881178c744f4232172dcd774c64e81fa6d05ecdf643a7e390624a0ebf3c7407a:-:1:2:b01934b13eb795d76f3a80717d469639b4da0cfb644161340ef44fdeb375e54d684dbb85:-:1:1:ea8e16d8078bf60c781da90f508d4deccba70595258b9d31888d33987cd31af0c9cced2e:- + * @endcode + * + * + * @code + * kvno=27,{key: masterkvno=1,keytype=des3-cbc-sha1,keydata=..., default salt}... + * @endcode + * + * - time + * + * Format of the time is: YYYYmmddHHMMSS, corresponding to strftime + * format "%Y%m%d%k%M%S". + * + * Time is expressed in UTC. + * + * Time can be optional (using -), when the time 0 is used. + * + * Example: + * + * @code + * 20041221112428 + * @endcode + * + * - event + * + * @code + * time:principal + * @endcode + * + * time is as given in format time + * + * principal is a string. Not quoting it may not work in earlier + * versions of Heimdal. + * + * Example: + * @code + * 20041221112428:bloggs\@CODE.COM + * @endcode + * + * - hdb flags + * + * Integer encoding of HDB flags, see HDBFlags in lib/hdb/hdb.asn1. Each + * bit in the integer is the same as the bit in the specification. + * + * - generation: + * + * @code + * time:usec:gen + * @endcode + * + * + * usec is a the microsecond, integer. + * gen is generation number, integer. + * + * The generation can be defaulted (using '-') or the empty string + * + * - extensions: + * + * @code + * first-hex-encoded-HDB-Extension[:second-...] + * @endcode + * + * HDB-extension is encoded the DER encoded HDB-Extension from + * lib/hdb/hdb.asn1. Consumers HDB extensions should be aware that + * unknown entires needs to be preserved even thought the ASN.1 data + * content might be unknown. There is a critical flag in the data to show + * to the KDC that the entry MUST be understod if the entry is to be + * used. + * + * + */ diff --git a/third_party/heimdal/lib/krb5/eai_to_heim_errno.c b/third_party/heimdal/lib/krb5/eai_to_heim_errno.c new file mode 100644 index 0000000..a6e14ab --- /dev/null +++ b/third_party/heimdal/lib/krb5/eai_to_heim_errno.c @@ -0,0 +1,118 @@ +/* + * 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 "krb5_locl.h" + +/** + * Convert the getaddrinfo() error code to a Kerberos et error code. + * + * @param eai_errno contains the error code from getaddrinfo(). + * @param system_error should have the value of errno after the failed getaddrinfo(). + * + * @return Kerberos error code representing the EAI errors. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_eai_to_heim_errno(int eai_errno, int system_error) +{ + switch(eai_errno) { + case EAI_NOERROR: + return 0; +#ifdef EAI_ADDRFAMILY + case EAI_ADDRFAMILY: + return HEIM_EAI_ADDRFAMILY; +#endif + case EAI_AGAIN: + return HEIM_EAI_AGAIN; + case EAI_BADFLAGS: + return HEIM_EAI_BADFLAGS; + case EAI_FAIL: + return HEIM_EAI_FAIL; + case EAI_FAMILY: + return HEIM_EAI_FAMILY; + case EAI_MEMORY: + return HEIM_EAI_MEMORY; +#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME + case EAI_NODATA: + return HEIM_EAI_NODATA; +#endif +#ifdef WSANO_DATA + case WSANO_DATA: + return HEIM_EAI_NODATA; +#endif + case EAI_NONAME: + return HEIM_EAI_NONAME; + case EAI_SERVICE: + return HEIM_EAI_SERVICE; + case EAI_SOCKTYPE: + return HEIM_EAI_SOCKTYPE; +#ifdef EAI_SYSTEM + case EAI_SYSTEM: + return system_error; +#endif + default: + return HEIM_EAI_UNKNOWN; /* XXX */ + } +} + +/** + * Convert the gethostname() error code (h_error) to a Kerberos et + * error code. + * + * @param eai_errno contains the error code from gethostname(). + * + * @return Kerberos error code representing the gethostname errors. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_h_errno_to_heim_errno(int eai_errno) +{ + switch(eai_errno) { + case 0: + return 0; + case HOST_NOT_FOUND: + return HEIM_EAI_NONAME; + case TRY_AGAIN: + return HEIM_EAI_AGAIN; + case NO_RECOVERY: + return HEIM_EAI_FAIL; + case NO_DATA: + return HEIM_EAI_NONAME; + default: + return HEIM_EAI_UNKNOWN; /* XXX */ + } +} diff --git a/third_party/heimdal/lib/krb5/enomem.c b/third_party/heimdal/lib/krb5/enomem.c new file mode 100644 index 0000000..b4444e5 --- /dev/null +++ b/third_party/heimdal/lib/krb5/enomem.c @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1997 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#undef krb5_enomem +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_enomem(krb5_context context) +{ + krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); + return ENOMEM; +} diff --git a/third_party/heimdal/lib/krb5/error_string.c b/third_party/heimdal/lib/krb5/error_string.c new file mode 100644 index 0000000..da86b37 --- /dev/null +++ b/third_party/heimdal/lib/krb5/error_string.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2001, 2003, 2005 - 2020 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#undef __attribute__ +#define __attribute__(x) + +/** + * Clears the error message from the Kerberos 5 context. + * + * @param context The Kerberos 5 context to clear + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_clear_error_message(krb5_context context) +{ + heim_clear_error_message(context->hcontext); +} + +/** + * Set the context full error string for a specific error code. + * The error that is stored should be internationalized. + * + * The if context is NULL, no error string is stored. + * + * @param context Kerberos 5 context + * @param ret The error code + * @param fmt Error string for the error code + * @param ... printf(3) style parameters. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_set_error_message(krb5_context context, krb5_error_code ret, + const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))) +{ + va_list ap; + + va_start(ap, fmt); + krb5_vset_error_message (context, ret, fmt, ap); + va_end(ap); +} + +/** + * Set the context full error string for a specific error code. + * + * The if context is NULL, no error string is stored. + * + * @param context Kerberos 5 context + * @param ret The error code + * @param fmt Error string for the error code + * @param args printf(3) style parameters. + * + * @ingroup krb5_error + */ + + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_vset_error_message(krb5_context context, krb5_error_code ret, + const char *fmt, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))) +{ + const char *msg; + + if (context == NULL) + return; + + heim_vset_error_message(context->hcontext, ret, fmt, args); + msg = heim_get_error_message(context->hcontext, ret); + if (msg) { + _krb5_debug(context, 100, "error message: %s: %d", msg, ret); + heim_free_error_message(context->hcontext, msg); + } +} + +/** + * Prepend the context full error string for a specific error code. + * The error that is stored should be internationalized. + * + * The if context is NULL, no error string is stored. + * + * @param context Kerberos 5 context + * @param ret The error code + * @param fmt Error string for the error code + * @param ... printf(3) style parameters. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_prepend_error_message(krb5_context context, krb5_error_code ret, + const char *fmt, ...) + __attribute__ ((__format__ (__printf__, 3, 4))) +{ + va_list ap; + + va_start(ap, fmt); + krb5_vprepend_error_message(context, ret, fmt, ap); + va_end(ap); +} + +/** + * Prepend the contexts's full error string for a specific error code. + * + * The if context is NULL, no error string is stored. + * + * @param context Kerberos 5 context + * @param ret The error code + * @param fmt Error string for the error code + * @param args printf(3) style parameters. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_vprepend_error_message(krb5_context context, krb5_error_code ret, + const char *fmt, va_list args) + __attribute__ ((__format__ (__printf__, 3, 0))) +{ + if (context) + heim_vprepend_error_message(context->hcontext, ret, fmt, args); +} + +/** + * Return the error message for `code' in context. On memory + * allocation error the function returns NULL. + * + * @param context Kerberos 5 context + * @param code Error code related to the error + * + * @return an error string, needs to be freed with + * krb5_free_error_message(). The functions return NULL on error. + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL +krb5_get_error_message(krb5_context context, krb5_error_code code) +{ + const char *cstr = NULL; + + if (code == 0) + return strdup("Success"); + + /* + * The MIT version of this function ignores the krb5_context + * and several widely deployed applications call krb5_get_error_message() + * with a NULL context in order to translate an error code as a + * replacement for error_message(). Another reason a NULL context + * might be provided is if the krb5_init_context() call itself + * failed. + */ + if (context == NULL && krb5_init_context(&context) == 0) { + cstr = heim_get_error_message(context->hcontext, code); + krb5_free_context(context); + } else if (context) { + cstr = heim_get_error_message(context->hcontext, code); + } else { + cstr = heim_get_error_message(NULL, code); + } + return cstr; +} + + +/** + * Free the error message returned by krb5_get_error_message(). + * + * @param context Kerberos context + * @param msg error message to free, returned byg + * krb5_get_error_message(). + * + * @ingroup krb5_error + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_error_message(krb5_context context, const char *msg) +{ + heim_free_error_message(context ? context->hcontext : NULL, msg); +} + + +/** + * Return the error string for the error code. The caller must not + * free the string. + * + * This function is deprecated since its not threadsafe. + * + * @param context Kerberos 5 context. + * @param code Kerberos error code. + * + * @return the error message matching code + * + * @ingroup krb5 + */ + +KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL +krb5_get_err_text(krb5_context context, krb5_error_code code) + KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead") +{ + return krb5_get_error_message(context, code); +} diff --git a/third_party/heimdal/lib/krb5/expand_hostname.c b/third_party/heimdal/lib/krb5/expand_hostname.c new file mode 100644 index 0000000..5023d16 --- /dev/null +++ b/third_party/heimdal/lib/krb5/expand_hostname.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 1999 - 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 "krb5_locl.h" + +static krb5_error_code +copy_hostname(krb5_context context, + const char *orig_hostname, + char **new_hostname) +{ + *new_hostname = strdup (orig_hostname); + if (*new_hostname == NULL) + return krb5_enomem(context); + strlwr (*new_hostname); + return 0; +} + +/** + * krb5_expand_hostname() tries to make orig_hostname into a more + * canonical one in the newly allocated space returned in + * new_hostname. + + * @param context a Keberos context + * @param orig_hostname hostname to canonicalise. + * @param new_hostname output hostname, caller must free hostname with + * krb5_xfree(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_expand_hostname (krb5_context context, + const char *orig_hostname, + char **new_hostname) +{ + struct addrinfo *ai, *a, hints; + int error; + + if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0) + return copy_hostname (context, orig_hostname, new_hostname); + + memset (&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + error = getaddrinfo (orig_hostname, NULL, &hints, &ai); + if (error) + return copy_hostname (context, orig_hostname, new_hostname); + for (a = ai; a != NULL; a = a->ai_next) { + if (a->ai_canonname != NULL) { + *new_hostname = strdup (a->ai_canonname); + freeaddrinfo (ai); + if (*new_hostname == NULL) + return krb5_enomem(context); + else + return 0; + } + } + freeaddrinfo (ai); + return copy_hostname (context, orig_hostname, new_hostname); +} + +/* + * handle the case of the hostname being unresolvable and thus identical + */ + +static krb5_error_code +vanilla_hostname (krb5_context context, + const char *orig_hostname, + char **new_hostname, + char ***realms) +{ + krb5_error_code ret; + + ret = copy_hostname (context, orig_hostname, new_hostname); + if (ret) + return ret; + strlwr (*new_hostname); + + ret = krb5_get_host_realm (context, *new_hostname, realms); + if (ret) { + free (*new_hostname); + return ret; + } + return 0; +} + +/** + * krb5_expand_hostname_realms() expands orig_hostname to a name we + * believe to be a hostname in newly allocated space in new_hostname + * and return the realms new_hostname is believed to belong to in + * realms. + * + * @param context a Keberos context + * @param orig_hostname hostname to canonicalise. + * @param new_hostname output hostname, caller must free hostname with + * krb5_xfree(). + * @param realms output possible realms, is an array that is terminated + * with NULL. Caller must free with krb5_free_host_realm(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_expand_hostname_realms (krb5_context context, + const char *orig_hostname, + char **new_hostname, + char ***realms) +{ + struct addrinfo *ai, *a, hints; + int error; + krb5_error_code ret = 0; + + if ((context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) == 0) + return vanilla_hostname (context, orig_hostname, new_hostname, + realms); + + memset (&hints, 0, sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + + error = getaddrinfo (orig_hostname, NULL, &hints, &ai); + if (error) + return vanilla_hostname (context, orig_hostname, new_hostname, + realms); + + for (a = ai; a != NULL; a = a->ai_next) { + if (a->ai_canonname != NULL) { + ret = copy_hostname (context, a->ai_canonname, new_hostname); + if (ret) { + freeaddrinfo (ai); + return ret; + } + strlwr (*new_hostname); + ret = krb5_get_host_realm (context, *new_hostname, realms); + if (ret == 0) { + freeaddrinfo (ai); + return 0; + } + free (*new_hostname); + } + } + freeaddrinfo(ai); + return vanilla_hostname (context, orig_hostname, new_hostname, realms); +} diff --git a/third_party/heimdal/lib/krb5/expand_path.c b/third_party/heimdal/lib/krb5/expand_path.c new file mode 100644 index 0000000..a040235 --- /dev/null +++ b/third_party/heimdal/lib/krb5/expand_path.c @@ -0,0 +1,94 @@ + +/*********************************************************************** + * 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. + * + **********************************************************************/ + +#include "krb5_locl.h" + +#include + +/** + * Internal function to expand tokens in paths. + * + * Inputs: + * + * @context A krb5_context + * @path_in The path to expand tokens from + * @filepath True if the value is a filesystem path (converts slashes to + * backslashes on Windows) + * @ppath_out The expanded path + * + * Outputs: + * + * @ppath_out Path with expanded tokens (caller must free() this) + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_expand_path_tokens(krb5_context context, + const char *path_in, + int filepath, + char **ppath_out) +{ + return heim_expand_path_tokens(context ? context->hcontext : NULL, path_in, + filepath, ppath_out, NULL); +} + +/** + * Internal function to expand tokens in paths. + * + * Inputs: + * + * @context A krb5_context + * @path_in The path to expand tokens from + * @filepath True if the value is a filesystem path (converts slashes to + * backslashes on Windows) + * @ppath_out The expanded path + * @... Variable number of pairs of strings, the first of each + * being a token (e.g., "luser") and the second a string to + * replace it with. The list is terminated by a NULL. + * + * Outputs: + * + * @ppath_out Path with expanded tokens (caller must free() this) + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_expand_path_tokensv(krb5_context context, + const char *path_in, + int filepath, + char **ppath_out, ...) +{ + krb5_error_code ret; + va_list ap; + + va_start(ap, ppath_out); + ret = heim_expand_path_tokensv(context->hcontext, path_in, filepath, ppath_out, ap); + va_end(ap); + + return ret; +} diff --git a/third_party/heimdal/lib/krb5/fast.c b/third_party/heimdal/lib/krb5/fast.c new file mode 100644 index 0000000..90133a7 --- /dev/null +++ b/third_party/heimdal/lib/krb5/fast.c @@ -0,0 +1,963 @@ +/* + * Copyright (c) 2011 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" +#ifndef WIN32 +#include +#endif + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_fast_cf2(krb5_context context, + krb5_keyblock *key1, + const char *pepper1, + krb5_keyblock *key2, + const char *pepper2, + krb5_keyblock *armorkey, + krb5_crypto *armor_crypto) +{ + krb5_crypto crypto1, crypto2; + krb5_data pa1, pa2; + krb5_error_code ret; + + ret = krb5_crypto_init(context, key1, 0, &crypto1); + if (ret) + return ret; + + ret = krb5_crypto_init(context, key2, 0, &crypto2); + if (ret) { + krb5_crypto_destroy(context, crypto1); + return ret; + } + + pa1.data = rk_UNCONST(pepper1); + pa1.length = strlen(pepper1); + pa2.data = rk_UNCONST(pepper2); + pa2.length = strlen(pepper2); + + ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2, + key1->keytype, armorkey); + krb5_crypto_destroy(context, crypto1); + krb5_crypto_destroy(context, crypto2); + if (ret) + return ret; + + if (armor_crypto) { + ret = krb5_crypto_init(context, armorkey, 0, armor_crypto); + if (ret) + krb5_free_keyblock_contents(context, armorkey); + } + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_fast_armor_key(krb5_context context, + krb5_keyblock *subkey, + krb5_keyblock *sessionkey, + krb5_keyblock *armorkey, + krb5_crypto *armor_crypto) +{ + return _krb5_fast_cf2(context, + subkey, + "subkeyarmor", + sessionkey, + "ticketarmor", + armorkey, + armor_crypto); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_fast_explicit_armor_key(krb5_context context, + krb5_keyblock *armorkey, + krb5_keyblock *subkey, + krb5_keyblock *explicit_armorkey, + krb5_crypto *explicit_armor_crypto) +{ + return _krb5_fast_cf2(context, + armorkey, + "explicitarmor", + subkey, + "tgsarmor", + explicit_armorkey, + explicit_armor_crypto); +} + +static krb5_error_code +check_fast(krb5_context context, struct krb5_fast_state *state) +{ + if (state && (state->flags & KRB5_FAST_EXPECTED)) { + krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, + "Expected FAST, but no FAST " + "was in the response from the KDC"); + return KRB5KRB_AP_ERR_MODIFIED; + } + return 0; +} + +static krb5_error_code +make_local_fast_ap_fxarmor(krb5_context context, + krb5_ccache armor_ccache, + krb5_const_realm realm, + krb5_data *armor_value, + krb5_keyblock *armor_key, + krb5_crypto *armor_crypto) +{ + krb5_auth_context auth_context = NULL; + krb5_creds cred, *credp = NULL; + krb5_error_code ret; + krb5_data empty; + krb5_const_realm tgs_realm; + + if (armor_ccache == NULL) { + krb5_set_error_message(context, EINVAL, + "Armor credential cache required"); + return EINVAL; + } + + krb5_data_zero(&empty); + memset(&cred, 0, sizeof(cred)); + + ret = krb5_auth_con_init (context, &auth_context); + if (ret) + goto out; + + ret = krb5_cc_get_principal(context, armor_ccache, &cred.client); + if (ret) + goto out; + + /* + * Make sure we don't ask for a krbtgt/WELLKNOWN:ANONYMOUS + */ + if (krb5_principal_is_anonymous(context, cred.client, + KRB5_ANON_MATCH_UNAUTHENTICATED)) + tgs_realm = realm; + else + tgs_realm = cred.client->realm; + + ret = krb5_make_principal(context, &cred.server, + tgs_realm, + KRB5_TGS_NAME, + tgs_realm, + NULL); + if (ret) + goto out; + + ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp); + if (ret) + goto out; + + ret = krb5_auth_con_add_AuthorizationData(context, auth_context, + KRB5_AUTHDATA_FX_FAST_ARMOR, + &empty); + if (ret) + goto out; + + ret = krb5_mk_req_extended(context, + &auth_context, + AP_OPTS_USE_SUBKEY, + NULL, + credp, + armor_value); + if (ret) + goto out; + + ret = _krb5_fast_armor_key(context, + auth_context->local_subkey, + auth_context->keyblock, + armor_key, + armor_crypto); + if (ret) + goto out; + + out: + if (auth_context) + krb5_auth_con_free(context, auth_context); + if (credp) + krb5_free_creds(context, credp); + krb5_free_principal(context, cred.server); + krb5_free_principal(context, cred.client); + + return ret; +} + +#ifndef WIN32 +static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT; +static heim_ipc armor_service = NULL; + +static void +fast_armor_init_ipc(void *ctx) +{ + heim_ipc *ipc = ctx; + heim_ipc_init_context("ANY:org.h5l.armor-service", ipc); +} +#endif + +static krb5_error_code +make_fast_ap_fxarmor(krb5_context context, + struct krb5_fast_state *state, + krb5_const_realm realm, + KrbFastArmor **armor) +{ + KrbFastArmor *fxarmor = NULL; + krb5_error_code ret; + + *armor = NULL; + + ALLOC(fxarmor, 1); + if (fxarmor == NULL) { + ret = ENOMEM; + goto out; + } + + if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) { +#ifdef WIN32 + krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows"); + ret = ENOTSUP; + goto out; +#else + KERB_ARMOR_SERVICE_REPLY msg; + krb5_data request, reply; + + heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc); + if (armor_service == NULL) { + krb5_set_error_message(context, ENOENT, "Failed to open fast armor service"); + ret = ENOENT; + goto out; + } + + krb5_data_zero(&reply); + + request.data = rk_UNCONST(realm); + request.length = strlen(realm); + + ret = heim_ipc_call(armor_service, &request, &reply, NULL); + if (ret) { + krb5_set_error_message(context, ret, "Failed to get armor service credential"); + goto out; + } + + ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL); + krb5_data_free(&reply); + if (ret) + goto out; + + ret = copy_KrbFastArmor(fxarmor, &msg.armor); + if (ret) { + free_KERB_ARMOR_SERVICE_REPLY(&msg); + goto out; + } + + ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key); + free_KERB_ARMOR_SERVICE_REPLY(&msg); + if (ret) + goto out; + + ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto); + if (ret) + goto out; +#endif /* WIN32 */ + } else { + fxarmor->armor_type = 1; + + ret = make_local_fast_ap_fxarmor(context, + state->armor_ccache, + realm, + &fxarmor->armor_value, + &state->armor_key, + &state->armor_crypto); + if (ret) + goto out; + } + + + *armor = fxarmor; + fxarmor = NULL; + + out: + if (fxarmor) { + free_KrbFastArmor(fxarmor); + free(fxarmor); + } + return ret; +} + +static krb5_error_code +unwrap_fast_rep(krb5_context context, + struct krb5_fast_state *state, + PA_DATA *pa, + KrbFastResponse *fastrep) +{ + PA_FX_FAST_REPLY fxfastrep; + krb5_error_code ret; + + memset(&fxfastrep, 0, sizeof(fxfastrep)); + + ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, + pa->padata_value.length, + &fxfastrep, NULL); + if (ret) + return ret; + + if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) { + krb5_data data; + + ret = krb5_decrypt_EncryptedData(context, + state->armor_crypto, + KRB5_KU_FAST_REP, + &fxfastrep.u.armored_data.enc_fast_rep, + &data); + if (ret) + goto out; + + ret = decode_KrbFastResponse(data.data, data.length, fastrep, NULL); + krb5_data_free(&data); + if (ret) + goto out; + + } else { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + out: + free_PA_FX_FAST_REPLY(&fxfastrep); + + return ret; +} + +static krb5_error_code +set_anon_principal(krb5_context context, PrincipalName **p) +{ + + ALLOC((*p), 1); + if (*p == NULL) + goto fail; + + (*p)->name_type = KRB5_NT_PRINCIPAL; + + ALLOC_SEQ(&(*p)->name_string, 2); + if ((*p)->name_string.val == NULL) + goto fail; + + (*p)->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME); + if ((*p)->name_string.val[0] == NULL) + goto fail; + + (*p)->name_string.val[1] = strdup(KRB5_ANON_NAME); + if ((*p)->name_string.val[1] == NULL) + goto fail; + + return 0; + fail: + if (*p) { + if ((*p)->name_string.val) { + free((*p)->name_string.val[0]); + free((*p)->name_string.val[1]); + free((*p)->name_string.val); + } + free(*p); + } + + return krb5_enomem(context); +} + +krb5_error_code +_krb5_fast_create_armor(krb5_context context, + struct krb5_fast_state *state, + const char *realm) +{ + krb5_error_code ret; + + if (state->armor_crypto == NULL) { + if (state->armor_ccache || state->armor_ac || (state->flags & KRB5_FAST_AP_ARMOR_SERVICE)) { + /* + * Instead of keeping state in FX_COOKIE in the KDC, we + * rebuild a new armor key for every request, because this + * is what the MIT KDC expect and RFC6113 is vage about + * what the behavior should be. + */ + state->type = choice_PA_FX_FAST_REQUEST_armored_data; + } else { + return check_fast(context, state); + } + } + + if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { + if (state->armor_crypto) { + krb5_crypto_destroy(context, state->armor_crypto); + state->armor_crypto = NULL; + } + if (state->strengthen_key) { + krb5_free_keyblock(context, state->strengthen_key); + state->strengthen_key = NULL; + } + krb5_free_keyblock_contents(context, &state->armor_key); + + /* + * If we have a armor auth context, its because the caller + * wants us to do an implicit FAST armor (TGS-REQ). + */ + if (state->armor_ac) { + heim_assert((state->flags & KRB5_FAST_AS_REQ) == 0, "FAST AS with AC"); + + ret = _krb5_fast_armor_key(context, + state->armor_ac->local_subkey, + state->armor_ac->keyblock, + &state->armor_key, + &state->armor_crypto); + if (ret) + goto out; + } else { + heim_assert((state->flags & KRB5_FAST_AS_REQ) != 0, "FAST TGS without AC"); + + if (state->armor_data) { + free_KrbFastArmor(state->armor_data); + free(state->armor_data); + state->armor_data = NULL; + } + ret = make_fast_ap_fxarmor(context, state, realm, + &state->armor_data); + if (ret) + goto out; + } + } else { + heim_abort("unknown state type: %d", (int)state->type); + } + out: + return ret; +} + + +krb5_error_code +_krb5_fast_wrap_req(krb5_context context, + struct krb5_fast_state *state, + KDC_REQ *req) +{ + PA_FX_FAST_REQUEST fxreq; + krb5_error_code ret; + KrbFastReq fastreq; + krb5_data data, aschecksum_data, tgschecksum_data; + const krb5_data *checksum_data = NULL; + size_t size = 0; + krb5_boolean readd_padata_to_outer = FALSE; + + if (state->flags & KRB5_FAST_DISABLED) { + _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping"); + return 0; + } + + memset(&fxreq, 0, sizeof(fxreq)); + memset(&fastreq, 0, sizeof(fastreq)); + krb5_data_zero(&data); + krb5_data_zero(&aschecksum_data); + krb5_data_zero(&tgschecksum_data); + + if (state->armor_crypto == NULL) + return check_fast(context, state); + + state->flags |= KRB5_FAST_EXPECTED; + + fastreq.fast_options.hide_client_names = 1; + + ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body); + if (ret) + goto out; + + /* + * In the case of a AS-REQ, remove all account names. Want to this + * for TGS-REQ too, but due to layering this is tricky. + * + * 1. TGS-REQ need checksum of REQ-BODY + * 2. FAST needs checksum of TGS-REQ, so, FAST needs to happen after TGS-REQ + * 3. FAST privacy mangaling needs to happen before TGS-REQ does the checksum in 1. + * + * So lets not modify the bits for now for TGS-REQ + */ + if (state->flags & KRB5_FAST_AS_REQ) { + free_KDC_REQ_BODY(&req->req_body); + + req->req_body.realm = strdup(KRB5_ANON_REALM); + if (req->req_body.realm == NULL) { + ret = krb5_enomem(context); + goto out; + } + + ret = set_anon_principal(context, &req->req_body.cname); + if (ret) + goto out; + + ALLOC(req->req_body.till, 1); + *req->req_body.till = 0; + + ASN1_MALLOC_ENCODE(KDC_REQ_BODY, + aschecksum_data.data, + aschecksum_data.length, + &req->req_body, + &size, ret); + if (ret) + goto out; + heim_assert(aschecksum_data.length == size, "ASN.1 internal error"); + + checksum_data = &aschecksum_data; + + if (req->padata) { + ret = copy_METHOD_DATA(req->padata, &fastreq.padata); + free_METHOD_DATA(req->padata); + if (ret) + goto out; + } + } else { + const PA_DATA *tgs_req_ptr = NULL; + int tgs_req_idx = 0; + size_t i; + + heim_assert(req->padata != NULL, "req->padata is NULL"); + + tgs_req_ptr = krb5_find_padata(req->padata->val, + req->padata->len, + KRB5_PADATA_TGS_REQ, + &tgs_req_idx); + heim_assert(tgs_req_ptr != NULL, "KRB5_PADATA_TGS_REQ not found"); + heim_assert(tgs_req_idx == 0, "KRB5_PADATA_TGS_REQ not first"); + + tgschecksum_data.data = tgs_req_ptr->padata_value.data; + tgschecksum_data.length = tgs_req_ptr->padata_value.length; + checksum_data = &tgschecksum_data; + + /* + * Now copy all remaining once to + * the fastreq.padata and clear + * them in the outer req first, + * and remember to readd them later. + */ + readd_padata_to_outer = TRUE; + + for (i = 1; i < req->padata->len; i++) { + PA_DATA *val = &req->padata->val[i]; + + ret = krb5_padata_add(context, + &fastreq.padata, + val->padata_type, + val->padata_value.data, + val->padata_value.length); + if (ret) { + krb5_set_error_message(context, ret, + N_("malloc: out of memory", "")); + goto out; + } + val->padata_value.data = NULL; + val->padata_value.length = 0; + } + + /* + * Only TGS-REQ remaining + */ + req->padata->len = 1; + } + + if (req->padata == NULL) { + ALLOC(req->padata, 1); + if (req->padata == NULL) { + ret = krb5_enomem(context); + goto out; + } + } + + ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + fxreq.element = state->type; + + if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { + fxreq.u.armored_data.armor = state->armor_data; + state->armor_data = NULL; + + heim_assert(state->armor_crypto != NULL, + "FAST armor key missing when FAST started"); + + ret = krb5_create_checksum(context, state->armor_crypto, + KRB5_KU_FAST_REQ_CHKSUM, 0, + checksum_data->data, + checksum_data->length, + &fxreq.u.armored_data.req_checksum); + if (ret) + goto out; + + ret = krb5_encrypt_EncryptedData(context, state->armor_crypto, + KRB5_KU_FAST_ENC, + data.data, + data.length, + 0, + &fxreq.u.armored_data.enc_fast_req); + krb5_data_free(&data); + if (ret) + goto out; + + } else { + krb5_data_free(&data); + heim_assert(false, "unknown FAST type, internal error"); + } + + ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + + ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length); + if (ret) + goto out; + krb5_data_zero(&data); + + if (readd_padata_to_outer) { + size_t i; + + for (i = 0; i < fastreq.padata.len; i++) { + PA_DATA *val = &fastreq.padata.val[i]; + + ret = krb5_padata_add(context, + req->padata, + val->padata_type, + val->padata_value.data, + val->padata_value.length); + if (ret) { + krb5_set_error_message(context, ret, + N_("malloc: out of memory", "")); + goto out; + } + val->padata_value.data = NULL; + val->padata_value.length = 0; + } + } + + out: + free_KrbFastReq(&fastreq); + free_PA_FX_FAST_REQUEST(&fxreq); + krb5_data_free(&data); + krb5_data_free(&aschecksum_data); + + return ret; +} + +krb5_error_code +_krb5_fast_unwrap_error(krb5_context context, + int32_t nonce, + struct krb5_fast_state *state, + METHOD_DATA *md, + KRB_ERROR *error) +{ + KrbFastResponse fastrep; + krb5_error_code ret; + PA_DATA *pa; + int idx; + + if (state->armor_crypto == NULL) + return check_fast(context, state); + + memset(&fastrep, 0, sizeof(fastrep)); + + idx = 0; + pa = krb5_find_padata(md->val, md->len, KRB5_PADATA_FX_FAST, &idx); + if (pa == NULL) { + ret = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, ret, + N_("FAST fast response is missing FX-FAST", "")); + goto out; + } + + ret = unwrap_fast_rep(context, state, pa, &fastrep); + if (ret) + goto out; + + if (fastrep.strengthen_key || nonce != (int32_t)fastrep.nonce) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + idx = 0; + pa = krb5_find_padata(fastrep.padata.val, fastrep.padata.len, KRB5_PADATA_FX_ERROR, &idx); + if (pa == NULL) { + ret = KRB5_KDCREP_MODIFIED; + krb5_set_error_message(context, ret, N_("No wrapped error", "")); + goto out; + } + + free_KRB_ERROR(error); + + ret = krb5_rd_error(context, &pa->padata_value, error); + if (ret) + goto out; + + if (error->e_data) + _krb5_debug(context, 10, "FAST wrapped KBB_ERROR contained e_data: %d", + (int)error->e_data->length); + + free_METHOD_DATA(md); + md->val = fastrep.padata.val; + md->len = fastrep.padata.len; + + fastrep.padata.val = NULL; + fastrep.padata.len = 0; + + out: + free_KrbFastResponse(&fastrep); + return ret; +} + +krb5_error_code +_krb5_fast_unwrap_kdc_rep(krb5_context context, int32_t nonce, + krb5_data *chksumdata, + struct krb5_fast_state *state, AS_REP *rep) +{ + KrbFastResponse fastrep; + krb5_error_code ret; + PA_DATA *pa = NULL; + int idx = 0; + + if (state == NULL || state->armor_crypto == NULL || rep->padata == NULL) + return check_fast(context, state); + + /* find PA_FX_FAST_REPLY */ + + pa = krb5_find_padata(rep->padata->val, rep->padata->len, + KRB5_PADATA_FX_FAST, &idx); + if (pa == NULL) + return check_fast(context, state); + + memset(&fastrep, 0, sizeof(fastrep)); + + ret = unwrap_fast_rep(context, state, pa, &fastrep); + if (ret) + goto out; + + free_METHOD_DATA(rep->padata); + ret = copy_METHOD_DATA(&fastrep.padata, rep->padata); + if (ret) + goto out; + + if (fastrep.strengthen_key) { + if (state->strengthen_key) + krb5_free_keyblock(context, state->strengthen_key); + + ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key); + if (ret) + goto out; + } + + if (nonce != (int32_t)fastrep.nonce) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + if (fastrep.finished) { + PrincipalName cname; + krb5_realm crealm = NULL; + + if (chksumdata == NULL) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + ret = krb5_verify_checksum(context, state->armor_crypto, + KRB5_KU_FAST_FINISHED, + chksumdata->data, chksumdata->length, + &fastrep.finished->ticket_checksum); + if (ret) + goto out; + + /* update */ + ret = copy_Realm(&fastrep.finished->crealm, &crealm); + if (ret) + goto out; + free_Realm(&rep->crealm); + rep->crealm = crealm; + + ret = copy_PrincipalName(&fastrep.finished->cname, &cname); + if (ret) + goto out; + free_PrincipalName(&rep->cname); + rep->cname = cname; + } else if (chksumdata) { + /* expected fastrep.finish but didn't get it */ + ret = KRB5KDC_ERR_PREAUTH_FAILED; + } + + out: + free_KrbFastResponse(&fastrep); + return ret; +} + +void +_krb5_fast_free(krb5_context context, struct krb5_fast_state *state) +{ + if (state->armor_ccache) { + if (state->flags & KRB5_FAST_ANON_PKINIT_ARMOR) + krb5_cc_destroy(context, state->armor_ccache); + else + krb5_cc_close(context, state->armor_ccache); + } + if (state->armor_service) + krb5_free_principal(context, state->armor_service); + if (state->armor_crypto) + krb5_crypto_destroy(context, state->armor_crypto); + if (state->strengthen_key) + krb5_free_keyblock(context, state->strengthen_key); + krb5_free_keyblock_contents(context, &state->armor_key); + if (state->armor_data) { + free_KrbFastArmor(state->armor_data); + free(state->armor_data); + } + + if (state->anon_pkinit_ctx) + krb5_init_creds_free(context, state->anon_pkinit_ctx); + if (state->anon_pkinit_opt) + krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt); + + memset(state, 0, sizeof(*state)); +} + +krb5_error_code +_krb5_fast_anon_pkinit_step(krb5_context context, + krb5_init_creds_context ctx, + struct krb5_fast_state *state, + const krb5_data *in, + krb5_data *out, + krb5_realm *out_realm, + unsigned int *flags) +{ + krb5_error_code ret; + krb5_const_realm realm = _krb5_init_creds_get_cred_client(context, ctx)->realm; + krb5_init_creds_context anon_pk_ctx; + krb5_principal principal = NULL, anon_pk_client; + krb5_ccache ccache = NULL; + krb5_creds cred; + krb5_data data = { 3, rk_UNCONST("yes") }; + + krb5_data_zero(out); + *out_realm = NULL; + + memset(&cred, 0, sizeof(cred)); + + if (state->anon_pkinit_opt == NULL) { + ret = krb5_get_init_creds_opt_alloc(context, &state->anon_pkinit_opt); + if (ret) + goto out; + + krb5_get_init_creds_opt_set_tkt_life(state->anon_pkinit_opt, 60); + krb5_get_init_creds_opt_set_anonymous(state->anon_pkinit_opt, TRUE); + + ret = krb5_make_principal(context, &principal, realm, + KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); + if (ret) + goto out; + + ret = krb5_get_init_creds_opt_set_pkinit(context, + state->anon_pkinit_opt, + principal, + NULL, NULL, NULL, NULL, + KRB5_GIC_OPT_PKINIT_ANONYMOUS | + KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR, + NULL, NULL, NULL); + if (ret) + goto out; + + ret = krb5_init_creds_init(context, principal, NULL, NULL, + _krb5_init_creds_get_cred_starttime(context, ctx), + state->anon_pkinit_opt, + &state->anon_pkinit_ctx); + if (ret) + goto out; + } + + anon_pk_ctx = state->anon_pkinit_ctx; + + ret = krb5_init_creds_step(context, anon_pk_ctx, in, out, out_realm, flags); + if (ret || + (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) + goto out; + + ret = krb5_process_last_request(context, state->anon_pkinit_opt, anon_pk_ctx); + if (ret) + goto out; + + ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache); + if (ret) + goto out; + + ret = krb5_init_creds_get_creds(context, anon_pk_ctx, &cred); + if (ret) + goto out; + + if (!cred.flags.b.enc_pa_rep) { + ret = KRB5KDC_ERR_BADOPTION; /* KDC does not support FAST */ + goto out; + } + + anon_pk_client = _krb5_init_creds_get_cred_client(context, anon_pk_ctx); + + ret = krb5_cc_initialize(context, ccache, anon_pk_client); + if (ret) + goto out; + + ret = krb5_cc_store_cred(context, ccache, &cred); + if (ret) + goto out; + + ret = krb5_cc_set_config(context, ccache, cred.server, + "fast_avail", &data); + if (ret && ret != KRB5_CC_NOSUPP) + return ret; + + if (_krb5_pk_is_kdc_verified(context, state->anon_pkinit_opt)) + state->flags |= KRB5_FAST_KDC_VERIFIED; + else + state->flags &= ~(KRB5_FAST_KDC_VERIFIED); + + state->armor_ccache = ccache; + ccache = NULL; + + krb5_init_creds_free(context, state->anon_pkinit_ctx); + state->anon_pkinit_ctx = NULL; + + krb5_get_init_creds_opt_free(context, state->anon_pkinit_opt); + state->anon_pkinit_opt = NULL; + +out: + krb5_free_principal(context, principal); + krb5_free_cred_contents(context, &cred); + if (ccache) + krb5_cc_destroy(context, ccache); + + return ret; +} diff --git a/third_party/heimdal/lib/krb5/fcache.c b/third_party/heimdal/lib/krb5/fcache.c new file mode 100644 index 0000000..20c335d --- /dev/null +++ b/third_party/heimdal/lib/krb5/fcache.c @@ -0,0 +1,1693 @@ +/* + * Copyright (c) 1997 - 2017 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 "krb5_locl.h" + +typedef struct krb5_fcache{ + char *filename; + char *res; + char *sub; + char *tmpfn; + int version; +}krb5_fcache; + +struct fcc_cursor { + int fd; + off_t cred_start; + off_t cred_end; + krb5_storage *sp; +}; + +#define KRB5_FCC_FVNO_1 1 +#define KRB5_FCC_FVNO_2 2 +#define KRB5_FCC_FVNO_3 3 +#define KRB5_FCC_FVNO_4 4 + +#define FCC_TAG_DELTATIME 1 + +#define FCACHE(X) ((krb5_fcache*)(X)->data.data) + +#define FILENAME(X) (FCACHE(X)->filename) +#define TMPFILENAME(X) (FCACHE(X)->tmpfn) +#define RESFILENAME(X) (FCACHE(X)->res) +#define SUBFILENAME(X) (FCACHE(X)->sub) + +#define FCC_CURSOR(C) ((struct fcc_cursor*)(C)) + +static krb5_error_code KRB5_CALLCONV +fcc_get_name_2(krb5_context context, + krb5_ccache id, + const char **name, + const char **colname, + const char **sub) +{ + if (FCACHE(id) == NULL) + return KRB5_CC_NOTFOUND; + + if (name) + *name = FILENAME(id); + if (colname) + *colname = FILENAME(id); + if (sub) + *sub = NULL; + return 0; +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive, + const char *filename) +{ + int ret; +#ifdef HAVE_FCNTL + struct flock l; + + l.l_start = 0; + l.l_len = 0; + l.l_type = exclusive ? F_WRLCK : F_RDLCK; + l.l_whence = SEEK_SET; + ret = fcntl(fd, F_SETLKW, &l); +#else + ret = flock(fd, exclusive ? LOCK_EX : LOCK_SH); +#endif + if(ret < 0) + ret = errno; + if(ret == EACCES) /* fcntl can return EACCES instead of EAGAIN */ + ret = EAGAIN; + + switch (ret) { + case 0: + break; + case EINVAL: /* filesystem doesn't support locking, let the user have it */ + ret = 0; + break; + case EAGAIN: + krb5_set_error_message(context, ret, + N_("timed out locking cache file %s", "file"), + filename); + break; + default: { + char buf[128]; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, + N_("error locking cache file %s: %s", + "file, error"), filename, buf); + break; + } + } + return ret; +} + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +_krb5_xunlock(krb5_context context, int fd) +{ + int ret; +#ifdef HAVE_FCNTL + struct flock l; + l.l_start = 0; + l.l_len = 0; + l.l_type = F_UNLCK; + l.l_whence = SEEK_SET; + ret = fcntl(fd, F_SETLKW, &l); +#else + ret = flock(fd, LOCK_UN); +#endif + if (ret < 0) + ret = errno; + switch (ret) { + case 0: + break; + case EINVAL: /* filesystem doesn't support locking, let the user have it */ + ret = 0; + break; + default: { + char buf[128]; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, + N_("Failed to unlock file: %s", ""), buf); + break; + } + } + return ret; +} + +static krb5_error_code +write_storage(krb5_context context, krb5_storage *sp, int fd) +{ + krb5_error_code ret; + krb5_data data; + ssize_t sret; + + ret = krb5_storage_to_data(sp, &data); + if (ret) { + krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + return ret; + } + sret = write(fd, data.data, data.length); + ret = (sret != (ssize_t)data.length); + krb5_data_free(&data); + if (ret) { + ret = errno; + krb5_set_error_message(context, ret, + N_("Failed to write FILE credential data", "")); + return ret; + } + return 0; +} + + +static krb5_error_code KRB5_CALLCONV +fcc_lock(krb5_context context, krb5_ccache id, + int fd, krb5_boolean exclusive) +{ + krb5_error_code ret; + const char *name; + + if (exclusive == FALSE) + return 0; + ret = fcc_get_name_2(context, id, &name, NULL, NULL); + if (ret == 0) + ret = _krb5_xlock(context, fd, exclusive, name); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_get_default_name(krb5_context, char **); + +/* + * This is the character used to separate the residual from the subsidiary name + * when both are given. It's tempting to use ':' just as we do in the ccache + * names, but we can't on Windows. + */ +#define FILESUBSEP "+" +#define FILESUBSEPCHR ((FILESUBSEP)[0]) + +static krb5_error_code KRB5_CALLCONV +fcc_resolve_2(krb5_context context, + krb5_ccache *id, + const char *res, + const char *sub) +{ + krb5_fcache *f; + char *freeme = NULL; + + if (res == NULL && sub == NULL) + return krb5_einval(context, 3); + if (res == NULL) { + krb5_error_code ret; + + if ((ret = fcc_get_default_name(context, &freeme))) + return ret; + res = freeme + sizeof("FILE:") - 1; + } else if (!sub && (sub = strchr(res, FILESUBSEPCHR))) { + if (sub[1] == '\0') { + sub = NULL; + } else { + /* `res' has a subsidiary component, so split on it */ + if ((freeme = strndup(res, sub - res)) == NULL) + return krb5_enomem(context); + res = freeme; + sub++; + } + } + + if ((f = calloc(1, sizeof(*f))) == NULL || + (f->res = strdup(res)) == NULL || + (f->sub = sub ? strdup(sub) : NULL) == (sub ? NULL : "") || + asprintf(&f->filename, "%s%s%s", + res, sub ? FILESUBSEP : "", sub ? sub : "") == -1 || + f->filename == NULL) { + if (f) { + free(f->filename); + free(f->res); + free(f->sub); + } + free(f); + free(freeme); + return krb5_enomem(context); + } + f->tmpfn = NULL; + f->version = 0; + (*id)->data.data = f; + (*id)->data.length = sizeof(*f); + + free(freeme); + return 0; +} + +/* + * Try to scrub the contents of `filename' safely. + */ + +static int +scrub_file (int fd) +{ + off_t pos; + char buf[128]; + + pos = lseek(fd, 0, SEEK_END); + if (pos < 0) + return errno; + if (lseek(fd, 0, SEEK_SET) < 0) + return errno; + memset(buf, 0, sizeof(buf)); + while(pos > 0) { + ssize_t tmp; + size_t wr = sizeof(buf); + if (wr > pos) + wr = (size_t)pos; + tmp = write(fd, buf, wr); + + if (tmp < 0) + return errno; + pos -= tmp; + } +#ifdef _MSC_VER + _commit (fd); +#else + fsync (fd); +#endif + return 0; +} + +/* + * Erase `filename' if it exists, trying to remove the contents if + * it's `safe'. We always try to remove the file, it it exists. It's + * only overwritten if it's a regular file (not a symlink and not a + * hardlink) + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_erase_file(krb5_context context, const char *filename) +{ + int fd; + struct stat sb1, sb2; + int ret; + + ret = lstat (filename, &sb1); + if (ret < 0) { + if(errno == ENOENT) + return 0; + else + return errno; + } + + fd = open(filename, O_RDWR | O_BINARY | O_CLOEXEC | O_NOFOLLOW); + if(fd < 0) { + if(errno == ENOENT) + return 0; + else + return errno; + } + rk_cloexec(fd); + ret = _krb5_xlock(context, fd, 1, filename); + if (ret) { + close(fd); + return ret; + } + if (unlink(filename) < 0) { + ret = errno; + close (fd); + krb5_set_error_message(context, errno, + N_("krb5_cc_destroy: unlinking \"%s\": %s", ""), + filename, strerror(ret)); + return ret; + } + ret = fstat(fd, &sb2); + if (ret < 0) { + ret = errno; + close (fd); + return ret; + } + + /* check if someone was playing with symlinks */ + + if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) { + close(fd); + return EPERM; + } + + /* there are still hard links to this file */ + + if (sb2.st_nlink != 0) { + close(fd); + return 0; + } + + ret = scrub_file(fd); + close(fd); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_gen_new(krb5_context context, krb5_ccache *id) +{ + char *file = NULL, *exp_file = NULL; + krb5_error_code ret; + krb5_fcache *f; + int fd; + + f = calloc(1, sizeof(*f)); + if(f == NULL) { + krb5_set_error_message(context, KRB5_CC_NOMEM, + N_("malloc: out of memory", "")); + return KRB5_CC_NOMEM; + } + f->tmpfn = NULL; + /* + * XXX We should asprintf(&file, "%s:XXXXXX", KRB5_DEFAULT_CCNAME_FILE) + * instead so that new unique FILE ccaches can be found in the user's + * default collection. + * */ + ret = asprintf(&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT); + if(ret < 0 || file == NULL) { + free(f); + krb5_set_error_message(context, KRB5_CC_NOMEM, + N_("malloc: out of memory", "")); + return KRB5_CC_NOMEM; + } + ret = _krb5_expand_path_tokens(context, file, 1, &exp_file); + free(file); + if (ret) { + free(f); + return ret; + } + + file = exp_file; + + fd = mkostemp(exp_file, O_CLOEXEC); + if(fd < 0) { + ret = (krb5_error_code)errno; + krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), exp_file); + free(f); + free(exp_file); + return ret; + } + close(fd); + f->filename = exp_file; + f->res = strdup(exp_file); /* XXX See above commentary about collection */ + f->sub = NULL; + f->version = 0; + (*id)->data.data = f; + (*id)->data.length = sizeof(*f); + return 0; +} + +static void +storage_set_flags(krb5_context context, krb5_storage *sp, int vno) +{ + int flags = 0; + switch(vno) { + case KRB5_FCC_FVNO_1: + flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; + flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; + flags |= KRB5_STORAGE_HOST_BYTEORDER; + break; + case KRB5_FCC_FVNO_2: + flags |= KRB5_STORAGE_HOST_BYTEORDER; + break; + case KRB5_FCC_FVNO_3: + flags |= KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE; + break; + case KRB5_FCC_FVNO_4: + break; + default: + krb5_abortx(context, + "storage_set_flags called with bad vno (%x)", vno); + } + krb5_storage_set_flags(sp, flags); +} + +static krb5_error_code KRB5_CALLCONV +fcc_open(krb5_context context, + krb5_ccache id, + const char *operation, + int *fd_ret, + int flags, + mode_t mode) +{ + krb5_boolean exclusive = ((flags | O_WRONLY) == flags || + (flags | O_RDWR) == flags); + krb5_error_code ret; + const char *filename; + struct stat sb1, sb2; +#ifndef _WIN32 + struct stat sb3; + size_t tries = 3; +#endif + int strict_checking; + int fd; + + flags |= O_BINARY | O_CLOEXEC | O_NOFOLLOW; + + *fd_ret = -1; + + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + if ((flags & O_EXCL)) { + /* + * FIXME Instead of mkostemp()... we could instead try to use a .new + * file... with care. Or the O_TMPFILE / linkat() extensions. We need + * a roken / heimbase abstraction for that. + */ + if (TMPFILENAME(id)) + (void) unlink(TMPFILENAME(id)); + free(TMPFILENAME(id)); + TMPFILENAME(id) = NULL; + if (asprintf(&TMPFILENAME(id), "%s-XXXXXX", FILENAME(id)) < 0 || + TMPFILENAME(id) == NULL) + return krb5_enomem(context); + if ((fd = mkostemp(TMPFILENAME(id), O_CLOEXEC)) == -1) { + krb5_set_error_message(context, ret = errno, + N_("Could not make temp ccache FILE:%s", ""), + TMPFILENAME(id)); + free(TMPFILENAME(id)); + TMPFILENAME(id) = NULL; + return ret; + } + goto out; + } + + filename = TMPFILENAME(id) ? TMPFILENAME(id) : FILENAME(id); + strict_checking = (flags & O_CREAT) == 0 && + (context->flags & KRB5_CTX_F_FCACHE_STRICT_CHECKING) != 0; + +#ifndef WIN32 +again: +#endif + memset(&sb1, 0, sizeof(sb1)); + ret = lstat(filename, &sb1); + if (ret == 0) { + if (!S_ISREG(sb1.st_mode)) { + krb5_set_error_message(context, EPERM, + N_("Refuses to open symlinks for caches FILE:%s", ""), filename); + return EPERM; + } + } else if (errno != ENOENT || !(flags & O_CREAT)) { + krb5_set_error_message(context, errno, N_("%s lstat(%s)", "file, error"), + operation, filename); + return errno; + } + + fd = open(filename, flags, mode); + if(fd < 0) { + char buf[128]; + ret = errno; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, N_("%s open(%s): %s", "file, error"), + operation, filename, buf); + return ret; + } + rk_cloexec(fd); + + ret = fstat(fd, &sb2); + if (ret < 0) { + krb5_clear_error_message(context); + close(fd); + return errno; + } + + if (!S_ISREG(sb2.st_mode)) { + krb5_set_error_message(context, EPERM, N_("Refuses to open non files caches: FILE:%s", ""), filename); + close(fd); + return EPERM; + } + +#ifndef _WIN32 + if (sb1.st_dev && sb1.st_ino && + (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino)) { + /* + * Perhaps we raced with a rename(). To complain about + * symlinks in that case would cause unnecessary concern, so + * we check for that possibility and loop. This has no + * TOCTOU problems because we redo the open(). We could also + * not do any of this checking if O_NOFOLLOW != 0... + */ + close(fd); + ret = lstat(filename, &sb3); + if (ret || sb1.st_dev != sb2.st_dev || + sb3.st_dev != sb2.st_dev || sb3.st_ino != sb2.st_ino) { + krb5_set_error_message(context, EPERM, N_("Refuses to open possible symlink for caches: FILE:%s", ""), filename); + return EPERM; + } + if (--tries == 0) { + krb5_set_error_message(context, EPERM, N_("Raced too many times with renames of FILE:%s", ""), filename); + return EPERM; + } + goto again; + } +#endif + + /* + * /tmp (or wherever default ccaches go) might not be on its own + * filesystem, or on a filesystem different /etc, say, and even if + * it were, suppose a user hard-links another's ccache to her + * default ccache, then runs a set-uid program that will user her + * default ccache (even if it ignores KRB5CCNAME)... + * + * Default ccache locations should really be on per-user non-tmp + * locations on tmpfs "run" directories. But we don't know here + * that this is the case. Thus: no hard-links, no symlinks. + */ + if (sb2.st_nlink > 1) { + krb5_set_error_message(context, EPERM, N_("Refuses to open hardlinks for caches FILE:%s", ""), filename); + close(fd); + return EPERM; + } + + if (strict_checking) { +#ifndef _WIN32 + /* + * XXX WIN32: Needs to have ACL checking code! + * st_mode comes out as 100666, and st_uid is no use. + */ + /* + * XXX Should probably add options to improve control over this + * check. We might want strict checking of everything except + * this. + */ + if (sb2.st_uid != geteuid()) { + krb5_set_error_message(context, EPERM, N_("Refuses to open cache files not own by myself FILE:%s (owned by %d)", ""), filename, (int)sb2.st_uid); + close(fd); + return EPERM; + } + if ((sb2.st_mode & 077) != 0) { + krb5_set_error_message(context, EPERM, + N_("Refuses to open group/other readable files FILE:%s", ""), filename); + close(fd); + return EPERM; + } +#endif + } + +out: + if((ret = fcc_lock(context, id, fd, exclusive)) != 0) { + close(fd); + return ret; + } + *fd_ret = fd; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fcc_initialize(krb5_context context, + krb5_ccache id, + krb5_principal primary_principal) +{ + krb5_fcache *f = FCACHE(id); + int ret = 0; + int fd; + + if (f == NULL) + return krb5_einval(context, 2); + + /* + * fcc_open() will notice the O_EXCL and will make a temporary file that + * will later be renamed into place. + */ + ret = fcc_open(context, id, "initialize", &fd, O_RDWR | O_CREAT | O_EXCL, 0600); + if(ret) + return ret; + { + krb5_storage *sp; + sp = krb5_storage_emem(); + if (sp == NULL) + return krb5_enomem(context); + krb5_storage_set_eof_code(sp, KRB5_CC_END); + if(context->fcache_vno != 0) + f->version = context->fcache_vno; + else + f->version = KRB5_FCC_FVNO_4; + if (ret == 0) + ret = krb5_store_int8(sp, 5); + if (ret == 0) + ret = krb5_store_int8(sp, f->version); + storage_set_flags(context, sp, f->version); + if(f->version == KRB5_FCC_FVNO_4 && ret == 0) { + /* V4 stuff */ + if (context->kdc_sec_offset) { + if (ret == 0) + ret = krb5_store_int16 (sp, 12); /* length */ + if (ret == 0) + ret = krb5_store_int16 (sp, FCC_TAG_DELTATIME); /* Tag */ + if (ret == 0) + ret = krb5_store_int16 (sp, 8); /* length of data */ + if (ret == 0) + ret = krb5_store_int32 (sp, context->kdc_sec_offset); + if (ret == 0) + ret = krb5_store_int32 (sp, context->kdc_usec_offset); + } else { + if (ret == 0) + ret = krb5_store_int16 (sp, 0); + } + } + if (ret == 0) + ret = krb5_store_principal(sp, primary_principal); + + if (ret == 0) + ret = write_storage(context, sp, fd); + + krb5_storage_free(sp); + } + if (close(fd) < 0) + if (ret == 0) { + char buf[128]; + ret = errno; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, N_("close %s: %s", ""), + FILENAME(id), buf); + } + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_close(krb5_context context, + krb5_ccache id) +{ + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + if (TMPFILENAME(id)) + (void) unlink(TMPFILENAME(id)); + free(TMPFILENAME(id)); + free(RESFILENAME(id)); + free(SUBFILENAME(id)); + free(FILENAME(id)); + krb5_data_free(&id->data); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fcc_destroy(krb5_context context, + krb5_ccache id) +{ + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + if (TMPFILENAME(id)) + (void) _krb5_erase_file(context, TMPFILENAME(id)); + return _krb5_erase_file(context, FILENAME(id)); +} + +static krb5_error_code KRB5_CALLCONV +fcc_store_cred(krb5_context context, + krb5_ccache id, + krb5_creds *creds) +{ + int ret; + int fd; + + ret = fcc_open(context, id, "store", &fd, O_WRONLY | O_APPEND, 0); + if(ret) + return ret; + { + krb5_storage *sp; + + sp = krb5_storage_emem(); + if (sp == NULL) + return krb5_enomem(context); + krb5_storage_set_eof_code(sp, KRB5_CC_END); + storage_set_flags(context, sp, FCACHE(id)->version); + ret = krb5_store_creds(sp, creds); + if (ret == 0) + ret = write_storage(context, sp, fd); + krb5_storage_free(sp); + } + if (close(fd) < 0) { + if (ret == 0) { + char buf[128]; + ret = errno; + rk_strerror_r(ret, buf, sizeof(buf)); + krb5_set_error_message(context, ret, N_("close %s: %s", ""), + FILENAME(id), buf); + } + } + if (ret == 0 && TMPFILENAME(id) && + !krb5_is_config_principal(context, creds->server)) { + + /* + * Portability note: there's no need to have WIN32 or other code here + * for odd rename cases because rk_rename() is meant to handle that. + */ + ret = rk_rename(TMPFILENAME(id), FILENAME(id)); + if (ret == 0) { + free(TMPFILENAME(id)); + TMPFILENAME(id) = NULL; + } else { + ret = errno; + } + } + return ret; +} + +static krb5_error_code +init_fcc(krb5_context context, + krb5_ccache id, + const char *operation, + krb5_storage **ret_sp, + int *ret_fd, + krb5_deltat *kdc_offset) +{ + int fd; + int8_t pvno, tag; + krb5_storage *sp; + krb5_error_code ret; + + *ret_fd = -1; + *ret_sp = NULL; + if (kdc_offset) + *kdc_offset = 0; + + ret = fcc_open(context, id, operation, &fd, O_RDONLY, 0); + if(ret) + return ret; + + sp = krb5_storage_stdio_from_fd(fd, "r"); + if(sp == NULL) { + krb5_clear_error_message(context); + ret = ENOMEM; + goto out; + } + krb5_storage_set_eof_code(sp, KRB5_CC_END); + ret = krb5_ret_int8(sp, &pvno); + if (ret != 0) { + if(ret == KRB5_CC_END) { + ret = ENOENT; + krb5_set_error_message(context, ret, + N_("Empty credential cache file: %s", ""), + FILENAME(id)); + } else + krb5_set_error_message(context, ret, N_("Error reading pvno " + "in cache file: %s", ""), + FILENAME(id)); + goto out; + } + if (pvno != 5) { + ret = KRB5_CCACHE_BADVNO; + krb5_set_error_message(context, ret, N_("Bad version number in credential " + "cache file: %s", ""), + FILENAME(id)); + goto out; + } + ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */ + if (ret != 0) { + ret = KRB5_CC_FORMAT; + krb5_set_error_message(context, ret, "Error reading tag in " + "cache file: %s", FILENAME(id)); + goto out; + } + FCACHE(id)->version = tag; + storage_set_flags(context, sp, FCACHE(id)->version); + switch (tag) { + case KRB5_FCC_FVNO_4: { + int16_t length; + + ret = krb5_ret_int16 (sp, &length); + if(ret) { + ret = KRB5_CC_FORMAT; + krb5_set_error_message(context, ret, + N_("Error reading tag length in " + "cache file: %s", ""), FILENAME(id)); + goto out; + } + while(length > 0) { + int16_t dtag, data_len; + int i; + int8_t dummy; + + ret = krb5_ret_int16 (sp, &dtag); + if(ret) { + ret = KRB5_CC_FORMAT; + krb5_set_error_message(context, ret, N_("Error reading dtag in " + "cache file: %s", ""), + FILENAME(id)); + goto out; + } + ret = krb5_ret_int16 (sp, &data_len); + if(ret) { + ret = KRB5_CC_FORMAT; + krb5_set_error_message(context, ret, + N_("Error reading dlength " + "in cache file: %s",""), + FILENAME(id)); + goto out; + } + switch (dtag) { + case FCC_TAG_DELTATIME : { + int32_t offset; + + ret = krb5_ret_int32 (sp, &offset); + ret |= krb5_ret_int32 (sp, &context->kdc_usec_offset); + if(ret) { + ret = KRB5_CC_FORMAT; + krb5_set_error_message(context, ret, + N_("Error reading kdc_sec in " + "cache file: %s", ""), + FILENAME(id)); + goto out; + } + context->kdc_sec_offset = offset; + if (kdc_offset) + *kdc_offset = offset; + break; + } + default : + for (i = 0; i < data_len; ++i) { + ret = krb5_ret_int8 (sp, &dummy); + if(ret) { + ret = KRB5_CC_FORMAT; + krb5_set_error_message(context, ret, + N_("Error reading unknown " + "tag in cache file: %s", ""), + FILENAME(id)); + goto out; + } + } + break; + } + length -= 4 + data_len; + } + break; + } + case KRB5_FCC_FVNO_3: + case KRB5_FCC_FVNO_2: + case KRB5_FCC_FVNO_1: + break; + default : + ret = KRB5_CCACHE_BADVNO; + krb5_set_error_message(context, ret, + N_("Unknown version number (%d) in " + "credential cache file: %s", ""), + (int)tag, FILENAME(id)); + goto out; + } + *ret_sp = sp; + *ret_fd = fd; + + return 0; + out: + if(sp != NULL) + krb5_storage_free(sp); + close(fd); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_get_principal(krb5_context context, + krb5_ccache id, + krb5_principal *principal) +{ + krb5_error_code ret; + int fd; + krb5_storage *sp; + + ret = init_fcc (context, id, "get-principal", &sp, &fd, NULL); + if (ret) + return ret; + ret = krb5_ret_principal(sp, principal); + if (ret) + krb5_clear_error_message(context); + krb5_storage_free(sp); + close(fd); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_end_get(krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor); + +static krb5_error_code KRB5_CALLCONV +fcc_get_first(krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + krb5_error_code ret; + krb5_principal principal; + + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + *cursor = calloc(1, sizeof(struct fcc_cursor)); + if (*cursor == NULL) { + krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); + return ENOMEM; + } + + ret = init_fcc(context, id, "get-first", &FCC_CURSOR(*cursor)->sp, + &FCC_CURSOR(*cursor)->fd, NULL); + if (ret) { + free(*cursor); + *cursor = NULL; + return ret; + } + ret = krb5_ret_principal (FCC_CURSOR(*cursor)->sp, &principal); + if(ret) { + krb5_clear_error_message(context); + fcc_end_get(context, id, cursor); + return ret; + } + krb5_free_principal (context, principal); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fcc_get_next (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *creds) +{ + krb5_error_code ret; + + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + if (FCC_CURSOR(*cursor) == NULL) + return krb5_einval(context, 3); + + FCC_CURSOR(*cursor)->cred_start = + krb5_storage_seek(FCC_CURSOR(*cursor)->sp, 0, SEEK_CUR); + + ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds); + if (ret) + krb5_clear_error_message(context); + + FCC_CURSOR(*cursor)->cred_end = + krb5_storage_seek(FCC_CURSOR(*cursor)->sp, 0, SEEK_CUR); + + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_end_get (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + if (FCC_CURSOR(*cursor) == NULL) + return krb5_einval(context, 3); + + krb5_storage_free(FCC_CURSOR(*cursor)->sp); + close (FCC_CURSOR(*cursor)->fd); + free(*cursor); + *cursor = NULL; + return 0; +} + +static void KRB5_CALLCONV +cred_delete(krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *cred) +{ + krb5_error_code ret; + krb5_storage *sp; + krb5_data orig_cred_data; + unsigned char *cred_data_in_file = NULL; + off_t new_cred_sz; + struct stat sb1, sb2; + int fd = -1; + ssize_t bytes; + krb5_const_realm srealm = krb5_principal_get_realm(context, cred->server); + + /* This is best-effort code; if we lose track of errors here it's OK */ + + heim_assert(FCC_CURSOR(*cursor)->cred_start < FCC_CURSOR(*cursor)->cred_end, + "fcache internal error"); + + krb5_data_zero(&orig_cred_data); + + sp = krb5_storage_emem(); + if (sp == NULL) + return; + krb5_storage_set_eof_code(sp, KRB5_CC_END); + storage_set_flags(context, sp, FCACHE(id)->version); + + /* Get a copy of what the cred should look like in the file; see below */ + ret = krb5_store_creds(sp, cred); + if (ret) + goto out; + + ret = krb5_storage_to_data(sp, &orig_cred_data); + if (ret) + goto out; + krb5_storage_free(sp); + + cred_data_in_file = malloc(orig_cred_data.length); + if (cred_data_in_file == NULL) + goto out; + + /* + * Mark the cred expired; krb5_cc_retrieve_cred() callers should use + * KRB5_TC_MATCH_TIMES, so this should be good enough... + */ + cred->times.endtime = 0; + + /* ...except for config creds because we don't check their endtimes */ + if (srealm && strcmp(srealm, "X-CACHECONF:") == 0) { + ret = krb5_principal_set_realm(context, cred->server, "X-RMED-CONF:"); + if (ret) + goto out; + } + + sp = krb5_storage_emem(); + if (sp == NULL) + goto out; + krb5_storage_set_eof_code(sp, KRB5_CC_END); + storage_set_flags(context, sp, FCACHE(id)->version); + + ret = krb5_store_creds(sp, cred); + + /* The new cred must be the same size as the old cred */ + new_cred_sz = krb5_storage_seek(sp, 0, SEEK_END); + if (new_cred_sz != orig_cred_data.length || new_cred_sz != + (FCC_CURSOR(*cursor)->cred_end - FCC_CURSOR(*cursor)->cred_start)) { + /* XXX This really can't happen. Assert like above? */ + krb5_set_error_message(context, EINVAL, + N_("Credential deletion failed on ccache " + "FILE:%s: new credential size did not " + "match old credential size", ""), + FILENAME(id)); + goto out; + } + + ret = fcc_open(context, id, "remove_cred", &fd, O_RDWR, 0); + if (ret) + goto out; + + /* + * Check that we're updating the same file where we got the + * cred's offset, else we'd be corrupting a new ccache. + */ + if (fstat(FCC_CURSOR(*cursor)->fd, &sb1) == -1 || + fstat(fd, &sb2) == -1) + goto out; + if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) + goto out; + + /* + * Make sure what we overwrite is what we expected. + * + * FIXME: We *really* need the ccache v4 tag for ccache ID. This + * check that we're only overwriting something that looks exactly + * like what we want to is probably good enough in practice, but + * it's not guaranteed to work. + */ + if (lseek(fd, FCC_CURSOR(*cursor)->cred_start, SEEK_SET) == (off_t)-1) + goto out; + bytes = read(fd, cred_data_in_file, orig_cred_data.length); + if (bytes != orig_cred_data.length) + goto out; + if (memcmp(orig_cred_data.data, cred_data_in_file, bytes) != 0) + goto out; + if (lseek(fd, FCC_CURSOR(*cursor)->cred_start, SEEK_SET) == (off_t)-1) + goto out; + ret = write_storage(context, sp, fd); +out: + if (fd > -1) { + if (close(fd) < 0 && ret == 0) { + krb5_set_error_message(context, errno, N_("close %s", ""), + FILENAME(id)); + } + } + krb5_data_free(&orig_cred_data); + free(cred_data_in_file); + krb5_storage_free(sp); + return; +} + +static krb5_error_code KRB5_CALLCONV +fcc_remove_cred(krb5_context context, + krb5_ccache id, + krb5_flags which, + krb5_creds *mcred) +{ + krb5_error_code ret, ret2; + krb5_cc_cursor cursor; + krb5_creds found_cred; + + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + ret = krb5_cc_start_seq_get(context, id, &cursor); + if (ret) + return ret; + while ((ret = krb5_cc_next_cred(context, id, &cursor, &found_cred)) == 0) { + if (!krb5_compare_creds(context, which, mcred, &found_cred)) { + krb5_free_cred_contents(context, &found_cred); + continue; + } + cred_delete(context, id, &cursor, &found_cred); + krb5_free_cred_contents(context, &found_cred); + } + ret2 = krb5_cc_end_seq_get(context, id, &cursor); + if (ret2) /* not expected to fail */ + return ret2; + if (ret == KRB5_CC_END) + return 0; + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_set_flags(krb5_context context, + krb5_ccache id, + krb5_flags flags) +{ + if (FCACHE(id) == NULL) + return krb5_einval(context, 2); + + return 0; /* XXX */ +} + +static int KRB5_CALLCONV +fcc_get_version(krb5_context context, + krb5_ccache id) +{ + if (FCACHE(id) == NULL) + return -1; + + return FCACHE(id)->version; +} + +static const char * +my_basename(const char *fn) +{ + const char *base, *p; + + if (strncmp(fn, "FILE:", sizeof("FILE:") - 1) == 0) + fn += sizeof("FILE:") - 1; + for (p = base = fn; *p; p++) { +#ifdef WIN32 + if (*p == '/' || *p == '\\') + base = p + 1; +#else + if (*p == '/') + base = p + 1; +#endif + } + return base; +} + +/* We could use an rk_dirname()... */ +static char * +my_dirname(const char *fn) +{ + size_t len, i; + char *dname; + + if (strncmp(fn, "FILE:", sizeof("FILE:") - 1) == 0) + fn += sizeof("FILE:") - 1; + + if ((dname = strdup(fn)) == NULL) + return NULL; + len = strlen(dname); + for (i = 0; i < len; i++) { +#ifdef WIN32 + if (dname[len - i] == '\\' || + dname[len - i] == '/') { + dname[len - i] = '\0'; + break; + } +#else + if (dname[len - i] == '/') { + dname[len - i] = '\0'; + break; + } +#endif + } + if (i < len) + return dname; + free(dname); + return strdup("."); +} + +/* + * This checks that a directory entry matches a required basename and has a + * non-empty subsidiary component. + */ +static int +matchbase(const char *fn, const char *base, size_t baselen) +{ + return strncmp(fn, base, baselen) == 0 && + (fn[baselen] == FILESUBSEPCHR && fn[baselen + 1] != '\0'); +} + +/* + * Check if `def_locs' contains `name' (which must be the default ccache name), + * in which case the caller may look for subsidiaries of all of `def_locs'. + * + * This is needed because the collection iterators don't take a base location + * as an argument, so we can only search default locations, but only if the + * current default ccache name is indeed a default (as opposed to from + * KRB5CCNAME being set in the environment pointing to a non-default name). + */ +static krb5_error_code +is_default_collection(krb5_context context, const char *name, + const char * const *def_locs, int *res) +{ + krb5_error_code ret; + const char *def_loc[2] = { KRB5_DEFAULT_CCNAME_FILE, NULL }; + const char *sep; + size_t namelen; + size_t i; + + *res = 0; + if (name == NULL) { + *res = 1; + return 0; + } + if ((sep = strchr(name, FILESUBSEPCHR))) + namelen = (size_t)(sep - name); + else + namelen = strlen(name); + if (def_locs == NULL) + def_locs = def_loc; + for (i = 0; !(*res) && def_locs[i]; i++) { + char *e = NULL; + + if ((ret = _krb5_expand_default_cc_name(context, def_locs[i], &e))) + return ret; + *res = strncmp(e, name, namelen) == 0 && + (sep == NULL || e[namelen] == FILESUBSEPCHR || e[namelen] == '\0'); + free(e); + } + return 0; +} + +/* + * Collection iterator cursor. + * + * There may be an array of locations, and for each location we'll try + * resolving it, as well as doing a readdir() of the dirname of it and output + * all ccache names in that directory that begin with the current location and + * end in "+${subsidiary}". + */ +struct fcache_iter { + const char *curr_location; + char *def_ccname; /* The default ccname */ + char **locations; /* All the other places we'll look for a ccache */ + char *dname; /* dirname() of curr_location */ + DIR *d; + struct dirent *dentry; + int location; /* Index of `locations' */ + unsigned int first:1; + unsigned int dead:1; +}; + +/* Initiate FILE collection iteration */ +static krb5_error_code KRB5_CALLCONV +fcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) +{ + struct fcache_iter *iter = NULL; + krb5_error_code ret; + const char *def_ccname = NULL; + char **def_locs = NULL; + int is_def_coll = 0; + + if (krb5_config_get_bool_default(context, NULL, FALSE, "libdefaults", + "enable_file_cache_iteration", NULL)) { + def_ccname = krb5_cc_default_name(context); + def_locs = krb5_config_get_strings(context, NULL, "libdefaults", + "default_file_cache_collections", + NULL); + } + + /* + * Note: do not allow krb5_cc_default_name() to recurse via + * krb5_cc_cache_match(). + * Note that context->default_cc_name will be NULL even though + * KRB5CCNAME is set in the environment if neither krb5_cc_default_name() + * nor krb5_cc_set_default_name() have been called. + */ + + /* + * Figure out if the current default ccache name is a really a default one + * so we know whether to search any other default FILE collection + * locations. + */ + if ((ret = is_default_collection(context, def_ccname, + (const char **)def_locs, + &is_def_coll))) + goto out; + + /* Setup the cursor */ + if ((iter = calloc(1, sizeof(*iter))) == NULL || + (def_ccname && (iter->def_ccname = strdup(def_ccname)) == NULL)) { + ret = krb5_enomem(context); + goto out; + } + + if (is_def_coll) { + /* Since def_ccname is in the `def_locs', we'll include those */ + iter->locations = def_locs; + free(iter->def_ccname); + iter->def_ccname = NULL; + def_locs = NULL; + } else { + /* Since def_ccname is NOT in the `def_locs', we'll exclude those */ + iter->locations = NULL; + } + iter->curr_location = NULL; + iter->location = -1; /* Pre-incremented */ + iter->first = 1; + iter->dname = NULL; + iter->d = NULL; + *cursor = iter; + iter = NULL; + ret = 0; + +out: + krb5_config_free_strings(def_locs); + free(iter); + return ret; +} + +/* Pick the next location as the `iter->curr_location' */ +static krb5_error_code +next_location(krb5_context context, struct fcache_iter *iter) +{ + if (iter->first && iter->def_ccname) { + iter->curr_location = iter->def_ccname; + iter->first = 0; + return 0; + } + iter->first = 0; + + if (iter->d) + closedir(iter->d); + iter->d = NULL; + iter->curr_location = NULL; + if (iter->locations && + (iter->curr_location = iter->locations[++(iter->location)])) + return 0; + + iter->dead = 1; /* Do not run off the end of iter->locations */ + return KRB5_CC_END; +} + +/* Output the next match for `iter->curr_location' from readdir() */ +static krb5_error_code +next_dir_match(krb5_context context, struct fcache_iter *iter, char **fn) +{ + struct stat st; + const char *base = my_basename(iter->curr_location); + size_t baselen = strlen(base); + char *s; + + *fn = NULL; + if (iter->d == NULL) + return 0; + for (iter->dentry = readdir(iter->d); + iter->dentry; + iter->dentry = readdir(iter->d)) { + if (!matchbase(iter->dentry->d_name, base, baselen)) + continue; + if (asprintf(&s, "FILE:%s/%s", iter->dname, iter->dentry->d_name) == -1 || + s == NULL) + return krb5_enomem(context); + if (stat(s + sizeof("FILE:") - 1, &st) == 0 && S_ISREG(st.st_mode)) { + *fn = s; + return 0; + } + free(s); + } + iter->curr_location = NULL; + closedir(iter->d); + iter->d = NULL; + return 0; +} + +/* See if the given `ccname' is a FILE ccache we can resolve */ +static krb5_error_code +try1(krb5_context context, const char *ccname, krb5_ccache *id) +{ + krb5_error_code ret; + krb5_ccache cc; + + ret = krb5_cc_resolve(context, ccname, &cc); + if (ret == ENOMEM) + return ret; + if (ret == 0) { + if (strcmp(krb5_cc_get_type(context, cc), "FILE") == 0) { + *id = cc; + cc = NULL; + } + krb5_cc_close(context, cc); + } + return 0; +} + +/* Output the next FILE ccache in the FILE ccache collection */ +static krb5_error_code KRB5_CALLCONV +fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) +{ + struct fcache_iter *iter = cursor; + krb5_error_code ret; + char *name = NULL; + + *id = NULL; + if (iter == NULL) + return krb5_einval(context, 2); + + /* Do not run off the end of iter->locations */ + if (iter->dead) + return KRB5_CC_END; + + if (!iter->curr_location) { + /* Next base location */ + if ((ret = next_location(context, iter))) + return ret; + /* Output the current base location */ + if ((ret = try1(context, iter->curr_location, id)) || *id) + return ret; + } + + /* Look for subsidiaries of iter->curr_location */ + if (!iter->d) { + free(iter->dname); + if ((iter->dname = my_dirname(iter->curr_location)) == NULL) + return krb5_enomem(context); + if ((iter->d = opendir(iter->dname)) == NULL) { + /* Dirname ENOENT -> next location */ + if ((ret = next_location(context, iter))) + return ret; + /* Tail-recurse */ + return fcc_get_cache_next(context, cursor, id); + } + } + for (ret = next_dir_match(context, iter, &name); + ret == 0 && name != NULL; + ret = next_dir_match(context, iter, &name)) { + if ((ret = try1(context, name, id)) || *id) { + free(name); + return ret; + } + free(name); + } + + /* Directory listing exhausted -> go to next location, tail-recurse */ + if ((ret = next_location(context, iter))) + return ret; + return fcc_get_cache_next(context, cursor, id); +} + +static krb5_error_code KRB5_CALLCONV +fcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) +{ + struct fcache_iter *iter = cursor; + + if (iter == NULL) + return krb5_einval(context, 2); + + krb5_config_free_strings(iter->locations); + if (iter->d) + closedir(iter->d); + free(iter->def_ccname); + free(iter->dname); + free(iter); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to) +{ + krb5_error_code ret = 0; + krb5_fcache *f = FCACHE(from); + krb5_fcache *t = FCACHE(to); + + if (f->tmpfn) { + /* + * If `from' has a temp file and we haven't renamed it into place yet, + * then we should rename TMPFILENAME(from) to FILENAME(to). + * + * This can only happen if we're moving a ccache where only cc config + * entries, or no entries, have been written. That's not likely. + */ + if (rk_rename(f->tmpfn, t->filename)) { + ret = errno; + } else { + free(f->tmpfn); + f->tmpfn = NULL; + } + } else if (rk_rename(f->filename, t->filename)) { + ret = errno; + } + /* + * We need only close from -- we can't destroy it since the rename + * succeeded, which "destroyed" it at its old name. + */ + if (ret == 0) + krb5_cc_close(context, from); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_get_default_name(krb5_context context, char **str) +{ + return _krb5_expand_default_cc_name(context, + KRB5_DEFAULT_CCNAME_FILE, + str); +} + +static krb5_error_code KRB5_CALLCONV +fcc_set_default_cache(krb5_context context, krb5_ccache id) +{ + krb5_error_code ret; + krb5_ccache dest; + char *s = NULL; + + if (SUBFILENAME(id) == NULL) + return 0; /* Already a primary */ + if (asprintf(&s, "FILE:%s", RESFILENAME(id)) == -1 || s == NULL) + return krb5_enomem(context); + + /* + * We can't hard-link, since we refuse to open ccaches with st_nlink > 1, + * and we can't rename() the ccache because the old name should remain + * available. Ergo, we copy the ccache. + */ + ret = krb5_cc_resolve(context, s, &dest); + if (ret == 0) + ret = krb5_cc_copy_cache(context, id, dest); + free(s); + if (ret) + krb5_set_error_message(context, ret, + N_("Failed to copy subsidiary cache file %s to " + "default %s", ""), FILENAME(id), + RESFILENAME(id)); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) +{ + krb5_error_code ret; + struct stat sb; + int fd; + + ret = fcc_open(context, id, "lastchange", &fd, O_RDONLY, 0); + if(ret) + return ret; + ret = fstat(fd, &sb); + close(fd); + if (ret) { + ret = errno; + krb5_set_error_message(context, ret, N_("Failed to stat cache file", "")); + return ret; + } + *mtime = sb.st_mtime; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) +{ + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) +{ + krb5_error_code ret; + krb5_storage *sp = NULL; + int fd; + ret = init_fcc(context, id, "get-kdc-offset", &sp, &fd, kdc_offset); + if (sp) + krb5_storage_free(sp); + close(fd); + + return ret; +} + + +/** + * Variable containing the FILE based credential cache implemention. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops = { + KRB5_CC_OPS_VERSION_5, + "FILE", + NULL, + NULL, + fcc_gen_new, + fcc_initialize, + fcc_destroy, + fcc_close, + fcc_store_cred, + NULL, /* fcc_retrieve */ + fcc_get_principal, + fcc_get_first, + fcc_get_next, + fcc_end_get, + fcc_remove_cred, + fcc_set_flags, + fcc_get_version, + fcc_get_cache_first, + fcc_get_cache_next, + fcc_end_cache_get, + fcc_move, + fcc_get_default_name, + fcc_set_default_cache, + fcc_lastchange, + fcc_set_kdc_offset, + fcc_get_kdc_offset, + fcc_get_name_2, + fcc_resolve_2 +}; diff --git a/third_party/heimdal/lib/krb5/free.c b/third_party/heimdal/lib/krb5/free.c new file mode 100644 index 0000000..5bb33b4 --- /dev/null +++ b/third_party/heimdal/lib/krb5/free.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1997 - 1999, 2004 - 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 "krb5_locl.h" + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_kdc_rep(krb5_context context, krb5_kdc_rep *rep) +{ + free_KDC_REP(&rep->kdc_rep); + free_EncTGSRepPart(&rep->enc_part); + free_KRB_ERROR(&rep->error); + memset(rep, 0, sizeof(*rep)); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_xfree (void *ptr) +{ + free (ptr); + return 0; +} diff --git a/third_party/heimdal/lib/krb5/free_host_realm.c b/third_party/heimdal/lib/krb5/free_host_realm.c new file mode 100644 index 0000000..0932674 --- /dev/null +++ b/third_party/heimdal/lib/krb5/free_host_realm.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1997, 1999 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/** + * Free all memory allocated by `realmlist' + * + * @param context A Kerberos 5 context. + * @param realmlist realmlist to free, NULL is ok + * + * @return a Kerberos error code, always 0. + * + * @ingroup krb5_support + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_free_host_realm(krb5_context context, + krb5_realm *realmlist) +{ + krb5_realm *p; + + if(realmlist == NULL) + return 0; + for (p = realmlist; *p; ++p) + free (*p); + free (realmlist); + return 0; +} diff --git a/third_party/heimdal/lib/krb5/generate_seq_number.c b/third_party/heimdal/lib/krb5/generate_seq_number.c new file mode 100644 index 0000000..6001d69 --- /dev/null +++ b/third_party/heimdal/lib/krb5/generate_seq_number.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_generate_seq_number(krb5_context context, + const krb5_keyblock *key, + uint32_t *seqno) +{ + if (RAND_bytes((void *)seqno, sizeof(*seqno)) <= 0) + krb5_abortx(context, "Failed to generate random block"); + /* MIT used signed numbers, lets not stomp into that space directly */ + *seqno &= 0x3fffffff; + if (*seqno == 0) + *seqno = 1; + return 0; +} diff --git a/third_party/heimdal/lib/krb5/generate_subkey.c b/third_party/heimdal/lib/krb5/generate_subkey.c new file mode 100644 index 0000000..767d94c --- /dev/null +++ b/third_party/heimdal/lib/krb5/generate_subkey.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/** + * Generate subkey, from keyblock + * + * @param context kerberos context + * @param key session key + * @param etype encryption type of subkey, if ETYPE_NULL, use key's enctype + * @param subkey returned new, free with krb5_free_keyblock(). + * + * @return 0 on success or a Kerberos 5 error code + * +* @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_generate_subkey_extended(krb5_context context, + const krb5_keyblock *key, + krb5_enctype etype, + krb5_keyblock **subkey) +{ + krb5_error_code ret; + + ALLOC(*subkey, 1); + if (*subkey == NULL) + return krb5_enomem(context); + + if (etype == ETYPE_NULL) + etype = key->keytype; /* use session key etype */ + + /* XXX should we use the session key as input to the RF? */ + ret = krb5_generate_random_keyblock(context, etype, *subkey); + if (ret != 0) { + free(*subkey); + *subkey = NULL; + } + + return ret; +} + diff --git a/third_party/heimdal/lib/krb5/get_addrs.c b/third_party/heimdal/lib/krb5/get_addrs.c new file mode 100644 index 0000000..8246504 --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_addrs.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#ifdef __osf__ +/* hate */ +struct rtentry; +struct mbuf; +#endif +#ifdef HAVE_NET_IF_H +#include +#endif +#include + +static krb5_error_code +gethostname_fallback (krb5_context context, krb5_addresses *res) +{ + krb5_error_code ret; + char hostname[MAXHOSTNAMELEN]; + struct hostent *hostent; + + if (gethostname (hostname, sizeof(hostname))) { + ret = errno; + krb5_set_error_message(context, ret, "gethostname: %s", strerror(ret)); + return ret; + } + hostent = roken_gethostbyname (hostname); + if (hostent == NULL) { + ret = errno; + krb5_set_error_message (context, ret, "gethostbyname %s: %s", + hostname, strerror(ret)); + return ret; + } + res->len = 1; + res->val = malloc (sizeof(*res->val)); + if (res->val == NULL) + return krb5_enomem(context); + res->val[0].addr_type = hostent->h_addrtype; + res->val[0].address.data = NULL; + res->val[0].address.length = 0; + ret = krb5_data_copy (&res->val[0].address, + hostent->h_addr, + hostent->h_length); + if (ret) { + free (res->val); + return ret; + } + return 0; +} + +enum { + LOOP = 1, /* do include loopback addrs */ + LOOP_IF_NONE = 2, /* include loopback addrs if no others */ + EXTRA_ADDRESSES = 4, /* include extra addresses */ + SCAN_INTERFACES = 8 /* scan interfaces for addresses */ +}; + +/* + * Try to figure out the addresses of all configured interfaces with a + * lot of magic ioctls. + */ + +static krb5_error_code +find_all_addresses (krb5_context context, krb5_addresses *res, int flags) +{ + struct sockaddr sa_zero; + struct ifaddrs *ifa0, *ifa; + krb5_error_code ret = ENXIO; + unsigned int num, idx; + krb5_addresses ignore_addresses; + + if (getifaddrs(&ifa0) == -1) { + ret = errno; + krb5_set_error_message(context, ret, "getifaddrs: %s", strerror(ret)); + return (ret); + } + + memset(&sa_zero, 0, sizeof(sa_zero)); + + /* First, count all the ifaddrs. */ + for (ifa = ifa0, num = 0; ifa != NULL; ifa = ifa->ifa_next, num++) + /* nothing */; + + if (num == 0) { + freeifaddrs(ifa0); + krb5_set_error_message(context, ENXIO, N_("no addresses found", "")); + return (ENXIO); + } + + if (flags & EXTRA_ADDRESSES) { + /* we'll remove the addresses we don't care about */ + ret = krb5_get_ignore_addresses(context, &ignore_addresses); + if(ret) + return ret; + } + + /* Allocate storage for them. */ + res->val = calloc(num, sizeof(*res->val)); + if (res->val == NULL) { + if (flags & EXTRA_ADDRESSES) + krb5_free_addresses(context, &ignore_addresses); + freeifaddrs(ifa0); + return krb5_enomem(context); + } + + /* Now traverse the list. */ + for (ifa = ifa0, idx = 0; ifa != NULL; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_UP) == 0) + continue; + if (ifa->ifa_addr == NULL) + continue; + if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0) + continue; + if (krb5_sockaddr_uninteresting(ifa->ifa_addr)) + continue; + if (krb5_sockaddr_is_loopback(ifa->ifa_addr) && (flags & LOOP) == 0) + /* We'll deal with the LOOP_IF_NONE case later. */ + continue; + + ret = krb5_sockaddr2address(context, ifa->ifa_addr, &res->val[idx]); + if (ret) { + /* + * The most likely error here is going to be "Program + * lacks support for address type". This is no big + * deal -- just continue, and we'll listen on the + * addresses who's type we *do* support. + */ + continue; + } + /* possibly skip this address? */ + if((flags & EXTRA_ADDRESSES) && + krb5_address_search(context, &res->val[idx], &ignore_addresses)) { + krb5_free_address(context, &res->val[idx]); + flags &= ~LOOP_IF_NONE; /* we actually found an address, + so don't add any loop-back + addresses */ + continue; + } + + idx++; + } + + /* + * If no addresses were found, and LOOP_IF_NONE is set, then find + * the loopback addresses and add them to our list. + */ + if ((flags & LOOP_IF_NONE) != 0 && idx == 0) { + for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { + if ((ifa->ifa_flags & IFF_UP) == 0) + continue; + if (ifa->ifa_addr == NULL) + continue; + if (memcmp(ifa->ifa_addr, &sa_zero, sizeof(sa_zero)) == 0) + continue; + if (krb5_sockaddr_uninteresting(ifa->ifa_addr)) + continue; + if (!krb5_sockaddr_is_loopback(ifa->ifa_addr)) + continue; + if ((ifa->ifa_flags & IFF_LOOPBACK) == 0) + /* Presumably loopback addrs are only used on loopback ifs! */ + continue; + ret = krb5_sockaddr2address(context, + ifa->ifa_addr, &res->val[idx]); + if (ret) + continue; /* We don't consider this failure fatal */ + if((flags & EXTRA_ADDRESSES) && + krb5_address_search(context, &res->val[idx], + &ignore_addresses)) { + krb5_free_address(context, &res->val[idx]); + continue; + } + idx++; + } + } + + if (flags & EXTRA_ADDRESSES) + krb5_free_addresses(context, &ignore_addresses); + freeifaddrs(ifa0); + if (ret) { + free(res->val); + res->val = NULL; + } else + res->len = idx; /* Now a count. */ + return (ret); +} + +static krb5_error_code +get_addrs_int (krb5_context context, krb5_addresses *res, int flags) +{ + krb5_error_code ret = -1; + + res->len = 0; + res->val = NULL; + + if (flags & SCAN_INTERFACES) { + ret = find_all_addresses (context, res, flags); + if(ret || res->len == 0) + ret = gethostname_fallback (context, res); + } else { + ret = 0; + } + + if(ret == 0 && (flags & EXTRA_ADDRESSES)) { + krb5_addresses a; + /* append user specified addresses */ + ret = krb5_get_extra_addresses(context, &a); + if(ret) { + krb5_free_addresses(context, res); + return ret; + } + ret = krb5_append_addresses(context, res, &a); + if(ret) { + krb5_free_addresses(context, res); + return ret; + } + krb5_free_addresses(context, &a); + } + if(res->len == 0) { + free(res->val); + res->val = NULL; + } + return ret; +} + +/* + * Try to get all addresses, but return the one corresponding to + * `hostname' if we fail. + * + * Only include loopback address if there are no other. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_all_client_addrs (krb5_context context, krb5_addresses *res) +{ + int flags = LOOP_IF_NONE | EXTRA_ADDRESSES; + + if (context->scan_interfaces) + flags |= SCAN_INTERFACES; + + return get_addrs_int (context, res, flags); +} + +/* + * Try to get all local addresses that a server should listen to. + * If that fails, we return the address corresponding to `hostname'. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_all_server_addrs (krb5_context context, krb5_addresses *res) +{ + return get_addrs_int (context, res, LOOP | SCAN_INTERFACES); +} diff --git a/third_party/heimdal/lib/krb5/get_cred.c b/third_party/heimdal/lib/krb5/get_cred.c new file mode 100644 index 0000000..ff06325 --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_cred.c @@ -0,0 +1,2044 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 - 2010 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 "krb5_locl.h" +#include + +static krb5_error_code +get_cred_kdc_capath(krb5_context, krb5_kdc_flags, + krb5_ccache, struct krb5_fast_state *, + krb5_creds *, krb5_principal, + Ticket *, const char *, const char *, + krb5_creds **, krb5_creds ***); + +/* + * Take the `body' and encode it into `padata' using the credentials + * in `creds'. + */ + +static krb5_error_code +make_pa_tgs_req(krb5_context context, + krb5_auth_context *ac, + KDC_REQ_BODY *body, + krb5_ccache ccache, + krb5_creds *creds, + krb5_data *tgs_req) +{ + krb5_error_code ret; + krb5_data in_data; + size_t buf_size; + size_t len = 0; + uint8_t *buf; + + ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, buf_size, body, &len, ret); + if (ret) + return ret; + + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + in_data.length = len; + in_data.data = buf; + ret = _krb5_mk_req_internal(context, ac, 0, &in_data, + creds, tgs_req, + KRB5_KU_TGS_REQ_AUTH_CKSUM, + KRB5_KU_TGS_REQ_AUTH); + free (buf); + return ret; +} + +/* + * Set the `enc-authorization-data' in `req_body' based on `authdata' + */ + +static krb5_error_code +set_auth_data (krb5_context context, + KDC_REQ_BODY *req_body, + krb5_authdata *authdata, + krb5_keyblock *subkey) +{ + if(authdata->len) { + size_t len = 0, buf_size; + unsigned char *buf; + krb5_crypto crypto; + krb5_error_code ret; + + ASN1_MALLOC_ENCODE(AuthorizationData, buf, buf_size, authdata, + &len, ret); + if (ret) + return ret; + if (buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ALLOC(req_body->enc_authorization_data, 1); + if (req_body->enc_authorization_data == NULL) { + free (buf); + return krb5_enomem(context); + } + ret = krb5_crypto_init(context, subkey, 0, &crypto); + if (ret) { + free (buf); + free (req_body->enc_authorization_data); + req_body->enc_authorization_data = NULL; + return ret; + } + ret = krb5_encrypt_EncryptedData(context, + crypto, + KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY, + buf, + len, + 0, + req_body->enc_authorization_data); + free (buf); + krb5_crypto_destroy(context, crypto); + return ret; + } else { + req_body->enc_authorization_data = NULL; + return 0; + } +} + +/* + * Create a tgs-req in `t' with `addresses', `flags', `second_ticket' + * (if not-NULL), `in_creds', `krbtgt', and returning the generated + * subkey in `subkey'. + */ + +static krb5_error_code +init_tgs_req (krb5_context context, + krb5_ccache ccache, + struct krb5_fast_state *state, + krb5_addresses *addresses, + krb5_kdc_flags flags, + Ticket *second_ticket, + krb5_creds *in_creds, + krb5_creds *krbtgt, + unsigned nonce, + const METHOD_DATA *padata, + krb5_keyblock **subkey, + TGS_REQ *t) +{ + krb5_auth_context ac = NULL; + krb5_error_code ret = 0; + krb5_data tgs_req; + + krb5_data_zero(&tgs_req); + memset(t, 0, sizeof(*t)); + + t->pvno = 5; + t->msg_type = krb_tgs_req; + if (in_creds->session.keytype) { + ALLOC_SEQ(&t->req_body.etype, 1); + if(t->req_body.etype.val == NULL) { + ret = krb5_enomem(context); + goto fail; + } + t->req_body.etype.val[0] = in_creds->session.keytype; + } else { + ret = _krb5_init_etype(context, + KRB5_PDU_TGS_REQUEST, + &t->req_body.etype.len, + &t->req_body.etype.val, + NULL); + } + if (ret) + goto fail; + t->req_body.addresses = addresses; + t->req_body.kdc_options = flags.b; + t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable; + t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable; + t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable; + ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm); + if (ret) + goto fail; + ALLOC(t->req_body.sname, 1); + if (t->req_body.sname == NULL) { + ret = krb5_enomem(context); + goto fail; + } + + /* some versions of some code might require that the client be + present in TGS-REQs, but this is clearly against the spec */ + + ret = copy_PrincipalName(&in_creds->server->name, t->req_body.sname); + if (ret) + goto fail; + + if (krbtgt->times.starttime) { + ALLOC(t->req_body.from, 1); + if(t->req_body.from == NULL){ + ret = krb5_enomem(context); + goto fail; + } + *t->req_body.from = in_creds->times.starttime; + } + + /* req_body.till should be NULL if there is no endtime specified, + but old MIT code (like DCE secd) doesn't like that */ + ALLOC(t->req_body.till, 1); + if(t->req_body.till == NULL){ + ret = krb5_enomem(context); + goto fail; + } + *t->req_body.till = in_creds->times.endtime; + + if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) { + ALLOC(t->req_body.rtime, 1); + if(t->req_body.rtime == NULL){ + ret = krb5_enomem(context); + goto fail; + } + *t->req_body.rtime = in_creds->times.renew_till; + } + + t->req_body.nonce = nonce; + if(second_ticket){ + ALLOC(t->req_body.additional_tickets, 1); + if (t->req_body.additional_tickets == NULL) { + ret = krb5_enomem(context); + goto fail; + } + ALLOC_SEQ(t->req_body.additional_tickets, 1); + if (t->req_body.additional_tickets->val == NULL) { + ret = krb5_enomem(context); + goto fail; + } + ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val); + if (ret) + goto fail; + } + + ret = krb5_auth_con_init(context, &ac); + if(ret) + goto fail; + + ret = krb5_auth_con_generatelocalsubkey(context, ac, &krbtgt->session); + if (ret) + goto fail; + + if (state) { + krb5_data empty; + + krb5_data_zero(&empty); + ret = krb5_auth_con_add_AuthorizationData(context, ac, + KRB5_AUTHDATA_FX_FAST_USED, + &empty); + if (ret) + goto fail; + } + + ret = set_auth_data(context, &t->req_body, + &in_creds->authdata, ac->local_subkey); + if (ret) + goto fail; + + ret = make_pa_tgs_req(context, + &ac, + &t->req_body, + ccache, + krbtgt, + &tgs_req); + if(ret) + goto fail; + + /* + * Add KRB5_PADATA_TGS_REQ first + * followed by all others. + */ + + if (t->padata == NULL) { + ALLOC(t->padata, 1); + if (t->padata == NULL) { + ret = krb5_enomem(context); + goto fail; + } + } + + ret = krb5_padata_add(context, t->padata, KRB5_PADATA_TGS_REQ, + tgs_req.data, tgs_req.length); + if (ret) + goto fail; + + krb5_data_zero(&tgs_req); + + { + size_t i; + for (i = 0; i < padata->len; i++) { + const PA_DATA *val1 = &padata->val[i]; + PA_DATA val2; + + ret = copy_PA_DATA(val1, &val2); + if (ret) { + krb5_set_error_message(context, ret, + N_("malloc: out of memory", "")); + goto fail; + } + + ret = krb5_padata_add(context, t->padata, + val2.padata_type, + val2.padata_value.data, + val2.padata_value.length); + if (ret) { + free_PA_DATA(&val2); + + krb5_set_error_message(context, ret, + N_("malloc: out of memory", "")); + goto fail; + } + } + } + + if (state) { + state->armor_ac = ac; + ret = _krb5_fast_create_armor(context, state, NULL); + state->armor_ac = NULL; + if (ret) + goto fail; + + ret = _krb5_fast_wrap_req(context, state, t); + if (ret) + goto fail; + + /* Its ok if there is no fast in the TGS-REP, older heimdal only support it in the AS code path */ + state->flags &= ~KRB5_FAST_EXPECTED; + } + + ret = krb5_auth_con_getlocalsubkey(context, ac, subkey); + if (ret) + goto fail; + +fail: + if (ac) + krb5_auth_con_free(context, ac); + if (ret) { + t->req_body.addresses = NULL; + free_TGS_REQ (t); + } + krb5_data_free(&tgs_req); + + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_get_krbtgt(krb5_context context, + krb5_ccache id, + krb5_realm realm, + krb5_creds **cred) +{ + krb5_error_code ret; + krb5_creds tmp_cred; + + memset(&tmp_cred, 0, sizeof(tmp_cred)); + + ret = krb5_cc_get_principal(context, id, &tmp_cred.client); + if (ret) + return ret; + + if (realm == NULL) + realm = tmp_cred.client->realm; + + ret = krb5_make_principal(context, + &tmp_cred.server, + realm, + KRB5_TGS_NAME, + realm, + NULL); + if(ret) { + krb5_free_principal(context, tmp_cred.client); + return ret; + } + /* + * The forwardable TGT might not be the start TGT, in which case, it is + * generally, but not always already cached. Just in case, get it again if + * lost. + */ + ret = krb5_get_credentials(context, + 0, + id, + &tmp_cred, + cred); + krb5_free_principal(context, tmp_cred.client); + krb5_free_principal(context, tmp_cred.server); + if(ret) + return ret; + return 0; +} + +static krb5_error_code +fast_tgs_strengthen_key(krb5_context context, + struct krb5_fast_state *state, + krb5_keyblock *reply_key, + krb5_keyblock *extract_key) +{ + krb5_error_code ret; + + if (state && state->strengthen_key) { + _krb5_debug(context, 5, "_krb5_fast_tgs_strengthen_key"); + + if (state->strengthen_key->keytype != reply_key->keytype) { + krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, + N_("strengthen_key %d not same enctype as reply key %d", ""), + state->strengthen_key->keytype, reply_key->keytype); + return KRB5KRB_AP_ERR_MODIFIED; + } + + ret = _krb5_fast_cf2(context, + state->strengthen_key, + "strengthenkey", + reply_key, + "replykey", + extract_key, + NULL); + if (ret) + return ret; + } else { + ret = krb5_copy_keyblock_contents(context, reply_key, extract_key); + if (ret) + return ret; + } + + return 0; +} + +/* DCE compatible decrypt proc */ +static krb5_error_code KRB5_CALLCONV +decrypt_tkt_with_subkey (krb5_context context, + krb5_keyblock *key, + krb5_key_usage usage, + krb5_const_pointer skey, + krb5_kdc_rep *dec_rep) +{ + struct krb5_decrypt_tkt_with_subkey_state *state; + krb5_error_code ret = 0; + krb5_data data; + size_t size; + krb5_crypto crypto; + krb5_keyblock extract_key; + + state = (struct krb5_decrypt_tkt_with_subkey_state *)skey; + + assert(usage == 0); + + krb5_data_zero(&data); + + /* + * start out with trying with subkey if we have one + */ + if (state->subkey) { + ret = fast_tgs_strengthen_key(context, state->fast_state, + state->subkey, &extract_key); + if (ret) + return ret; + + ret = krb5_crypto_init(context, &extract_key, 0, &crypto); + krb5_free_keyblock_contents(context, &extract_key); + if (ret) + return ret; + ret = krb5_decrypt_EncryptedData (context, + crypto, + KRB5_KU_TGS_REP_ENC_PART_SUB_KEY, + &dec_rep->kdc_rep.enc_part, + &data); + /* + * If the is Windows 2000 DC, we need to retry with key usage + * 8 when doing ARCFOUR. + */ + if (ret && state->subkey->keytype == ETYPE_ARCFOUR_HMAC_MD5) { + ret = krb5_decrypt_EncryptedData(context, + crypto, + 8, + &dec_rep->kdc_rep.enc_part, + &data); + } + krb5_crypto_destroy(context, crypto); + } + if (state->subkey == NULL || ret) { + ret = fast_tgs_strengthen_key(context, state->fast_state, key, &extract_key); + if (ret) + return ret; + + ret = krb5_crypto_init(context, key, 0, &crypto); + if (ret) + return ret; + ret = krb5_decrypt_EncryptedData (context, + crypto, + KRB5_KU_TGS_REP_ENC_PART_SESSION, + &dec_rep->kdc_rep.enc_part, + &data); + krb5_crypto_destroy(context, crypto); + } + if (ret) + return ret; + + ret = decode_EncASRepPart(data.data, + data.length, + &dec_rep->enc_part, + &size); + if (ret) + ret = decode_EncTGSRepPart(data.data, + data.length, + &dec_rep->enc_part, + &size); + if (ret) + krb5_set_error_message(context, ret, + N_("Failed to decode encpart in ticket", "")); + krb5_data_free (&data); + return ret; +} + +static krb5_error_code +get_cred_kdc(krb5_context context, + krb5_ccache id, + struct krb5_fast_state *fast_state, + krb5_kdc_flags flags, + krb5_addresses *addresses, + krb5_creds *in_creds, + krb5_creds *krbtgt, + krb5_principal impersonate_principal, + Ticket *second_ticket, + const char *kdc_hostname, + const char *sitename, + krb5_creds *out_creds) +{ + TGS_REQ req; + krb5_data enc; + krb5_data resp; + krb5_kdc_rep rep; + krb5_error_code ret; + unsigned nonce; + krb5_keyblock *subkey = NULL; + size_t len = 0; + Ticket second_ticket_data; + METHOD_DATA padata; + + memset(&rep, 0, sizeof(rep)); + krb5_data_zero(&resp); + krb5_data_zero(&enc); + padata.val = NULL; + padata.len = 0; + + krb5_generate_random_block(&nonce, sizeof(nonce)); + nonce &= 0xffffffff; + + if(flags.b.enc_tkt_in_skey && second_ticket == NULL){ + ret = decode_Ticket(in_creds->second_ticket.data, + in_creds->second_ticket.length, + &second_ticket_data, &len); + if(ret) + return ret; + second_ticket = &second_ticket_data; + } + + + if (impersonate_principal) { + krb5_crypto crypto; + PA_S4U2Self self; + krb5_data data; + void *buf; + size_t size = 0; + + self.name = impersonate_principal->name; + self.realm = impersonate_principal->realm; + self.auth = estrdup("Kerberos"); + + ret = _krb5_s4u2self_to_checksumdata(context, &self, &data); + if (ret) { + free(self.auth); + goto out; + } + + ret = krb5_crypto_init(context, &krbtgt->session, 0, &crypto); + if (ret) { + free(self.auth); + krb5_data_free(&data); + goto out; + } + + ret = krb5_create_checksum(context, + crypto, + KRB5_KU_OTHER_CKSUM, + 0, + data.data, + data.length, + &self.cksum); + krb5_crypto_destroy(context, crypto); + krb5_data_free(&data); + if (ret) { + free(self.auth); + goto out; + } + + ASN1_MALLOC_ENCODE(PA_S4U2Self, buf, len, &self, &size, ret); + free(self.auth); + free_Checksum(&self.cksum); + if (ret) + goto out; + if (len != size) + krb5_abortx(context, "internal asn1 error"); + + ret = krb5_padata_add(context, &padata, KRB5_PADATA_FOR_USER, buf, len); + if (ret) + goto out; + } + + ret = init_tgs_req (context, + id, + fast_state, + addresses, + flags, + second_ticket, + in_creds, + krbtgt, + nonce, + &padata, + &subkey, + &req); + if (ret) + goto out; + + ASN1_MALLOC_ENCODE(TGS_REQ, enc.data, enc.length, &req, &len, ret); + if (ret) + goto out; + if(enc.length != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + /* don't free addresses */ + req.req_body.addresses = NULL; + free_TGS_REQ(&req); + + /* + * Send and receive + */ + { + krb5_sendto_ctx stctx; + ret = krb5_sendto_ctx_alloc(context, &stctx); + if (ret) + return ret; + krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); + + if (kdc_hostname) + krb5_sendto_set_hostname(context, stctx, kdc_hostname); + if (sitename) + krb5_sendto_set_sitename(context, stctx, sitename); + + ret = krb5_sendto_context (context, stctx, &enc, + krbtgt->server->name.name_string.val[1], + &resp); + krb5_sendto_ctx_free(context, stctx); + } + if(ret) + goto out; + + if(decode_TGS_REP(resp.data, resp.length, &rep.kdc_rep, &len) == 0) { + struct krb5_decrypt_tkt_with_subkey_state state; + unsigned eflags = 0; + krb5_data data; + size_t size; + + ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, + &rep.kdc_rep.ticket, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + ret = _krb5_fast_unwrap_kdc_rep(context, nonce, &data, + fast_state, &rep.kdc_rep); + krb5_data_free(&data); + if (ret) + goto out; + + ret = krb5_copy_principal(context, + in_creds->client, + &out_creds->client); + if(ret) + goto out; + ret = krb5_copy_principal(context, + in_creds->server, + &out_creds->server); + if(ret) + goto out; + /* this should go someplace else */ + out_creds->times.endtime = in_creds->times.endtime; + + /* XXX should do better testing */ + if (flags.b.cname_in_addl_tkt || impersonate_principal) + eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; + if (flags.b.request_anonymous) + eflags |= EXTRACT_TICKET_MATCH_ANON; + + state.subkey = subkey; + state.fast_state = fast_state; + + ret = _krb5_extract_ticket(context, + &rep, + out_creds, + &krbtgt->session, + NULL, + 0, + &krbtgt->addresses, + nonce, + eflags, + NULL, + decrypt_tkt_with_subkey, + &state); + } else if(krb5_rd_error(context, &resp, &rep.error) == 0) { + METHOD_DATA md; + + memset(&md, 0, sizeof(md)); + + if (rep.error.e_data) { + KERB_ERROR_DATA kerb_error_data; + + memset(&kerb_error_data, 0, sizeof(kerb_error_data)); + + /* First try to decode the e-data as KERB-ERROR-DATA. */ + ret = decode_KERB_ERROR_DATA(rep.error.e_data->data, + rep.error.e_data->length, + &kerb_error_data, + &len); + if (ret) { + /* That failed, so try to decode it as METHOD-DATA. */ + ret = decode_METHOD_DATA(rep.error.e_data->data, + rep.error.e_data->length, + &md, NULL); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to decode METHOD-DATA", "")); + goto out; + } + } else if (len != rep.error.e_data->length) { + /* Trailing data — just ignore the error. */ + free_KERB_ERROR_DATA(&kerb_error_data); + } else { + /* OK. */ + free_KERB_ERROR_DATA(&kerb_error_data); + } + } + + ret = _krb5_fast_unwrap_error(context, nonce, fast_state, &md, &rep.error); + free_METHOD_DATA(&md); + if (ret) + goto out; + + ret = krb5_error_from_rd_error(context, &rep.error, in_creds); + + /* log the failure */ + if (_krb5_have_debug(context, 5)) { + const char *str = krb5_get_error_message(context, ret); + _krb5_debug(context, 5, "parse_tgs_rep: KRB-ERROR %d/%s", ret, str); + krb5_free_error_message(context, str); + } + } else if(resp.length > 0 && ((char*)resp.data)[0] == 4) { + ret = KRB5KRB_AP_ERR_V4_REPLY; + krb5_clear_error_message(context); + } else { + ret = KRB5KRB_AP_ERR_MSG_TYPE; + krb5_clear_error_message(context); + } + +out: + krb5_free_kdc_rep(context, &rep); + if (second_ticket == &second_ticket_data) + free_Ticket(&second_ticket_data); + free_METHOD_DATA(&padata); + krb5_data_free(&resp); + krb5_data_free(&enc); + if(subkey) + krb5_free_keyblock(context, subkey); + return ret; + +} + +/* + * same as above, just get local addresses first if the krbtgt have + * them and the realm is not addressless + */ + +static krb5_error_code +get_cred_kdc_address(krb5_context context, + krb5_ccache id, + struct krb5_fast_state *fast_state, + krb5_kdc_flags flags, + krb5_addresses *addrs, + krb5_creds *in_creds, + krb5_creds *krbtgt, + krb5_principal impersonate_principal, + Ticket *second_ticket, + const char *kdc_hostname, + const char *sitename, + krb5_creds *out_creds) +{ + krb5_error_code ret; + krb5_addresses addresses = { 0, NULL }; + + /* + * Inherit the address-ness of the krbtgt if the address is not + * specified. + */ + + if (addrs == NULL && krbtgt->addresses.len != 0) { + krb5_boolean noaddr; + + krb5_appdefault_boolean(context, NULL, krbtgt->server->realm, + "no-addresses", FALSE, &noaddr); + + if (!noaddr) { + ret = krb5_get_all_client_addrs(context, &addresses); + if (ret) + return ret; + /* XXX this sucks. */ + addrs = &addresses; + if(addresses.len == 0) + addrs = NULL; + } + } + ret = get_cred_kdc(context, id, fast_state, flags, addrs, + in_creds, krbtgt, impersonate_principal, + second_ticket, kdc_hostname, sitename, out_creds); + krb5_free_addresses(context, &addresses); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_kdc_cred(krb5_context context, + krb5_ccache id, + krb5_kdc_flags flags, + krb5_addresses *addresses, + Ticket *second_ticket, + krb5_creds *in_creds, + krb5_creds **out_creds + ) +{ + krb5_error_code ret; + krb5_creds *krbtgt; + struct krb5_fast_state fast_state; + + memset(&fast_state, 0, sizeof(fast_state)); + + *out_creds = calloc(1, sizeof(**out_creds)); + if(*out_creds == NULL) + return krb5_enomem(context); + ret = _krb5_get_krbtgt (context, + id, + in_creds->server->realm, + &krbtgt); + if(ret) { + free(*out_creds); + *out_creds = NULL; + return ret; + } + ret = get_cred_kdc(context, id, &fast_state, flags, + addresses, in_creds, krbtgt, + NULL, NULL, NULL, NULL, *out_creds); + krb5_free_creds (context, krbtgt); + _krb5_fast_free(context, &fast_state); + if(ret) { + free(*out_creds); + *out_creds = NULL; + } + return ret; +} + +static int +not_found(krb5_context context, krb5_const_principal p, krb5_error_code code) +{ + krb5_error_code ret; + char *str; + const char *err; + + ret = krb5_unparse_name(context, p, &str); + if(ret) { + krb5_clear_error_message(context); + return code; + } + err = krb5_get_error_message(context, code); + krb5_set_error_message(context, code, N_("%s (%s)", ""), err, str); + krb5_free_error_message(context, err); + free(str); + return code; +} + +static krb5_error_code +find_cred(krb5_context context, + krb5_ccache id, + krb5_principal server, + krb5_creds **tgts, + krb5_creds *out_creds) +{ + krb5_error_code ret; + krb5_creds mcreds; + + krb5_cc_clear_mcred(&mcreds); + mcreds.server = server; + krb5_timeofday(context, &mcreds.times.endtime); + ret = krb5_cc_retrieve_cred(context, id, + KRB5_TC_DONT_MATCH_REALM | + KRB5_TC_MATCH_TIMES, + &mcreds, out_creds); + if(ret == 0) + return 0; + while(tgts && *tgts){ + if(krb5_compare_creds(context, KRB5_TC_DONT_MATCH_REALM, + &mcreds, *tgts)){ + ret = krb5_copy_creds_contents(context, *tgts, out_creds); + return ret; + } + tgts++; + } + return not_found(context, server, KRB5_CC_NOTFOUND); +} + +static krb5_error_code +add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts) +{ + int i; + krb5_error_code ret; + krb5_creds **tmp = *tgts; + + for(i = 0; tmp && tmp[i]; i++); /* XXX */ + tmp = realloc(tmp, (i+2)*sizeof(*tmp)); + if(tmp == NULL) + return krb5_enomem(context); + *tgts = tmp; + ret = krb5_copy_creds(context, tkt, &tmp[i]); + tmp[i+1] = NULL; + return ret; +} + +static krb5_error_code +get_cred_kdc_capath_worker(krb5_context context, + krb5_kdc_flags flags, + krb5_ccache ccache, + struct krb5_fast_state *fast_state, + krb5_creds *in_creds, + krb5_const_realm try_realm, + krb5_principal impersonate_principal, + Ticket *second_ticket, + const char *kdc_hostname, + const char *sitename, + krb5_creds **out_creds, + krb5_creds ***ret_tgts) +{ + krb5_error_code ret; + krb5_creds *tgt = NULL; + krb5_creds tmp_creds; + krb5_const_realm client_realm, server_realm; + int ok_as_delegate = 1; + + *out_creds = calloc(1, sizeof(**out_creds)); + if (*out_creds == NULL) + return krb5_enomem(context); + + memset(&tmp_creds, 0, sizeof(tmp_creds)); + + client_realm = krb5_principal_get_realm(context, in_creds->client); + server_realm = krb5_principal_get_realm(context, in_creds->server); + ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client); + if (ret) + goto out; + + ret = krb5_make_principal(context, + &tmp_creds.server, + try_realm, + KRB5_TGS_NAME, + server_realm, + NULL); + if (ret) + goto out; + + { + krb5_creds tgts; + + /* + * If we have krbtgt/server_realm@try_realm cached, use it and we're + * done. + */ + ret = find_cred(context, ccache, tmp_creds.server, + *ret_tgts, &tgts); + if (ret == 0) { + /* only allow implicit ok_as_delegate if the realm is the clients realm */ + if (strcmp(try_realm, client_realm) != 0 + || strcmp(try_realm, server_realm) != 0) { + ok_as_delegate = tgts.flags.b.ok_as_delegate; + } + + ret = get_cred_kdc_address(context, ccache, fast_state, + flags, NULL, + in_creds, &tgts, + impersonate_principal, + second_ticket, + kdc_hostname, + sitename, + *out_creds); + krb5_free_cred_contents(context, &tgts); + if (ret == 0 && + !krb5_principal_compare(context, in_creds->server, + (*out_creds)->server)) { + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + } + if (ret == 0 && ok_as_delegate == 0) + (*out_creds)->flags.b.ok_as_delegate = 0; + + goto out; + } + } + + if (krb5_realm_compare(context, in_creds->client, in_creds->server)) { + ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND); + goto out; + } + + /* + * XXX This can loop forever, plus we recurse, so we can't just keep a + * count here. The count would have to get passed around by reference. + * + * The KDCs check for transit loops for us, and capath data is finite, so + * in fact we'll fall out of this loop at some point. We should do our own + * transit loop checking (like get_cred_kdc_referral()), and we should + * impose a max number of iterations altogether. But barring malicious or + * broken KDCs, this is good enough. + */ + while (1) { + heim_general_string tgt_inst; + + ret = get_cred_kdc_capath(context, flags, ccache, fast_state, + &tmp_creds, NULL, NULL, + kdc_hostname, sitename, + &tgt, ret_tgts); + if (ret) + goto out; + + /* + * if either of the chain or the ok_as_delegate was stripped + * by the kdc, make sure we strip it too. + */ + if (ok_as_delegate == 0 || tgt->flags.b.ok_as_delegate == 0) { + ok_as_delegate = 0; + tgt->flags.b.ok_as_delegate = 0; + } + + ret = add_cred(context, tgt, ret_tgts); + if (ret) + goto out; + tgt_inst = tgt->server->name.name_string.val[1]; + if (strcmp(tgt_inst, server_realm) == 0) + break; + krb5_free_principal(context, tmp_creds.server); + tmp_creds.server = NULL; + ret = krb5_make_principal(context, &tmp_creds.server, + tgt_inst, KRB5_TGS_NAME, server_realm, NULL); + if (ret) + goto out; + ret = krb5_free_creds(context, tgt); + tgt = NULL; + if (ret) + goto out; + } + + ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL, + in_creds, tgt, impersonate_principal, + second_ticket, kdc_hostname, sitename, *out_creds); + if (ret == 0 && + !krb5_principal_compare(context, in_creds->server, + (*out_creds)->server)) { + krb5_free_cred_contents(context, *out_creds); + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + } + if (ret == 0 && ok_as_delegate == 0) + (*out_creds)->flags.b.ok_as_delegate = 0; + +out: + if (ret) { + krb5_free_creds(context, *out_creds); + *out_creds = NULL; + } + if (tmp_creds.server) + krb5_free_principal(context, tmp_creds.server); + if (tmp_creds.client) + krb5_free_principal(context, tmp_creds.client); + if (tgt) + krb5_free_creds(context, tgt); + return ret; +} + +/* +get_cred(server) + creds = cc_get_cred(server) + if(creds) return creds + tgt = cc_get_cred(krbtgt/server_realm@any_realm) + if(tgt) + return get_cred_tgt(server, tgt) + if(client_realm == server_realm) + return NULL + tgt = get_cred(krbtgt/server_realm@client_realm) + while(tgt_inst != server_realm) + tgt = get_cred(krbtgt/server_realm@tgt_inst) + return get_cred_tgt(server, tgt) + */ + +static krb5_error_code +get_cred_kdc_capath(krb5_context context, + krb5_kdc_flags flags, + krb5_ccache ccache, + struct krb5_fast_state *fast_state, + krb5_creds *in_creds, + krb5_principal impersonate_principal, + Ticket *second_ticket, + const char *kdc_hostname, + const char *sitename, + krb5_creds **out_creds, + krb5_creds ***ret_tgts) +{ + krb5_error_code ret; + krb5_const_realm client_realm, server_realm, try_realm; + + client_realm = krb5_principal_get_realm(context, in_creds->client); + server_realm = krb5_principal_get_realm(context, in_creds->server); + + try_realm = client_realm; + ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state, + in_creds, try_realm, impersonate_principal, + second_ticket, kdc_hostname, sitename, + out_creds, ret_tgts); + + if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN) { + try_realm = krb5_config_get_string(context, NULL, "capaths", + client_realm, server_realm, NULL); + + if (try_realm != NULL && strcmp(try_realm, client_realm) != 0) { + ret = get_cred_kdc_capath_worker(context, flags, ccache, fast_state, + in_creds, try_realm, impersonate_principal, + second_ticket, kdc_hostname, sitename, + out_creds, ret_tgts); + } + } + + return ret; +} + +static krb5_boolean skip_referrals(krb5_principal server, + krb5_kdc_flags *flags) +{ + return server->name.name_string.len < 2 && !flags->b.canonicalize; +} + +/* + * Get a service ticket from a KDC by chasing referrals from a start realm. + * + * All referral TGTs produced in the process are thrown away when we're done. + * We don't store them, and we don't allow other search mechanisms (capaths) to + * use referral TGTs produced here. + */ +static krb5_error_code +get_cred_kdc_referral(krb5_context context, + krb5_kdc_flags flags, + krb5_ccache ccache, + struct krb5_fast_state *fast_state, + krb5_creds *in_creds, + krb5_principal impersonate_principal, + Ticket *second_ticket, + const char *kdc_hostname, + const char *sitename, + krb5_creds **out_creds) +{ + krb5_realm start_realm = NULL; + krb5_data config_start_realm; + krb5_error_code ret; + krb5_creds tgt, referral, ticket; + krb5_creds **referral_tgts = NULL; /* used for loop detection */ + int loop = 0; + int ok_as_delegate = 1; + int want_tgt; + size_t i; + + if (skip_referrals(in_creds->server, &flags)) { + krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED, + N_("Name too short to do referals, skipping", "")); + return KRB5KDC_ERR_PATH_NOT_ACCEPTED; + } + + memset(&tgt, 0, sizeof(tgt)); + memset(&ticket, 0, sizeof(ticket)); + + flags.b.canonicalize = 1; + + *out_creds = NULL; + + + ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm); + if (ret == 0) { + start_realm = strndup(config_start_realm.data, config_start_realm.length); + krb5_data_free(&config_start_realm); + } else { + start_realm = strdup(krb5_principal_get_realm(context, in_creds->client)); + } + if (start_realm == NULL) + return krb5_enomem(context); + + /* find tgt for the clients base realm */ + { + krb5_principal tgtname; + + ret = krb5_make_principal(context, &tgtname, + start_realm, + KRB5_TGS_NAME, + start_realm, + NULL); + if (ret) { + free(start_realm); + return ret; + } + + ret = find_cred(context, ccache, tgtname, NULL, &tgt); + krb5_free_principal(context, tgtname); + if (ret) { + free(start_realm); + return ret; + } + } + + /* + * If the desired service principal service/host@REALM is not a TGT, start + * by asking for a ticket for service/host@START_REALM and process referrals + * from there. + * + * However, when we ask for a TGT, krbtgt/A@B, we're actually looking for a + * path to realm B, so that we can explicitly obtain a ticket for krbtgt/A + * from B, and not some other realm. Therefore, in this case our starting + * point will be krbtgt/B@START_REALM. Only once we obtain a ticket for + * krbtgt/B@some-transit, do we switch to requesting krbtgt/A@B on our + * final request. + */ + referral = *in_creds; + want_tgt = in_creds->server->realm[0] != '\0' && + krb5_principal_is_krbtgt(context, in_creds->server); + if (!want_tgt) + ret = krb5_copy_principal(context, in_creds->server, &referral.server); + else + ret = krb5_make_principal(context, &referral.server, start_realm, + KRB5_TGS_NAME, in_creds->server->realm, NULL); + + if (ret) { + krb5_free_cred_contents(context, &tgt); + free(start_realm); + return ret; + } + if (!want_tgt) + ret = krb5_principal_set_realm(context, referral.server, start_realm); + free(start_realm); + start_realm = NULL; + if (ret) { + krb5_free_cred_contents(context, &tgt); + krb5_free_principal(context, referral.server); + return ret; + } + + while (loop++ < 17) { + krb5_creds **tickets; + krb5_creds mcreds; + char *referral_realm; + + /* Use cache if we are not doing impersonation or contrained deleg */ + if (impersonate_principal == NULL && !flags.b.cname_in_addl_tkt) { + krb5_cc_clear_mcred(&mcreds); + mcreds.server = referral.server; + krb5_timeofday(context, &mcreds.times.endtime); + ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES, + &mcreds, &ticket); + } else + ret = EINVAL; + + if (ret) { + ret = get_cred_kdc_address(context, ccache, fast_state, flags, NULL, + &referral, &tgt, impersonate_principal, + second_ticket, kdc_hostname, sitename, &ticket); + if (ret) + goto out; + } + + /* + * Did we get the right ticket? + * + * If we weren't asking for a TGT, then we don't mind if we took a realm + * change (referral.server has a referral realm, not necessarily the + * original). + * + * However, if we were looking for a TGT (which wouldn't be the start + * TGT, since that one must be in the ccache) then we actually want the + * one from the realm we wanted, since otherwise a _referral_ will + * confuse us and we will store that referral. In Heimdal we mostly + * never ask krb5_get_cred*() for TGTs, but some sites have code to ask + * for a ktbgt/REMOTE.REALM@REMOTE.REALM, and one could always use + * kgetcred(1) to get here asking for a krbtgt/C@D and we need to handle + * the case where last hop we get is krbtgt/C@B (in which case we must + * stop so we don't beat up on B for the remaining tries). + */ + if (!want_tgt && + krb5_principal_compare(context, referral.server, ticket.server)) + break; + + if (!krb5_principal_is_krbtgt(context, ticket.server)) { + krb5_set_error_message(context, KRB5KRB_AP_ERR_NOT_US, + N_("Got back an non krbtgt " + "ticket referrals", "")); + ret = KRB5KRB_AP_ERR_NOT_US; + goto out; + } + + referral_realm = ticket.server->name.name_string.val[1]; + + /* check that there are no referrals loops */ + tickets = referral_tgts; + + krb5_cc_clear_mcred(&mcreds); + mcreds.server = ticket.server; + + while (tickets && *tickets){ + if (krb5_compare_creds(context, + KRB5_TC_DONT_MATCH_REALM, + &mcreds, + *tickets)) { + krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, + N_("Referral from %s " + "loops back to realm %s", ""), + tgt.server->realm, + referral_realm); + ret = KRB5_GET_IN_TKT_LOOP; + goto out; + } + tickets++; + } + + /* + * if either of the chain or the ok_as_delegate was stripped + * by the kdc, make sure we strip it too. + */ + + if (ok_as_delegate == 0 || ticket.flags.b.ok_as_delegate == 0) { + ok_as_delegate = 0; + ticket.flags.b.ok_as_delegate = 0; + } + + _krb5_debug(context, 6, "get_cred_kdc_referral: got referral " + "to %s from %s", referral_realm, referral.server->realm); + ret = add_cred(context, &ticket, &referral_tgts); + if (ret) + goto out; + + /* try realm in the referral */ + if (!want_tgt || strcmp(referral_realm, in_creds->server->realm) != 0) + ret = krb5_principal_set_realm(context, + referral.server, + referral_realm); + else { + /* + * Now that we have a ticket for the desired realm, we reset + * want_tgt and reinstate the desired principal so that the we can + * match it and break out of the loop. + */ + want_tgt = 0; + krb5_free_principal(context, referral.server); + referral.server = NULL; + ret = krb5_copy_principal(context, in_creds->server, &referral.server); + } + krb5_free_cred_contents(context, &tgt); + tgt = ticket; + memset(&ticket, 0, sizeof(ticket)); + if (ret) + goto out; + } + + ret = krb5_copy_creds(context, &ticket, out_creds); + +out: + for (i = 0; referral_tgts && referral_tgts[i]; i++) + krb5_free_creds(context, referral_tgts[i]); + free(referral_tgts); + krb5_free_principal(context, referral.server); + krb5_free_cred_contents(context, &tgt); + krb5_free_cred_contents(context, &ticket); + return ret; +} + + +/* + * Glue function between referrals version and old client chasing + * codebase. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_get_cred_kdc_any(krb5_context context, + krb5_kdc_flags flags, + krb5_ccache ccache, + struct krb5_fast_state *fast_state, + krb5_creds *in_creds, + krb5_principal impersonate_principal, + Ticket *second_ticket, + krb5_creds **out_creds, + krb5_creds ***ret_tgts) +{ + char *kdc_hostname = NULL; + char *sitename = NULL; + krb5_error_code ret; + krb5_deltat offset; + krb5_data data; + + krb5_data_zero(&data); + + /* + * If we are using LKDC, lets pull out the addreses from the + * ticket and use that. + */ + + ret = krb5_cc_get_config(context, ccache, NULL, "lkdc-hostname", &data); + if (ret == 0) { + if ((kdc_hostname = strndup(data.data, data.length)) == NULL) { + ret = krb5_enomem(context); + goto out; + } + krb5_data_free(&data); + } + + ret = krb5_cc_get_config(context, ccache, NULL, "sitename", &data); + if (ret == 0) { + if ((sitename = strndup(data.data, data.length)) == NULL) { + ret = krb5_enomem(context); + goto out; + } + krb5_data_free(&data); + } + + ret = krb5_cc_get_kdc_offset(context, ccache, &offset); + if (ret == 0) { + context->kdc_sec_offset = offset; + context->kdc_usec_offset = 0; + } + + if (strcmp(in_creds->server->realm, "") != 0) { + /* + * Non-empty realm? Try capaths first. We might have local + * policy (capaths) to honor. + */ + ret = get_cred_kdc_capath(context, + flags, + ccache, + fast_state, + in_creds, + impersonate_principal, + second_ticket, + kdc_hostname, + sitename, + out_creds, + ret_tgts); + if (ret == 0 || skip_referrals(in_creds->server, &flags)) + goto out; + } + + /* Otherwise try referrals */ + ret = get_cred_kdc_referral(context, + flags, + ccache, + fast_state, + in_creds, + impersonate_principal, + second_ticket, + kdc_hostname, + sitename, + out_creds); + +out: + krb5_data_free(&data); + free(kdc_hostname); + free(sitename); + return ret; +} + +static krb5_error_code +check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache, + krb5_creds *in_creds, krb5_creds *out_creds) +{ + krb5_error_code ret; + krb5_timestamp now; + krb5_creds mcreds = *in_creds; + + krb5_timeofday(context, &now); + + if (!(options & KRB5_GC_EXPIRED_OK) && + mcreds.times.endtime < now) { + mcreds.times.renew_till = 0; + krb5_timeofday(context, &mcreds.times.endtime); + options |= KRB5_TC_MATCH_TIMES; + } + + if (mcreds.server->name.name_type == KRB5_NT_SRV_HST_NEEDS_CANON) { + /* Avoid name canonicalization in krb5_cc_retrieve_cred() */ + krb5_principal_set_type(context, mcreds.server, KRB5_NT_SRV_HST); + } + + if (options & KRB5_GC_ANONYMOUS) { + ret = krb5_make_principal(context, + &mcreds.client, + krb5_principal_get_realm(context, mcreds.client), + KRB5_WELLKNOWN_NAME, + KRB5_ANON_NAME, + NULL); + if (ret) + return ret; + } + + ret = krb5_cc_retrieve_cred(context, ccache, + (options & + (KRB5_TC_DONT_MATCH_REALM | + KRB5_TC_MATCH_KEYTYPE | + KRB5_TC_MATCH_TIMES)), + &mcreds, out_creds); + + if (options & KRB5_GC_ANONYMOUS) + krb5_free_principal(context, mcreds.client); + + if (ret == 0 && out_creds->server->realm && + out_creds->server->realm[0] == '\0') { + Ticket ticket; + + /* + * We only write tickets to the ccache that have been validated, as in, + * the sname/srealm from the KDC-REP enc-part have been checked to + * match the sname/realm from the Ticket from the KDC-REP. + * + * Our caller needs the canonical realm of the service in order to be + * able to get forwarded credentials for it when destination-TGT + * forwarding is enabled. + * + * As well, gss_init_sec_context() ought to arrange for + * gss_inquire_context() to output the canonical acceptor name on the + * initiator side. + */ + ret = decode_Ticket(out_creds->ticket.data, out_creds->ticket.length, + &ticket, NULL); + if (ret == 0) { + ret = krb5_principal_set_realm(context, out_creds->server, + ticket.realm); + free_Ticket(&ticket); + } else { + krb5_free_cred_contents(context, out_creds); + } + } + return ret; +} + +static void +store_cred(krb5_context context, krb5_ccache ccache, + krb5_const_principal server_princ, krb5_creds *creds) +{ + if (context->no_ticket_store) + return; + if (!krb5_principal_compare(context, creds->server, server_princ) && + !krb5_principal_is_krbtgt(context, server_princ)) { + krb5_principal tmp_princ = creds->server; + /* + * Store the cred with the pre-canon server princ first so it + * can be found quickly in the future. + */ + creds->server = (krb5_principal)server_princ; + krb5_cc_store_cred(context, ccache, creds); + creds->server = tmp_princ; + /* Then store again with the canonicalized server princ */ + } + krb5_cc_store_cred(context, ccache, creds); +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_credentials_with_flags(krb5_context context, + krb5_flags options, + krb5_kdc_flags flags, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_creds **out_creds) +{ + struct krb5_fast_state fast_state; + krb5_error_code ret; + krb5_name_canon_iterator name_canon_iter = NULL; + krb5_name_canon_rule_options rule_opts; + krb5_const_principal try_princ = NULL; + krb5_principal save_princ = in_creds->server; + krb5_creds **tgts; + krb5_creds *res_creds; + int i; + + memset(&fast_state, 0, sizeof(fast_state)); + + if (_krb5_have_debug(context, 5)) { + char *unparsed; + + ret = krb5_unparse_name(context, in_creds->server, &unparsed); + if (ret) { + _krb5_debug(context, 5, "krb5_get_creds: unable to display " + "requested service principal"); + } else { + _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket " + "for %s", unparsed); + free(unparsed); + } + } + + if (in_creds->session.keytype) { + ret = krb5_enctype_valid(context, in_creds->session.keytype); + if (ret) + return ret; + options |= KRB5_TC_MATCH_KEYTYPE; + } + + *out_creds = NULL; + res_creds = calloc(1, sizeof(*res_creds)); + if (res_creds == NULL) + return krb5_enomem(context); + + ret = krb5_name_canon_iterator_start(context, in_creds->server, + &name_canon_iter); + if (ret) + goto out; + +next_rule: + krb5_free_cred_contents(context, res_creds); + memset(res_creds, 0, sizeof (*res_creds)); + ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, + &rule_opts); + in_creds->server = rk_UNCONST(try_princ); + if (ret) + goto out; + + if (name_canon_iter == NULL) { + if (options & KRB5_GC_CACHED) + ret = KRB5_CC_NOTFOUND; + else + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; + } + + ret = check_cc(context, options, ccache, in_creds, res_creds); + if (ret == 0) { + *out_creds = res_creds; + res_creds = NULL; + goto out; + } else if(ret != KRB5_CC_END) { + goto out; + } + if (options & KRB5_GC_CACHED) + goto next_rule; + + if(options & KRB5_GC_USER_USER) + flags.b.enc_tkt_in_skey = 1; + if (flags.b.enc_tkt_in_skey) + options |= KRB5_GC_NO_STORE; + + tgts = NULL; + ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state, + in_creds, NULL, NULL, out_creds, &tgts); + for (i = 0; tgts && tgts[i]; i++) { + if ((options & KRB5_GC_NO_STORE) == 0) + krb5_cc_store_cred(context, ccache, tgts[i]); + krb5_free_creds(context, tgts[i]); + } + free(tgts); + + /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */ + if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && + !(rule_opts & KRB5_NCRO_USE_FAST)) + goto next_rule; + + if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0) + store_cred(context, ccache, in_creds->server, *out_creds); + + if (ret == 0 && _krb5_have_debug(context, 5)) { + char *unparsed; + + ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed); + if (ret) { + _krb5_debug(context, 5, "krb5_get_creds: unable to display " + "service principal"); + } else { + _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s", + unparsed); + free(unparsed); + } + } + +out: + in_creds->server = save_princ; + krb5_free_creds(context, res_creds); + krb5_free_name_canon_iterator(context, name_canon_iter); + _krb5_fast_free(context, &fast_state); + if (ret) + return not_found(context, in_creds->server, ret); + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_credentials(krb5_context context, + krb5_flags options, + krb5_ccache ccache, + krb5_creds *in_creds, + krb5_creds **out_creds) +{ + krb5_kdc_flags flags; + flags.i = 0; + return krb5_get_credentials_with_flags(context, options, flags, + ccache, in_creds, out_creds); +} + +struct krb5_get_creds_opt_data { + krb5_principal self; + krb5_flags options; + krb5_enctype enctype; + Ticket *ticket; +}; + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt) +{ + *opt = calloc(1, sizeof(**opt)); + if (*opt == NULL) + return krb5_enomem(context); + return 0; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_creds_opt_free(krb5_context context, krb5_get_creds_opt opt) +{ + if (opt->self) + krb5_free_principal(context, opt->self); + if (opt->ticket) { + free_Ticket(opt->ticket); + free(opt->ticket); + } + memset(opt, 0, sizeof(*opt)); + free(opt); +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_creds_opt_set_options(krb5_context context, + krb5_get_creds_opt opt, + krb5_flags options) +{ + opt->options = options; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_creds_opt_add_options(krb5_context context, + krb5_get_creds_opt opt, + krb5_flags options) +{ + opt->options |= options; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_creds_opt_set_enctype(krb5_context context, + krb5_get_creds_opt opt, + krb5_enctype enctype) +{ + opt->enctype = enctype; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_creds_opt_set_impersonate(krb5_context context, + krb5_get_creds_opt opt, + krb5_const_principal self) +{ + if (opt->self) + krb5_free_principal(context, opt->self); + return krb5_copy_principal(context, self, &opt->self); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_creds_opt_set_ticket(krb5_context context, + krb5_get_creds_opt opt, + const Ticket *ticket) +{ + if (opt->ticket) { + free_Ticket(opt->ticket); + free(opt->ticket); + opt->ticket = NULL; + } + if (ticket) { + krb5_error_code ret; + + opt->ticket = malloc(sizeof(*ticket)); + if (opt->ticket == NULL) + return krb5_enomem(context); + ret = copy_Ticket(ticket, opt->ticket); + if (ret) { + free(opt->ticket); + opt->ticket = NULL; + krb5_set_error_message(context, ret, + N_("malloc: out of memory", "")); + return ret; + } + } + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_creds(krb5_context context, + krb5_get_creds_opt opt, + krb5_ccache ccache, + krb5_const_principal inprinc, + krb5_creds **out_creds) +{ + struct krb5_fast_state fast_state; + krb5_kdc_flags flags; + krb5_flags options; + krb5_creds in_creds; + krb5_error_code ret; + krb5_creds **tgts; + krb5_creds *res_creds; + krb5_const_principal try_princ = NULL; + krb5_name_canon_iterator name_canon_iter = NULL; + krb5_name_canon_rule_options rule_opts; + int i; + int type; + const char *comp; + + memset(&fast_state, 0, sizeof(fast_state)); + memset(&in_creds, 0, sizeof(in_creds)); + in_creds.server = rk_UNCONST(inprinc); + + if (_krb5_have_debug(context, 5)) { + char *unparsed; + + ret = krb5_unparse_name(context, in_creds.server, &unparsed); + if (ret) { + _krb5_debug(context, 5, "krb5_get_creds: unable to display " + "requested service principal"); + } else { + _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket " + "for %s", unparsed); + free(unparsed); + } + } + + if (opt && opt->enctype) { + ret = krb5_enctype_valid(context, opt->enctype); + if (ret) + return ret; + } + + ret = krb5_cc_get_principal(context, ccache, &in_creds.client); + if (ret) + return ret; + + if (opt) + options = opt->options; + else + options = 0; + flags.i = 0; + + *out_creds = NULL; + res_creds = calloc(1, sizeof(*res_creds)); + if (res_creds == NULL) { + krb5_free_principal(context, in_creds.client); + return krb5_enomem(context); + } + + if (opt && opt->enctype) { + in_creds.session.keytype = opt->enctype; + options |= KRB5_TC_MATCH_KEYTYPE; + } + + ret = krb5_name_canon_iterator_start(context, in_creds.server, + &name_canon_iter); + if (ret) + goto out; + +next_rule: + ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, + &rule_opts); + in_creds.server = rk_UNCONST(try_princ); + if (ret) + goto out; + + if (name_canon_iter == NULL) { + if (options & KRB5_GC_CACHED) + ret = KRB5_CC_NOTFOUND; + else + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; + } + + if ((options & KRB5_GC_CONSTRAINED_DELEGATION) == 0) { + ret = check_cc(context, options, ccache, &in_creds, res_creds); + if (ret == 0) { + *out_creds = res_creds; + res_creds = NULL; + goto out; + } else if (ret != KRB5_CC_END) { + goto out; + } + } + if (options & KRB5_GC_CACHED) + goto next_rule; + + type = krb5_principal_get_type(context, try_princ); + comp = krb5_principal_get_comp_string(context, try_princ, 0); + if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) && + comp != NULL && strcmp(comp, "host") == 0) + flags.b.canonicalize = 1; + if (rule_opts & KRB5_NCRO_NO_REFERRALS) + flags.b.canonicalize = 0; + else + flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0; + if (options & KRB5_GC_USER_USER) { + flags.b.enc_tkt_in_skey = 1; + options |= KRB5_GC_NO_STORE; + } + if (options & KRB5_GC_FORWARDABLE) + flags.b.forwardable = 1; + if (options & KRB5_GC_NO_TRANSIT_CHECK) + flags.b.disable_transited_check = 1; + if (options & KRB5_GC_CONSTRAINED_DELEGATION) + flags.b.cname_in_addl_tkt = 1; + if (options & KRB5_GC_ANONYMOUS) + flags.b.request_anonymous = 1; + + tgts = NULL; + ret = _krb5_get_cred_kdc_any(context, flags, ccache, &fast_state, + &in_creds, opt ? opt->self : 0, + opt ? opt->ticket : 0, out_creds, + &tgts); + for (i = 0; tgts && tgts[i]; i++) { + if ((options & KRB5_GC_NO_STORE) == 0) + krb5_cc_store_cred(context, ccache, tgts[i]); + krb5_free_creds(context, tgts[i]); + } + free(tgts); + + /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */ + if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && + !(rule_opts & KRB5_NCRO_USE_FAST)) + goto next_rule; + + if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0) + store_cred(context, ccache, inprinc, *out_creds); + + if (ret == 0 && _krb5_have_debug(context, 5)) { + char *unparsed; + + ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed); + if (ret) { + _krb5_debug(context, 5, "krb5_get_creds: unable to display " + "service principal"); + } else { + _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s", + unparsed); + free(unparsed); + } + } + +out: + _krb5_fast_free(context, &fast_state); + krb5_free_creds(context, res_creds); + krb5_free_principal(context, in_creds.client); + krb5_free_name_canon_iterator(context, name_canon_iter); + if (ret) + return not_found(context, inprinc, ret); + return ret; +} + +/* + * + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_renewed_creds(krb5_context context, + krb5_creds *creds, + krb5_const_principal client, + krb5_ccache ccache, + const char *in_tkt_service) +{ + krb5_error_code ret; + krb5_kdc_flags flags; + krb5_creds in, *template, *out = NULL; + + memset(&in, 0, sizeof(in)); + memset(creds, 0, sizeof(*creds)); + + ret = krb5_copy_principal(context, client, &in.client); + if (ret) + return ret; + + if (in_tkt_service) { + ret = krb5_parse_name(context, in_tkt_service, &in.server); + if (ret) { + krb5_free_principal(context, in.client); + return ret; + } + } else { + const char *realm = krb5_principal_get_realm(context, client); + + ret = krb5_make_principal(context, &in.server, realm, KRB5_TGS_NAME, + realm, NULL); + if (ret) { + krb5_free_principal(context, in.client); + return ret; + } + } + + flags.i = 0; + flags.b.renewable = flags.b.renew = 1; + + /* + * Get template from old credential cache for the same entry, if + * this failes, no worries. + */ + ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &in, &template); + if (ret == 0) { + flags.b.forwardable = template->flags.b.forwardable; + flags.b.proxiable = template->flags.b.proxiable; + krb5_free_creds (context, template); + } + + ret = krb5_get_kdc_cred(context, ccache, flags, NULL, NULL, &in, &out); + krb5_free_principal(context, in.client); + krb5_free_principal(context, in.server); + if (ret) + return ret; + + ret = krb5_copy_creds_contents(context, out, creds); + krb5_free_creds(context, out); + + return ret; +} diff --git a/third_party/heimdal/lib/krb5/get_default_principal.c b/third_party/heimdal/lib/krb5/get_default_principal.c new file mode 100644 index 0000000..3548074 --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_default_principal.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* + * Try to find out what's a reasonable default principal. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_get_default_principal_local (krb5_context context, + krb5_principal *princ) +{ + const char *user = NULL; + const char *second_component = NULL; + char userbuf[128]; + + *princ = NULL; + + /* + * NOTE: We prefer getlogin_r() (via roken_get_loginname()) to using $USER, + * $LOGNAME, or getpwuid_r() (via roken_get_username()), in that + * order, otherwise we won't figure out to output + * /root@DEFAULT_REALM. + */ +#ifndef WIN32 + if (geteuid() == 0) + user = roken_get_loginname(userbuf, sizeof(userbuf)); +#endif + if (user == NULL) + user = roken_get_username(userbuf, sizeof(userbuf)); + if (user == NULL) { + krb5_set_error_message(context, ENOTTY, + N_("unable to figure out current principal", + "")); + return ENOTTY; /* XXX */ + } + +#ifndef WIN32 + if (!issuid() && getuid() == 0 && strcmp(user, "root") != 0) + second_component = "root"; /* We'll use /root */ +#endif + return krb5_make_principal(context, princ, NULL, user, + second_component, NULL); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_default_principal (krb5_context context, + krb5_principal *princ) +{ + krb5_error_code ret; + krb5_ccache id; + + *princ = NULL; + + ret = krb5_cc_default (context, &id); + if (ret == 0) { + ret = krb5_cc_get_principal (context, id, princ); + krb5_cc_close (context, id); + if (ret == 0) + return 0; + } + + return _krb5_get_default_principal_local(context, princ); +} diff --git a/third_party/heimdal/lib/krb5/get_default_realm.c b/third_party/heimdal/lib/krb5/get_default_realm.c new file mode 100644 index 0000000..81a55bb --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_default_realm.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 1997 - 2001, 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 "krb5_locl.h" + +/* + * Return a NULL-terminated list of default realms in `realms'. + * Free this memory with krb5_free_host_realm. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_default_realms (krb5_context context, + krb5_realm **realms) +{ + if (context->default_realms == NULL) { + krb5_error_code ret = krb5_set_default_realm (context, NULL); + if (ret) + return KRB5_CONFIG_NODEFREALM; + } + + return krb5_copy_host_realm (context, + context->default_realms, + realms); +} + +/* + * Return the first default realm. For compatibility. + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_default_realm(krb5_context context, + krb5_realm *realm) +{ + krb5_error_code ret; + char *res; + + if (context->default_realms == NULL + || context->default_realms[0] == NULL) { + krb5_clear_error_message(context); + ret = krb5_set_default_realm (context, NULL); + if (ret) + return ret; + } + + res = strdup (context->default_realms[0]); + if (res == NULL) + return krb5_enomem(context); + *realm = res; + return 0; +} diff --git a/third_party/heimdal/lib/krb5/get_for_creds.c b/third_party/heimdal/lib/krb5/get_for_creds.c new file mode 100644 index 0000000..3a6be10 --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_for_creds.c @@ -0,0 +1,367 @@ +/* + * 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 "krb5_locl.h" + +static krb5_error_code set_tgs_creds(krb5_context, krb5_ccache, + krb5_const_principal, + krb5_const_principal, krb5_creds *); +static krb5_error_code get_cred(krb5_context, krb5_ccache, krb5_creds *, + krb5_flags, const char *, krb5_creds **); +static krb5_error_code get_addresses(krb5_context, krb5_ccache, krb5_creds *, + const char *, krb5_addresses *); + +static krb5_error_code +add_addrs(krb5_context context, + krb5_addresses *addr, + struct addrinfo *ai) +{ + krb5_error_code ret; + unsigned n, i; + void *tmp; + struct addrinfo *a; + + n = 0; + for (a = ai; a != NULL; a = a->ai_next) + ++n; + + tmp = realloc(addr->val, (addr->len + n) * sizeof(*addr->val)); + if (tmp == NULL && (addr->len + n) != 0) { + ret = krb5_enomem(context); + goto fail; + } + addr->val = tmp; + for (i = addr->len; i < (addr->len + n); ++i) { + addr->val[i].addr_type = 0; + krb5_data_zero(&addr->val[i].address); + } + i = addr->len; + for (a = ai; a != NULL; a = a->ai_next) { + krb5_address ad; + + ret = krb5_sockaddr2address (context, a->ai_addr, &ad); + if (ret == 0) { + if (krb5_address_search(context, &ad, addr)) + krb5_free_address(context, &ad); + else + addr->val[i++] = ad; + } + else if (ret == KRB5_PROG_ATYPE_NOSUPP) + krb5_clear_error_message (context); + else + goto fail; + addr->len = i; + } + return 0; +fail: + krb5_free_addresses (context, addr); + return ret; +} + +/** + * Forward credentials for client to host hostname, making them + * forwardable if forwardable, and returning the blob of data to sent + * in out_data. If hostname == NULL, pick it from server. + * + * If the server's realm is configured for delegation of destination + * TGTs, forward a TGT for the server realm, rather than the client + * realm. This works better with destinations on the far side of a + * firewall. We also forward the destination TGT when the client + * TGT is not available (we may have just the destination TGT). + * + * @param context A kerberos 5 context. + * @param auth_context the auth context with the key to encrypt the out_data. + * @param hostname the host to forward the tickets too. + * @param client the client to delegate from. + * @param server the server to delegate the credential too. + * @param ccache credential cache to use. + * @param forwardable make the forwarded ticket forwabledable. + * @param out_data the resulting credential. + * + * @return Return an error code or 0. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_fwd_tgt_creds(krb5_context context, + krb5_auth_context auth_context, + const char *hostname, + krb5_const_principal client, + krb5_const_principal server, + krb5_ccache ccache, + int forwardable, + krb5_data *out_data) +{ + krb5_flags flags = 0; + krb5_creds creds; + krb5_error_code ret; + + flags |= KDC_OPT_FORWARDED; + + if (forwardable) + flags |= KDC_OPT_FORWARDABLE; + + if (hostname == NULL && + krb5_principal_get_type(context, server) == KRB5_NT_SRV_HST) { + const char *inst = krb5_principal_get_comp_string(context, server, 0); + const char *host = krb5_principal_get_comp_string(context, server, 1); + + if (inst != NULL && + strcmp(inst, "host") == 0 && + host != NULL && + krb5_principal_get_comp_string(context, server, 2) == NULL) + hostname = host; + } + + /* + * Fill-in the request creds, the server principal will be the TGS + * of either the client's or the server's realm. + */ + ret = set_tgs_creds(context, ccache, client, server, &creds); + if (ret) + return ret; + + ret = krb5_get_forwarded_creds (context, + auth_context, + ccache, + flags, + hostname, + &creds, + out_data); + + krb5_free_cred_contents(context, &creds); + return ret; +} + +/** + * Gets tickets forwarded to hostname. If the tickets that are + * forwarded are address-less, the forwarded tickets will also be + * address-less. + * + * If the ticket have any address, hostname will be used for figure + * out the address to forward the ticket too. This since this might + * use DNS, its insecure and also doesn't represent configured all + * addresses of the host. For example, the host might have two + * adresses, one IPv4 and one IPv6 address where the later is not + * published in DNS. This IPv6 address might be used communications + * and thus the resulting ticket useless. + * + * @param context A kerberos 5 context. + * @param auth_context the auth context with the key to encrypt the out_data. + * @param ccache credential cache to use + * @param flags the flags to control the resulting ticket flags + * @param hostname the host to forward the tickets too. + * @param in_creds the in client and server ticket names. The client + * and server components forwarded to the remote host. + * @param out_data the resulting credential. + * + * @return Return an error code or 0. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_forwarded_creds (krb5_context context, + krb5_auth_context auth_context, + krb5_ccache ccache, + krb5_flags flags, + const char *hostname, + krb5_creds *in_creds, + krb5_data *out_data) +{ + krb5_error_code ret; + krb5_creds *creds; + + /* Obtain the requested TGT */ + ret = get_cred(context, ccache, in_creds, flags, hostname, &creds); + if (ret) + return ret; + + /* Forward obtained creds */ + ret = _krb5_mk_1cred(context, auth_context, creds, out_data, NULL); + krb5_free_creds(context, creds); + return ret; +} + +/* + * Get a TGT for forwarding to hostname. If the client TGT is + * addressless, the forwarded ticket will also be addressless. + * + * If the TGT has any addresses, hostname will be used to determine + * the address to forward the ticket to. Thus, since this might use DNS, + * it's insecure and also may not capture all the addresses of the host. + * In general addressless tickets are more robust, be it at a small + * security penalty. + * + * @param context A kerberos 5 context. + * @param ccache The credential cache to use + * @param creds Creds with client and server principals + * @param flags The flags to control the resulting ticket flags + * @param hostname The hostname of server + * @param out_creds The resulting credential + * + * @return Return an error code or 0. + */ + +static krb5_error_code +get_cred(krb5_context context, + krb5_ccache ccache, + krb5_creds *creds, + krb5_flags flags, + const char *hostname, + krb5_creds **out_creds) +{ + krb5_error_code ret; + krb5_kdc_flags kdc_flags; + krb5_addresses addrs; + + addrs.len = 0; + addrs.val = NULL; + ret = get_addresses(context, ccache, creds, hostname, &addrs); + if (ret) + return ret; + + kdc_flags.b = int2KDCOptions(flags); + ret = krb5_get_kdc_cred(context, ccache, kdc_flags, &addrs, NULL, + creds, out_creds); + + krb5_free_addresses(context, &addrs); + return ret; +} + +static krb5_error_code +set_tgs_creds(krb5_context context, + krb5_ccache ccache, + krb5_const_principal client, + krb5_const_principal server, + krb5_creds *creds) +{ + krb5_error_code ret; + krb5_const_realm client_realm; + krb5_const_realm server_realm; + krb5_boolean fwd_dest_tgt; + krb5_creds *client_tgt; + + client_realm = krb5_principal_get_realm(context, client); + server_realm = krb5_principal_get_realm(context, server); + + memset (creds, 0, sizeof(*creds)); + ret = krb5_copy_principal(context, client, &creds->client); + if (ret) + return ret; + ret = krb5_make_principal(context, &creds->server, client_realm, + KRB5_TGS_NAME, client_realm, NULL); + if (ret) { + krb5_free_principal(context, creds->client); + return ret; + } + + /* + * Optionally delegate a TGT for the server's realm, rather than + * the client's. Do this also when we don't have a client realm TGT. + * + * XXX: Note, when we have a start-realm, and delegate-destination-tgt + * is not set, we must use the start-realm. + */ + krb5_appdefault_boolean(context, NULL, server_realm, + "delegate-destination-tgt", FALSE, &fwd_dest_tgt); + + if (!fwd_dest_tgt) { + ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, creds, + &client_tgt); + if (ret == 0) { + krb5_free_creds(context, client_tgt); + return ret; + } + } + + /* + * Client TGT inapplicable or unavailable + */ + krb5_free_principal(context, creds->server); + creds->server = 0; + return krb5_make_principal(context, &creds->server, server_realm, + KRB5_TGS_NAME, server_realm, NULL); +} + +/* + * Obtain address list for hostname if server realm policy is not addressless. + */ +static krb5_error_code +get_addresses(krb5_context context, + krb5_ccache ccache, + krb5_creds *creds, + const char *hostname, + krb5_addresses *addrs) +{ + krb5_error_code ret; + krb5_creds *ticket; + krb5_const_realm realm; + krb5_boolean noaddr; + struct addrinfo *ai; + int eai; + + if (hostname == 0) + return 0; + + ret = krb5_get_credentials(context, 0, ccache, creds, &ticket); + if (ret == 0) { + noaddr = (ticket->addresses.len == 0) ? TRUE : FALSE; + krb5_free_creds(context, ticket); + } else { + realm = krb5_principal_get_realm(context, creds->server); + krb5_appdefault_boolean(context, NULL, realm, "no-addresses", + KRB5_ADDRESSLESS_DEFAULT, &noaddr); + } + + if (noaddr) + return 0; + + /* Need addresses, get the address of the remote host. */ + + eai = getaddrinfo (hostname, NULL, NULL, &ai); + if (eai) { + ret = krb5_eai_to_heim_errno(eai, errno); + krb5_set_error_message(context, ret, + N_("resolving host %s failed: %s", + "hostname, error"), + hostname, gai_strerror(eai)); + return ret; + } + + ret = add_addrs(context, addrs, ai); + freeaddrinfo(ai); + + return ret; +} diff --git a/third_party/heimdal/lib/krb5/get_host_realm.c b/third_party/heimdal/lib/krb5/get_host_realm.c new file mode 100644 index 0000000..7b58fe9 --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_host_realm.c @@ -0,0 +1,308 @@ +/* + * 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 "krb5_locl.h" +#include + +/* To automagically find the correct realm of a host (without + * [domain_realm] in krb5.conf) add a text record for your domain with + * the name of your realm, like this: + * + * _kerberos IN TXT "FOO.SE" + * + * The search is recursive, so you can add entries for specific + * hosts. To find the realm of host a.b.c, it first tries + * _kerberos.a.b.c, then _kerberos.b.c and so on. + * + * This method is described in draft-ietf-cat-krb-dns-locate-03.txt. + * + */ + +static int +copy_txt_to_realms(krb5_context context, + const char *domain, + struct rk_resource_record *head, + krb5_realm **realms) +{ + struct rk_resource_record *rr; + unsigned int n, i; + + for(n = 0, rr = head; rr; rr = rr->next) + if (rr->type == rk_ns_t_txt) + ++n; + + if (n == 0) + return -1; + + *realms = malloc ((n + 1) * sizeof(krb5_realm)); + if (*realms == NULL) + return krb5_enomem(context);; + + for (i = 0; i < n + 1; ++i) + (*realms)[i] = NULL; + + for (i = 0, rr = head; rr; rr = rr->next) { + if (rr->type == rk_ns_t_txt) { + char *tmp = NULL; + int invalid_tld = 1; + + /* Check for a gTLD controlled interruption */ + if (strcmp("Your DNS configuration needs immediate " + "attention see https://icann.org/namecollision", + rr->u.txt) != 0) { + invalid_tld = 0; + tmp = strdup(rr->u.txt); + } + if (tmp == NULL) { + for (i = 0; i < n; ++i) + free ((*realms)[i]); + free (*realms); + if (invalid_tld) { + krb5_warnx(context, + "Realm lookup failed: " + "Domain '%s' needs immediate attention " + "see https://icann.org/namecollision", + domain); + return KRB5_KDC_UNREACH; + } + return krb5_enomem(context);; + } + (*realms)[i] = tmp; + ++i; + } + } + return 0; +} + +static int +dns_find_realm(krb5_context context, + const char *domain, + krb5_realm **realms) +{ + static const char *const default_labels[] = { "_kerberos", NULL }; + char dom[MAXHOSTNAMELEN]; + struct rk_dns_reply *r; + const char *const *labels; + char **config_labels; + int i, ret = 0; + + config_labels = krb5_config_get_strings(context, NULL, "libdefaults", + "dns_lookup_realm_labels", NULL); + if(config_labels != NULL) + labels = (const char *const *)config_labels; + else + labels = default_labels; + if(*domain == '.') + domain++; + for (i = 0; labels[i] != NULL; i++) { + ret = snprintf(dom, sizeof(dom), "%s.%s.", labels[i], domain); + if(ret < 0 || (size_t)ret >= sizeof(dom)) { + ret = krb5_enomem(context); + goto out; + } + r = rk_dns_lookup(dom, "TXT"); + if(r != NULL) { + ret = copy_txt_to_realms(context, domain, r->head, realms); + rk_dns_free_data(r); + if(ret == 0) + goto out; + } + } + krb5_set_error_message(context, KRB5_KDC_UNREACH, + "Realm lookup failed: " + "No DNS TXT record for %s", + domain); + ret = KRB5_KDC_UNREACH; +out: + if (config_labels) + krb5_config_free_strings(config_labels); + return ret; +} + +/* + * Try to figure out what realms host in `domain' belong to from the + * configuration file. + */ + +static int +config_find_realm(krb5_context context, + const char *domain, + krb5_realm **realms) +{ + char **tmp = krb5_config_get_strings (context, NULL, + "domain_realm", + domain, + NULL); + + if (tmp == NULL) + return -1; + *realms = tmp; + return 0; +} + +/* + * This function assumes that `host' is a FQDN (and doesn't handle the + * special case of host == NULL either). + * Try to find mapping in the config file or DNS and it that fails, + * fall back to guessing + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_get_host_realm_int(krb5_context context, + const char *host, + krb5_boolean use_dns, + krb5_realm **realms) +{ + const char *p, *q; + const char *port; + char *freeme = NULL; + krb5_boolean dns_locate_enable; + krb5_error_code ret = 0; + + /* Strip off any trailing ":port" suffix. */ + port = strchr(host, ':'); + if (port != NULL && port != host && port[1] != '\0') { + host = freeme = strndup(host, port - host); + if (host == NULL) + return krb5_enomem(context); + } + + dns_locate_enable = krb5_config_get_bool_default(context, NULL, TRUE, + "libdefaults", "dns_lookup_realm", NULL); + for (p = host; p != NULL && p[0] != '\0'; p = strchr (p + 1, '.')) { + if (config_find_realm(context, p, realms) == 0) { + if (strcasecmp(*realms[0], "dns_locate") != 0) + break; + krb5_free_host_realm(context, *realms); + *realms = NULL; + if (!use_dns) + continue; + for (q = host; q != NULL; q = strchr(q + 1, '.')) + if (dns_find_realm(context, q, realms) == 0) + break; + if (q) + break; + } else if (use_dns && dns_locate_enable) { + if (dns_find_realm(context, p, realms) == 0) + break; + } + } + + /* + * If 'p' is NULL, we did not find an explicit realm mapping in either the + * configuration file or DNS. Try the hostname suffix -upcased- as a realm + * as a last resort. + * + * NOTE: If we implement a KDC-specific variant of this function just for + * referrals, we could check whether we have a cross-realm TGT for the + * realm in question, and if not try the parent (loop again). Such a + * variant would have to have access to the HDB, naturally. + * + * We should start by adding an argument to this function that + * indicates whether this fallback here is desired (the KDC wouldn't + * desire it). Then when the KDC gets KRB5_ERR_HOST_REALM_UNKNOWN + * from this function, the KDC would search the HDB for cross-realm + * krbtgt principals that denote a hierarchical path to a realm that + * matches the host's domain suffix (or a suffix of it...). + */ + if (p == NULL) { + p = strchr(host, '.'); + if (p != NULL) { + p++; + *realms = malloc(2 * sizeof(krb5_realm)); + if (*realms != NULL && + ((*realms)[0] = strdup(p)) != NULL) { + strupr((*realms)[0]); + (*realms)[1] = NULL; + } else { + free(*realms); + ret = krb5_enomem(context); + } + } else { + krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN, + N_("unable to find realm of host %s", ""), + host); + ret = KRB5_ERR_HOST_REALM_UNKNOWN; + } + } + + free(freeme); + return ret; +} + +/* + * Return the realm(s) of `host' as a NULL-terminated list in + * `realms'. Free `realms' with krb5_free_host_realm(). + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_host_realm(krb5_context context, + const char *targethost, + krb5_realm **realms) +{ + const char *host = targethost; + char hostname[MAXHOSTNAMELEN]; + krb5_error_code ret; + int use_dns; + + if (host == NULL) { + if (gethostname (hostname, sizeof(hostname))) { + *realms = NULL; + return errno; + } + host = hostname; + } + + /* + * If our local hostname is without components, don't even try to dns. + */ + + use_dns = (strchr(host, '.') != NULL); + + ret = _krb5_get_host_realm_int (context, host, use_dns, realms); + if (ret && targethost != NULL) { + /* + * If there was no realm mapping for the host (and we wasn't + * looking for ourself), guess at the local realm, maybe our + * KDC knows better then we do and we get a referral back. + */ + ret = krb5_get_default_realms(context, realms); + if (ret) { + krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN, + N_("Unable to find realm of host %s", ""), + host); + return KRB5_ERR_HOST_REALM_UNKNOWN; + } + } + return ret; +} diff --git a/third_party/heimdal/lib/krb5/get_in_tkt.c b/third_party/heimdal/lib/krb5/get_in_tkt.c new file mode 100644 index 0000000..476844c --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_in_tkt.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define KRB5_DEPRECATED_FUNCTION(x) + +#include "krb5_locl.h" + +#ifndef HEIMDAL_SMALLER + +static krb5_error_code +make_pa_enc_timestamp(krb5_context context, PA_DATA *pa, + krb5_enctype etype, krb5_keyblock *key) +{ + PA_ENC_TS_ENC p; + unsigned char *buf; + size_t buf_size; + size_t len = 0; + EncryptedData encdata; + krb5_error_code ret; + int32_t usec; + int usec2; + krb5_crypto crypto; + + krb5_us_timeofday (context, &p.patimestamp, &usec); + usec2 = usec; + p.pausec = &usec2; + + ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + ret = krb5_crypto_init(context, key, 0, &crypto); + if (ret) { + free(buf); + return ret; + } + ret = krb5_encrypt_EncryptedData(context, + crypto, + KRB5_KU_PA_ENC_TIMESTAMP, + buf, + len, + 0, + &encdata); + free(buf); + krb5_crypto_destroy(context, crypto); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); + free_EncryptedData(&encdata); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; + pa->padata_value.length = len; + pa->padata_value.data = buf; + return 0; +} + +static krb5_error_code +add_padata(krb5_context context, + METHOD_DATA *md, + krb5_principal client, + krb5_key_proc key_proc, + krb5_const_pointer keyseed, + krb5_enctype *enctypes, + unsigned netypes, + krb5_salt *salt) +{ + krb5_error_code ret; + PA_DATA *pa2; + krb5_salt salt2; + krb5_enctype *ep; + size_t i; + + if(salt == NULL) { + /* default to standard salt */ + ret = krb5_get_pw_salt (context, client, &salt2); + if (ret) + return ret; + salt = &salt2; + } + if (!enctypes) { + enctypes = context->etypes; + netypes = 0; + for (ep = enctypes; *ep != ETYPE_NULL; ep++) + netypes++; + } + pa2 = realloc (md->val, (md->len + netypes) * sizeof(*md->val)); + if (pa2 == NULL) + return krb5_enomem(context); + md->val = pa2; + + for (i = 0; i < netypes; ++i) { + krb5_keyblock *key; + + ret = (*key_proc)(context, enctypes[i], *salt, keyseed, &key); + if (ret) + continue; + ret = make_pa_enc_timestamp (context, &md->val[md->len], + enctypes[i], key); + krb5_free_keyblock (context, key); + if (ret) + return ret; + ++md->len; + } + if(salt == &salt2) + krb5_free_salt(context, salt2); + return 0; +} + +static krb5_error_code +init_as_req (krb5_context context, + KDCOptions opts, + krb5_creds *creds, + const krb5_addresses *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *ptypes, + const krb5_preauthdata *preauth, + krb5_key_proc key_proc, + krb5_const_pointer keyseed, + unsigned nonce, + AS_REQ *a) +{ + krb5_error_code ret; + krb5_salt salt; + + memset(a, 0, sizeof(*a)); + + a->pvno = 5; + a->msg_type = krb_as_req; + a->req_body.kdc_options = opts; + a->req_body.cname = malloc(sizeof(*a->req_body.cname)); + if (a->req_body.cname == NULL) { + ret = krb5_enomem(context); + goto fail; + } + a->req_body.sname = malloc(sizeof(*a->req_body.sname)); + if (a->req_body.sname == NULL) { + ret = krb5_enomem(context); + goto fail; + } + ret = _krb5_principal2principalname (a->req_body.cname, creds->client); + if (ret) + goto fail; + ret = _krb5_principal2principalname (a->req_body.sname, creds->server); + if (ret) + goto fail; + ret = copy_Realm(&creds->client->realm, &a->req_body.realm); + if (ret) + goto fail; + + if(creds->times.starttime) { + a->req_body.from = malloc(sizeof(*a->req_body.from)); + if (a->req_body.from == NULL) { + ret = krb5_enomem(context); + goto fail; + } + *a->req_body.from = creds->times.starttime; + } + if(creds->times.endtime){ + ALLOC(a->req_body.till, 1); + *a->req_body.till = creds->times.endtime; + } + if(creds->times.renew_till){ + a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); + if (a->req_body.rtime == NULL) { + ret = krb5_enomem(context); + goto fail; + } + *a->req_body.rtime = creds->times.renew_till; + } + a->req_body.nonce = nonce; + ret = _krb5_init_etype(context, + KRB5_PDU_AS_REQUEST, + &a->req_body.etype.len, + &a->req_body.etype.val, + etypes); + if (ret) + goto fail; + + /* + * This means no addresses + */ + + if (addrs && addrs->len == 0) { + a->req_body.addresses = NULL; + } else { + a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); + if (a->req_body.addresses == NULL) { + ret = krb5_enomem(context); + goto fail; + } + + if (addrs) + ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); + else { + ret = krb5_get_all_client_addrs (context, a->req_body.addresses); + if(ret == 0 && a->req_body.addresses->len == 0) { + free(a->req_body.addresses); + a->req_body.addresses = NULL; + } + } + if (ret) + return ret; + } + + a->req_body.enc_authorization_data = NULL; + a->req_body.additional_tickets = NULL; + + if(preauth != NULL) { + size_t i; + ALLOC(a->padata, 1); + if(a->padata == NULL) { + ret = krb5_enomem(context); + goto fail; + } + a->padata->val = NULL; + a->padata->len = 0; + for(i = 0; i < preauth->len; i++) { + if(preauth->val[i].type == KRB5_PADATA_ENC_TIMESTAMP){ + size_t j; + + for(j = 0; j < preauth->val[i].info.len; j++) { + krb5_salt *sp = &salt; + if(preauth->val[i].info.val[j].salttype) + salt.salttype = *preauth->val[i].info.val[j].salttype; + else + salt.salttype = KRB5_PW_SALT; + if(preauth->val[i].info.val[j].salt) + salt.saltvalue = *preauth->val[i].info.val[j].salt; + else + if(salt.salttype == KRB5_PW_SALT) + sp = NULL; + else + krb5_data_zero(&salt.saltvalue); + ret = add_padata(context, a->padata, creds->client, + key_proc, keyseed, + &preauth->val[i].info.val[j].etype, 1, + sp); + if (ret == 0) + break; + } + } + } + } else + /* not sure this is the way to use `ptypes' */ + if (ptypes == NULL || *ptypes == KRB5_PADATA_NONE) + a->padata = NULL; + else if (*ptypes == KRB5_PADATA_ENC_TIMESTAMP) { + ALLOC(a->padata, 1); + if (a->padata == NULL) { + ret = krb5_enomem(context); + goto fail; + } + a->padata->len = 0; + a->padata->val = NULL; + + /* make a v5 salted pa-data */ + add_padata(context, a->padata, creds->client, + key_proc, keyseed, a->req_body.etype.val, + a->req_body.etype.len, NULL); + + /* make a v4 salted pa-data */ + salt.salttype = KRB5_PW_SALT; + krb5_data_zero(&salt.saltvalue); + add_padata(context, a->padata, creds->client, + key_proc, keyseed, a->req_body.etype.val, + a->req_body.etype.len, &salt); + } else { + ret = KRB5_PREAUTH_BAD_TYPE; + krb5_set_error_message (context, ret, + N_("pre-auth type %d not supported", ""), + *ptypes); + goto fail; + } + return 0; +fail: + free_AS_REQ(a); + return ret; +} + +static int +set_ptypes(krb5_context context, + KRB_ERROR *error, + const krb5_preauthtype **ptypes, + krb5_preauthdata **preauth) +{ + static krb5_preauthdata preauth2; + static const krb5_preauthtype ptypes2[] = { + KRB5_PADATA_ENC_TIMESTAMP, KRB5_PADATA_NONE + }; + + if(error->e_data) { + METHOD_DATA md; + size_t i; + decode_METHOD_DATA(error->e_data->data, + error->e_data->length, + &md, + NULL); + for(i = 0; i < md.len; i++){ + switch(md.val[i].padata_type){ + case KRB5_PADATA_ENC_TIMESTAMP: + *ptypes = ptypes2; + break; + case KRB5_PADATA_ETYPE_INFO: + *preauth = &preauth2; + ALLOC_SEQ(*preauth, 1); + (*preauth)->val[0].type = KRB5_PADATA_ENC_TIMESTAMP; + decode_ETYPE_INFO(md.val[i].padata_value.data, + md.val[i].padata_value.length, + &(*preauth)->val[0].info, + NULL); + break; + default: + break; + } + } + free_METHOD_DATA(&md); + } else { + *ptypes = ptypes2; + } + return(1); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_in_cred(krb5_context context, + krb5_flags options, + const krb5_addresses *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *ptypes, + const krb5_preauthdata *preauth, + krb5_key_proc key_proc, + krb5_const_pointer keyseed, + krb5_decrypt_proc decrypt_proc, + krb5_const_pointer decryptarg, + krb5_creds *creds, + krb5_kdc_rep *ret_as_reply) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_error_code ret; + AS_REQ a; + krb5_kdc_rep rep; + krb5_data req, resp; + size_t len = 0; + krb5_salt salt; + krb5_keyblock *key; + size_t size; + KDCOptions opts; + PA_DATA *pa; + krb5_enctype etype; + krb5_preauthdata *my_preauth = NULL; + unsigned nonce; + int done; + + opts = int2KDCOptions(options); + + krb5_generate_random_block (&nonce, sizeof(nonce)); + nonce &= 0xffffffff; + + do { + done = 1; + ret = init_as_req (context, + opts, + creds, + addrs, + etypes, + ptypes, + preauth, + key_proc, + keyseed, + nonce, + &a); + if (my_preauth) { + free_ETYPE_INFO(&my_preauth->val[0].info); + free (my_preauth->val); + my_preauth = NULL; + } + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(AS_REQ, req.data, req.length, &a, &len, ret); + free_AS_REQ(&a); + if (ret) + return ret; + if(len != req.length) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_sendto_kdc (context, &req, &creds->client->realm, &resp); + krb5_data_free(&req); + if (ret) + return ret; + + memset (&rep, 0, sizeof(rep)); + ret = decode_AS_REP(resp.data, resp.length, &rep.kdc_rep, &size); + if(ret) { + /* let's try to parse it as a KRB-ERROR */ + KRB_ERROR error; + int ret2; + + ret2 = krb5_rd_error(context, &resp, &error); + if(ret2 && resp.data && ((char*)resp.data)[0] == 4) + ret = KRB5KRB_AP_ERR_V4_REPLY; + krb5_data_free(&resp); + if (ret2 == 0) { + ret = krb5_error_from_rd_error(context, &error, creds); + /* if no preauth was set and KDC requires it, give it + one more try */ + if (!ptypes && !preauth + && ret == KRB5KDC_ERR_PREAUTH_REQUIRED + && set_ptypes(context, &error, &ptypes, &my_preauth)) { + done = 0; + preauth = my_preauth; + krb5_free_error_contents(context, &error); + krb5_clear_error_message(context); + continue; + } + if(ret_as_reply) + ret_as_reply->error = error; + else + free_KRB_ERROR (&error); + return ret; + } + return ret; + } + krb5_data_free(&resp); + } while(!done); + + pa = NULL; + etype = rep.kdc_rep.enc_part.etype; + if(rep.kdc_rep.padata){ + int i = 0; + pa = krb5_find_padata(rep.kdc_rep.padata->val, rep.kdc_rep.padata->len, + KRB5_PADATA_PW_SALT, &i); + if(pa == NULL) { + i = 0; + pa = krb5_find_padata(rep.kdc_rep.padata->val, + rep.kdc_rep.padata->len, + KRB5_PADATA_AFS3_SALT, &i); + } + } + if(pa) { + salt.salttype = (krb5_salttype)pa->padata_type; + salt.saltvalue = pa->padata_value; + + ret = (*key_proc)(context, etype, salt, keyseed, &key); + } else { + /* make a v5 salted pa-data */ + ret = krb5_get_pw_salt (context, creds->client, &salt); + + if (ret) + goto out; + ret = (*key_proc)(context, etype, salt, keyseed, &key); + krb5_free_salt(context, salt); + } + if (ret) + goto out; + + { + unsigned flags = EXTRACT_TICKET_TIMESYNC; + if (opts.request_anonymous) + flags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH | EXTRACT_TICKET_MATCH_ANON; + + ret = _krb5_extract_ticket(context, + &rep, + creds, + key, + keyseed, + KRB5_KU_AS_REP_ENC_PART, + NULL, + nonce, + flags, + NULL, + decrypt_proc, + decryptarg); + } + memset (key->keyvalue.data, 0, key->keyvalue.length); + krb5_free_keyblock_contents (context, key); + free (key); + +out: + if (ret == 0 && ret_as_reply) + *ret_as_reply = rep; + else + krb5_free_kdc_rep (context, &rep); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_in_tkt(krb5_context context, + krb5_flags options, + const krb5_addresses *addrs, + const krb5_enctype *etypes, + const krb5_preauthtype *ptypes, + krb5_key_proc key_proc, + krb5_const_pointer keyseed, + krb5_decrypt_proc decrypt_proc, + krb5_const_pointer decryptarg, + krb5_creds *creds, + krb5_ccache ccache, + krb5_kdc_rep *ret_as_reply) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + krb5_error_code ret; + + ret = krb5_get_in_cred (context, + options, + addrs, + etypes, + ptypes, + NULL, + key_proc, + keyseed, + decrypt_proc, + decryptarg, + creds, + ret_as_reply); + if(ret) + return ret; + if (ccache) + ret = krb5_cc_store_cred (context, ccache, creds); + return ret; +} + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/get_port.c b/third_party/heimdal/lib/krb5/get_port.c new file mode 100644 index 0000000..93d9433 --- /dev/null +++ b/third_party/heimdal/lib/krb5/get_port.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1997-2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +KRB5_LIB_FUNCTION int KRB5_LIB_CALL +krb5_getportbyname (krb5_context context, + const char *service, + const char *proto, + int default_port) +{ + struct servent *sp; + + if ((sp = roken_getservbyname (service, proto)) == NULL) { +#if 0 + krb5_warnx(context, "%s/%s unknown service, using default port %d", + service, proto, default_port); +#endif + return htons(default_port); + } else + return sp->s_port; +} diff --git a/third_party/heimdal/lib/krb5/init_creds.c b/third_party/heimdal/lib/krb5/init_creds.c new file mode 100644 index 0000000..6e77578 --- /dev/null +++ b/third_party/heimdal/lib/krb5/init_creds.c @@ -0,0 +1,482 @@ +/* + * 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 "krb5_locl.h" + +#undef __attribute__ +#define __attribute__(x) + +/** + * @page krb5_init_creds_intro The initial credential handing functions + * @section section_krb5_init_creds Initial credential + * + * Functions to get initial credentials: @ref krb5_credential . + */ + +/** + * Allocate a new krb5_get_init_creds_opt structure, free with + * krb5_get_init_creds_opt_free(). + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_alloc(krb5_context context, + krb5_get_init_creds_opt **opt) +{ + krb5_get_init_creds_opt *o; + + *opt = NULL; + o = calloc(1, sizeof(*o)); + if (o == NULL) + return krb5_enomem(context); + + o->opt_private = calloc(1, sizeof(*o->opt_private)); + if (o->opt_private == NULL) { + free(o); + return krb5_enomem(context); + } + o->opt_private->refcount = 1; + *opt = o; + return 0; +} + +/** + * Free krb5_get_init_creds_opt structure. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_free(krb5_context context, + krb5_get_init_creds_opt *opt) +{ + if (opt == NULL || opt->opt_private == NULL) + return; + if (opt->opt_private->refcount < 1) /* abort ? */ + return; + if (--opt->opt_private->refcount == 0) { + _krb5_get_init_creds_opt_free_pkinit(opt); + free(opt->opt_private->fast_armor_ccache_name); + free(opt->opt_private); + } + memset(opt, 0, sizeof(*opt)); + free(opt); +} + +static int +get_config_time (krb5_context context, + const char *realm, + const char *name, + int def) +{ + int ret; + + ret = krb5_config_get_time (context, NULL, + "realms", + realm, + name, + NULL); + if (ret >= 0) + return ret; + ret = krb5_config_get_time (context, NULL, + "libdefaults", + name, + NULL); + if (ret >= 0) + return ret; + return def; +} + +static krb5_boolean +get_config_bool (krb5_context context, + krb5_boolean def_value, + const char *realm, + const char *name) +{ + krb5_boolean b; + + b = krb5_config_get_bool_default(context, NULL, def_value, + "realms", realm, name, NULL); + if (b != def_value) + return b; + b = krb5_config_get_bool_default (context, NULL, def_value, + "libdefaults", name, NULL); + if (b != def_value) + return b; + return def_value; +} + +/* + * set all the values in `opt' to the appropriate values for + * application `appname' (default to getprogname() if NULL), and realm + * `realm'. First looks in [appdefaults] but falls back to + * [realms] or [libdefaults] for some of the values. + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_default_flags(krb5_context context, + const char *appname, + krb5_const_realm realm, + krb5_get_init_creds_opt *opt) +{ + krb5_boolean b; + time_t t; + + b = get_config_bool (context, KRB5_FORWARDABLE_DEFAULT, + realm, "forwardable"); + krb5_appdefault_boolean(context, appname, realm, "forwardable", b, &b); + krb5_get_init_creds_opt_set_forwardable(opt, b); + + b = get_config_bool (context, FALSE, realm, "proxiable"); + krb5_appdefault_boolean(context, appname, realm, "proxiable", b, &b); + krb5_get_init_creds_opt_set_proxiable (opt, b); + + krb5_appdefault_time(context, appname, realm, "ticket_lifetime", 0, &t); + if (t == 0) + t = get_config_time (context, realm, "ticket_lifetime", 0); + if(t != 0) + krb5_get_init_creds_opt_set_tkt_life(opt, t); + + krb5_appdefault_time(context, appname, realm, "renew_lifetime", 0, &t); + if (t == 0) + t = get_config_time (context, realm, "renew_lifetime", 0); + if(t != 0) + krb5_get_init_creds_opt_set_renew_life(opt, t); + + krb5_appdefault_boolean(context, appname, realm, "no-addresses", + KRB5_ADDRESSLESS_DEFAULT, &b); + krb5_get_init_creds_opt_set_addressless (context, opt, b); + +#if 0 + krb5_appdefault_boolean(context, appname, realm, "anonymous", FALSE, &b); + krb5_get_init_creds_opt_set_anonymous (opt, b); + + krb5_get_init_creds_opt_set_etype_list(opt, enctype, + etype_str.num_strings); + + krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, + krb5_data *salt); + + krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt, + krb5_preauthtype *preauth_list, + int preauth_list_length); +#endif +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt *opt, + int change_password_prompt) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT; + opt->change_password_prompt = change_password_prompt; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt, + krb5_deltat tkt_life) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_TKT_LIFE; + opt->tkt_life = tkt_life; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_renew_life(krb5_get_init_creds_opt *opt, + krb5_deltat renew_life) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE; + opt->renew_life = renew_life; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_forwardable(krb5_get_init_creds_opt *opt, + int forwardable) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_FORWARDABLE; + opt->forwardable = forwardable; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_proxiable(krb5_get_init_creds_opt *opt, + int proxiable) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_PROXIABLE; + opt->proxiable = proxiable; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_etype_list(krb5_get_init_creds_opt *opt, + krb5_enctype *etype_list, + int etype_list_length) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST; + opt->etype_list = etype_list; + opt->etype_list_length = etype_list_length; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_address_list(krb5_get_init_creds_opt *opt, + krb5_addresses *addresses) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST; + opt->address_list = addresses; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_preauth_list(krb5_get_init_creds_opt *opt, + krb5_preauthtype *preauth_list, + int preauth_list_length) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST; + opt->preauth_list_length = preauth_list_length; + opt->preauth_list = preauth_list; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_salt(krb5_get_init_creds_opt *opt, + krb5_data *salt) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_SALT; + opt->salt = salt; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_set_anonymous(krb5_get_init_creds_opt *opt, + int anonymous) +{ + opt->flags |= KRB5_GET_INIT_CREDS_OPT_ANONYMOUS; + opt->anonymous = anonymous; +} + +static krb5_error_code +require_ext_opt(krb5_context context, + krb5_get_init_creds_opt *opt, + const char *type) +{ + if (opt->opt_private == NULL) { + krb5_set_error_message(context, EINVAL, + N_("%s on non extendable opt", ""), type); + return EINVAL; + } + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_pa_password(krb5_context context, + krb5_get_init_creds_opt *opt, + const char *password, + krb5_s2k_proc key_proc) +{ + krb5_error_code ret; + ret = require_ext_opt(context, opt, "init_creds_opt_set_pa_password"); + if (ret) + return ret; + opt->opt_private->password = password; + opt->opt_private->key_proc = key_proc; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_pac_request(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_boolean req_pac) +{ + krb5_error_code ret; + ret = require_ext_opt(context, opt, "init_creds_opt_set_pac_req"); + if (ret) + return ret; + opt->opt_private->req_pac = req_pac ? + KRB5_INIT_CREDS_TRISTATE_TRUE : + KRB5_INIT_CREDS_TRISTATE_FALSE; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_addressless(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_boolean addressless) +{ + krb5_error_code ret; + ret = require_ext_opt(context, opt, "init_creds_opt_set_pac_req"); + if (ret) + return ret; + if (addressless) + opt->opt_private->addressless = KRB5_INIT_CREDS_TRISTATE_TRUE; + else + opt->opt_private->addressless = KRB5_INIT_CREDS_TRISTATE_FALSE; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_canonicalize(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_boolean req) +{ + krb5_error_code ret; + ret = require_ext_opt(context, opt, "init_creds_opt_set_canonicalize"); + if (ret) + return ret; + if (req) + opt->opt_private->flags |= KRB5_INIT_CREDS_CANONICALIZE; + else + opt->opt_private->flags &= ~KRB5_INIT_CREDS_CANONICALIZE; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_win2k(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_boolean req) +{ + krb5_error_code ret; + ret = require_ext_opt(context, opt, "init_creds_opt_set_win2k"); + if (ret) + return ret; + if (req) { + opt->opt_private->flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK; + opt->opt_private->flags |= KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK; + opt->opt_private->flags |= KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK; + } else { + opt->opt_private->flags &= ~KRB5_INIT_CREDS_NO_C_CANON_CHECK; + opt->opt_private->flags &= ~KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK; + opt->opt_private->flags &= ~KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK; + } + return 0; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_process_last_req(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_gic_process_last_req func, + void *ctx) +{ + krb5_error_code ret; + ret = require_ext_opt(context, opt, "init_creds_opt_set_process_last_req"); + if (ret) + return ret; + + opt->opt_private->lr.func = func; + opt->opt_private->lr.ctx = ctx; + + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_fast_ccache(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_ccache fast_ccache) +{ + char *fast_ccache_name; + int ret = krb5_cc_get_full_name(context, + fast_ccache, + &fast_ccache_name); + if (ret) + return ret; + + ret = krb5_get_init_creds_opt_set_fast_ccache_name(context, + opt, + fast_ccache_name); + krb5_xfree(fast_ccache_name); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_fast_ccache_name(krb5_context context, + krb5_get_init_creds_opt *opt, + const char *fast_ccache_name) +{ + if (opt->opt_private->fast_armor_ccache_name) + free(opt->opt_private->fast_armor_ccache_name); + + opt->opt_private->fast_armor_ccache_name = strdup(fast_ccache_name); + if (opt->opt_private->fast_armor_ccache_name == NULL) + return krb5_enomem(context); + + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_set_fast_flags(krb5_context context, + krb5_get_init_creds_opt *opt, + krb5_flags flags) +{ + heim_assert((flags & ~KRB5_FAST_PUBLIC_FLAGS) == 0, "invalid flags passed to krb5_get_init_creds_opt_set_fast_flags()"); + opt->opt_private->fast_flags = flags; + return 0; +} + + + +#ifndef HEIMDAL_SMALLER + +/** + * Deprecated: use krb5_get_init_creds_opt_alloc(). + * + * The reason krb5_get_init_creds_opt_init() is deprecated is that + * krb5_get_init_creds_opt is a static structure and for ABI reason it + * can't grow, ie can't add new functionality. + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_get_init_creds_opt_init(krb5_get_init_creds_opt *opt) + KRB5_DEPRECATED_FUNCTION("Use krb5_get_init_creds_opt_alloc instead") +{ + memset (opt, 0, sizeof(*opt)); +} + +/** + * Deprecated: use the new krb5_init_creds_init() and + * krb5_init_creds_get_error(). + * + * @ingroup krb5_deprecated + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_opt_get_error(krb5_context context, + krb5_get_init_creds_opt *opt, + KRB_ERROR **error) + KRB5_DEPRECATED_FUNCTION("Use X instead") +{ + *error = calloc(1, sizeof(**error)); + if (*error == NULL) + return krb5_enomem(context); + + return 0; +} + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/init_creds_pw.c b/third_party/heimdal/lib/krb5/init_creds_pw.c new file mode 100644 index 0000000..4790c7e --- /dev/null +++ b/third_party/heimdal/lib/krb5/init_creds_pw.c @@ -0,0 +1,4052 @@ +/* + * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved. + * Portions Copyright (c) 2021, PADL Software Pty Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#include + +struct pa_info_data { + krb5_enctype etype; + krb5_salt salt; + krb5_data *s2kparams; +}; + +struct krb5_gss_init_ctx_data { + krb5_gssic_step step; + krb5_gssic_finish finish; + krb5_gssic_release_cred release_cred; + krb5_gssic_delete_sec_context delete_sec_context; + + const struct gss_OID_desc_struct *mech; + struct gss_cred_id_t_desc_struct *cred; + + struct { + unsigned int release_cred : 1; + } flags; +}; + +struct krb5_get_init_creds_ctx { + KDCOptions flags; + krb5_creds cred; + const krb5_addresses *addrs; + krb5_enctype *etypes; + krb5_preauthtype *pre_auth_types; + char *in_tkt_service; + unsigned nonce; + unsigned pk_nonce; + + krb5_data req_buffer; + AS_REQ as_req; + int pa_counter; + + /* password and keytab_data is freed on completion */ + char *password; + krb5_keytab_key_proc_args *keytab_data; + + krb5_pointer *keyseed; + krb5_s2k_proc keyproc; + + krb5_get_init_creds_tristate req_pac; + + krb5_pk_init_ctx pk_init_ctx; + krb5_gss_init_ctx gss_init_ctx; + int ic_flags; + + char *kdc_hostname; + char *sitename; + + struct { + unsigned int change_password:1; + unsigned int change_password_prompt:1; + unsigned int allow_enc_pa_rep:1; + unsigned int allow_save_as_reply_key:1; + } runflags; + + struct pa_info_data paid; + + METHOD_DATA md; + KRB_ERROR error; + EncKDCRepPart enc_part; + + krb5_prompter_fct prompter; + void *prompter_data; + int warned_user; + + struct pa_info_data *ppaid; + + struct krb5_fast_state fast_state; + krb5_enctype as_enctype; + krb5_keyblock *as_reply_key; + + /* current and available pa mechansm in this exchange */ + struct pa_auth_mech *pa_mech; + heim_array_t available_pa_mechs; + const char *pa_used; + + struct { + struct timeval run_time; + } stats; +}; + +static void +free_paid(krb5_context context, struct pa_info_data *ppaid) +{ + krb5_free_salt(context, ppaid->salt); + if (ppaid->s2kparams) + krb5_free_data(context, ppaid->s2kparams); + memset(ppaid, 0, sizeof(*ppaid)); +} + +static krb5_error_code KRB5_CALLCONV +default_s2k_func(krb5_context context, krb5_enctype type, + krb5_const_pointer keyseed, + krb5_salt salt, krb5_data *s2kparms, + krb5_keyblock **key) +{ + krb5_error_code ret; + krb5_data password; + krb5_data opaque; + + if (_krb5_have_debug(context, 5)) { + char *str = NULL; + ret = krb5_enctype_to_string(context, type, &str); + if (ret) + return ret; + + _krb5_debug(context, 5, "krb5_get_init_creds: using default_s2k_func: %s (%d)", str, (int)type); + free(str); + } + + password.data = rk_UNCONST(keyseed); + password.length = keyseed ? strlen(keyseed) : 0; + if (s2kparms) + opaque = *s2kparms; + else + krb5_data_zero(&opaque); + + *key = malloc(sizeof(**key)); + if (*key == NULL) + return krb5_enomem(context); + ret = krb5_string_to_key_data_salt_opaque(context, type, password, + salt, opaque, *key); + if (ret) { + free(*key); + *key = NULL; + } + return ret; +} + +static void +free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic) +{ + if (gssic == NULL) + return; + + if (gssic->flags.release_cred) + gssic->release_cred(context, gssic, gssic->cred); + free(gssic); +} + +static void +free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) +{ + if (ctx->etypes) + free(ctx->etypes); + if (ctx->pre_auth_types) + free (ctx->pre_auth_types); + if (ctx->in_tkt_service) + free(ctx->in_tkt_service); + if (ctx->keytab_data) + free(ctx->keytab_data); + if (ctx->password) { + size_t len; + len = strlen(ctx->password); + memset_s(ctx->password, len, 0, len); + free(ctx->password); + } + free_gss_init_ctx(context, ctx->gss_init_ctx); + /* + * FAST state + */ + _krb5_fast_free(context, &ctx->fast_state); + if (ctx->as_reply_key) + krb5_free_keyblock(context, ctx->as_reply_key); + + krb5_data_free(&ctx->req_buffer); + krb5_free_cred_contents(context, &ctx->cred); + free_METHOD_DATA(&ctx->md); + free_EncKDCRepPart(&ctx->enc_part); + free_KRB_ERROR(&ctx->error); + free_AS_REQ(&ctx->as_req); + + heim_release(ctx->available_pa_mechs); + heim_release(ctx->pa_mech); + ctx->pa_mech = NULL; + free(ctx->kdc_hostname); + free(ctx->sitename); + free_paid(context, &ctx->paid); + memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx)); +} + +static krb5_deltat +get_config_time (krb5_context context, + const char *realm, + const char *name, + int def) +{ + krb5_deltat ret; + + ret = krb5_config_get_time (context, NULL, + "realms", + realm, + name, + NULL); + if (ret >= 0) + return ret; + ret = krb5_config_get_time (context, NULL, + "libdefaults", + name, + NULL); + if (ret >= 0) + return ret; + return def; +} + +static krb5_error_code +init_cred (krb5_context context, + krb5_creds *cred, + krb5_principal client, + krb5_deltat start_time, + krb5_get_init_creds_opt *options) +{ + krb5_error_code ret; + krb5_deltat tmp; + krb5_timestamp now; + + krb5_timeofday (context, &now); + + memset (cred, 0, sizeof(*cred)); + + if (client) + ret = krb5_copy_principal(context, client, &cred->client); + else + ret = krb5_get_default_principal(context, &cred->client); + if (ret) + goto out; + + if (start_time) + cred->times.starttime = now + start_time; + + if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE) + tmp = options->tkt_life; + else + tmp = KRB5_TKT_LIFETIME_DEFAULT; + cred->times.endtime = now + tmp; + + if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) { + if (options->renew_life > 0) + tmp = options->renew_life; + else + tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT; + cred->times.renew_till = now + tmp; + } + + return 0; + +out: + krb5_free_cred_contents (context, cred); + return ret; +} + +/* + * Print a message (str) to the user about the expiration in `lr' + */ + +static void +report_expiration (krb5_context context, + krb5_prompter_fct prompter, + krb5_data *data, + const char *str, + time_t now) +{ + char *p = NULL; + + if (asprintf(&p, "%s%s", str, ctime(&now)) < 0 || p == NULL) + return; + (*prompter)(context, data, NULL, p, 0, NULL); + free(p); +} + +/* + * Check the context, and in the case there is a expiration warning, + * use the prompter to print the warning. + * + * @param context A Kerberos 5 context. + * @param options An GIC options structure + * @param ctx The krb5_init_creds_context check for expiration. + */ + +krb5_error_code +krb5_process_last_request(krb5_context context, + krb5_get_init_creds_opt *options, + krb5_init_creds_context ctx) +{ + LastReq *lr; + size_t i; + + /* + * First check if there is a API consumer. + */ + + lr = &ctx->enc_part.last_req; + + if (options && options->opt_private && options->opt_private->lr.func) { + krb5_last_req_entry **lre; + + lre = calloc(lr->len + 1, sizeof(*lre)); + if (lre == NULL) + return krb5_enomem(context); + + for (i = 0; i < lr->len; i++) { + lre[i] = calloc(1, sizeof(*lre[i])); + if (lre[i] == NULL) + break; + lre[i]->lr_type = lr->val[i].lr_type; + lre[i]->value = lr->val[i].lr_value; + } + + (*options->opt_private->lr.func)(context, lre, + options->opt_private->lr.ctx); + + for (i = 0; i < lr->len; i++) + free(lre[i]); + free(lre); + } + + return krb5_init_creds_warn_user(context, ctx); +} + +/** + * Warn the user using prompter in the krb5_init_creds_context about + * possible password and account expiration. + * + * @param context a Kerberos 5 context. + * @param ctx a krb5_init_creds_context context. + * + * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_warn_user(krb5_context context, + krb5_init_creds_context ctx) +{ + krb5_timestamp sec; + krb5_const_realm realm; + krb5_enctype weak_enctype = KRB5_ENCTYPE_NULL; + LastReq *lr; + unsigned i; + time_t t; + + if (ctx->prompter == NULL) + return 0; + + if (ctx->warned_user) + return 0; + + ctx->warned_user = 1; + + krb5_timeofday (context, &sec); + + realm = krb5_principal_get_realm (context, ctx->cred.client); + lr = &ctx->enc_part.last_req; + + t = sec + get_config_time (context, + realm, + "warn_pwexpire", + 7 * 24 * 60 * 60); + + for (i = 0; i < lr->len; ++i) { + if (lr->val[i].lr_value <= t) { + switch (lr->val[i].lr_type) { + case LR_PW_EXPTIME : + report_expiration(context, ctx->prompter, + ctx->prompter_data, + "Your password will expire at ", + lr->val[i].lr_value); + break; + case LR_ACCT_EXPTIME : + report_expiration(context, ctx->prompter, + ctx->prompter_data, + "Your account will expire at ", + lr->val[i].lr_value); + break; + default: + break; + } + } + } + + if (krb5_is_enctype_weak(context, ctx->as_enctype)) + weak_enctype = ctx->as_enctype; + else if (krb5_is_enctype_weak(context, ctx->cred.session.keytype)) + weak_enctype = ctx->cred.session.keytype; + + if (ctx->prompter && weak_enctype != KRB5_ENCTYPE_NULL) { + int suppress = krb5_config_get_bool_default(context, NULL, false, + "libdefaults", + "suppress_weak_enctype", NULL); + if (!suppress) { + char *str = NULL, *p = NULL; + int aret; + + (void) krb5_enctype_to_string(context, weak_enctype, &str); + aret = asprintf(&p, "Encryption type %s(%d) used for authentication is weak and will be deprecated", + str ? str : "unknown", weak_enctype); + if (aret >= 0 && p) { + (*ctx->prompter)(context, ctx->prompter_data, NULL, p, 0, NULL); + free(p); + } + free(str); + } + } + + return 0; +} + +static const krb5_addresses no_addrs = { 0, NULL }; + +static krb5_error_code +get_init_creds_common(krb5_context context, + krb5_principal client, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_deltat start_time, + krb5_get_init_creds_opt *options, + krb5_init_creds_context ctx) +{ + krb5_get_init_creds_opt *default_opt = NULL; + krb5_error_code ret; + krb5_enctype *etypes; + krb5_preauthtype *pre_auth_types; + + memset(ctx, 0, sizeof(*ctx)); + + if (options == NULL) { + const char *realm = krb5_principal_get_realm(context, client); + + ret = krb5_get_init_creds_opt_alloc(context, &default_opt); + if (ret) + return ret; + options = default_opt; + krb5_get_init_creds_opt_set_default_flags(context, NULL, realm, options); + } + + if (options->opt_private) { + if (options->opt_private->password) { + ret = krb5_init_creds_set_password(context, ctx, + options->opt_private->password); + if (ret) + goto out; + } + + ctx->keyproc = options->opt_private->key_proc; + ctx->req_pac = options->opt_private->req_pac; + ctx->pk_init_ctx = options->opt_private->pk_init_ctx; + ctx->ic_flags = options->opt_private->flags; + } else + ctx->req_pac = KRB5_INIT_CREDS_TRISTATE_UNSET; + + if (ctx->keyproc == NULL) + ctx->keyproc = default_s2k_func; + + if (ctx->ic_flags & KRB5_INIT_CREDS_CANONICALIZE) + ctx->flags.canonicalize = 1; + if (krb5_principal_get_type(context, client) == KRB5_NT_ENTERPRISE_PRINCIPAL) + ctx->flags.canonicalize = 1; + + ctx->pre_auth_types = NULL; + ctx->addrs = NULL; + ctx->etypes = NULL; + ctx->pre_auth_types = NULL; + + ret = init_cred(context, &ctx->cred, client, start_time, options); + if (ret) + goto out; + + ret = krb5_init_creds_set_service(context, ctx, NULL); + if (ret) + goto out; + + if (options->flags & KRB5_GET_INIT_CREDS_OPT_FORWARDABLE) + ctx->flags.forwardable = options->forwardable; + + if (options->flags & KRB5_GET_INIT_CREDS_OPT_PROXIABLE) + ctx->flags.proxiable = options->proxiable; + + if (start_time) + ctx->flags.postdated = 1; + if (ctx->cred.times.renew_till) + ctx->flags.renewable = 1; + if (options->flags & KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST) { + ctx->addrs = options->address_list; + } else if (options->opt_private) { + switch (options->opt_private->addressless) { + case KRB5_INIT_CREDS_TRISTATE_UNSET: +#if KRB5_ADDRESSLESS_DEFAULT == TRUE + ctx->addrs = &no_addrs; +#else + ctx->addrs = NULL; +#endif + break; + case KRB5_INIT_CREDS_TRISTATE_FALSE: + ctx->addrs = NULL; + break; + case KRB5_INIT_CREDS_TRISTATE_TRUE: + ctx->addrs = &no_addrs; + break; + } + } + if (options->flags & KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST) { + if (ctx->etypes) + free(ctx->etypes); + + etypes = malloc((options->etype_list_length + 1) + * sizeof(krb5_enctype)); + if (etypes == NULL) { + ret = krb5_enomem(context); + goto out; + } + memcpy (etypes, options->etype_list, + options->etype_list_length * sizeof(krb5_enctype)); + etypes[options->etype_list_length] = ETYPE_NULL; + ctx->etypes = etypes; + } + if (options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST) { + pre_auth_types = malloc((options->preauth_list_length + 1) + * sizeof(krb5_preauthtype)); + if (pre_auth_types == NULL) { + ret = krb5_enomem(context); + goto out; + } + memcpy (pre_auth_types, options->preauth_list, + options->preauth_list_length * sizeof(krb5_preauthtype)); + pre_auth_types[options->preauth_list_length] = KRB5_PADATA_NONE; + ctx->pre_auth_types = pre_auth_types; + } + if (options->flags & KRB5_GET_INIT_CREDS_OPT_ANONYMOUS) + ctx->flags.request_anonymous = options->anonymous; + + ctx->prompter = prompter; + ctx->prompter_data = prompter_data; + + if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) && + !options->change_password_prompt) + ctx->runflags.change_password_prompt = 0; + else + ctx->runflags.change_password_prompt = ctx->prompter != NULL; + + if (options->opt_private->fast_armor_ccache_name) { + /* Open the caller-supplied FAST ccache and set the caller flags */ + ret = krb5_cc_resolve(context, options->opt_private->fast_armor_ccache_name, + &ctx->fast_state.armor_ccache); + if (ret) + goto out; + } + + ctx->fast_state.flags = options->opt_private->fast_flags; + + /* + * If FAST is required with a real credential cache, then the KDC + * will be verified. This allows the + * krb5_get_init_creds_opt_set_fast API to work like MIT without + * exposing KRB5_FAST_KDC_VERIFIED to callers + */ + if (ctx->fast_state.flags & KRB5_FAST_REQUIRED) + ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED; + + out: + if (default_opt) + krb5_get_init_creds_opt_free(context, default_opt); + return ret; +} + +static krb5_error_code +change_password (krb5_context context, + krb5_principal client, + const char *password, + char *newpw, + size_t newpw_sz, + krb5_prompter_fct prompter, + void *data, + krb5_get_init_creds_opt *old_options) +{ + krb5_prompt prompts[2]; + krb5_error_code ret; + krb5_creds cpw_cred; + char buf1[BUFSIZ], buf2[BUFSIZ]; + krb5_data password_data[2]; + int result_code; + krb5_data result_code_string; + krb5_data result_string; + char *p; + krb5_get_init_creds_opt *options; + + heim_assert(prompter != NULL, "unexpected NULL prompter"); + + memset (&cpw_cred, 0, sizeof(cpw_cred)); + + ret = krb5_get_init_creds_opt_alloc(context, &options); + if (ret) + return ret; + krb5_get_init_creds_opt_set_tkt_life (options, 60); + krb5_get_init_creds_opt_set_forwardable (options, FALSE); + krb5_get_init_creds_opt_set_proxiable (options, FALSE); + if (old_options && + (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)) + krb5_get_init_creds_opt_set_preauth_list(options, + old_options->preauth_list, + old_options->preauth_list_length); + if (old_options && + (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT)) + krb5_get_init_creds_opt_set_change_password_prompt(options, + old_options->change_password_prompt); + + krb5_data_zero (&result_code_string); + krb5_data_zero (&result_string); + + ret = krb5_get_init_creds_password (context, + &cpw_cred, + client, + password, + prompter, + data, + 0, + "kadmin/changepw", + options); + krb5_get_init_creds_opt_free(context, options); + if (ret) + goto out; + + for(;;) { + password_data[0].data = buf1; + password_data[0].length = sizeof(buf1); + + prompts[0].hidden = 1; + prompts[0].prompt = "New password: "; + prompts[0].reply = &password_data[0]; + prompts[0].type = KRB5_PROMPT_TYPE_NEW_PASSWORD; + + password_data[1].data = buf2; + password_data[1].length = sizeof(buf2); + + prompts[1].hidden = 1; + prompts[1].prompt = "Repeat new password: "; + prompts[1].reply = &password_data[1]; + prompts[1].type = KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN; + + ret = (*prompter) (context, data, NULL, "Changing password", + 2, prompts); + if (ret) { + memset (buf1, 0, sizeof(buf1)); + memset (buf2, 0, sizeof(buf2)); + goto out; + } + + if (strcmp (buf1, buf2) == 0) + break; + memset (buf1, 0, sizeof(buf1)); + memset (buf2, 0, sizeof(buf2)); + } + + ret = krb5_set_password (context, + &cpw_cred, + buf1, + client, + &result_code, + &result_code_string, + &result_string); + if (ret) + goto out; + + if (asprintf(&p, "%s: %.*s\n", + result_code ? "Error" : "Success", + (int)result_string.length, + result_string.length > 0 ? (char*)result_string.data : "") < 0) + { + ret = krb5_enomem(context); + goto out; + } + + /* return the result */ + (*prompter) (context, data, NULL, p, 0, NULL); + + if (result_code == 0) { + strlcpy (newpw, buf1, newpw_sz); + ret = 0; + } else { + krb5_set_error_message(context, ret = KRB5_CHPW_FAIL, + N_("failed changing password: %s", ""), p); + } + free (p); + +out: + memset_s(buf1, sizeof(buf1), 0, sizeof(buf1)); + memset_s(buf2, sizeof(buf2), 0, sizeof(buf2)); + krb5_data_free (&result_string); + krb5_data_free (&result_code_string); + krb5_free_cred_contents (context, &cpw_cred); + return ret; +} + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_keyblock_key_proc (krb5_context context, + krb5_keytype type, + krb5_data *salt, + krb5_const_pointer keyseed, + krb5_keyblock **key) +{ + return krb5_copy_keyblock (context, keyseed, key); +} + +/* + * + */ + +static krb5_error_code +init_as_req (krb5_context context, + KDCOptions opts, + const krb5_creds *creds, + const krb5_addresses *addrs, + const krb5_enctype *etypes, + AS_REQ *a) +{ + krb5_error_code ret; + + memset(a, 0, sizeof(*a)); + + a->pvno = 5; + a->msg_type = krb_as_req; + a->req_body.kdc_options = opts; + a->req_body.cname = calloc(1, sizeof(*a->req_body.cname)); + if (a->req_body.cname == NULL) { + ret = krb5_enomem(context); + goto fail; + } + a->req_body.sname = calloc(1, sizeof(*a->req_body.sname)); + if (a->req_body.sname == NULL) { + ret = krb5_enomem(context); + goto fail; + } + + ret = _krb5_principal2principalname (a->req_body.cname, creds->client); + if (ret) + goto fail; + ret = copy_Realm(&creds->client->realm, &a->req_body.realm); + if (ret) + goto fail; + + ret = _krb5_principal2principalname (a->req_body.sname, creds->server); + if (ret) + goto fail; + + if(creds->times.starttime) { + a->req_body.from = malloc(sizeof(*a->req_body.from)); + if (a->req_body.from == NULL) { + ret = krb5_enomem(context); + goto fail; + } + *a->req_body.from = creds->times.starttime; + } + if(creds->times.endtime){ + if ((ALLOC(a->req_body.till, 1)) != NULL) + *a->req_body.till = creds->times.endtime; + else { + ret = krb5_enomem(context); + goto fail; + } + } + if(creds->times.renew_till){ + a->req_body.rtime = malloc(sizeof(*a->req_body.rtime)); + if (a->req_body.rtime == NULL) { + ret = krb5_enomem(context); + goto fail; + } + *a->req_body.rtime = creds->times.renew_till; + } + a->req_body.nonce = 0; + ret = _krb5_init_etype(context, + KRB5_PDU_AS_REQUEST, + &a->req_body.etype.len, + &a->req_body.etype.val, + etypes); + if (ret) + goto fail; + + /* + * This means no addresses + */ + + if (addrs && addrs->len == 0) { + a->req_body.addresses = NULL; + } else { + a->req_body.addresses = malloc(sizeof(*a->req_body.addresses)); + if (a->req_body.addresses == NULL) { + ret = krb5_enomem(context); + goto fail; + } + + if (addrs) + ret = krb5_copy_addresses(context, addrs, a->req_body.addresses); + else { + ret = krb5_get_all_client_addrs (context, a->req_body.addresses); + if(ret == 0 && a->req_body.addresses->len == 0) { + free(a->req_body.addresses); + a->req_body.addresses = NULL; + } + } + if (ret) + goto fail; + } + + a->req_body.enc_authorization_data = NULL; + a->req_body.additional_tickets = NULL; + + a->padata = NULL; + + return 0; + fail: + free_AS_REQ(a); + memset_s(a, sizeof(*a), 0, sizeof(*a)); + return ret; +} + + +static krb5_error_code +set_paid(struct pa_info_data *paid, krb5_context context, + krb5_enctype etype, + krb5_salttype salttype, void *salt_string, size_t salt_len, + krb5_data *s2kparams) +{ + paid->etype = etype; + paid->salt.salttype = salttype; + paid->salt.saltvalue.data = malloc(salt_len + 1); + if (paid->salt.saltvalue.data == NULL) { + krb5_clear_error_message(context); + return krb5_enomem(context); + } + memcpy(paid->salt.saltvalue.data, salt_string, salt_len); + ((char *)paid->salt.saltvalue.data)[salt_len] = '\0'; + paid->salt.saltvalue.length = salt_len; + if (s2kparams) { + krb5_error_code ret; + + ret = krb5_copy_data(context, s2kparams, &paid->s2kparams); + if (ret) { + krb5_clear_error_message(context); + krb5_free_salt(context, paid->salt); + return ret; + } + } else + paid->s2kparams = NULL; + + return 0; +} + +static struct pa_info_data * +pa_etype_info2(krb5_context context, + const krb5_principal client, + const AS_REQ *asreq, + struct pa_info_data *paid, + heim_octet_string *data) +{ + krb5_error_code ret; + ETYPE_INFO2 e; + size_t sz; + size_t i, j; + + memset(&e, 0, sizeof(e)); + ret = decode_ETYPE_INFO2(data->data, data->length, &e, &sz); + if (ret) + goto out; + if (e.len == 0) + goto out; + for (j = 0; j < asreq->req_body.etype.len; j++) { + for (i = 0; i < e.len; i++) { + + if (krb5_enctype_valid(context, e.val[i].etype) != 0) + continue; + + if (asreq->req_body.etype.val[j] == e.val[i].etype) { + krb5_salt salt; + if (e.val[i].salt == NULL) + ret = krb5_get_pw_salt(context, client, &salt); + else { + salt.saltvalue.data = *e.val[i].salt; + salt.saltvalue.length = strlen(*e.val[i].salt); + ret = 0; + } + if (ret == 0) + ret = set_paid(paid, context, e.val[i].etype, + KRB5_PW_SALT, + salt.saltvalue.data, + salt.saltvalue.length, + e.val[i].s2kparams); + if (e.val[i].salt == NULL) + krb5_free_salt(context, salt); + if (ret == 0) { + free_ETYPE_INFO2(&e); + return paid; + } + } + } + } + out: + free_ETYPE_INFO2(&e); + return NULL; +} + +static struct pa_info_data * +pa_etype_info(krb5_context context, + const krb5_principal client, + const AS_REQ *asreq, + struct pa_info_data *paid, + heim_octet_string *data) +{ + krb5_error_code ret; + ETYPE_INFO e; + size_t sz; + size_t i, j; + + memset(&e, 0, sizeof(e)); + ret = decode_ETYPE_INFO(data->data, data->length, &e, &sz); + if (ret) + goto out; + if (e.len == 0) + goto out; + for (j = 0; j < asreq->req_body.etype.len; j++) { + for (i = 0; i < e.len; i++) { + + if (krb5_enctype_valid(context, e.val[i].etype) != 0) + continue; + + if (asreq->req_body.etype.val[j] == e.val[i].etype) { + krb5_salt salt; + salt.salttype = KRB5_PW_SALT; + if (e.val[i].salt == NULL) + ret = krb5_get_pw_salt(context, client, &salt); + else { + salt.saltvalue = *e.val[i].salt; + ret = 0; + } + if (e.val[i].salttype) + salt.salttype = *e.val[i].salttype; + if (ret == 0) { + ret = set_paid(paid, context, e.val[i].etype, + salt.salttype, + salt.saltvalue.data, + salt.saltvalue.length, + NULL); + if (e.val[i].salt == NULL) + krb5_free_salt(context, salt); + } + if (ret == 0) { + free_ETYPE_INFO(&e); + return paid; + } + } + } + } + out: + free_ETYPE_INFO(&e); + return NULL; +} + +static struct pa_info_data * +pa_pw_or_afs3_salt(krb5_context context, + const krb5_principal client, + const AS_REQ *asreq, + struct pa_info_data *paid, + heim_octet_string *data) +{ + krb5_error_code ret; + if (paid->etype == KRB5_ENCTYPE_NULL) + return NULL; + if (krb5_enctype_valid(context, paid->etype) != 0) + return NULL; + + ret = set_paid(paid, context, + paid->etype, + paid->salt.salttype, + data->data, + data->length, + NULL); + if (ret) + return NULL; + return paid; +} + + +static krb5_error_code +make_pa_enc_timestamp(krb5_context context, METHOD_DATA *md, + krb5_enctype etype, krb5_keyblock *key) +{ + PA_ENC_TS_ENC p; + unsigned char *buf; + size_t buf_size; + size_t len = 0; + EncryptedData encdata; + krb5_error_code ret; + int32_t usec; + int usec2; + krb5_crypto crypto; + + krb5_us_timeofday (context, &p.patimestamp, &usec); + usec2 = usec; + p.pausec = &usec2; + + ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_crypto_init(context, key, 0, &crypto); + if (ret) { + free(buf); + return ret; + } + ret = krb5_encrypt_EncryptedData(context, + crypto, + KRB5_KU_PA_ENC_TIMESTAMP, + buf, + len, + 0, + &encdata); + free(buf); + krb5_crypto_destroy(context, crypto); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); + free_EncryptedData(&encdata); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_padata_add(context, md, KRB5_PADATA_ENC_TIMESTAMP, buf, len); + if (ret) + free(buf); + return ret; +} + +static krb5_error_code +add_enc_ts_padata(krb5_context context, + METHOD_DATA *md, + krb5_principal client, + krb5_s2k_proc keyproc, + krb5_const_pointer keyseed, + krb5_enctype *enctypes, + unsigned netypes, + krb5_salt *salt, + krb5_data *s2kparams) +{ + krb5_error_code ret; + krb5_salt salt2; + krb5_enctype *ep; + size_t i; + + memset(&salt2, 0, sizeof(salt2)); + + if(salt == NULL) { + /* default to standard salt */ + ret = krb5_get_pw_salt (context, client, &salt2); + if (ret) + return ret; + salt = &salt2; + } + if (!enctypes) { + enctypes = context->etypes; + netypes = 0; + for (ep = enctypes; *ep != ETYPE_NULL; ep++) + netypes++; + } + + for (i = 0; i < netypes; ++i) { + krb5_keyblock *key; + + _krb5_debug(context, 5, "krb5_get_init_creds: using ENC-TS with enctype %d", enctypes[i]); + + ret = (*keyproc)(context, enctypes[i], keyseed, + *salt, s2kparams, &key); + if (ret) + continue; + ret = make_pa_enc_timestamp (context, md, enctypes[i], key); + krb5_free_keyblock (context, key); + if (ret) + return ret; + } + if(salt == &salt2) + krb5_free_salt(context, salt2); + return 0; +} + +static krb5_error_code +pa_data_to_md_ts_enc(krb5_context context, + const AS_REQ *a, + const krb5_principal client, + krb5_init_creds_context ctx, + struct pa_info_data *ppaid, + METHOD_DATA *md) +{ + if (ctx->keyproc == NULL || ctx->keyseed == NULL) + return 0; + + if (ppaid) { + add_enc_ts_padata(context, md, client, + ctx->keyproc, ctx->keyseed, + &ppaid->etype, 1, + &ppaid->salt, ppaid->s2kparams); + } else { + krb5_salt salt; + + _krb5_debug(context, 5, "krb5_get_init_creds: pa-info not found, guessing salt"); + + /* make a v5 salted pa-data */ + add_enc_ts_padata(context, md, client, + ctx->keyproc, ctx->keyseed, + a->req_body.etype.val, a->req_body.etype.len, + NULL, NULL); + + /* make a v4 salted pa-data */ + salt.salttype = KRB5_PW_SALT; + krb5_data_zero(&salt.saltvalue); + add_enc_ts_padata(context, md, client, + ctx->keyproc, ctx->keyseed, + a->req_body.etype.val, a->req_body.etype.len, + &salt, NULL); + } + return 0; +} + +static krb5_error_code +pa_data_to_key_plain(krb5_context context, + const krb5_principal client, + krb5_init_creds_context ctx, + krb5_salt salt, + krb5_data *s2kparams, + krb5_enctype etype, + krb5_keyblock **key) +{ + krb5_error_code ret; + + ret = (*ctx->keyproc)(context, etype, ctx->keyseed, + salt, s2kparams, key); + return ret; +} + +struct pkinit_context { + unsigned int win2k : 1; + unsigned int used_pkinit : 1; +}; + + +static krb5_error_code +pa_data_to_md_pkinit(krb5_context context, + const AS_REQ *a, + const krb5_principal client, + int win2k, + krb5_init_creds_context ctx, + METHOD_DATA *md) +{ + if (ctx->pk_init_ctx == NULL) + return 0; +#ifdef PKINIT + return _krb5_pk_mk_padata(context, + ctx->pk_init_ctx, + ctx->ic_flags, + win2k, + &a->req_body, + ctx->pk_nonce, + md); +#else + krb5_set_error_message(context, EINVAL, + N_("no support for PKINIT compiled in", "")); + return EINVAL; +#endif +} + +static krb5_error_code +pkinit_configure_ietf(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx) +{ + struct pkinit_context *pkinit_ctx = pa_ctx; + + pkinit_ctx->win2k = 0; + + if (ctx->pk_init_ctx == NULL) + return HEIM_ERR_PA_CANT_CONTINUE; + + return 0; +} + +static krb5_error_code +pkinit_configure_win(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx) +{ + struct pkinit_context *pkinit_ctx = pa_ctx; + + pkinit_ctx->win2k = 1; + pkinit_ctx->used_pkinit = 0; + + if (ctx->pk_init_ctx == NULL) + return HEIM_ERR_PA_CANT_CONTINUE; + + return 0; +} + +static krb5_error_code +pkinit_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) +{ + krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE; + struct pkinit_context *pkinit_ctx = pa_ctx; + + if (rep == NULL) { + if (pkinit_ctx->used_pkinit) { + krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, + "Already tried PKINIT(%s), looping", + pkinit_ctx->win2k ? "win2k" : "ietf"); + } else { + ret = pa_data_to_md_pkinit(context, a, ctx->cred.client, + (pkinit_ctx->win2k != 0), + ctx, out_md); + if (ret == 0) + ret = HEIM_ERR_PA_CONTINUE_NEEDED; + + pkinit_ctx->used_pkinit = 1; + } + } else if (pa) { + ret = _krb5_pk_rd_pa_reply(context, + a->req_body.realm, + ctx->pk_init_ctx, + rep->enc_part.etype, + ctx->pk_nonce, + &ctx->req_buffer, + pa, + &ctx->fast_state.reply_key); + if (ret == 0) + ctx->runflags.allow_save_as_reply_key = 1; + } + + return ret; +} + +static void +pkinit_release(void *pa_ctx) +{ +} + +/* + * GSS-API pre-authentication support + */ + +struct pa_gss_context { + struct gss_ctx_id_t_desc_struct *context_handle; + int open; +}; + +static krb5_error_code +pa_gss_configure(krb5_context context, + krb5_init_creds_context ctx, + void *pa_ctx) +{ + krb5_gss_init_ctx gssic = ctx->gss_init_ctx; + struct pa_gss_context *pa_gss_ctx = pa_ctx; + + if (gssic == NULL) + return HEIM_ERR_PA_CANT_CONTINUE; + + pa_gss_ctx->context_handle = NULL; + pa_gss_ctx->open = 0; + + return 0; +} + +static krb5_error_code +pa_data_to_md_gss(krb5_context context, + const AS_REQ *a, + const krb5_creds *creds, + krb5_init_creds_context ctx, + struct pa_gss_context *pa_gss_ctx, + PA_DATA *pa, + METHOD_DATA *out_md) +{ + krb5_error_code ret; + krb5_gss_init_ctx gssic = ctx->gss_init_ctx; + krb5_data req_body; + krb5_data *input_token, output_token; + size_t len = 0; + + krb5_data_zero(&req_body); + krb5_data_zero(&output_token); + + input_token = pa ? &pa->padata_value : NULL; + + if ((input_token == NULL || input_token->length == 0) && + pa_gss_ctx->context_handle) { + krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE, + "Missing GSS preauthentication data from KDC"); + return HEIM_ERR_PA_CANT_CONTINUE; + } + + ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length, + &ctx->as_req.req_body, &len, ret); + if (ret) + goto out; + heim_assert(req_body.length == len, "ASN.1 internal error"); + + ret = gssic->step(context, gssic, creds, &pa_gss_ctx->context_handle, + ctx->flags, &req_body, + input_token, &output_token); + + /* + * If FAST authenticated the KDC (which will be the case unless anonymous + * PKINIT was used without KDC certificate validation) then we can relax + * the mutual authentication requirement. + */ + if (ret == KRB5_MUTUAL_FAILED && + (ctx->fast_state.flags & KRB5_FAST_EXPECTED) && + (ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED)) + ret = 0; + if (ret == 0) { + /* + * Always require a strengthen key if FAST was used, to avoid a MITM + * attack that could result in unintended privilege escalation should + * the KDC add positive authorization data from the armor ticket. + */ + if ((ctx->fast_state.flags & KRB5_FAST_EXPECTED) && + ctx->fast_state.strengthen_key == NULL) { + krb5_set_error_message(context, HEIM_ERR_PA_CANT_CONTINUE, + "FAST GSS pre-authentication without strengthen key"); + ret = KRB5_KDCREP_MODIFIED; + goto out; + } + + pa_gss_ctx->open = 1; + } + + if (output_token.length) { + ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS, + output_token.data, output_token.length); + if (ret) + goto out; + + krb5_data_zero(&output_token); + } + +out: + krb5_data_free(&output_token); + krb5_data_free(&req_body); + + return ret; +} + +static krb5_error_code +pa_gss_step(krb5_context context, + krb5_init_creds_context ctx, + void *pa_ctx, + PA_DATA *pa, + const AS_REQ *a, + const AS_REP *rep, + METHOD_DATA *in_md, + METHOD_DATA *out_md) +{ + krb5_error_code ret; + krb5_principal cname; + krb5_gss_init_ctx gssic = ctx->gss_init_ctx; + struct pa_gss_context *pa_gss_ctx = pa_ctx; + + heim_assert(gssic != NULL, "invalid context passed to pa_gss_step"); + + if (!pa_gss_ctx->open) { + ret = pa_data_to_md_gss(context, a, &ctx->cred, ctx, + pa_gss_ctx, pa, out_md); + if (ret == HEIM_ERR_PA_CONTINUE_NEEDED && rep) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "KDC sent AS-REP before GSS " + "pre-authentication completed"); + ret = KRB5_KDCREP_MODIFIED; + } else if (ret == 0 && rep == NULL) { + ret = HEIM_ERR_PA_CONTINUE_NEEDED; /* odd number of legs */ + } + if (ret) + return ret; + } else if (pa && pa->padata_value.length) { + krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, + "Already completed GSS pre-authentication"); + return KRB5_GET_IN_TKT_LOOP; + } else if (rep == NULL) { + krb5_set_error_message(context, KRB5_PREAUTH_FAILED, + "Completed GSS pre-authentication before KDC"); + return KRB5_PREAUTH_FAILED; + } + + heim_assert(pa_gss_ctx->open, + "GSS pre-authentication incomplete"); + + ret = gssic->finish(context, gssic, &ctx->cred, + pa_gss_ctx->context_handle, ctx->nonce, + rep->enc_part.etype, &cname, + &ctx->fast_state.reply_key); + if (ret) + return ret; + + { + char *from = NULL; + char *to = NULL; + + if (krb5_unparse_name(context, ctx->cred.client, &from) == 0) { + if (krb5_unparse_name(context, cname, &to) == 0) { + _krb5_debug(context, 1, "pa_gss_step: %s as %s", + from, to); + krb5_xfree(to); + } + krb5_xfree(from); + } + } + + if (krb5_principal_is_federated(context, ctx->cred.client)) { + /* + * The well-known federated name will be replaced with the cname + * in the AS-REP, but save the locally mapped initiator name in the + * cred for logging. + */ + krb5_free_principal(context, ctx->cred.client); + ctx->cred.client = cname; + + ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK; + } else { + krb5_free_principal(context, cname); + } + + ctx->runflags.allow_save_as_reply_key = 1; + + gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle); + pa_gss_ctx->context_handle = NULL; + pa_gss_ctx->open = 0; + + return 0; +} + +static krb5_error_code +pa_gss_restart(krb5_context context, + krb5_init_creds_context ctx, + void *pa_ctx) +{ + krb5_gss_init_ctx gssic = ctx->gss_init_ctx; + struct pa_gss_context *pa_gss_ctx = pa_ctx; + + if (gssic == NULL) + return HEIM_ERR_PA_CANT_CONTINUE; + + gssic->delete_sec_context(context, gssic, pa_gss_ctx->context_handle); + pa_gss_ctx->context_handle = NULL; + pa_gss_ctx->open = 0; + + return 0; +} + +static void +pa_gss_release(void *pa_ctx) +{ +} + +krb5_error_code +_krb5_make_pa_enc_challenge(krb5_context context, + krb5_crypto crypto, + krb5_key_usage usage, + METHOD_DATA *md) +{ + PA_ENC_TS_ENC p; + unsigned char *buf; + size_t buf_size; + size_t len = 0; + EncryptedData encdata; + krb5_error_code ret; + int32_t usec; + int usec2; + + krb5_us_timeofday (context, &p.patimestamp, &usec); + usec2 = usec; + p.pausec = &usec2; + + ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_encrypt_EncryptedData(context, + crypto, + usage, + buf, + len, + 0, + &encdata); + free(buf); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); + free_EncryptedData(&encdata); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len); + if (ret) + free(buf); + return ret; +} + +krb5_error_code +_krb5_validate_pa_enc_challenge(krb5_context context, + krb5_crypto crypto, + krb5_key_usage usage, + EncryptedData *enc_data, + const char *peer_name) +{ + krb5_error_code ret; + krb5_data ts_data; + PA_ENC_TS_ENC p; + time_t timestamp; + int32_t usec; + size_t size; + + ret = krb5_decrypt_EncryptedData(context, crypto, usage, enc_data, &ts_data); + if (ret) + return ret; + + ret = decode_PA_ENC_TS_ENC(ts_data.data, + ts_data.length, + &p, + &size); + krb5_data_free(&ts_data); + if(ret){ + ret = KRB5KDC_ERR_PREAUTH_FAILED; + _krb5_debug(context, 5, "Failed to decode PA-ENC-TS_ENC -- %s", peer_name); + goto out; + } + + krb5_us_timeofday(context, ×tamp, &usec); + + if (krb5_time_abs(timestamp, p.patimestamp) > context->max_skew) { + char client_time[100]; + + krb5_format_time(context, p.patimestamp, + client_time, sizeof(client_time), TRUE); + + ret = KRB5KRB_AP_ERR_SKEW; + _krb5_debug(context, 0, "Too large time skew, " + "client time %s is out by %u > %d seconds -- %s", + client_time, + (unsigned)krb5_time_abs(timestamp, p.patimestamp), + (int)context->max_skew, + peer_name); + } else { + ret = 0; + } + + out: + free_PA_ENC_TS_ENC(&p); + + return ret; +} + + +static struct pa_info_data * +process_pa_info(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, METHOD_DATA *); + + +static krb5_error_code +enc_chal_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) +{ + struct pa_info_data paid, *ppaid; + krb5_keyblock challengekey; + krb5_data pepper1, pepper2; + krb5_crypto crypto = NULL; + krb5_enctype aenctype; + krb5_error_code ret; + + memset(&paid, 0, sizeof(paid)); + + if (rep == NULL) + paid.etype = KRB5_ENCTYPE_NULL; + else + paid.etype = rep->enc_part.etype; + ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md); + + /* + * If we don't have ppaid, it's because the KDC has not sent any + * salt info. Let's do the first roundtrip so the KDC has a chance + * to send some. + */ + if (ppaid == NULL) { + _krb5_debug(context, 5, "no ppaid found"); + return HEIM_ERR_PA_CONTINUE_NEEDED; + } + if (ppaid->etype == KRB5_ENCTYPE_NULL) { + return HEIM_ERR_PA_CANT_CONTINUE; + } + + if (ctx->fast_state.reply_key) + krb5_free_keyblock(context, ctx->fast_state.reply_key); + + ret = pa_data_to_key_plain(context, ctx->cred.client, ctx, + ppaid->salt, ppaid->s2kparams, ppaid->etype, + &ctx->fast_state.reply_key); + free_paid(context, &paid); + if (ret) { + _krb5_debug(context, 5, "enc-chal: failed to build key"); + return ret; + } + + ret = krb5_crypto_init(context, ctx->fast_state.reply_key, 0, &crypto); + if (ret) + return ret; + + krb5_crypto_getenctype(context, ctx->fast_state.armor_crypto, &aenctype); + + pepper1.data = rep ? "kdcchallengearmor" : "clientchallengearmor"; + pepper1.length = strlen(pepper1.data); + pepper2.data = "challengelongterm"; + pepper2.length = strlen(pepper2.data); + + ret = krb5_crypto_fx_cf2(context, ctx->fast_state.armor_crypto, crypto, + &pepper1, &pepper2, aenctype, + &challengekey); + krb5_crypto_destroy(context, crypto); + if (ret) + return ret; + + ret = krb5_crypto_init(context, &challengekey, 0, &crypto); + krb5_free_keyblock_contents(context, &challengekey); + if (ret) + return ret; + + if (rep) { + EncryptedData enc_data; + size_t size; + + _krb5_debug(context, 5, "ENC_CHAL rep key"); + + if (ctx->fast_state.strengthen_key == NULL) { + krb5_crypto_destroy(context, crypto); + _krb5_debug(context, 5, "ENC_CHAL w/o strengthen_key"); + return KRB5_KDCREP_MODIFIED; + } + + if (pa == NULL) { + krb5_crypto_destroy(context, crypto); + _krb5_debug(context, 0, "KDC response missing"); + return HEIM_ERR_PA_CANT_CONTINUE; + } + + ret = decode_EncryptedData(pa->padata_value.data, + pa->padata_value.length, + &enc_data, + &size); + if (ret) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + _krb5_debug(context, 5, "Failed to decode ENC_CHAL KDC reply"); + return ret; + } + + ret = _krb5_validate_pa_enc_challenge(context, crypto, + KRB5_KU_ENC_CHALLENGE_KDC, + &enc_data, + "KDC"); + free_EncryptedData(&enc_data); + krb5_crypto_destroy(context, crypto); + + return ret; + + } else { + + ret = _krb5_make_pa_enc_challenge(context, crypto, + KRB5_KU_ENC_CHALLENGE_CLIENT, + out_md); + krb5_crypto_destroy(context, crypto); + if (ret) { + _krb5_debug(context, 5, "enc-chal: failed build enc challenge"); + return ret; + } + + return HEIM_ERR_PA_CONTINUE_NEEDED; + } +} + +struct enc_ts_context { + int used_pa_types; +#define USED_ENC_TS_GUESS 4 +#define USED_ENC_TS_INFO 8 +#define USED_ENC_TS_RENEG 16 + krb5_principal user; +}; + +static krb5_error_code +enc_ts_restart(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx) +{ + struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; + pactx->used_pa_types = 0; + krb5_free_principal(context, pactx->user); + pactx->user = NULL; + return 0; +} + +static krb5_error_code +enc_ts_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, + const AS_REQ *a, + const AS_REP *rep, + METHOD_DATA *in_md, METHOD_DATA *out_md) +{ + struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; + struct pa_info_data paid, *ppaid; + krb5_error_code ret; + const char *state; + unsigned flag; + + /* + * Keep track of the user we used so that we can restart + * authentication when we get referrals. + */ + + if (pactx->user && !krb5_principal_compare(context, pactx->user, ctx->cred.client)) { + pactx->used_pa_types = 0; + krb5_free_principal(context, pactx->user); + pactx->user = NULL; + } + + if (pactx->user == NULL) { + ret = krb5_copy_principal(context, ctx->cred.client, &pactx->user); + if (ret) + return ret; + } + + memset(&paid, 0, sizeof(paid)); + + if (rep == NULL) + paid.etype = KRB5_ENCTYPE_NULL; + else + paid.etype = rep->enc_part.etype; + + ppaid = process_pa_info(context, ctx->cred.client, a, &paid, in_md); + + if (rep) { + /* + * Some KDC's don't send salt info in the reply when there is + * success pre-auth happened before, so use cached copy (or + * even better, if there is just one pre-auth, save reply-key). + */ + if (ppaid == NULL && ctx->paid.etype != KRB5_ENCTYPE_NULL) { + ppaid = &ctx->paid; + + } else if (ppaid == NULL) { + _krb5_debug(context, 0, "no paid when building key, build a default salt structure ?"); + return HEIM_ERR_PA_CANT_CONTINUE; + } + + ret = pa_data_to_key_plain(context, ctx->cred.client, ctx, + ppaid->salt, ppaid->s2kparams, rep->enc_part.etype, + &ctx->fast_state.reply_key); + free_paid(context, &paid); + return ret; + } + + /* + * If we don't have ppaid, it's because the KDC has not sent any + * salt info. Let's do the first roundtrip so the KDC has a chance + * to send some. + * + * Don't bother guessing, it sounds like a good idea until you run + * into KDCs that are doing failed auth counting based on the + * ENC_TS tries. + * + * Stashing the salt for the next run is a different issue and + * could be considered in the future. + */ + + if (ppaid == NULL) { + _krb5_debug(context, 5, + "TS-ENC: waiting for KDC to set pw-salt/etype_info{,2}"); + return HEIM_ERR_PA_CONTINUE_NEEDED; + } + if (ppaid->etype == KRB5_ENCTYPE_NULL) { + free_paid(context, &paid); + _krb5_debug(context, 5, + "TS-ENC: kdc proposes enctype NULL ?"); + return HEIM_ERR_PA_CANT_CONTINUE; + } + + /* + * We have to allow the KDC to re-negotiate the PA-TS data + * once, this is since a windows read only + * KDC that doesn't have the keys simply guesses what the + * master is supposed to support. The case where this + * breaks is when the RO-KDC is a newer version than the RW-KDC + * and the RO-KDC announced a enctype that the older doesn't + * support. + */ + if (pactx->used_pa_types & USED_ENC_TS_INFO) { + flag = USED_ENC_TS_RENEG; + state = "reneg"; + } else { + flag = USED_ENC_TS_INFO; + state = "info"; + } + + if (pactx->used_pa_types & flag) { + free_paid(context, &paid); + krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, + "Already tried ENC-TS-%s, looping", state); + return KRB5_GET_IN_TKT_LOOP; + } + + pactx->used_pa_types |= flag; + + free_paid(context, &ctx->paid); + ctx->paid = *ppaid; + + ret = pa_data_to_md_ts_enc(context, a, ctx->cred.client, ctx, ppaid, out_md); + if (ret) + return ret; + + return HEIM_ERR_PA_CONTINUE_NEEDED; +} + +static void +enc_ts_release(void *pa_ctx) +{ + struct enc_ts_context *pactx = (struct enc_ts_context *)pa_ctx; + + if (pactx->user) + krb5_free_principal(NULL, pactx->user); +} + +static krb5_error_code +pa_pac_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) +{ + size_t len = 0, length; + krb5_error_code ret; + PA_PAC_REQUEST req; + void *buf; + + switch (ctx->req_pac) { + case KRB5_INIT_CREDS_TRISTATE_UNSET: + return 0; /* don't bother */ + case KRB5_INIT_CREDS_TRISTATE_TRUE: + req.include_pac = 1; + break; + case KRB5_INIT_CREDS_TRISTATE_FALSE: + req.include_pac = 0; + } + + ASN1_MALLOC_ENCODE(PA_PAC_REQUEST, buf, length, + &req, &len, ret); + if (ret) + return ret; + heim_assert(len == length, "internal error in ASN.1 encoder"); + + ret = krb5_padata_add(context, out_md, KRB5_PADATA_PA_PAC_REQUEST, buf, len); + if (ret) + free(buf); + + return 0; +} + +static krb5_error_code +pa_enc_pa_rep_step(krb5_context context, krb5_init_creds_context ctx, void *pa_ctx, PA_DATA *pa, const AS_REQ *a, + const AS_REP *rep, METHOD_DATA *in_md, METHOD_DATA *out_md) +{ + if (ctx->runflags.allow_enc_pa_rep) + return krb5_padata_add(context, out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0); + + return 0; +} + +static krb5_error_code +pa_fx_cookie_step(krb5_context context, + krb5_init_creds_context ctx, + void *pa_ctx, + PA_DATA *pa, + const AS_REQ *a, + const AS_REP *rep, + METHOD_DATA *in_md, + METHOD_DATA *out_md) +{ + krb5_error_code ret; + void *cookie; + PA_DATA *pad; + int idx = 0; + + pad = krb5_find_padata(in_md->val, in_md->len, KRB5_PADATA_FX_COOKIE, &idx); + if (pad == NULL) { + /* + * RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC + * expects at least one more message from the client. + */ + if (ctx->error.error_code == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) + return KRB5_PREAUTH_FAILED; + else + return 0; + } + + cookie = malloc(pad->padata_value.length); + if (cookie == NULL) + return krb5_enomem(context); + + memcpy(cookie, pad->padata_value.data, pad->padata_value.length); + + ret = krb5_padata_add(context, out_md, KRB5_PADATA_FX_COOKIE, + cookie, pad->padata_value.length); + if (ret) + free(cookie); + else + _krb5_debug(context, 5, "Mirrored FX-COOKIE to KDC"); + + return ret; +} + +typedef struct pa_info_data *(*pa_salt_info_f)(krb5_context, const krb5_principal, const AS_REQ *, struct pa_info_data *, heim_octet_string *); +typedef krb5_error_code (*pa_configure_f)(krb5_context, krb5_init_creds_context, void *); +typedef krb5_error_code (*pa_restart_f)(krb5_context, krb5_init_creds_context, void *); +typedef krb5_error_code (*pa_step_f)(krb5_context, krb5_init_creds_context, void *, PA_DATA *, const AS_REQ *, const AS_REP *, METHOD_DATA *, METHOD_DATA *); +typedef void (*pa_release_f)(void *); + +static const struct patype { + int type; + const char *name; + int flags; +#define PA_F_ANNOUNCE 1 +#define PA_F_CONFIG 2 +#define PA_F_FAST 4 /* available inside FAST */ +#define PA_F_NOT_FAST 8 /* only available without FAST */ + size_t pa_ctx_size; + pa_salt_info_f salt_info; + /** + * Return 0 if the PA-mechanism is available and optionally set pa_ctx pointer to non-NULL. + */ + pa_configure_f configure; + /** + * Return 0 if the PA-mechanism can be restarted (time skew, referrals, etc) + */ + pa_restart_f restart; + /** + * Return 0 when complete, HEIM_ERR_PA_CONTINUE_NEEDED if more steps are required + */ + pa_step_f step; + pa_release_f release; +} patypes[] = { + { + KRB5_PADATA_PK_AS_REP, + "PKINIT(IETF)", + PA_F_FAST | PA_F_NOT_FAST, + sizeof(struct pkinit_context), + NULL, + pkinit_configure_ietf, + NULL, + pkinit_step, + pkinit_release + }, + { + KRB5_PADATA_PK_AS_REP_19, + "PKINIT(win)", + PA_F_FAST | PA_F_NOT_FAST, + sizeof(struct pkinit_context), + NULL, + pkinit_configure_win, + NULL, + pkinit_step, + pkinit_release + }, + { + KRB5_PADATA_GSS, + "GSS", + PA_F_FAST | PA_F_NOT_FAST, + sizeof(struct pa_gss_context), + NULL, + pa_gss_configure, + pa_gss_restart, + pa_gss_step, + pa_gss_release + }, + { + KRB5_PADATA_ENCRYPTED_CHALLENGE, + "ENCRYPTED_CHALLENGE", + PA_F_FAST, + 0, + NULL, + NULL, + NULL, + enc_chal_step, + NULL + }, + { + KRB5_PADATA_ENC_TIMESTAMP, + "ENCRYPTED_TIMESTAMP", + PA_F_NOT_FAST, + sizeof(struct enc_ts_context), + NULL, + NULL, + enc_ts_restart, + enc_ts_step, + enc_ts_release + }, + { + KRB5_PADATA_PA_PAC_REQUEST, + "PA_PAC_REQUEST", + PA_F_CONFIG, + 0, + NULL, + NULL, + NULL, + pa_pac_step, + NULL + }, + { + KRB5_PADATA_REQ_ENC_PA_REP, + "REQ-ENC-PA-REP", + PA_F_CONFIG, + 0, + NULL, + NULL, + NULL, + pa_enc_pa_rep_step, + NULL + }, + { + KRB5_PADATA_FX_COOKIE, + "FX-COOKIE", + PA_F_CONFIG, + 0, + NULL, + NULL, + NULL, + pa_fx_cookie_step, + NULL + }, +#define patype_salt(n, f) { KRB5_PADATA_##n, #n, 0, 0, f, NULL, NULL, NULL, NULL } + patype_salt(ETYPE_INFO2, pa_etype_info2), + patype_salt(ETYPE_INFO, pa_etype_info), + patype_salt(PW_SALT, pa_pw_or_afs3_salt), + patype_salt(AFS3_SALT, pa_pw_or_afs3_salt), +#undef patype_salt + /* below are just for pretty printing */ +#define patype_info(n) { KRB5_PADATA_##n, #n, 0, 0, NULL, NULL, NULL, NULL, NULL } + patype_info(AUTHENTICATION_SET), + patype_info(AUTH_SET_SELECTED), + patype_info(FX_FAST), + patype_info(FX_ERROR), + patype_info(PKINIT_KX), + patype_info(PK_AS_REQ) +#undef patype_info +}; + +static const char * +get_pa_type_name(int type) +{ + size_t n; + for (n = 0; n < sizeof(patypes)/sizeof(patypes[0]); n++) + if (type == patypes[n].type) + return patypes[n].name; + return "unknown"; +} + +/* + * + */ + +struct pa_auth_mech { + const struct patype *patype; + struct pa_auth_mech *next; /* when doing authentication sets */ + char pactx[1]; +}; + +/* + * + */ + +static struct pa_info_data * +process_pa_info(krb5_context context, + const krb5_principal client, + const AS_REQ *asreq, + struct pa_info_data *paid, + METHOD_DATA *md) +{ + struct pa_info_data *p = NULL; + PA_DATA *pa; + size_t i; + + if (md == NULL) + return NULL; + + for (i = 0; p == NULL && i < sizeof(patypes)/sizeof(patypes[0]); i++) { + int idx = 0; + + if (patypes[i].salt_info == NULL) + continue; + + pa = krb5_find_padata(md->val, md->len, patypes[i].type, &idx); + if (pa == NULL) + continue; + + paid->salt.salttype = (krb5_salttype)patypes[i].type; + p = patypes[i].salt_info(context, client, asreq, paid, &pa->padata_value); + } + return p; +} + +static krb5_error_code +pa_announce(krb5_context context, + int types, + krb5_init_creds_context ctx, + METHOD_DATA *in_md, + METHOD_DATA *out_md) +{ + krb5_error_code ret = 0; + size_t n; + + for (n = 0; ret == 0 && n < sizeof(patypes)/sizeof(patypes[0]); n++) { + if ((patypes[n].flags & types) == 0) + continue; + + if (patypes[n].step) + patypes[n].step(context, ctx, NULL, NULL, NULL, NULL, in_md, out_md); + else + ret = krb5_padata_add(context, out_md, patypes[n].type, NULL, 0); + } + return ret; +} + + +static void HEIM_CALLCONV +mech_dealloc(void *ctx) +{ + struct pa_auth_mech *pa_mech = ctx; + if (pa_mech->patype->release) + pa_mech->patype->release((void *)&pa_mech->pactx[0]); +} + +static const struct heim_type_data pa_auth_mech_object = { + HEIM_TID_PA_AUTH_MECH, + "heim-pa-mech-context", + NULL, + mech_dealloc, + NULL, + NULL, + NULL, + NULL +}; + +static struct pa_auth_mech * +pa_mech_create(krb5_context context, krb5_init_creds_context ctx, int pa_type) +{ + struct pa_auth_mech *pa_mech; + const struct patype *patype = NULL; + size_t n; + + for (n = 0; patype == NULL && n < sizeof(patypes)/sizeof(patypes[0]); n++) { + if (patypes[n].type == pa_type) + patype = &patypes[n]; + } + if (patype == NULL) + return NULL; + + pa_mech = _heim_alloc_object(&pa_auth_mech_object, sizeof(*pa_mech) - 1 + patype->pa_ctx_size); + if (pa_mech == NULL) + return NULL; + + pa_mech->patype = patype; + + if (pa_mech->patype->configure) { + krb5_error_code ret; + + ret = pa_mech->patype->configure(context, ctx, &pa_mech->pactx[0]); + if (ret) { + heim_release(pa_mech); + return NULL; + } + } + + _krb5_debug(context, 5, "Adding PA mech: %s", patype->name); + + return pa_mech; +} + +static void +pa_mech_add(krb5_context context, krb5_init_creds_context ctx, int pa_type) +{ + struct pa_auth_mech *mech; + + mech = pa_mech_create(context, ctx, pa_type); + if (mech) { + heim_array_append_value(ctx->available_pa_mechs, mech); + heim_release(mech); + } +} + +static krb5_error_code +pa_configure(krb5_context context, + krb5_init_creds_context ctx, + METHOD_DATA *in_md) +{ + ctx->available_pa_mechs = heim_array_create(); + + if (ctx->gss_init_ctx) { + pa_mech_add(context, ctx, KRB5_PADATA_GSS); + } else if (ctx->pk_init_ctx) { + pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP); + pa_mech_add(context, ctx, KRB5_PADATA_PK_AS_REP_19); + } else if (ctx->keyproc || ctx->keyseed || ctx->prompter) { + pa_mech_add(context, ctx, KRB5_PADATA_ENCRYPTED_CHALLENGE); + pa_mech_add(context, ctx, KRB5_PADATA_ENC_TIMESTAMP); + } + /* XXX setup context based on KDC reply */ + + return 0; +} + +static krb5_error_code +pa_restart(krb5_context context, + krb5_init_creds_context ctx) +{ + krb5_error_code ret = HEIM_ERR_PA_CANT_CONTINUE; + + if (ctx->pa_mech && ctx->pa_mech->patype->restart) + ret = ctx->pa_mech->patype->restart(context, ctx, (void *)&ctx->pa_mech->pactx[0]); + + return ret; +} + + +static krb5_error_code +pa_step(krb5_context context, + krb5_init_creds_context ctx, + const AS_REQ *a, + const AS_REP *rep, + METHOD_DATA *in_md, + METHOD_DATA *out_md) +{ + krb5_error_code ret; + PA_DATA *pa = NULL; + int idx; + + next: + do { + if (ctx->pa_mech == NULL) { + size_t len = heim_array_get_length(ctx->available_pa_mechs); + if (len == 0) { + _krb5_debug(context, 0, "no more available_pa_mechs to try"); + return HEIM_ERR_NO_MORE_PA_MECHS; + } + + ctx->pa_mech = heim_array_copy_value(ctx->available_pa_mechs, 0); + heim_array_delete_value(ctx->available_pa_mechs, 0); + } + + if (ctx->fast_state.armor_crypto) { + if ((ctx->pa_mech->patype->flags & PA_F_FAST) == 0) { + _krb5_debug(context, 0, "pa-mech %s dropped under FAST (not supported)", + ctx->pa_mech->patype->name); + heim_release(ctx->pa_mech); + ctx->pa_mech = NULL; + continue; + } + } else { + if ((ctx->pa_mech->patype->flags & PA_F_NOT_FAST) == 0) { + _krb5_debug(context, 0, "dropped pa-mech %s since not running under FAST", + ctx->pa_mech->patype->name); + heim_release(ctx->pa_mech); + ctx->pa_mech = NULL; + continue; + } + } + + _krb5_debug(context, 0, "pa-mech trying: %s, searching for %d", + ctx->pa_mech->patype->name, ctx->pa_mech->patype->type); + + idx = 0; + if (in_md) + pa = krb5_find_padata(in_md->val, in_md->len, ctx->pa_mech->patype->type, &idx); + else + pa = NULL; + + } while (ctx->pa_mech == NULL); + + _krb5_debug(context, 5, "Stepping pa-mech: %s", ctx->pa_mech->patype->name); + + ret = ctx->pa_mech->patype->step(context, ctx, (void *)&ctx->pa_mech->pactx[0], pa, a, rep, in_md, out_md); + _krb5_debug(context, 10, "PA type %s returned %d", ctx->pa_mech->patype->name, ret); + if (ret == 0) { + struct pa_auth_mech *next_pa = ctx->pa_mech->next; + + if (next_pa) { + _krb5_debug(context, 5, "Next PA type in set is: %s", + next_pa->patype->name); + ret = HEIM_ERR_PA_CONTINUE_NEEDED; + } else if (rep == NULL) { + _krb5_debug(context, 5, "PA %s done, but no ticket in sight!!!", + ctx->pa_mech->patype->name); + ret = HEIM_ERR_PA_CANT_CONTINUE; + } else { + ctx->pa_used = ctx->pa_mech->patype->name; + } + + heim_retain(next_pa); + heim_release(ctx->pa_mech); + ctx->pa_mech = next_pa; + } + + if (ret == HEIM_ERR_PA_CANT_CONTINUE) { + if (ctx->pa_mech) { + _krb5_debug(context, 5, "Dropping PA type %s", ctx->pa_mech->patype->name); + heim_release(ctx->pa_mech); + ctx->pa_mech = NULL; + } + goto next; + } else if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { + _krb5_debug(context, 5, "Continue needed for %s", ctx->pa_mech->patype->name); + } else if (ret != 0) { + _krb5_debug(context, 5, "Other error from mech %s: %d", ctx->pa_mech->patype->name, ret); + heim_release(ctx->pa_mech); + ctx->pa_mech = NULL; + } + + return ret; +} + +static void +log_kdc_pa_types(krb5_context context, METHOD_DATA *in_md) +{ + if (_krb5_have_debug(context, 5)) { + unsigned i; + _krb5_debug(context, 5, "KDC sent %d patypes", in_md->len); + for (i = 0; i < in_md->len; i++) + _krb5_debug(context, 5, "KDC sent PA-DATA type: %d (%s)", + in_md->val[i].padata_type, + get_pa_type_name(in_md->val[i].padata_type)); + } +} + +/* + * Assumes caller always will free `out_md', even on error. + */ + +static krb5_error_code +process_pa_data_to_md(krb5_context context, + const krb5_creds *creds, + const AS_REQ *a, + krb5_init_creds_context ctx, + METHOD_DATA *in_md, + METHOD_DATA **out_md) +{ + krb5_error_code ret; + + ALLOC(*out_md, 1); + if (*out_md == NULL) { + return krb5_enomem(context); + } + (*out_md)->len = 0; + (*out_md)->val = NULL; + + log_kdc_pa_types(context, in_md); + + ret = pa_step(context, ctx, a, NULL, in_md, *out_md); + if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { + _krb5_debug(context, 0, "pamech need more stepping"); + } else if (ret == 0) { + _krb5_debug(context, 0, "pamech done step"); + } else { + return ret; + } + + /* + * Send announcement (what we support) and configuration (user + * introduced behavior change) + */ + ret = pa_announce(context, PA_F_ANNOUNCE|PA_F_CONFIG, ctx, in_md, *out_md); + + /* + * + */ + + if ((*out_md)->len == 0) { + free(*out_md); + *out_md = NULL; + } + + return ret; +} + +static krb5_error_code +process_pa_data_to_key(krb5_context context, + krb5_init_creds_context ctx, + krb5_creds *creds, + AS_REQ *a, + AS_REP *rep, + krb5_keyblock **key) +{ + struct pa_info_data paid, *ppaid = NULL; + krb5_error_code ret; + krb5_enctype etype = rep->enc_part.etype; + + memset(&paid, 0, sizeof(paid)); + + if (rep->padata) + log_kdc_pa_types(context, rep->padata); + + if (rep->padata) { + paid.etype = etype; + ppaid = process_pa_info(context, creds->client, a, &paid, + rep->padata); + } + if (ppaid == NULL) { + if (ctx->paid.etype == KRB5_ENCTYPE_NULL) { + ctx->paid.etype = etype; + ctx->paid.s2kparams = NULL; + ret = krb5_get_pw_salt (context, creds->client, &ctx->paid.salt); + if (ret) + return ret; + } + } + + ret = pa_step(context, ctx, a, rep, rep->padata, NULL); + if (ret == HEIM_ERR_PA_CONTINUE_NEEDED) { + _krb5_debug(context, 0, "In final stretch and pa require more stepping ?"); + return ret; + } else if (ret == 0) { + _krb5_debug(context, 0, "final pamech done step"); + goto out; + } else { + return ret; + } + out: + free_paid(context, &paid); + return ret; +} + +/* + * + */ + +static krb5_error_code +capture_lkdc_domain(krb5_context context, + krb5_init_creds_context ctx) +{ + size_t len; + + len = strlen(_krb5_wellknown_lkdc); + + if (ctx->kdc_hostname != NULL || + strncmp(ctx->cred.client->realm, _krb5_wellknown_lkdc, len) != 0 || + ctx->cred.client->realm[len] != ':') + return 0; + + ctx->kdc_hostname = strdup(&ctx->cred.client->realm[len + 1]); + + _krb5_debug(context, 5, "krb5_get_init_creds: setting LKDC hostname to: %s", + ctx->kdc_hostname); + return 0; +} + +/** + * Start a new context to get a new initial credential. + * + * @param context A Kerberos 5 context. + * @param client The Kerberos principal to get the credential for, if + * NULL is given, the default principal is used as determined by + * krb5_get_default_principal(). + * @param prompter + * @param prompter_data + * @param start_time the time the ticket should start to be valid or 0 for now. + * @param options a options structure, can be NULL for default options. + * @param rctx A new allocated free with krb5_init_creds_free(). + * + * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message(). + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_init(krb5_context context, + krb5_principal client, + krb5_prompter_fct prompter, + void *prompter_data, + krb5_deltat start_time, + krb5_get_init_creds_opt *options, + krb5_init_creds_context *rctx) +{ + krb5_init_creds_context ctx; + krb5_error_code ret; + + *rctx = NULL; + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) + return krb5_enomem(context); + + ret = get_init_creds_common(context, client, prompter, prompter_data, + start_time, options, ctx); + if (ret) { + free(ctx); + return ret; + } + + /* Set a new nonce. */ + /* FIXME should generate a new nonce for each AS-REQ */ + krb5_generate_random_block (&ctx->nonce, sizeof(ctx->nonce)); + ctx->nonce &= 0x7fffffff; + /* XXX these just need to be the same when using Windows PK-INIT */ + ctx->pk_nonce = ctx->nonce; + + ctx->prompter = prompter; + ctx->prompter_data = prompter_data; + + /* pick up hostname from LKDC realm name */ + ret = capture_lkdc_domain(context, ctx); + if (ret) { + free_init_creds_ctx(context, ctx); + return ret; + } + + ctx->runflags.allow_enc_pa_rep = 1; + + ctx->fast_state.flags |= KRB5_FAST_AS_REQ; + + *rctx = ctx; + + return ret; +} + +/** + * Set the KDC hostname for the initial request, it will not be + * considered in referrals to another KDC. + * + * @param context a Kerberos 5 context. + * @param ctx a krb5_init_creds_context context. + * @param hostname the hostname for the KDC of realm + * + * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). + * @ingroup krb5_credential + */ + +krb5_error_code KRB5_LIB_FUNCTION +krb5_init_creds_set_kdc_hostname(krb5_context context, + krb5_init_creds_context ctx, + const char *hostname) +{ + if (ctx->kdc_hostname) + free(ctx->kdc_hostname); + ctx->kdc_hostname = strdup(hostname); + if (ctx->kdc_hostname == NULL) + return krb5_enomem(context); + return 0; +} + +/** + * Set the sitename for the request + * + */ + +krb5_error_code KRB5_LIB_FUNCTION +krb5_init_creds_set_sitename(krb5_context context, + krb5_init_creds_context ctx, + const char *sitename) +{ + if (ctx->sitename) + free(ctx->sitename); + ctx->sitename = strdup(sitename); + if (ctx->sitename == NULL) + return krb5_enomem(context); + return 0; +} + +/** + * Sets the service that the is requested. This call is only neede for + * special initial tickets, by default the a krbtgt is fetched in the default realm. + * + * @param context a Kerberos 5 context. + * @param ctx a krb5_init_creds_context context. + * @param service the service given as a string, for example + * "kadmind/admin". If NULL, the default krbtgt in the clients + * realm is set. + * + * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_service(krb5_context context, + krb5_init_creds_context ctx, + const char *service) +{ + krb5_const_realm client_realm; + krb5_principal principal; + krb5_error_code ret; + + client_realm = krb5_principal_get_realm (context, ctx->cred.client); + + if (service) { + ret = krb5_parse_name (context, service, &principal); + if (ret) + return ret; + ret = krb5_principal_set_realm (context, principal, client_realm); + if (ret) { + krb5_free_principal(context, principal); + return ret; + } + } else { + ret = krb5_make_principal(context, &principal, + client_realm, KRB5_TGS_NAME, client_realm, + NULL); + if (ret) + return ret; + } + + /* + * This is for Windows RODC that are picky about what name type + * the server principal have, and the really strange part is that + * they are picky about the AS-REQ name type and not the TGS-REQ + * later. Oh well. + */ + + if (krb5_principal_is_krbtgt(context, principal)) + krb5_principal_set_type(context, principal, KRB5_NT_SRV_INST); + + krb5_free_principal(context, ctx->cred.server); + ctx->cred.server = principal; + + return 0; +} + +/** + * Sets the password that will use for the request. + * + * @param context a Kerberos 5 context. + * @param ctx ctx krb5_init_creds_context context. + * @param password the password to use. + * + * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_password(krb5_context context, + krb5_init_creds_context ctx, + const char *password) +{ + if (ctx->password) { + size_t len; + len = strlen(ctx->password); + memset_s(ctx->password, len, 0, len); + free(ctx->password); + } + if (password) { + ctx->password = strdup(password); + if (ctx->password == NULL) + return krb5_enomem(context); + ctx->keyseed = (void *) ctx->password; + } else { + ctx->keyseed = NULL; + ctx->password = NULL; + } + + return 0; +} + +static krb5_error_code KRB5_CALLCONV +keytab_key_proc(krb5_context context, krb5_enctype enctype, + krb5_const_pointer keyseed, + krb5_salt salt, krb5_data *s2kparms, + krb5_keyblock **key) +{ + krb5_keytab_key_proc_args *args = rk_UNCONST(keyseed); + krb5_keytab keytab = args->keytab; + krb5_principal principal = args->principal; + krb5_error_code ret; + krb5_keytab real_keytab = NULL; + krb5_keytab_entry entry; + + if (keytab == NULL) { + ret = krb5_kt_default(context, &real_keytab); + if (ret) + return ret; + keytab = real_keytab; + } + + ret = krb5_kt_get_entry (context, keytab, principal, 0, enctype, &entry); + if (ret == 0) { + ret = krb5_copy_keyblock(context, &entry.keyblock, key); + krb5_kt_free_entry(context, &entry); + } + + krb5_kt_close(context, real_keytab); + return ret; +} + + +/** + * Set the keytab to use for authentication. + * + * @param context a Kerberos 5 context. + * @param ctx ctx krb5_init_creds_context context. + * @param keytab the keytab to read the key from. + * + * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message(). + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_keytab(krb5_context context, + krb5_init_creds_context ctx, + krb5_keytab keytab) +{ + krb5_keytab_key_proc_args *a; + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + krb5_enctype *etypes = NULL; + krb5_error_code ret; + size_t netypes = 0; + int kvno = 0, found = 0; + unsigned n; + + a = malloc(sizeof(*a)); + if (a == NULL) + return krb5_enomem(context); + + a->principal = ctx->cred.client; + a->keytab = keytab; + + ctx->keytab_data = a; + ctx->keyseed = (void *)a; + ctx->keyproc = keytab_key_proc; + + /* + * We need to tell the KDC what enctypes we support for this keytab, + * especially if the keytab is really a password based entry, then the + * KDC might have more enctypes in the database then what we have + * in the keytab. + */ + + ret = krb5_kt_start_seq_get(context, keytab, &cursor); + if(ret) + goto out; + + while(krb5_kt_next_entry(context, keytab, &entry, &cursor) == 0){ + void *ptr; + + if (!krb5_principal_compare(context, entry.principal, ctx->cred.client)) + goto next; + + found = 1; + + /* check if we have this kvno already */ + if (entry.vno > kvno) { + /* remove old list of etype */ + if (etypes) + free(etypes); + etypes = NULL; + netypes = 0; + kvno = entry.vno; + } else if (entry.vno != kvno) + goto next; + + /* check if enctype is supported */ + if (krb5_enctype_valid(context, entry.keyblock.keytype) != 0) + goto next; + + /* + * If user already provided a enctype list, use that as an + * additonal filter. + */ + if (ctx->etypes) { + for (n = 0; ctx->etypes[n] != KRB5_ENCTYPE_NULL; n++) { + if (ctx->etypes[n] == entry.keyblock.keytype) + break; + } + if (ctx->etypes[n] == KRB5_ENCTYPE_NULL) + goto next; + } + + /* add enctype to supported list */ + ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2)); + if (ptr == NULL) { + free(etypes); + ret = krb5_enomem(context); + goto out; + } + + etypes = ptr; + etypes[netypes] = entry.keyblock.keytype; + etypes[netypes + 1] = ETYPE_NULL; + netypes++; + next: + krb5_kt_free_entry(context, &entry); + } + krb5_kt_end_seq_get(context, keytab, &cursor); + + if (etypes) { + if (ctx->etypes) + free(ctx->etypes); + ctx->etypes = etypes; + } + + out: + if (!found) { + if (ret == 0) + ret = KRB5_KT_NOTFOUND; + _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0); + } + + return ret; +} + +static krb5_error_code KRB5_CALLCONV +keyblock_key_proc(krb5_context context, krb5_enctype enctype, + krb5_const_pointer keyseed, + krb5_salt salt, krb5_data *s2kparms, + krb5_keyblock **key) +{ + return krb5_copy_keyblock (context, keyseed, key); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_keyblock(krb5_context context, + krb5_init_creds_context ctx, + krb5_keyblock *keyblock) +{ + ctx->keyseed = (void *)keyblock; + ctx->keyproc = keyblock_key_proc; + + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_fast_ccache(krb5_context context, + krb5_init_creds_context ctx, + krb5_ccache fast_ccache) +{ + ctx->fast_state.armor_ccache = fast_ccache; + ctx->fast_state.flags |= KRB5_FAST_REQUIRED; + ctx->fast_state.flags |= KRB5_FAST_KDC_VERIFIED; + return 0; +} + +static krb5_error_code +validate_pkinit_fx(krb5_context context, + krb5_init_creds_context ctx, + AS_REP *rep, + krb5_keyblock *ticket_sessionkey) +{ + PA_DATA *pa = NULL; + int idx = 0; + + if (rep->padata) + pa = krb5_find_padata(rep->padata->val, rep->padata->len, KRB5_PADATA_PKINIT_KX, &idx); + + if (pa == NULL) { + if (ctx->flags.request_anonymous && ctx->pk_init_ctx) { + /* XXX handle the case where pkinit is not used */ + krb5_set_error_message(context, KRB5_KDCREP_MODIFIED, + N_("Requested anonymous with PKINIT and KDC didn't set PKINIT_KX", "")); + return KRB5_KDCREP_MODIFIED; + } + + return 0; + } + + heim_assert(ctx->fast_state.reply_key != NULL, "must have a reply key at this stage"); + + return _krb5_pk_kx_confirm(context, + ctx->pk_init_ctx, + ctx->fast_state.reply_key, + ticket_sessionkey, + pa); +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_fast_ap_armor_service(krb5_context context, + krb5_init_creds_context ctx, + krb5_const_principal armor_service) +{ + krb5_error_code ret; + + if (ctx->fast_state.armor_service) + krb5_free_principal(context, ctx->fast_state.armor_service); + if (armor_service) { + ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service); + if (ret) + return ret; + } else { + ctx->fast_state.armor_service = NULL; + } + ctx->fast_state.flags |= KRB5_FAST_AP_ARMOR_SERVICE; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_fast_anon_pkinit(krb5_context context, + krb5_init_creds_context ctx) +{ + if (ctx->fast_state.armor_ccache) + return EINVAL; + + ctx->fast_state.flags |= KRB5_FAST_REQUIRED; + ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR; + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_init_creds_set_fast_anon_pkinit_optimistic(krb5_context context, + krb5_init_creds_context ctx) +{ + if (ctx->fast_state.armor_ccache) + return EINVAL; + + ctx->fast_state.flags |= KRB5_FAST_REQUIRED; + ctx->fast_state.flags |= KRB5_FAST_ANON_PKINIT_ARMOR; + ctx->fast_state.flags |= KRB5_FAST_OPTIMISTIC; + return 0; +} + +static size_t +available_padata_count(METHOD_DATA *md) +{ + size_t i, count = 0; + + for (i = 0; i < md->len; i++) { + PA_DATA *pa = &md->val[i]; + + if (pa->padata_type == KRB5_PADATA_FX_COOKIE || + pa->padata_type == KRB5_PADATA_FX_ERROR) + continue; + + count++; + } + + return count; +} + +static krb5_error_code +init_creds_step(krb5_context context, + krb5_init_creds_context ctx, + const krb5_data *in, + krb5_data *out, + krb5_realm *out_realm, + unsigned int *flags) +{ + struct timeval start_time, end_time; + krb5_data checksum_data; + krb5_error_code ret; + size_t len = 0; + size_t size; + AS_REQ req2; + + gettimeofday(&start_time, NULL); + + krb5_data_zero(out); + *out_realm = NULL; + krb5_data_zero(&checksum_data); + + if (ctx->as_req.req_body.cname == NULL) { + ret = init_as_req(context, ctx->flags, &ctx->cred, + ctx->addrs, ctx->etypes, &ctx->as_req); + if (ret) + return ret; + if (ctx->fast_state.flags & KRB5_FAST_REQUIRED) + ; + else if (ctx->fast_state.flags & KRB5_FAST_AP_ARMOR_SERVICE) + /* Check with armor service if there is FAST */; + else + ctx->fast_state.flags |= KRB5_FAST_DISABLED; + + + /* XXX should happen after we get back reply from KDC */ + pa_configure(context, ctx, NULL); + } + +#define MAX_PA_COUNTER 15 + if (ctx->pa_counter > MAX_PA_COUNTER) { + krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP, + N_("Looping %d times while getting " + "initial credentials", ""), + ctx->pa_counter); + return KRB5_GET_IN_TKT_LOOP; + } + ctx->pa_counter++; + + _krb5_debug(context, 5, "krb5_get_init_creds: loop %d", ctx->pa_counter); + + /* Lets process the input packet */ + if (in && in->length) { + krb5_kdc_rep rep; + + memset(&rep, 0, sizeof(rep)); + + _krb5_debug(context, 5, "krb5_get_init_creds: processing input"); + + ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); + if (ret == 0) { + unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; + krb5_data data; + + /* + * Unwrap AS-REP + */ + ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, + &rep.kdc_rep.ticket, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + ret = _krb5_fast_unwrap_kdc_rep(context, ctx->nonce, &data, + &ctx->fast_state, &rep.kdc_rep); + krb5_data_free(&data); + if (ret) + goto out; + + /* + * Now check and extract the ticket + */ + + if (ctx->flags.canonicalize) { + eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; + eflags |= EXTRACT_TICKET_MATCH_REALM; + } + if (ctx->ic_flags & KRB5_INIT_CREDS_NO_C_CANON_CHECK) + eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; + if (ctx->flags.request_anonymous) + eflags |= EXTRACT_TICKET_MATCH_ANON; + + ret = process_pa_data_to_key(context, ctx, &ctx->cred, + &ctx->as_req, &rep.kdc_rep, + &ctx->fast_state.reply_key); + if (ret) { + free_AS_REP(&rep.kdc_rep); + goto out; + } + + if (ctx->fast_state.strengthen_key) { + krb5_keyblock result; + + _krb5_debug(context, 5, "krb5_get_init_creds: FAST strengthen_key"); + + ret = _krb5_fast_cf2(context, + ctx->fast_state.strengthen_key, + "strengthenkey", + ctx->fast_state.reply_key, + "replykey", + &result, + NULL); + if (ret) { + free_AS_REP(&rep.kdc_rep); + goto out; + } + + ctx->runflags.allow_save_as_reply_key = 1; + + krb5_free_keyblock_contents(context, ctx->fast_state.reply_key); + *ctx->fast_state.reply_key = result; + } + + _krb5_debug(context, 5, "krb5_get_init_creds: extracting ticket"); + + ret = _krb5_extract_ticket(context, + &rep, + &ctx->cred, + ctx->fast_state.reply_key, + NULL, + KRB5_KU_AS_REP_ENC_PART, + NULL, + ctx->nonce, + eflags, + &ctx->req_buffer, + NULL, + NULL); + + if (ret == 0) + ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); + if (ret == 0) + ret = validate_pkinit_fx(context, ctx, &rep.kdc_rep, &ctx->cred.session); + + ctx->as_enctype = ctx->fast_state.reply_key->keytype; + + if (ctx->runflags.allow_save_as_reply_key) { + ctx->as_reply_key = ctx->fast_state.reply_key; + ctx->fast_state.reply_key = NULL; + } else { + krb5_free_keyblock(context, ctx->fast_state.reply_key); + ctx->fast_state.reply_key = NULL; + } + ctx->ic_flags |= KRB5_INIT_CREDS_DONE; + *flags = 0; + + free_AS_REP(&rep.kdc_rep); + free_EncASRepPart(&rep.enc_part); + + gettimeofday(&end_time, NULL); + timevalsub(&end_time, &start_time); + timevaladd(&ctx->stats.run_time, &end_time); + + _krb5_debug(context, 1, "krb5_get_init_creds: wc: %lld.%06ld", + (long long)ctx->stats.run_time.tv_sec, + (long)ctx->stats.run_time.tv_usec); + return ret; + + } else { + /* let's try to parse it as a KRB-ERROR */ + + _krb5_debug(context, 5, "krb5_get_init_creds: got an KRB-ERROR from KDC"); + + free_KRB_ERROR(&ctx->error); + + ret = krb5_rd_error(context, in, &ctx->error); + if(ret && in->length && ((char*)in->data)[0] == 4) + ret = KRB5KRB_AP_ERR_V4_REPLY; + if (ret) { + _krb5_debug(context, 5, "krb5_get_init_creds: failed to read error"); + goto out; + } + + /* + * Unwrap method-data, if there is any, + * fast_unwrap_error() below might replace it with a + * wrapped version if we are using FAST. + */ + + free_METHOD_DATA(&ctx->md); + memset(&ctx->md, 0, sizeof(ctx->md)); + + if (ctx->error.e_data) { + KERB_ERROR_DATA kerb_error_data; + krb5_error_code ret2; + + memset(&kerb_error_data, 0, sizeof(kerb_error_data)); + + /* First try to decode the e-data as KERB-ERROR-DATA. */ + ret2 = decode_KERB_ERROR_DATA(ctx->error.e_data->data, + ctx->error.e_data->length, + &kerb_error_data, + &len); + if (ret2) { + /* That failed, so try to decode it as METHOD-DATA. */ + ret2 = decode_METHOD_DATA(ctx->error.e_data->data, + ctx->error.e_data->length, + &ctx->md, + NULL); + if (ret2) { + /* + * Just ignore any error, the error will be pushed + * out from krb5_error_from_rd_error() if there + * was one. + */ + _krb5_debug(context, 5, N_("Failed to decode METHOD-DATA", "")); + } + } else if (len != ctx->error.e_data->length) { + /* Trailing data — just ignore the error. */ + free_KERB_ERROR_DATA(&kerb_error_data); + } else { + /* OK. */ + free_KERB_ERROR_DATA(&kerb_error_data); + } + } + + /* + * Unwrap KRB-ERROR, we are always calling this so that + * FAST can tell us if your peer KDC suddenly dropped FAST + * wrapping and its really an attacker's packet (or a bug + * in the KDC). + */ + ret = _krb5_fast_unwrap_error(context, ctx->nonce, &ctx->fast_state, + &ctx->md, &ctx->error); + if (ret) + goto out; + + /* + * + */ + + ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); + + /* log the failure */ + if (_krb5_have_debug(context, 5)) { + const char *str = krb5_get_error_message(context, ret); + _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d/%s", ret, str); + krb5_free_error_message(context, str); + } + + /* + * Handle special error codes + */ + + if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED + || ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED + || ret == KRB5KDC_ERR_ETYPE_NOSUPP) + { + /* + * If no preauth was set and KDC requires it, give it one + * more try. + * + * If the KDC returned KRB5KDC_ERR_ETYPE_NOSUPP, just loop + * one more time since that might mean we are dealing with + * a Windows KDC that is confused about what enctypes are + * available. + */ + + if (available_padata_count(&ctx->md) == 0) { + krb5_set_error_message(context, ret, + N_("Preauth required but no preauth " + "options sent by KDC", "")); + goto out; + } + } else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) { + /* + * Try adapt to timeskrew when we are using pre-auth, and + * if there was a time skew, try again. + */ + krb5_set_real_time(context, ctx->error.stime, -1); + if (context->kdc_sec_offset) + ret = 0; + + _krb5_debug(context, 10, "init_creds: err skew updating kdc offset to %d", + context->kdc_sec_offset); + if (ret) + goto out; + + pa_restart(context, ctx); + + } else if (ret == KRB5_KDC_ERR_WRONG_REALM && ctx->flags.canonicalize) { + /* client referral to a new realm */ + char *ref_realm; + + if (ctx->error.crealm == NULL) { + krb5_set_error_message(context, ret, + N_("Got a client referral, not but no realm", "")); + goto out; + } + ref_realm = *ctx->error.crealm; + + _krb5_debug(context, 5, "krb5_get_init_creds: referral to realm %s", + ref_realm); + + /* + * If its a krbtgt, lets update the requested krbtgt too + */ + if (krb5_principal_is_krbtgt(context, ctx->cred.server)) { + + free(ctx->cred.server->name.name_string.val[1]); + ctx->cred.server->name.name_string.val[1] = strdup(ref_realm); + if (ctx->cred.server->name.name_string.val[1] == NULL) { + ret = krb5_enomem(context); + goto out; + } + + free_PrincipalName(ctx->as_req.req_body.sname); + ret = _krb5_principal2principalname(ctx->as_req.req_body.sname, ctx->cred.server); + if (ret) + goto out; + } + + free(ctx->as_req.req_body.realm); + ret = copy_Realm(&ref_realm, &ctx->as_req.req_body.realm); + if (ret) + goto out; + + ret = krb5_principal_set_realm(context, + ctx->cred.client, + *ctx->error.crealm); + if (ret) + goto out; + + ret = krb5_unparse_name(context, ctx->cred.client, &ref_realm); + if (ret == 0) { + _krb5_debug(context, 5, "krb5_get_init_creds: got referral to %s", ref_realm); + krb5_xfree(ref_realm); + } + + pa_restart(context, ctx); + + } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && + ctx->runflags.change_password_prompt) { + char buf2[1024]; + + ctx->runflags.change_password = 1; + + ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL); + + /* try to avoid recursion */ + if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0) + goto out; + + /* don't include prompter in runtime */ + gettimeofday(&end_time, NULL); + timevalsub(&end_time, &start_time); + timevaladd(&ctx->stats.run_time, &end_time); + + ret = change_password(context, + ctx->cred.client, + ctx->password, + buf2, + sizeof(buf2), + ctx->prompter, + ctx->prompter_data, + NULL); + if (ret) + goto out; + + gettimeofday(&start_time, NULL); + + krb5_init_creds_set_password(context, ctx, buf2); + + pa_restart(context, ctx); + + } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) { + + /* + * Old MIT KDC can't handle KRB5_PADATA_REQ_ENC_PA_REP, + * so drop it and try again. But only try that for MIT + * Kerberos servers by keying of no METHOD-DATA. + */ + if (ctx->runflags.allow_enc_pa_rep) { + if (ctx->md.len != 0) { + _krb5_debug(context, 10, "Server sent PA data with KRB-ERROR, " + "so not a pre 1.7 MIT KDC and won't retry w/o ENC-PA-REQ"); + goto out; + } + _krb5_debug(context, 10, "Disabling allow_enc_pa_rep and trying again"); + ctx->runflags.allow_enc_pa_rep = 0; + goto retry; + } + + if (ctx->fast_state.flags & KRB5_FAST_DISABLED) { + _krb5_debug(context, 10, "FAST disabled and got preauth failed"); + goto out; + } + + retry: + pa_restart(context, ctx); + + } else if (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC) { + _krb5_debug(context, 10, + "Some other error %d failed with optimistic FAST, trying w/o FAST", ret); + + ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC; + ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED; + ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR; + ctx->fast_state.flags |= KRB5_FAST_DISABLED; + pa_restart(context, ctx); + } else { + /* some other error code from the KDC, lets' return it to the user */ + goto out; + } + } + } + + if (ctx->as_req.padata) { + free_METHOD_DATA(ctx->as_req.padata); + free(ctx->as_req.padata); + ctx->as_req.padata = NULL; + } + + ret = _krb5_fast_create_armor(context, &ctx->fast_state, + ctx->cred.client->realm); + if (ret) + goto out; + + /* Set a new nonce. */ + ctx->as_req.req_body.nonce = ctx->nonce; + + + /* + * Step and announce PA-DATA + */ + + ret = process_pa_data_to_md(context, &ctx->cred, &ctx->as_req, ctx, + &ctx->md, &ctx->as_req.padata); + if (ret) + goto out; + + + /* + * Wrap with FAST + */ + ret = copy_AS_REQ(&ctx->as_req, &req2); + if (ret) + goto out; + + ret = _krb5_fast_wrap_req(context, + &ctx->fast_state, + &req2); + + krb5_data_free(&checksum_data); + if (ret) { + free_AS_REQ(&req2); + goto out; + } + + krb5_data_free(&ctx->req_buffer); + + ASN1_MALLOC_ENCODE(AS_REQ, + ctx->req_buffer.data, ctx->req_buffer.length, + &req2, &len, ret); + free_AS_REQ(&req2); + if (ret) + goto out; + if(len != ctx->req_buffer.length) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_data_copy(out, + ctx->req_buffer.data, + ctx->req_buffer.length); + if (ret) + goto out; + + *out_realm = strdup(ctx->cred.client->realm); + if (*out_realm == NULL) { + krb5_data_free(out); + ret = ENOMEM; + goto out; + } + + *flags = KRB5_INIT_CREDS_STEP_FLAG_CONTINUE; + + gettimeofday(&end_time, NULL); + timevalsub(&end_time, &start_time); + timevaladd(&ctx->stats.run_time, &end_time); + + return 0; + out: + return ret; +} + +/** + * The core loop if krb5_get_init_creds() function family. Create the + * packets and have the caller send them off to the KDC. + * + * If the caller want all work been done for them, use + * krb5_init_creds_get() instead. + * + * @param context a Kerberos 5 context. + * @param ctx ctx krb5_init_creds_context context. + * @param in input data from KDC, first round it should be reset by krb5_data_zero(). + * @param out reply to KDC. The caller needs to call krb5_data_free() + * @param out_realm the destination realm for 'out', free with krb5_xfree() + * @param flags status of the round, if + * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round. + * + * @return 0 for success, or an Kerberos 5 error code, see + * krb5_get_error_message(). + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_step(krb5_context context, + krb5_init_creds_context ctx, + const krb5_data *in, + krb5_data *out, + krb5_realm *out_realm, + unsigned int *flags) +{ + krb5_error_code ret; + krb5_data empty; + + krb5_data_zero(&empty); + krb5_data_zero(out); + *out_realm = NULL; + + if ((ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR) && + ctx->fast_state.armor_ccache == NULL) { + ret = _krb5_fast_anon_pkinit_step(context, ctx, &ctx->fast_state, + in, out, out_realm, flags); + if (ret && (ctx->fast_state.flags & KRB5_FAST_OPTIMISTIC)) { + _krb5_debug(context, 5, "Preauth failed with optimistic " + "FAST, trying w/o FAST"); + ctx->fast_state.flags &= ~KRB5_FAST_OPTIMISTIC; + ctx->fast_state.flags &= ~KRB5_FAST_REQUIRED; + ctx->fast_state.flags &= ~KRB5_FAST_ANON_PKINIT_ARMOR; + } else if (ret || + (*flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE)) + return ret; + + in = ∅ + } + + return init_creds_step(context, ctx, in, out, out_realm, flags); +} + +/** + * Extract the newly acquired credentials from krb5_init_creds_context + * context. + * + * @param context A Kerberos 5 context. + * @param ctx + * @param cred credentials, free with krb5_free_cred_contents(). + * + * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_get_creds(krb5_context context, + krb5_init_creds_context ctx, + krb5_creds *cred) +{ + return krb5_copy_creds_contents(context, &ctx->cred, cred); +} + +/** + * Extract the as-reply key from the context. + * + * Only allowed when the as-reply-key is not directly derived from the + * password like PK-INIT, GSS, FAST hardened key, etc. + * + * @param context A Kerberos 5 context. + * @param ctx ctx krb5_init_creds_context context. + * @param as_reply_key keyblock, free with krb5_free_keyblock_contents(). + * + * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message(). + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_get_as_reply_key(krb5_context context, + krb5_init_creds_context ctx, + krb5_keyblock *as_reply_key) +{ + if (ctx->as_reply_key == NULL) + return KRB5KDC_ERR_PREAUTH_REQUIRED; + return krb5_copy_keyblock_contents(context, ctx->as_reply_key, as_reply_key); +} + +KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL +_krb5_init_creds_get_cred_starttime(krb5_context context, krb5_init_creds_context ctx) +{ + return ctx->cred.times.starttime; +} + +KRB5_LIB_FUNCTION krb5_timestamp KRB5_LIB_CALL +_krb5_init_creds_get_cred_endtime(krb5_context context, krb5_init_creds_context ctx) +{ + return ctx->cred.times.endtime; +} + +KRB5_LIB_FUNCTION krb5_principal KRB5_LIB_CALL +_krb5_init_creds_get_cred_client(krb5_context context, krb5_init_creds_context ctx) +{ + return ctx->cred.client; +} + +/** + * Get the last error from the transaction. + * + * @return Returns 0 or an error code + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_get_error(krb5_context context, + krb5_init_creds_context ctx, + KRB_ERROR *error) +{ + krb5_error_code ret; + + ret = copy_KRB_ERROR(&ctx->error, error); + if (ret) + krb5_enomem(context); + + return ret; +} + +/** + * Store config + * + * @param context A Kerberos 5 context. + * @param ctx The krb5_init_creds_context to free. + * @param id store + * + * @return Returns 0 or an error code + * + * @ingroup krb5_credential + */ + +krb5_error_code KRB5_LIB_FUNCTION +krb5_init_creds_store_config(krb5_context context, + krb5_init_creds_context ctx, + krb5_ccache id) +{ + krb5_error_code ret; + + if (ctx->kdc_hostname) { + krb5_data data; + data.length = strlen(ctx->kdc_hostname); + data.data = ctx->kdc_hostname; + + ret = krb5_cc_set_config(context, id, NULL, "lkdc-hostname", &data); + if (ret) + return ret; + } + if (ctx->sitename) { + krb5_data data; + data.length = strlen(ctx->sitename); + data.data = ctx->sitename; + + ret = krb5_cc_set_config(context, id, NULL, "sitename", &data); + if (ret) + return ret; + } + + return 0; +} + +/** + * + * @ingroup krb5_credential + */ + +krb5_error_code +krb5_init_creds_store(krb5_context context, + krb5_init_creds_context ctx, + krb5_ccache id) +{ + krb5_error_code ret; + + if (ctx->cred.client == NULL) { + ret = KRB5KDC_ERR_PREAUTH_REQUIRED; + krb5_set_error_message(context, ret, "init creds not completed yet"); + return ret; + } + + ret = krb5_cc_initialize(context, id, ctx->cred.client); + if (ret) + return ret; + + ret = krb5_cc_store_cred(context, id, &ctx->cred); + if (ret) + return ret; + + if (ctx->cred.flags.b.enc_pa_rep) { + krb5_data data = { 3, rk_UNCONST("yes") }; + ret = krb5_cc_set_config(context, id, ctx->cred.server, + "fast_avail", &data); + if (ret && ret != KRB5_CC_NOSUPP) + return ret; + } + + return 0; +} + +/** + * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). + * + * @param context A Kerberos 5 context. + * @param ctx The krb5_init_creds_context to free. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_init_creds_free(krb5_context context, + krb5_init_creds_context ctx) +{ + free_init_creds_ctx(context, ctx); + free(ctx); +} + +/** + * Get new credentials as setup by the krb5_init_creds_context. + * + * @param context A Kerberos 5 context. + * @param ctx The krb5_init_creds_context to process. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx) +{ + krb5_sendto_ctx stctx = NULL; + krb5_error_code ret; + krb5_data in, out; + unsigned int flags = 0; + + krb5_data_zero(&in); + krb5_data_zero(&out); + + ret = krb5_sendto_ctx_alloc(context, &stctx); + if (ret) + goto out; + krb5_sendto_ctx_set_func(stctx, _krb5_kdc_retry, NULL); + + if (ctx->kdc_hostname) + krb5_sendto_set_hostname(context, stctx, ctx->kdc_hostname); + if (ctx->sitename) + krb5_sendto_set_sitename(context, stctx, ctx->sitename); + + while (1) { + struct timeval nstart, nend; + krb5_realm realm = NULL; + + flags = 0; + ret = krb5_init_creds_step(context, ctx, &in, &out, &realm, &flags); + krb5_data_free(&in); + if (ret) + goto out; + + if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0) + break; + + gettimeofday(&nstart, NULL); + + ret = krb5_sendto_context (context, stctx, &out, realm, &in); + krb5_data_free(&out); + free(realm); + if (ret) + goto out; + + gettimeofday(&nend, NULL); + timevalsub(&nend, &nstart); + timevaladd(&ctx->stats.run_time, &nend); + } + + out: + if (stctx) + krb5_sendto_ctx_free(context, stctx); + + return ret; +} + +/** + * Get new credentials using password. + * + * @ingroup krb5_credential + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_password(krb5_context context, + krb5_creds *creds, + krb5_principal client, + const char *password, + krb5_prompter_fct prompter, + void *data, + krb5_deltat start_time, + const char *in_tkt_service, + krb5_get_init_creds_opt *options) +{ + krb5_init_creds_context ctx; + char buf[BUFSIZ], buf2[BUFSIZ]; + krb5_error_code ret; + int chpw = 0; + + again: + ret = krb5_init_creds_init(context, client, prompter, data, start_time, options, &ctx); + if (ret) + goto out; + + ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); + if (ret) + goto out; + + if (prompter != NULL && ctx->password == NULL && password == NULL) { + krb5_prompt prompt; + krb5_data password_data; + char *p, *q = NULL; + int aret; + + ret = krb5_unparse_name(context, client, &p); + if (ret) + goto out; + + aret = asprintf(&q, "%s's Password: ", p); + free (p); + if (aret == -1 || q == NULL) { + ret = krb5_enomem(context); + goto out; + } + prompt.prompt = q; + password_data.data = buf; + password_data.length = sizeof(buf); + prompt.hidden = 1; + prompt.reply = &password_data; + prompt.type = KRB5_PROMPT_TYPE_PASSWORD; + + ret = (*prompter) (context, data, NULL, NULL, 1, &prompt); + free (q); + if (ret) { + memset_s(buf, sizeof(buf), 0, sizeof(buf)); + ret = KRB5_LIBOS_PWDINTR; + krb5_clear_error_message (context); + goto out; + } + password = password_data.data; + } + + if (password) { + ret = krb5_init_creds_set_password(context, ctx, password); + if (ret) + goto out; + } + + ret = krb5_init_creds_get(context, ctx); + + if (ret == 0) + krb5_process_last_request(context, options, ctx); + + + if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { + /* try to avoid recursion */ + if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0) + goto out; + + /* don't try to change password if no prompter or prompting disabled */ + if (!ctx->runflags.change_password_prompt) + goto out; + + ret = change_password (context, + client, + ctx->password, + buf2, + sizeof(buf2), + prompter, + data, + options); + if (ret) + goto out; + password = buf2; + chpw = 1; + krb5_init_creds_free(context, ctx); + goto again; + } + + out: + if (ret == 0) + krb5_init_creds_get_creds(context, ctx, creds); + + if (ctx) + krb5_init_creds_free(context, ctx); + + memset_s(buf, sizeof(buf), 0, sizeof(buf)); + memset_s(buf2, sizeof(buf), 0, sizeof(buf2)); + return ret; +} + +/** + * Get new credentials using keyblock. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_keyblock(krb5_context context, + krb5_creds *creds, + krb5_principal client, + krb5_keyblock *keyblock, + krb5_deltat start_time, + const char *in_tkt_service, + krb5_get_init_creds_opt *options) +{ + krb5_init_creds_context ctx; + krb5_error_code ret; + + memset(creds, 0, sizeof(*creds)); + + ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); + if (ret) + goto out; + + ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); + if (ret) + goto out; + + ret = krb5_init_creds_set_keyblock(context, ctx, keyblock); + if (ret) + goto out; + + ret = krb5_init_creds_get(context, ctx); + + if (ret == 0) + krb5_process_last_request(context, options, ctx); + + out: + if (ret == 0) + krb5_init_creds_get_creds(context, ctx, creds); + + if (ctx) + krb5_init_creds_free(context, ctx); + + return ret; +} + +/** + * Get new credentials using keytab. + * + * @ingroup krb5_credential + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_get_init_creds_keytab(krb5_context context, + krb5_creds *creds, + krb5_principal client, + krb5_keytab keytab, + krb5_deltat start_time, + const char *in_tkt_service, + krb5_get_init_creds_opt *options) +{ + krb5_init_creds_context ctx; + krb5_keytab_entry ktent; + krb5_error_code ret; + + memset(&ktent, 0, sizeof(ktent)); + memset(creds, 0, sizeof(*creds)); + + if (strcmp(client->realm, "") == 0) { + /* + * Referral realm. We have a keytab, so pick a realm by + * matching in the keytab. + */ + ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent); + if (ret == 0) + client = ktent.principal; + } + + ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx); + if (ret) + goto out; + + ret = krb5_init_creds_set_service(context, ctx, in_tkt_service); + if (ret) + goto out; + + ret = krb5_init_creds_set_keytab(context, ctx, keytab); + if (ret) + goto out; + + ret = krb5_init_creds_get(context, ctx); + if (ret == 0) + krb5_process_last_request(context, options, ctx); + + out: + krb5_kt_free_entry(context, &ktent); + if (ret == 0) + krb5_init_creds_get_creds(context, ctx, creds); + + if (ctx) + krb5_init_creds_free(context, ctx); + + return ret; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_init_creds_set_gss_mechanism(krb5_context context, + krb5_gss_init_ctx gssic, + const struct gss_OID_desc_struct *gss_mech) +{ + gssic->mech = gss_mech; /* OIDs are interned, so no copy required */ +} + +KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL +_krb5_init_creds_get_gss_mechanism(krb5_context context, + krb5_gss_init_ctx gssic) +{ + return gssic->mech; +} + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +_krb5_init_creds_set_gss_cred(krb5_context context, + krb5_gss_init_ctx gssic, + struct gss_cred_id_t_desc_struct *gss_cred) +{ + if (gssic->cred != gss_cred && gssic->flags.release_cred) + gssic->release_cred(context, gssic, gssic->cred); + + gssic->cred = gss_cred; + gssic->flags.release_cred = 1; +} + +KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL +_krb5_init_creds_get_gss_cred(krb5_context context, + krb5_gss_init_ctx gssic) +{ + return gssic->cred; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_init_creds_init_gss(krb5_context context, + krb5_init_creds_context ctx, + krb5_gssic_step step, + krb5_gssic_finish finish, + krb5_gssic_release_cred release_cred, + krb5_gssic_delete_sec_context delete_sec_context, + const struct gss_cred_id_t_desc_struct *gss_cred, + const struct gss_OID_desc_struct *gss_mech, + unsigned int flags) +{ + krb5_gss_init_ctx gssic; + + gssic = calloc(1, sizeof(*gssic)); + if (gssic == NULL) + return krb5_enomem(context); + + if (ctx->gss_init_ctx) + free_gss_init_ctx(context, ctx->gss_init_ctx); + ctx->gss_init_ctx = gssic; + + gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred; + gssic->mech = gss_mech; + if (flags & KRB5_GSS_IC_FLAG_RELEASE_CRED) + gssic->flags.release_cred = 1; + + gssic->step = step; + gssic->finish = finish; + gssic->release_cred = release_cred; + gssic->delete_sec_context = delete_sec_context; + + return 0; +} diff --git a/third_party/heimdal/lib/krb5/k524_err.et b/third_party/heimdal/lib/krb5/k524_err.et new file mode 100644 index 0000000..4827b39 --- /dev/null +++ b/third_party/heimdal/lib/krb5/k524_err.et @@ -0,0 +1,20 @@ +# +# Error messages for the k524 functions +# +# This might look like a com_err file, but is not +# +id "$Id$" + +error_table k524 + +prefix KRB524 +error_code BADKEY, "wrong keytype in ticket" +error_code BADADDR, "incorrect network address" +error_code BADPRINC, "cannot convert V5 principal" #unused +error_code BADREALM, "V5 realm name longer than V4 maximum" #unused +error_code V4ERR, "kerberos V4 error server" +error_code ENCFULL, "encoding too large at server" +error_code DECEMPTY, "decoding out of data" #unused +error_code NOTRESP, "service not responding" #unused +end + diff --git a/third_party/heimdal/lib/krb5/k5e1_err.et b/third_party/heimdal/lib/krb5/k5e1_err.et new file mode 100644 index 0000000..19414f1 --- /dev/null +++ b/third_party/heimdal/lib/krb5/k5e1_err.et @@ -0,0 +1,13 @@ +id "$Id$" + +error_table k5e1 + +index 4 + +prefix KRB5_DCC +error_code CANNOT_CREATE, "Can't create new subsidiary cache" + +prefix KRB5_KCC +error_code INVALID_ANCHOR, "Invalid keyring anchor name" +error_code UNKNOWN_VERSION, "Unknown keyring collection version" +error_code INVALID_UID, "Invalid UID in persistent keyring name" diff --git a/third_party/heimdal/lib/krb5/kcm.c b/third_party/heimdal/lib/krb5/kcm.c new file mode 100644 index 0000000..17a26e3 --- /dev/null +++ b/third_party/heimdal/lib/krb5/kcm.c @@ -0,0 +1,1505 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#ifdef HAVE_KCM +/* + * Client library for Kerberos Credentials Manager (KCM) daemon + */ + +#include "kcm.h" +#include + +static krb5_error_code +kcm_set_kdc_offset(krb5_context, krb5_ccache, krb5_deltat); + +static const char *kcm_ipc_name = "ANY:org.h5l.kcm"; + +typedef struct krb5_kcmcache { + char *name; +} krb5_kcmcache; + +typedef struct krb5_kcm_cursor { + unsigned long offset; + unsigned long length; + kcmuuid_t *uuids; +} *krb5_kcm_cursor; + + +#define KCMCACHE(X) ((krb5_kcmcache *)(X)->data.data) +#define CACHENAME(X) (KCMCACHE(X)->name) +#define KCMCURSOR(C) ((krb5_kcm_cursor)(C)) + +static HEIMDAL_MUTEX kcm_mutex = HEIMDAL_MUTEX_INITIALIZER; +static heim_ipc kcm_ipc = NULL; + +static krb5_error_code +kcm_send_request(krb5_context context, + krb5_storage *request, + krb5_data *response_data) +{ + krb5_error_code ret = 0; + krb5_data request_data; + + krb5_data_zero(response_data); + + HEIMDAL_MUTEX_lock(&kcm_mutex); + if (kcm_ipc == NULL) + ret = heim_ipc_init_context(kcm_ipc_name, &kcm_ipc); + HEIMDAL_MUTEX_unlock(&kcm_mutex); + if (ret) + return KRB5_CC_NOSUPP; + + ret = krb5_storage_to_data(request, &request_data); + if (ret) { + return krb5_enomem(context); + } + + ret = heim_ipc_call(kcm_ipc, &request_data, response_data, NULL); + krb5_data_free(&request_data); + return ret; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kcm_storage_request(krb5_context context, + uint16_t opcode, + krb5_storage **storage_p) +{ + krb5_storage *sp; + krb5_error_code ret; + + *storage_p = NULL; + + sp = krb5_storage_emem(); + if (sp == NULL) + return krb5_enomem(context); + + /* Send MAJOR | VERSION | OPCODE */ + ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MAJOR); + if (ret) + goto fail; + ret = krb5_store_int8(sp, KCM_PROTOCOL_VERSION_MINOR); + if (ret) + goto fail; + ret = krb5_store_int16(sp, opcode); + if (ret) + goto fail; + + *storage_p = sp; + fail: + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed to encode KCM request", "")); + krb5_storage_free(sp); + } + + return ret; +} + +/* + * A sort of a state() for caches -- we use this to see if the local default + * cache name for KCM happens to exist. See kcm_alloc() below. + */ +static krb5_error_code +kcm_stat(krb5_context context, const char *name) +{ + krb5_error_code ret; + krb5_storage *request = NULL; + krb5_data response_data; + + krb5_data_zero(&response_data); + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request); + if (ret == 0) + ret = krb5_store_stringz(request, name); + if (ret == 0) + ret = krb5_kcm_call(context, request, NULL, &response_data); + krb5_storage_free(request); + krb5_data_free(&response_data); + return ret; +} + +static krb5_error_code kcm_get_default_name(krb5_context, + const krb5_cc_ops *, + const char *, char **); + +static krb5_error_code +kcm_alloc(krb5_context context, + const krb5_cc_ops *ops, + const char *residual, + const char *sub, + krb5_ccache *id) +{ + krb5_error_code ret; + krb5_kcmcache *k; + size_t ops_prefix_len = strlen(ops->prefix); + size_t plen = 0; + size_t local_def_name_len; + char *local_def_name = NULL; /* Our idea of default KCM cache name */ + char *kcm_def_name = NULL; /* KCM's knowledge of default cache name */ + int aret; + + /* Get the KCM:%{UID} default */ + if (ops == &krb5_kcm_ops) + ret = _krb5_expand_default_cc_name(context, KRB5_DEFAULT_CCNAME_KCM_KCM, &local_def_name); + else + ret = _krb5_expand_default_cc_name(context, KRB5_DEFAULT_CCNAME_KCM_API, &local_def_name); + if (ret) + return ret; + local_def_name_len = strlen(local_def_name); + + /* Get the default ccache name from KCM if possible */ + (void) kcm_get_default_name(context, ops, NULL, &kcm_def_name); + + /* + * We have a sticky situation in that applications that call + * krb5_cc_default() will be getting the locally configured or compiled-in + * default KCM cache name, which may not exist in the user's KCM session, + * and which the KCM daemon may not be able to alias to the actual default + * for the user's session. + * + * To deal with this we heuristically detect when an application uses the + * default KCM ccache name. + * + * If the residual happens to be the local default KCM name we may end up + * using whatever the default KCM cache name is instead of the local + * default. + * + * Note that here `residual' may be any of: + * + * - %{UID} + * - %{UID}: + * - %{UID}: + * - + * - + * - + * + * Only the first two count as "maybe I mean the default KCM cache". + */ + if (residual && !sub && + strncmp(residual, local_def_name + ops_prefix_len + 1, + local_def_name_len - (ops_prefix_len + 1)) == 0) { + if (residual[local_def_name_len - (ops_prefix_len + 1)] == '\0' || + (residual[local_def_name_len - (ops_prefix_len + 1)] == ':' && + residual[local_def_name_len - ops_prefix_len] == '\0')) { + /* + * If we got a default cache name from KCM and the requested default + * cache does not exist, use the former. + */ + if (kcm_def_name && kcm_stat(context, residual)) + residual = kcm_def_name + ops_prefix_len + 1; + } + } + + if (residual && residual[0] == '\0') + residual = NULL; + if (sub && sub[0] == '\0') + sub = NULL; + + if (residual == NULL && sub == NULL) { + /* Use the default cache name, either from KCM or local default */ + if (kcm_def_name) + residual = kcm_def_name + ops_prefix_len + 1; + else + residual = local_def_name + ops_prefix_len + 1; + } + + if (residual) { + /* KCM cache names must start with {UID} or {UID}: */ + plen = strspn(residual, "0123456789"); + if (plen && residual[plen] != ':' && residual[plen] != '\0') + plen = 0; + /* + * If `plen', then residual is such a residual, else we'll want to + * prefix the {UID}:. + */ + } + + k = calloc(1, sizeof(*k)); + if (k == NULL) { + free(local_def_name); + free(kcm_def_name); + return krb5_enomem(context); + } + k->name = NULL; + + if (residual == NULL && sub == NULL) { + /* One more way to get a default */ + aret = asprintf(&k->name, "%llu", (unsigned long long)getuid()); + } else if (residual == NULL) { + /* + * Treat the subsidiary as the residual (maybe this will turn out to be + * wrong). + */ + aret = asprintf(&k->name, "%llu:%s", (unsigned long long)getuid(), + sub); + } else if (plen) { + /* The residual is a UID */ + aret = asprintf(&k->name, "%s%s%s", residual, + sub ? ":" : "", sub ? sub : ""); + } else if (sub == NULL) { + /* The residual is NOT a UID */ + aret = asprintf(&k->name, "%llu:%s", (unsigned long long)getuid(), + residual); + } else { + /* Ditto, plus we have a subsidiary. `residual && sub && !plen' */ + aret = asprintf(&k->name, "%llu:%s:%s", (unsigned long long)getuid(), + residual, sub); + } + if (aret == -1 || k->name == NULL) { + free(local_def_name); + free(kcm_def_name); + free(k); + return krb5_enomem(context); + } + + free(local_def_name); + free(kcm_def_name); + (*id)->data.data = k; + (*id)->data.length = sizeof(*k); + + return 0; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kcm_call(krb5_context context, + krb5_storage *request, + krb5_storage **response_p, + krb5_data *response_data_p) +{ + krb5_data response_data; + krb5_error_code ret; + int32_t status; + krb5_storage *response; + + if (response_p != NULL) + *response_p = NULL; + + krb5_data_zero(&response_data); + ret = kcm_send_request(context, request, &response_data); + if (ret) { + krb5_data_free(&response_data); + return ret; + } + + response = krb5_storage_from_data(&response_data); + if (response == NULL) { + krb5_data_free(&response_data); + return KRB5_CC_IO; + } + + ret = krb5_ret_int32(response, &status); + if (ret) { + krb5_storage_free(response); + krb5_data_free(&response_data); + return KRB5_CC_FORMAT; + } + + if (status) { + krb5_storage_free(response); + krb5_data_free(&response_data); + return status; + } + + if (response_p != NULL) { + *response_data_p = response_data; + *response_p = response; + + return 0; + } + + krb5_storage_free(response); + krb5_data_free(&response_data); + + return 0; +} + +static void +kcm_free(krb5_context context, krb5_ccache *id) +{ + krb5_kcmcache *k = KCMCACHE(*id); + + if (k != NULL) { + free(k->name); + memset_s(k, sizeof(*k), 0, sizeof(*k)); + krb5_data_free(&(*id)->data); + } +} + +static krb5_error_code KRB5_CALLCONV +kcm_get_name_2(krb5_context context, + krb5_ccache id, + const char **name, + const char **col, + const char **sub) +{ + /* + * TODO: + * + * - name should be : + * - col should be + * - sub should be + */ + if (name) + *name = CACHENAME(id); + if (col) + *col = NULL; + if (sub) + *sub = CACHENAME(id); + return 0; +} + +static krb5_error_code +kcm_resolve_2_kcm(krb5_context context, + krb5_ccache *id, + const char *res, + const char *sub) +{ + /* + * For now, for KCM the `res' is the `sub'. + * + * TODO: We should use `res' as the IPC name instead of the one currently + * hard-coded in `kcm_ipc_name'. + */ + return kcm_alloc(context, &krb5_kcm_ops, res, sub, id); +} + +static krb5_error_code +kcm_resolve_2_api(krb5_context context, + krb5_ccache *id, + const char *res, + const char *sub) +{ + /* + * For now, for KCM the `res' is the `sub'. + * + * TODO: We should use `res' as the IPC name instead of the one currently + * hard-coded in `kcm_ipc_name'. + */ + return kcm_alloc(context, &krb5_akcm_ops, res, sub, id); +} + +/* + * Request: + * + * Response: + * NameZ + */ +static krb5_error_code +kcm_gen_new(krb5_context context, const krb5_cc_ops *ops, krb5_ccache *id) +{ + krb5_kcmcache *k; + krb5_error_code ret; + krb5_storage *request, *response; + krb5_data response_data; + + ret = kcm_alloc(context, ops, NULL, NULL, id); + if (ret) + return ret; + + k = KCMCACHE(*id); + + ret = krb5_kcm_storage_request(context, KCM_OP_GEN_NEW, &request); + if (ret) { + kcm_free(context, id); + return ret; + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + if (ret) { + krb5_storage_free(request); + kcm_free(context, id); + return ret; + } + + free(k->name); + k->name = NULL; + ret = krb5_ret_stringz(response, &k->name); + if (ret) + ret = KRB5_CC_IO; + + krb5_storage_free(request); + krb5_storage_free(response); + krb5_data_free(&response_data); + + if (ret) + kcm_free(context, id); + + return ret; +} + +static krb5_error_code +kcm_gen_new_kcm(krb5_context context, krb5_ccache *id) +{ + return kcm_gen_new(context, &krb5_kcm_ops, id); +} + +static krb5_error_code +kcm_gen_new_api(krb5_context context, krb5_ccache *id) +{ + return kcm_gen_new(context, &krb5_akcm_ops, id); +} + +/* + * Request: + * NameZ + * Principal + * + * Response: + * + */ +static krb5_error_code +kcm_initialize(krb5_context context, + krb5_ccache id, + krb5_principal primary_principal) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_INITIALIZE, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_principal(request, primary_principal); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + + if (context->kdc_sec_offset) + kcm_set_kdc_offset(context, id, context->kdc_sec_offset); + + return ret; +} + +static krb5_error_code +kcm_close(krb5_context context, + krb5_ccache id) +{ + kcm_free(context, &id); + return 0; +} + +/* + * Request: + * NameZ + * + * Response: + * + */ +static krb5_error_code +kcm_destroy(krb5_context context, + krb5_ccache id) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_DESTROY, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + +/* + * Request: + * NameZ + * Creds + * + * Response: + * + */ +static krb5_error_code +kcm_store_cred(krb5_context context, + krb5_ccache id, + krb5_creds *creds) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_STORE, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_creds(request, creds); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + +#if 0 +/* + * Request: + * NameZ + * WhichFields + * MatchCreds + * + * Response: + * Creds + * + */ +static krb5_error_code +kcm_retrieve(krb5_context context, + krb5_ccache id, + krb5_flags which, + const krb5_creds *mcred, + krb5_creds *creds) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request, *response; + krb5_data response_data; + + ret = krb5_kcm_storage_request(context, KCM_OP_RETRIEVE, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_int32(request, which); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_creds_tag(request, rk_UNCONST(mcred)); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_ret_creds(response, creds); + if (ret) + ret = KRB5_CC_IO; + + krb5_storage_free(request); + krb5_storage_free(response); + krb5_data_free(&response_data); + + return ret; +} +#endif + +/* + * Request: + * NameZ + * + * Response: + * Principal + */ +static krb5_error_code +kcm_get_principal(krb5_context context, + krb5_ccache id, + krb5_principal *principal) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request, *response; + krb5_data response_data; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_PRINCIPAL, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_ret_principal(response, principal); + if (ret) + ret = KRB5_CC_IO; + + krb5_storage_free(request); + krb5_storage_free(response); + krb5_data_free(&response_data); + + return ret; +} + +/* + * Request: + * NameZ + * + * Response: + * Cursor + * + */ +static krb5_error_code +kcm_get_first (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + krb5_error_code ret; + krb5_kcm_cursor c; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request, *response; + krb5_data response_data; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_UUID_LIST, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + krb5_storage_free(request); + if (ret) + return ret; + + c = calloc(1, sizeof(*c)); + if (c == NULL) { + ret = krb5_enomem(context); + return ret; + } + + while (1) { + ssize_t sret; + kcmuuid_t uuid; + void *ptr; + + sret = krb5_storage_read(response, &uuid, sizeof(uuid)); + if (sret == 0) { + ret = 0; + break; + } else if (sret != sizeof(uuid)) { + ret = EINVAL; + break; + } + + ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); + if (ptr == NULL) { + free(c->uuids); + free(c); + return krb5_enomem(context); + } + c->uuids = ptr; + + memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); + c->length += 1; + } + + krb5_storage_free(response); + krb5_data_free(&response_data); + + if (ret) { + free(c->uuids); + free(c); + return ret; + } + + *cursor = c; + + return 0; +} + +/* + * Request: + * NameZ + * Cursor + * + * Response: + * Creds + */ +static krb5_error_code +kcm_get_next (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor, + krb5_creds *creds) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_kcm_cursor c = KCMCURSOR(*cursor); + krb5_storage *request, *response; + krb5_data response_data; + ssize_t sret; + + again: + + if (c->offset >= c->length) + return KRB5_CC_END; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_CRED_BY_UUID, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + sret = krb5_storage_write(request, + &c->uuids[c->offset], + sizeof(c->uuids[c->offset])); + c->offset++; + if (sret != sizeof(c->uuids[c->offset])) { + krb5_storage_free(request); + return krb5_enomem(context); + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + krb5_storage_free(request); + if (ret == KRB5_CC_END) { + goto again; + } else if (ret) + return ret; + + ret = krb5_ret_creds(response, creds); + if (ret) + ret = KRB5_CC_IO; + + krb5_storage_free(response); + krb5_data_free(&response_data); + + return ret; +} + +/* + * Request: + * NameZ + * Cursor + * + * Response: + * + */ +static krb5_error_code +kcm_end_get (krb5_context context, + krb5_ccache id, + krb5_cc_cursor *cursor) +{ + krb5_kcm_cursor c = KCMCURSOR(*cursor); + + free(c->uuids); + free(c); + + *cursor = NULL; + + return 0; +} + +/* + * Request: + * NameZ + * WhichFields + * MatchCreds + * + * Response: + * + */ +static krb5_error_code +kcm_remove_cred(krb5_context context, + krb5_ccache id, + krb5_flags which, + krb5_creds *cred) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_REMOVE_CRED, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_int32(request, which); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_creds_tag(request, cred); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + +static krb5_error_code +kcm_set_flags(krb5_context context, + krb5_ccache id, + krb5_flags flags) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_SET_FLAGS, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_int32(request, flags); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + +static int +kcm_get_version(krb5_context context, + krb5_ccache id) +{ + return 0; +} + +/* + * Send nothing + * get back list of uuids + */ + +static krb5_error_code +kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) +{ + krb5_error_code ret; + krb5_kcm_cursor c; + krb5_storage *request, *response; + krb5_data response_data; + + *cursor = NULL; + + c = calloc(1, sizeof(*c)); + if (c == NULL) { + ret = krb5_enomem(context); + goto out; + } + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_UUID_LIST, &request); + if (ret) + goto out; + + ret = krb5_kcm_call(context, request, &response, &response_data); + krb5_storage_free(request); + if (ret) + goto out; + + while (1) { + ssize_t sret; + kcmuuid_t uuid; + void *ptr; + + sret = krb5_storage_read(response, &uuid, sizeof(uuid)); + if (sret == 0) { + ret = 0; + break; + } else if (sret != sizeof(uuid)) { + ret = EINVAL; + goto out; + } + + ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1)); + if (ptr == NULL) { + ret = krb5_enomem(context); + goto out; + } + c->uuids = ptr; + + memcpy(&c->uuids[c->length], &uuid, sizeof(uuid)); + c->length += 1; + } + + krb5_storage_free(response); + krb5_data_free(&response_data); + + out: + if (ret && c) { + free(c->uuids); + free(c); + } else + *cursor = c; + + return ret; +} + +/* + * Send uuid + * Recv cache name + */ + +static krb5_error_code +kcm_get_cache_next(krb5_context context, krb5_cc_cursor cursor, const krb5_cc_ops *ops, krb5_ccache *id) +{ + krb5_error_code ret; + krb5_kcm_cursor c = KCMCURSOR(cursor); + krb5_storage *request, *response; + krb5_data response_data; + ssize_t sret; + char *name; + + *id = NULL; + + again: + + if (c->offset >= c->length) + return KRB5_CC_END; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_CACHE_BY_UUID, &request); + if (ret) + return ret; + + sret = krb5_storage_write(request, + &c->uuids[c->offset], + sizeof(c->uuids[c->offset])); + c->offset++; + if (sret != sizeof(c->uuids[c->offset])) { + krb5_storage_free(request); + return krb5_enomem(context); + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + krb5_storage_free(request); + if (ret == KRB5_CC_END) + goto again; + else if (ret) + return ret; + + ret = krb5_ret_stringz(response, &name); + krb5_storage_free(response); + krb5_data_free(&response_data); + + if (ret == 0) { + ret = _krb5_cc_allocate(context, ops, id); + if (ret == 0) + ret = kcm_alloc(context, ops, name, NULL, id); + krb5_xfree(name); + } + + return ret; +} + +static krb5_error_code +kcm_get_cache_next_kcm(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) +{ +#ifndef KCM_IS_API_CACHE + return kcm_get_cache_next(context, cursor, &krb5_kcm_ops, id); +#else + return KRB5_CC_END; +#endif +} + +static krb5_error_code +kcm_get_cache_next_api(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id) +{ + return kcm_get_cache_next(context, cursor, &krb5_akcm_ops, id); +} + + +static krb5_error_code +kcm_end_cache_get(krb5_context context, krb5_cc_cursor cursor) +{ + krb5_kcm_cursor c = KCMCURSOR(cursor); + + free(c->uuids); + free(c); + return 0; +} + + +static krb5_error_code +kcm_move(krb5_context context, krb5_ccache from, krb5_ccache to) +{ + krb5_error_code ret; + krb5_kcmcache *oldk = KCMCACHE(from); + krb5_kcmcache *newk = KCMCACHE(to); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_MOVE_CACHE, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, oldk->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_stringz(request, newk->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + + if (ret == 0) + krb5_cc_destroy(context, from); + return ret; +} + +static krb5_error_code +kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops, + const char *defstr, char **str) +{ + krb5_error_code ret; + krb5_storage *request, *response; + krb5_data response_data; + char *name; + int aret; + + *str = NULL; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_DEFAULT_CACHE, &request); + if (ret) + return ret; + + ret = krb5_kcm_call(context, request, &response, &response_data); + krb5_storage_free(request); + if (ret) { + if (defstr) + return _krb5_expand_default_cc_name(context, defstr, str); + return ret; + } + + ret = krb5_ret_stringz(response, &name); + krb5_storage_free(response); + krb5_data_free(&response_data); + if (ret) + return ret; + + aret = asprintf(str, "%s:%s", ops->prefix, name); + free(name); + if (aret == -1 || *str == NULL) + return krb5_enomem(context); + + return 0; +} + +static krb5_error_code +kcm_get_default_name_api(krb5_context context, char **str) +{ + return kcm_get_default_name(context, &krb5_akcm_ops, + KRB5_DEFAULT_CCNAME_KCM_API, str); +} + +static krb5_error_code +kcm_get_default_name_kcm(krb5_context context, char **str) +{ + return kcm_get_default_name(context, &krb5_kcm_ops, + KRB5_DEFAULT_CCNAME_KCM_KCM, str); +} + +static krb5_error_code +kcm_set_default(krb5_context context, krb5_ccache id) +{ + krb5_error_code ret; + krb5_storage *request; + krb5_kcmcache *k = KCMCACHE(id); + + ret = krb5_kcm_storage_request(context, KCM_OP_SET_DEFAULT_CACHE, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + krb5_storage_free(request); + + return ret; +} + +static krb5_error_code +kcm_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime) +{ + *mtime = time(NULL); + return 0; +} + +static krb5_error_code +kcm_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset) +{ + krb5_kcmcache *k = KCMCACHE(id); + krb5_error_code ret; + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_SET_KDC_OFFSET, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + ret = krb5_store_int32(request, kdc_offset); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + krb5_storage_free(request); + + return ret; +} + +static krb5_error_code +kcm_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset) +{ + krb5_kcmcache *k = KCMCACHE(id); + krb5_error_code ret; + krb5_storage *request, *response; + krb5_data response_data; + int32_t offset; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_KDC_OFFSET, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, &response, &response_data); + krb5_storage_free(request); + if (ret) + return ret; + + ret = krb5_ret_int32(response, &offset); + krb5_storage_free(response); + krb5_data_free(&response_data); + if (ret) + return ret; + + *kdc_offset = offset; + + return 0; +} + +/** + * Variable containing the KCM based credential cache implemention. + * + * @ingroup krb5_ccache + */ + +KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops = { + KRB5_CC_OPS_VERSION_5, + "KCM", + NULL, + NULL, + kcm_gen_new_kcm, + kcm_initialize, + kcm_destroy, + kcm_close, + kcm_store_cred, + NULL /* kcm_retrieve */, + kcm_get_principal, + kcm_get_first, + kcm_get_next, + kcm_end_get, + kcm_remove_cred, + kcm_set_flags, + kcm_get_version, + kcm_get_cache_first, + kcm_get_cache_next_kcm, + kcm_end_cache_get, + kcm_move, + kcm_get_default_name_kcm, + kcm_set_default, + kcm_lastchange, + kcm_set_kdc_offset, + kcm_get_kdc_offset, + kcm_get_name_2, + kcm_resolve_2_kcm +}; + +KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops = { + KRB5_CC_OPS_VERSION_5, + "API", + NULL, + NULL, + kcm_gen_new_api, + kcm_initialize, + kcm_destroy, + kcm_close, + kcm_store_cred, + NULL /* kcm_retrieve */, + kcm_get_principal, + kcm_get_first, + kcm_get_next, + kcm_end_get, + kcm_remove_cred, + kcm_set_flags, + kcm_get_version, + kcm_get_cache_first, + kcm_get_cache_next_api, + kcm_end_cache_get, + kcm_move, + kcm_get_default_name_api, + kcm_set_default, + kcm_lastchange, + NULL, + NULL, + kcm_get_name_2, + kcm_resolve_2_api +}; + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +_krb5_kcm_is_running(krb5_context context) +{ + krb5_error_code ret; + krb5_ccache_data ccdata; + krb5_ccache id = &ccdata; + krb5_boolean running; + + ret = kcm_alloc(context, &krb5_kcm_ops, NULL, NULL, &id); + if (ret) + return 0; + + running = (_krb5_kcm_noop(context, id) == 0); + + kcm_free(context, &id); + + return running; +} + +/* + * Request: + * + * Response: + * + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kcm_noop(krb5_context context, + krb5_ccache id) +{ + krb5_error_code ret; + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_NOOP, &request); + if (ret) + return ret; + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + + +/* + * Request: + * NameZ + * ServerPrincipalPresent + * ServerPrincipal OPTIONAL + * Key + * + * Repsonse: + * + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kcm_get_initial_ticket(krb5_context context, + krb5_ccache id, + krb5_principal server, + krb5_keyblock *key) +{ + krb5_kcmcache *k = KCMCACHE(id); + krb5_error_code ret; + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_INITIAL_TICKET, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_int8(request, (server == NULL) ? 0 : 1); + if (ret) { + krb5_storage_free(request); + return ret; + } + + if (server != NULL) { + ret = krb5_store_principal(request, server); + if (ret) { + krb5_storage_free(request); + return ret; + } + } + + ret = krb5_store_keyblock(request, *key); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + + +/* + * Request: + * NameZ + * KDCFlags + * EncryptionType + * ServerPrincipal + * + * Repsonse: + * + */ +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kcm_get_ticket(krb5_context context, + krb5_ccache id, + krb5_kdc_flags flags, + krb5_enctype enctype, + krb5_principal server) +{ + krb5_error_code ret; + krb5_kcmcache *k = KCMCACHE(id); + krb5_storage *request; + + ret = krb5_kcm_storage_request(context, KCM_OP_GET_TICKET, &request); + if (ret) + return ret; + + ret = krb5_store_stringz(request, k->name); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_int32(request, flags.i); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_int32(request, enctype); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_store_principal(request, server); + if (ret) { + krb5_storage_free(request); + return ret; + } + + ret = krb5_kcm_call(context, request, NULL, NULL); + + krb5_storage_free(request); + return ret; +} + +#endif /* HAVE_KCM */ diff --git a/third_party/heimdal/lib/krb5/kcm.h b/third_party/heimdal/lib/krb5/kcm.h new file mode 100644 index 0000000..ba484b9 --- /dev/null +++ b/third_party/heimdal/lib/krb5/kcm.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2005, PADL Software Pty Ltd. + * 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef __KCM_H__ +#define __KCM_H__ + +/* + * KCM protocol definitions + */ + +#define KCM_PROTOCOL_VERSION_MAJOR 2 +#define KCM_PROTOCOL_VERSION_MINOR 0 + +typedef unsigned char kcmuuid_t[16]; + +typedef enum kcm_operation { + KCM_OP_NOOP, + KCM_OP_GET_NAME, + KCM_OP_RESOLVE, + KCM_OP_GEN_NEW, + KCM_OP_INITIALIZE, + KCM_OP_DESTROY, + KCM_OP_STORE, + KCM_OP_RETRIEVE, + KCM_OP_GET_PRINCIPAL, + KCM_OP_GET_CRED_UUID_LIST, + KCM_OP_GET_CRED_BY_UUID, + KCM_OP_REMOVE_CRED, + KCM_OP_SET_FLAGS, + KCM_OP_CHOWN, + KCM_OP_CHMOD, + KCM_OP_GET_INITIAL_TICKET, + KCM_OP_GET_TICKET, + KCM_OP_MOVE_CACHE, + KCM_OP_GET_CACHE_UUID_LIST, + KCM_OP_GET_CACHE_BY_UUID, + KCM_OP_GET_DEFAULT_CACHE, + KCM_OP_SET_DEFAULT_CACHE, + KCM_OP_GET_KDC_OFFSET, + KCM_OP_SET_KDC_OFFSET, + /* NTLM operations */ + KCM_OP_ADD_NTLM_CRED, + KCM_OP_HAVE_NTLM_CRED, + KCM_OP_DEL_NTLM_CRED, + KCM_OP_DO_NTLM_AUTH, + KCM_OP_GET_NTLM_USER_LIST, + KCM_OP_MAX +} kcm_operation; + +#define KCM_NTLM_FLAG_SESSIONKEY 1 +#define KCM_NTLM_FLAG_NTLM2_SESSION 2 +#define KCM_NTLM_FLAG_KEYEX 4 +#define KCM_NTLM_FLAG_AV_GUEST 8 + +#endif /* __KCM_H__ */ + diff --git a/third_party/heimdal/lib/krb5/kerberos.8 b/third_party/heimdal/lib/krb5/kerberos.8 new file mode 100644 index 0000000..fdcea04 --- /dev/null +++ b/third_party/heimdal/lib/krb5/kerberos.8 @@ -0,0 +1,115 @@ +.\" Copyright (c) 2000 Kungliga Tekniska Högskolan +.\" (Royal Institute of Technology, Stockholm, Sweden). +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" 3. Neither the name of the Institute nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd Jun 27, 2013 +.Dt KERBEROS 8 +.Os HEIMDAL +.Sh NAME +.Nm kerberos +.Nd introduction to the Kerberos system +.Sh DESCRIPTION +Kerberos is a network authentication system. Its purpose is to +securely authenticate users and services in an insecure network +environment. +.Pp +This is done with a Kerberos server acting as a trusted third party, +keeping a database with secret keys for all users and services +(collectively called +.Em principals ) . +.Pp +Each principal belongs to exactly one +.Em realm , +which is the administrative domain in Kerberos. A realm usually +corresponds to an organisation, and the realm should normally be +derived from that organisation's domain name. A realm is served by one +or more Kerberos servers. +.Pp +The authentication process involves exchange of +.Sq tickets +and +.Sq authenticators +which together prove the principal's identity. +.Pp +When you login to the Kerberos system, either through the normal +system login or with the +.Xr kinit 1 +program, you acquire a +.Em ticket granting ticket +which allows you to get new tickets for other services, such as +.Ic telnet +or +.Ic ftp , +without giving your password. +.Pp +For more information on how Kerberos works, see the tutorial at +.Lk https://kerberos.org/software/tutorial.html +or the informal +.Dq dialogue +at +.Lk https://web.mit.edu/kerberos/dialogue.html . +.Pp +For setup instructions see the Heimdal Texinfo manual. +.Sh SEE ALSO +.Xr ftp 1 , +.Xr kdestroy 1 , +.Xr kinit 1 , +.Xr klist 1 , +.Xr kpasswd 1 , +.Xr telnet 1 , +.Xr krb5 3 , +.Xr krb5.conf 5 , +.Xr kadmin 1 , +.Xr kdc 8 , +.Xr ktutil 1 +.Sh HISTORY +The Kerberos authentication system was developed in the late 1980's as +part of the Athena Project at the Massachusetts Institute of +Technology. Versions one through three never reached outside MIT, but +version 4 was (and still is) quite popular, especially in the academic +community, but is also used in commercial products like the AFS +filesystem. +.Pp +The problems with version 4 are that it has many limitations, the code +was not too well written (since it had been developed over a long +time), and it has a number of known security problems. To resolve many +of these issues work on version five started, and resulted in IETF RFC +1510 in 1993. IETF RFC 1510 was obsoleted in 2005 with IETF RFC 4120, +also known as Kerberos clarifications. With the arrival of IETF RFC +4120, the work on adding extensibility and internationalization have +started (Kerberos extensions), and a new RFC will hopefully appear +soon. +.Pp +This manual page is part of the +.Nm Heimdal +Kerberos 5 distribution, which has been in development at the Royal +Institute of Technology in Stockholm, Sweden, since about 1997. diff --git a/third_party/heimdal/lib/krb5/keyblock.c b/third_party/heimdal/lib/krb5/keyblock.c new file mode 100644 index 0000000..317bed3 --- /dev/null +++ b/third_party/heimdal/lib/krb5/keyblock.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/** + * Zero out a keyblock + * + * @param keyblock keyblock to zero out + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_keyblock_zero(krb5_keyblock *keyblock) +{ + keyblock->keytype = 0; + krb5_data_zero(&keyblock->keyvalue); +} + +/** + * Free a keyblock's content, also zero out the content of the keyblock. + * + * @param context a Kerberos 5 context + * @param keyblock keyblock content to free, NULL is valid argument + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_keyblock_contents(krb5_context context, + krb5_keyblock *keyblock) +{ + if(keyblock) { + if (keyblock->keyvalue.data != NULL) + memset_s(keyblock->keyvalue.data, keyblock->keyvalue.length, + 0, keyblock->keyvalue.length); + krb5_data_free (&keyblock->keyvalue); + keyblock->keytype = KRB5_ENCTYPE_NULL; + } +} + +/** + * Free a keyblock, also zero out the content of the keyblock, uses + * krb5_free_keyblock_contents() to free the content. + * + * @param context a Kerberos 5 context + * @param keyblock keyblock to free, NULL is valid argument + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION void KRB5_LIB_CALL +krb5_free_keyblock(krb5_context context, + krb5_keyblock *keyblock) +{ + if(keyblock){ + krb5_free_keyblock_contents(context, keyblock); + free(keyblock); + } +} + +/** + * Copy a keyblock, free the output keyblock with + * krb5_free_keyblock_contents(). + * + * @param context a Kerberos 5 context + * @param inblock the key to copy + * @param to the output key. + * + * @return 0 on success or a Kerberos 5 error code + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_keyblock_contents (krb5_context context, + const krb5_keyblock *inblock, + krb5_keyblock *to) +{ + return copy_EncryptionKey(inblock, to); +} + +/** + * Copy a keyblock, free the output keyblock with + * krb5_free_keyblock(). + * + * @param context a Kerberos 5 context + * @param inblock the key to copy + * @param to the output key. + * + * @return 0 on success or a Kerberos 5 error code + * + * @ingroup krb5_crypto + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_copy_keyblock (krb5_context context, + const krb5_keyblock *inblock, + krb5_keyblock **to) +{ + krb5_error_code ret; + krb5_keyblock *k; + + *to = NULL; + + k = calloc (1, sizeof(*k)); + if (k == NULL) + return krb5_enomem(context); + + ret = krb5_copy_keyblock_contents (context, inblock, k); + if (ret) { + free(k); + return ret; + } + *to = k; + return 0; +} + +/** + * Get encryption type of a keyblock. + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_enctype KRB5_LIB_CALL +krb5_keyblock_get_enctype(const krb5_keyblock *block) +{ + return block->keytype; +} + +/** + * Fill in `key' with key data of type `enctype' from `data' of length + * `size'. Key should be freed using krb5_free_keyblock_contents(). + * + * @return 0 on success or a Kerberos 5 error code + * + * @ingroup krb5_crypto + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_keyblock_init(krb5_context context, + krb5_enctype type, + const void *data, + size_t size, + krb5_keyblock *key) +{ + krb5_error_code ret; + size_t len; + + memset(key, 0, sizeof(*key)); + + ret = krb5_enctype_keysize(context, type, &len); + if (ret) + return ret; + + if (len != size) { + krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP, + "Encryption key %d is %lu bytes " + "long, %lu was passed in", + type, (unsigned long)len, (unsigned long)size); + return KRB5_PROG_ETYPE_NOSUPP; + } + ret = krb5_data_copy(&key->keyvalue, data, len); + if(ret) { + krb5_set_error_message(context, ret, N_("malloc: out of memory", "")); + return ret; + } + key->keytype = type; + + return 0; +} diff --git a/third_party/heimdal/lib/krb5/keytab.c b/third_party/heimdal/lib/krb5/keytab.c new file mode 100644 index 0000000..bcb3ed8 --- /dev/null +++ b/third_party/heimdal/lib/krb5/keytab.c @@ -0,0 +1,976 @@ +/* + * 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 "krb5_locl.h" + +/** + * @page krb5_keytab_intro The keytab handing functions + * @section section_krb5_keytab Kerberos Keytabs + * + * See the library functions here: @ref krb5_keytab + * + * Keytabs are long term key storage for servers, their equvalment of + * password files. + * + * Normally the only function that useful for server are to specify + * what keytab to use to other core functions like krb5_rd_req() + * krb5_kt_resolve(), and krb5_kt_close(). + * + * @subsection krb5_keytab_names Keytab names + * + * A keytab name is on the form type:residual. The residual part is + * specific to each keytab-type. + * + * When a keytab-name is resolved, the type is matched with an internal + * list of keytab types. If there is no matching keytab type, + * the default keytab is used. The current default type is FILE. + * + * The default value can be changed in the configuration file + * /etc/krb5.conf by setting the variable + * [defaults]default_keytab_name. + * + * The keytab types that are implemented in Heimdal are: + * - file + * store the keytab in a file, the type's name is FILE . The + * residual part is a filename. For compatibility with other + * Kerberos implemtation WRFILE and JAVA14 is also accepted. WRFILE + * has the same format as FILE. JAVA14 have a format that is + * compatible with older versions of MIT kerberos and SUN's Java + * based installation. They store a truncted kvno, so when the knvo + * excess 255, they are truncted in this format. + * + * - keytab + * store the keytab in a AFS keyfile (usually /usr/afs/etc/KeyFile ), + * the type's name is AFSKEYFILE. The residual part is a filename. + * + * - memory + * The keytab is stored in a memory segment. This allows sensitive + * and/or temporary data not to be stored on disk. The type's name + * is MEMORY. Each MEMORY keytab is referenced counted by and + * opened by the residual name, so two handles can point to the + * same memory area. When the last user closes using krb5_kt_close() + * the keytab, the keys in they keytab is memset() to zero and freed + * and can no longer be looked up by name. + * + * + * @subsection krb5_keytab_example Keytab example + * + * This is a minimalistic version of ktutil. + * + * @code +int +main (int argc, char **argv) +{ + krb5_context context; + krb5_keytab keytab; + krb5_kt_cursor cursor; + krb5_keytab_entry entry; + krb5_error_code ret; + char *principal; + + if (krb5_init_context (&context) != 0) + errx(1, "krb5_context"); + + ret = krb5_kt_default (context, &keytab); + if (ret) + krb5_err(context, 1, ret, "krb5_kt_default"); + + ret = krb5_kt_start_seq_get(context, keytab, &cursor); + if (ret) + krb5_err(context, 1, ret, "krb5_kt_start_seq_get"); + while((ret = krb5_kt_next_entry(context, keytab, &entry, &cursor)) == 0){ + krb5_unparse_name(context, entry.principal, &principal); + printf("principal: %s\n", principal); + free(principal); + krb5_kt_free_entry(context, &entry); + } + ret = krb5_kt_end_seq_get(context, keytab, &cursor); + if (ret) + krb5_err(context, 1, ret, "krb5_kt_end_seq_get"); + ret = krb5_kt_close(context, keytab); + if (ret) + krb5_err(context, 1, ret, "krb5_kt_close"); + krb5_free_context(context); + return 0; +} + * @endcode + * + */ + + +/** + * Register a new keytab backend. + * + * @param context a Keberos context. + * @param ops a backend to register. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_register(krb5_context context, + const krb5_kt_ops *ops) +{ + struct krb5_keytab_data *tmp; + + if (strlen(ops->prefix) > KRB5_KT_PREFIX_MAX_LEN - 1) { + krb5_set_error_message(context, KRB5_KT_BADNAME, + N_("can't register cache type, prefix too long", "")); + return KRB5_KT_BADNAME; + } + + tmp = realloc(context->kt_types, + (context->num_kt_types + 1) * sizeof(*context->kt_types)); + if(tmp == NULL) + return krb5_enomem(context); + memcpy(&tmp[context->num_kt_types], ops, + sizeof(tmp[context->num_kt_types])); + context->kt_types = tmp; + context->num_kt_types++; + return 0; +} + +static const char * +keytab_name(const char *name, const char **type, size_t *type_len) +{ + const char *residual; + + residual = strchr(name, ':'); + + if (residual == NULL || + ISPATHSEP(name[0]) +#ifdef _WIN32 + /* Avoid treating : as a keytab type + * specification */ + || name + 1 == residual +#endif + ) { + + *type = "FILE"; + *type_len = strlen(*type); + residual = name; + } else { + *type = name; + *type_len = residual - name; + residual++; + } + + return residual; +} + +/** + * Resolve the keytab name (of the form `type:residual') in `name' + * into a keytab in `id'. + * + * @param context a Keberos context. + * @param name name to resolve + * @param id resulting keytab, free with krb5_kt_close(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_resolve(krb5_context context, + const char *name, + krb5_keytab *id) +{ + krb5_keytab k; + int i; + const char *type, *residual; + size_t type_len; + krb5_error_code ret; + + residual = keytab_name(name, &type, &type_len); + + for(i = 0; i < context->num_kt_types; i++) { + if(strncasecmp(type, context->kt_types[i].prefix, type_len) == 0) + break; + } + if(i == context->num_kt_types) { + krb5_set_error_message(context, KRB5_KT_UNKNOWN_TYPE, + N_("unknown keytab type %.*s", "type"), + (int)type_len, type); + return KRB5_KT_UNKNOWN_TYPE; + } + + k = malloc (sizeof(*k)); + if (k == NULL) + return krb5_enomem(context); + memcpy(k, &context->kt_types[i], sizeof(*k)); + k->data = NULL; + ret = (*k->resolve)(context, residual, k); + if(ret) { + free(k); + k = NULL; + } + *id = k; + return ret; +} + +/* + * Default ktname from context with possible environment + * override + */ +static const char *default_ktname(krb5_context context) +{ + const char *tmp = NULL; + + tmp = secure_getenv("KRB5_KTNAME"); + if(tmp != NULL) + return tmp; + return context->default_keytab; +} + +/** + * copy the name of the default keytab into `name'. + * + * @param context a Keberos context. + * @param name buffer where the name will be written + * @param namesize length of name + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_default_name(krb5_context context, char *name, size_t namesize) +{ + if (strlcpy (name, default_ktname(context), namesize) >= namesize) { + krb5_clear_error_message (context); + return KRB5_CONFIG_NOTENUFSPACE; + } + return 0; +} + +/** + * Copy the name of the default modify keytab into `name'. + * + * @param context a Keberos context. + * @param name buffer where the name will be written + * @param namesize length of name + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize) +{ + const char *kt; + + if(context->default_keytab_modify == NULL) { + kt = default_ktname(context); + + if (strncasecmp(kt, "ANY:", 4) == 0) { + size_t len = strcspn(kt + 4, ","); + if (len >= namesize) { + krb5_clear_error_message(context); + return KRB5_CONFIG_NOTENUFSPACE; + } + strlcpy(name, kt + 4, namesize); + name[len] = '\0'; + return 0; + } + } else + kt = context->default_keytab_modify; + if (strlcpy (name, kt, namesize) >= namesize) { + krb5_clear_error_message (context); + return KRB5_CONFIG_NOTENUFSPACE; + } + return 0; +} + +/** + * Set `id' to the default keytab. + * + * @param context a Keberos context. + * @param id the new default keytab. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_default(krb5_context context, krb5_keytab *id) +{ + return krb5_kt_resolve (context, default_ktname(context), id); +} + +/** + * Read the key identified by `(principal, vno, enctype)' from the + * keytab in `keyprocarg' (the default if == NULL) into `*key'. + * + * @param context a Keberos context. + * @param keyprocarg + * @param principal + * @param vno + * @param enctype + * @param key + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_read_service_key(krb5_context context, + krb5_pointer keyprocarg, + krb5_principal principal, + krb5_kvno vno, + krb5_enctype enctype, + krb5_keyblock **key) +{ + krb5_keytab keytab = NULL; /* Quiet lint */ + krb5_keytab_entry entry; + krb5_error_code ret; + + memset(&entry, 0, sizeof(entry)); + if (keyprocarg) + ret = krb5_kt_resolve (context, keyprocarg, &keytab); + else + ret = krb5_kt_default (context, &keytab); + + if (ret) + return ret; + + ret = krb5_kt_get_entry (context, keytab, principal, vno, enctype, &entry); + if (ret == 0) { + ret = krb5_copy_keyblock (context, &entry.keyblock, key); + krb5_kt_free_entry(context, &entry); + } + krb5_kt_close (context, keytab); + return ret; +} + +/** + * Return the type of the `keytab' in the string `prefix of length + * `prefixsize'. + * + * @param context a Keberos context. + * @param keytab the keytab to get the prefix for + * @param prefix prefix buffer + * @param prefixsize length of prefix buffer + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_get_type(krb5_context context, + krb5_keytab keytab, + char *prefix, + size_t prefixsize) +{ + strlcpy(prefix, keytab->prefix, prefixsize); + return 0; +} + +/** + * Retrieve the name of the keytab `keytab' into `name', `namesize' + * + * @param context a Keberos context. + * @param keytab the keytab to get the name for. + * @param name name buffer. + * @param namesize size of name buffer. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_get_name(krb5_context context, + krb5_keytab keytab, + char *name, + size_t namesize) +{ + return (*keytab->get_name)(context, keytab, name, namesize); +} + +/** + * Retrieve the full name of the keytab `keytab' and store the name in + * `str'. + * + * @param context a Keberos context. + * @param keytab keytab to get name for. + * @param str the name of the keytab name, usee krb5_xfree() to free + * the string. On error, *str is set to NULL. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_get_full_name(krb5_context context, + krb5_keytab keytab, + char **str) +{ + char type[KRB5_KT_PREFIX_MAX_LEN]; + char name[MAXPATHLEN]; + krb5_error_code ret; + + *str = NULL; + + ret = krb5_kt_get_type(context, keytab, type, sizeof(type)); + if (ret) + return ret; + + ret = krb5_kt_get_name(context, keytab, name, sizeof(name)); + if (ret) + return ret; + + if (asprintf(str, "%s:%s", type, name) == -1) { + *str = NULL; + return krb5_enomem(context); + } + + return 0; +} + +/** + * Finish using the keytab in `id'. All resources will be released, + * even on errors. + * + * @param context a Keberos context. + * @param id keytab to close. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_close(krb5_context context, + krb5_keytab id) +{ + krb5_error_code ret = 0; + + if (id) { + ret = (id->close)(context, id); + memset(id, 0, sizeof(*id)); + free(id); + } + return ret; +} + +/** + * Destroy (remove) the keytab in `id'. All resources will be released, + * even on errors, does the equvalment of krb5_kt_close() on the resources. + * + * @param context a Keberos context. + * @param id keytab to destroy. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_destroy(krb5_context context, + krb5_keytab id) +{ + krb5_error_code ret; + + ret = (*id->destroy)(context, id); + krb5_kt_close(context, id); + return ret; +} + +/* + * Match any aliases in keytab `entry' with `principal'. + */ + +static krb5_boolean +compare_aliases(krb5_context context, + krb5_keytab_entry *entry, + krb5_const_principal principal) +{ + unsigned int i; + if (entry->aliases == NULL) + return FALSE; + for (i = 0; i < entry->aliases->len; i++) + if (krb5_principal_compare(context, &entry->aliases->val[i], principal)) + return TRUE; + return FALSE; +} + +/** + * Compare `entry' against `principal, vno, enctype'. + * Any of `principal, vno, enctype' might be 0 which acts as a wildcard. + * Return TRUE if they compare the same, FALSE otherwise. + * + * @param context a Keberos context. + * @param entry an entry to match with. + * @param principal principal to match, NULL matches all principals. + * @param vno key version to match, 0 matches all key version numbers. + * @param enctype encryption type to match, 0 matches all encryption types. + * + * @return Return TRUE or match, FALSE if not matched. + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL +krb5_kt_compare(krb5_context context, + krb5_keytab_entry *entry, + krb5_const_principal principal, + krb5_kvno vno, + krb5_enctype enctype) +{ + /* krb5_principal_compare() does not special-case the referral realm */ + if (principal != NULL && strcmp(principal->realm, "") == 0 && + !(krb5_principal_compare_any_realm(context, entry->principal, principal) || + compare_aliases(context, entry, principal))) { + return FALSE; + } else if (principal != NULL && strcmp(principal->realm, "") != 0 && + !(krb5_principal_compare(context, entry->principal, principal) || + compare_aliases(context, entry, principal))) { + return FALSE; + } + if (vno && vno != entry->vno) + return FALSE; + if (enctype && enctype != entry->keyblock.keytype) + return FALSE; + return TRUE; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kt_principal_not_found(krb5_context context, + krb5_error_code ret, + krb5_keytab id, + krb5_const_principal principal, + krb5_enctype enctype, + int kvno) +{ + char kvno_str[25]; + char *enctype_str = NULL; + char *kt_name = NULL; + char *princ = NULL; + + (void) krb5_unparse_name(context, principal, &princ); + (void) krb5_kt_get_full_name(context, id, &kt_name); + if (enctype) + (void) krb5_enctype_to_string(context, enctype, &enctype_str); + + if (kvno) + snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno); + else + kvno_str[0] = '\0'; + + krb5_set_error_message(context, ret, + N_("Failed to find %s%s in keytab %s (%s)", + "principal, kvno, keytab file, enctype"), + princ ? princ : "", + kvno_str, + kt_name ? kt_name : "unknown keytab", + enctype_str ? enctype_str : "unknown enctype"); + free(princ); + free(kt_name); + free(enctype_str); + return ret; +} + +static krb5_error_code +krb5_kt_get_entry_wrapped(krb5_context context, + krb5_keytab id, + krb5_const_principal principal, + krb5_kvno kvno, + krb5_enctype enctype, + krb5_keytab_entry *entry) +{ + krb5_keytab_entry tmp; + krb5_error_code ret; + krb5_kt_cursor cursor; + + if(id->get) + return (*id->get)(context, id, principal, kvno, enctype, entry); + + memset(&tmp, 0, sizeof(tmp)); + ret = krb5_kt_start_seq_get (context, id, &cursor); + if (ret) { + /* This is needed for krb5_verify_init_creds, but keep error + * string from previous error for the human. */ + context->error_code = KRB5_KT_NOTFOUND; + return KRB5_KT_NOTFOUND; + } + + entry->vno = 0; + while (krb5_kt_next_entry(context, id, &tmp, &cursor) == 0) { + if (krb5_kt_compare(context, &tmp, principal, 0, enctype)) { + /* the file keytab might only store the lower 8 bits of + the kvno, so only compare those bits */ + if (kvno == tmp.vno + || (tmp.vno < 256 && kvno % 256 == tmp.vno)) { + krb5_kt_copy_entry_contents (context, &tmp, entry); + krb5_kt_free_entry (context, &tmp); + krb5_kt_end_seq_get(context, id, &cursor); + return 0; + } else if (kvno == 0 && tmp.vno > entry->vno) { + if (entry->vno) + krb5_kt_free_entry (context, entry); + krb5_kt_copy_entry_contents (context, &tmp, entry); + } + } + krb5_kt_free_entry(context, &tmp); + } + krb5_kt_end_seq_get (context, id, &cursor); + if (entry->vno == 0) + return _krb5_kt_principal_not_found(context, KRB5_KT_NOTFOUND, + id, principal, enctype, kvno); + return 0; +} + +/** + * Retrieve the keytab entry for `principal, kvno, enctype' into `entry' + * from the keytab `id'. Matching is done like krb5_kt_compare(). + * + * @param context a Keberos context. + * @param id a keytab. + * @param principal principal to match, NULL matches all principals. + * @param kvno key version to match, 0 matches all key version numbers. + * @param enctype encryption type to match, 0 matches all encryption types. + * @param entry the returned entry, free with krb5_kt_free_entry(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_get_entry(krb5_context context, + krb5_keytab id, + krb5_const_principal principal, + krb5_kvno kvno, + krb5_enctype enctype, + krb5_keytab_entry *entry) +{ + krb5_error_code ret; + krb5_const_principal try_princ; + krb5_name_canon_iterator name_canon_iter; + + if (!principal) + /* Use `NULL' instead of `principal' to quiet static analizers */ + return krb5_kt_get_entry_wrapped(context, id, NULL, kvno, enctype, + entry); + + ret = krb5_name_canon_iterator_start(context, principal, &name_canon_iter); + if (ret) + return ret; + + do { + ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ, + NULL); + if (ret) + break; + if (try_princ == NULL) { + ret = KRB5_KT_NOTFOUND; + continue; + } + ret = krb5_kt_get_entry_wrapped(context, id, try_princ, kvno, + enctype, entry); + } while (ret == KRB5_KT_NOTFOUND && name_canon_iter); + + if (ret && ret != KRB5_KT_NOTFOUND) + krb5_set_error_message(context, ret, + N_("Name canon failed while searching keytab", + "")); + krb5_free_name_canon_iterator(context, name_canon_iter); + return ret; +} + +/** + * Copy the contents of `in' into `out'. + * + * @param context a Keberos context. + * @param in the keytab entry to copy. + * @param out the copy of the keytab entry, free with krb5_kt_free_entry(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_copy_entry_contents(krb5_context context, + const krb5_keytab_entry *in, + krb5_keytab_entry *out) +{ + krb5_error_code ret; + + memset(out, 0, sizeof(*out)); + + ret = krb5_copy_principal (context, in->principal, &out->principal); + if (ret) + return ret; + ret = krb5_copy_keyblock_contents (context, + &in->keyblock, + &out->keyblock); + if (ret) { + krb5_free_principal(context, out->principal); + memset(out, 0, sizeof(*out)); + return ret; + } + out->vno = in->vno; + out->timestamp = in->timestamp; + return 0; +} + +/** + * Free the contents of `entry'. + * + * @param context a Keberos context. + * @param entry the entry to free + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_free_entry(krb5_context context, + krb5_keytab_entry *entry) +{ + krb5_free_principal (context, entry->principal); + krb5_free_keyblock_contents (context, &entry->keyblock); + memset(entry, 0, sizeof(*entry)); + return 0; +} + +/** + * Set `cursor' to point at the beginning of `id'. + * + * @param context a Keberos context. + * @param id a keytab. + * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get(). + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_start_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *cursor) +{ + if(id->start_seq_get == NULL) { + krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, + N_("start_seq_get is not supported " + "in the %s keytab type", ""), + id->prefix); + return HEIM_ERR_OPNOTSUPP; + } + return (*id->start_seq_get)(context, id, cursor); +} + +/** + * Get the next entry from keytab, advance the cursor. On last entry + * the function will return KRB5_KT_END. + * + * @param context a Keberos context. + * @param id a keytab. + * @param entry the returned entry, free with krb5_kt_free_entry(). + * @param cursor the cursor of the iteration. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_next_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry, + krb5_kt_cursor *cursor) +{ + if(id->next_entry == NULL) { + krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, + N_("next_entry is not supported in the %s " + " keytab", ""), + id->prefix); + return HEIM_ERR_OPNOTSUPP; + } + memset(entry, 0x0, sizeof(*entry)); + return (*id->next_entry)(context, id, entry, cursor); +} + +/** + * Release all resources associated with `cursor'. + * + * @param context a Keberos context. + * @param id a keytab. + * @param cursor the cursor to free. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_end_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *cursor) +{ + if(id->end_seq_get == NULL) { + krb5_set_error_message(context, HEIM_ERR_OPNOTSUPP, + "end_seq_get is not supported in the %s " + " keytab", id->prefix); + return HEIM_ERR_OPNOTSUPP; + } + return (*id->end_seq_get)(context, id, cursor); +} + +/** + * Add the entry in `entry' to the keytab `id'. + * + * @param context a Keberos context. + * @param id a keytab. + * @param entry the entry to add + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_add_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + if(id->add == NULL) { + krb5_set_error_message(context, KRB5_KT_NOWRITE, + N_("Add is not supported in the %s keytab", ""), + id->prefix); + return KRB5_KT_NOWRITE; + } + if (entry->timestamp == 0) + entry->timestamp = time(NULL); + return (*id->add)(context, id,entry); +} + +/** + * Remove an entry from the keytab, matching is done using + * krb5_kt_compare(). + + * @param context a Keberos context. + * @param id a keytab. + * @param entry the entry to remove + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_remove_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + if(id->remove == NULL) { + krb5_set_error_message(context, KRB5_KT_NOWRITE, + N_("Remove is not supported in the %s keytab", ""), + id->prefix); + return KRB5_KT_NOWRITE; + } + return (*id->remove)(context, id, entry); +} + +/** + * Return true if the keytab exists and have entries + * + * @param context a Keberos context. + * @param id a keytab. + * + * @return Return an error code or 0, see krb5_get_error_message(). + * + * @ingroup krb5_keytab + */ + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_kt_have_content(krb5_context context, + krb5_keytab id) +{ + krb5_keytab_entry entry; + krb5_kt_cursor cursor; + krb5_error_code ret; + char *name; + + memset(&entry, 0, sizeof(entry)); + ret = krb5_kt_start_seq_get(context, id, &cursor); + if (ret) + goto notfound; + + ret = krb5_kt_next_entry(context, id, &entry, &cursor); + krb5_kt_end_seq_get(context, id, &cursor); + if (ret) + goto notfound; + + krb5_kt_free_entry(context, &entry); + + return 0; + + notfound: + ret = krb5_kt_get_full_name(context, id, &name); + if (ret == 0) { + krb5_set_error_message(context, KRB5_KT_NOTFOUND, + N_("No entry in keytab: %s", ""), name); + free(name); + } + return KRB5_KT_NOTFOUND; +} + +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +_krb5_kt_client_default_name(krb5_context context, char **name) +{ + const char *tmp; + + tmp = secure_getenv("KRB5_CLIENT_KTNAME"); + if (tmp == NULL) + tmp = krb5_config_get_string(context, NULL, + "libdefaults", + "default_client_keytab_name", NULL); + if (tmp == NULL) + tmp = CLIENT_KEYTAB_DEFAULT; + + return _krb5_expand_path_tokens(context, tmp, 1, name); +} diff --git a/third_party/heimdal/lib/krb5/keytab_any.c b/third_party/heimdal/lib/krb5/keytab_any.c new file mode 100644 index 0000000..6663d17 --- /dev/null +++ b/third_party/heimdal/lib/krb5/keytab_any.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2001-2002 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +struct any_data { + krb5_keytab kt; + char *name; + struct any_data *next; +}; + +static void +free_list (krb5_context context, struct any_data *a) +{ + struct any_data *next; + + for (; a != NULL; a = next) { + next = a->next; + free (a->name); + if(a->kt) + krb5_kt_close(context, a->kt); + free (a); + } +} + +static krb5_error_code KRB5_CALLCONV +any_resolve(krb5_context context, const char *name, krb5_keytab id) +{ + struct any_data *a, *a0 = NULL, *prev = NULL; + krb5_error_code ret; + char buf[256]; + + while (strsep_copy(&name, ",", buf, sizeof(buf)) != -1) { + a = calloc(1, sizeof(*a)); + if (a == NULL) { + ret = krb5_enomem(context); + goto fail; + } + if (a0 == NULL) { + a0 = a; + a->name = strdup(buf); + if (a->name == NULL) { + ret = krb5_enomem(context); + goto fail; + } + } else + a->name = NULL; + if (prev != NULL) + prev->next = a; + a->next = NULL; + ret = krb5_kt_resolve (context, buf, &a->kt); + if (ret) + goto fail; + prev = a; + } + if (a0 == NULL) { + krb5_set_error_message(context, ENOENT, N_("empty ANY: keytab", "")); + return ENOENT; + } + id->data = a0; + return 0; + fail: + free_list (context, a0); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +any_get_name (krb5_context context, + krb5_keytab id, + char *name, + size_t namesize) +{ + struct any_data *a = id->data; + strlcpy(name, a->name, namesize); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +any_close (krb5_context context, + krb5_keytab id) +{ + struct any_data *a = id->data; + + free_list (context, a); + return 0; +} + +struct any_cursor_extra_data { + struct any_data *a; + krb5_kt_cursor cursor; +}; + +static krb5_error_code KRB5_CALLCONV +any_start_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *c) +{ + struct any_data *a = id->data; + struct any_cursor_extra_data *ed; + krb5_error_code ret; + + c->data = malloc (sizeof(struct any_cursor_extra_data)); + if(c->data == NULL) + return krb5_enomem(context); + ed = (struct any_cursor_extra_data *)c->data; + for (ed->a = a; ed->a != NULL; ed->a = ed->a->next) { + ret = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor); + if (ret == 0) + break; + } + if (ed->a == NULL) { + free (c->data); + c->data = NULL; + krb5_clear_error_message (context); + return KRB5_KT_END; + } + return 0; +} + +static krb5_error_code KRB5_CALLCONV +any_next_entry (krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry, + krb5_kt_cursor *cursor) +{ + krb5_error_code ret, ret2; + struct any_cursor_extra_data *ed; + + ed = (struct any_cursor_extra_data *)cursor->data; + do { + ret = krb5_kt_next_entry(context, ed->a->kt, entry, &ed->cursor); + if (ret == 0) + return 0; + else if (ret != KRB5_KT_END) + return ret; + + ret2 = krb5_kt_end_seq_get (context, ed->a->kt, &ed->cursor); + if (ret2) + return ret2; + while ((ed->a = ed->a->next) != NULL) { + ret2 = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor); + if (ret2 == 0) + break; + } + if (ed->a == NULL) { + krb5_clear_error_message (context); + return KRB5_KT_END; + } + } while (1); +} + +static krb5_error_code KRB5_CALLCONV +any_end_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *cursor) +{ + krb5_error_code ret = 0; + struct any_cursor_extra_data *ed; + + ed = (struct any_cursor_extra_data *)cursor->data; + if (ed->a != NULL) + ret = krb5_kt_end_seq_get(context, ed->a->kt, &ed->cursor); + free (ed); + cursor->data = NULL; + return ret; +} + +static krb5_error_code KRB5_CALLCONV +any_add_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + struct any_data *a = id->data; + krb5_error_code ret; + while(a != NULL) { + ret = krb5_kt_add_entry(context, a->kt, entry); + if(ret != 0 && ret != KRB5_KT_NOWRITE) { + krb5_set_error_message(context, ret, + N_("failed to add entry to %s", ""), + a->name); + return ret; + } + a = a->next; + } + return 0; +} + +static krb5_error_code KRB5_CALLCONV +any_remove_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + struct any_data *a = id->data; + krb5_error_code ret; + krb5_boolean found = FALSE; + while(a != NULL) { + ret = krb5_kt_remove_entry(context, a->kt, entry); + if(ret == 0) + found = TRUE; + else { + if(ret != KRB5_KT_NOWRITE && ret != KRB5_KT_NOTFOUND) { + krb5_set_error_message(context, ret, + N_("Failed to remove keytab " + "entry from %s", "keytab name"), + a->name); + return ret; + } + } + a = a->next; + } + if(!found) + return KRB5_KT_NOTFOUND; + return 0; +} + +const krb5_kt_ops krb5_any_ops = { + "ANY", + any_resolve, + any_get_name, + any_close, + NULL, /* destroy */ + NULL, /* get */ + any_start_seq_get, + any_next_entry, + any_end_seq_get, + any_add_entry, + any_remove_entry, + NULL, + 0 +}; diff --git a/third_party/heimdal/lib/krb5/keytab_file.c b/third_party/heimdal/lib/krb5/keytab_file.c new file mode 100644 index 0000000..61b5d6d --- /dev/null +++ b/third_party/heimdal/lib/krb5/keytab_file.c @@ -0,0 +1,856 @@ +/* + * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#define KRB5_KT_VNO_1 1 +#define KRB5_KT_VNO_2 2 +#define KRB5_KT_VNO KRB5_KT_VNO_2 + +#define KRB5_KT_FL_JAVA 1 + + +/* file operations -------------------------------------------- */ + +struct fkt_data { + char *filename; + int flags; +}; + +static krb5_error_code +krb5_kt_ret_data(krb5_context context, + krb5_storage *sp, + krb5_data *data) +{ + krb5_error_code ret; + krb5_ssize_t bytes; + int16_t size; + + ret = krb5_ret_int16(sp, &size); + if(ret) + return ret; + data->length = size; + data->data = malloc(size); + if (data->data == NULL) + return krb5_enomem(context); + bytes = krb5_storage_read(sp, data->data, size); + if (bytes != size) + return (bytes == -1) ? errno : KRB5_KT_END; + return 0; +} + +static krb5_error_code +krb5_kt_ret_string(krb5_context context, + krb5_storage *sp, + heim_general_string *data) +{ + krb5_error_code ret; + krb5_ssize_t bytes; + int16_t size; + + ret = krb5_ret_int16(sp, &size); + if(ret) + return ret; + *data = malloc(size + 1); + if (*data == NULL) + return krb5_enomem(context); + bytes = krb5_storage_read(sp, *data, size); + (*data)[size] = '\0'; + if (bytes != size) + return (bytes == -1) ? errno : KRB5_KT_END; + return 0; +} + +static krb5_error_code +krb5_kt_store_data(krb5_context context, + krb5_storage *sp, + krb5_data data) +{ + krb5_error_code ret; + krb5_ssize_t bytes; + + ret = krb5_store_int16(sp, data.length); + if (ret != 0) + return ret; + bytes = krb5_storage_write(sp, data.data, data.length); + if (bytes != (int)data.length) + return bytes == -1 ? errno : KRB5_KT_END; + return 0; +} + +static krb5_error_code +krb5_kt_store_string(krb5_storage *sp, + heim_general_string data) +{ + krb5_error_code ret; + krb5_ssize_t bytes; + size_t len = strlen(data); + + ret = krb5_store_int16(sp, len); + if (ret != 0) + return ret; + bytes = krb5_storage_write(sp, data, len); + if (bytes != (int)len) + return bytes == -1 ? errno : KRB5_KT_END; + return 0; +} + +static krb5_error_code +krb5_kt_ret_keyblock(krb5_context context, + struct fkt_data *fkt, + krb5_storage *sp, + krb5_keyblock *p) +{ + int ret; + int16_t tmp; + + ret = krb5_ret_int16(sp, &tmp); /* keytype + etype */ + if(ret) { + krb5_set_error_message(context, ret, + N_("Cant read keyblock from file %s", ""), + fkt->filename); + return ret; + } + p->keytype = tmp; + ret = krb5_kt_ret_data(context, sp, &p->keyvalue); + if (ret) + krb5_set_error_message(context, ret, + N_("Cant read keyblock from file %s", ""), + fkt->filename); + return ret; +} + +static krb5_error_code +krb5_kt_store_keyblock(krb5_context context, + struct fkt_data *fkt, + krb5_storage *sp, + krb5_keyblock *p) +{ + int ret; + + ret = krb5_store_int16(sp, p->keytype); /* keytype + etype */ + if(ret) { + krb5_set_error_message(context, ret, + N_("Cant store keyblock to file %s", ""), + fkt->filename); + return ret; + } + ret = krb5_kt_store_data(context, sp, p->keyvalue); + if (ret) + krb5_set_error_message(context, ret, + N_("Cant store keyblock to file %s", ""), + fkt->filename); + return ret; +} + + +static krb5_error_code +krb5_kt_ret_principal(krb5_context context, + struct fkt_data *fkt, + krb5_storage *sp, + krb5_principal *princ) +{ + size_t i; + int ret; + krb5_principal p; + int16_t len; + + ALLOC(p, 1); + if(p == NULL) + return krb5_enomem(context); + + ret = krb5_ret_int16(sp, &len); + if(ret) { + krb5_set_error_message(context, ret, + N_("Failed decoding length of " + "keytab principal in keytab file %s", ""), + fkt->filename); + goto out; + } + if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS)) + len--; + if (len < 0) { + ret = KRB5_KT_END; + krb5_set_error_message(context, ret, + N_("Keytab principal contains " + "invalid length in keytab %s", ""), + fkt->filename); + goto out; + } + ret = krb5_kt_ret_string(context, sp, &p->realm); + if(ret) { + krb5_set_error_message(context, ret, + N_("Can't read realm from keytab: %s", ""), + fkt->filename); + goto out; + } + p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val)); + if(p->name.name_string.val == NULL) { + ret = krb5_enomem(context); + goto out; + } + p->name.name_string.len = len; + for(i = 0; i < p->name.name_string.len; i++){ + ret = krb5_kt_ret_string(context, sp, p->name.name_string.val + i); + if(ret) { + krb5_set_error_message(context, ret, + N_("Can't read principal from " + "keytab: %s", ""), + fkt->filename); + goto out; + } + } + if (krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) + p->name.name_type = KRB5_NT_UNKNOWN; + else { + int32_t tmp32; + ret = krb5_ret_int32(sp, &tmp32); + p->name.name_type = tmp32; + if (ret) { + krb5_set_error_message(context, ret, + N_("Can't read name-type from " + "keytab: %s", ""), + fkt->filename); + goto out; + } + } + *princ = p; + return 0; +out: + krb5_free_principal(context, p); + return ret; +} + +static krb5_error_code +krb5_kt_store_principal(krb5_context context, + krb5_storage *sp, + krb5_principal p) +{ + size_t i; + int ret; + + if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS)) + ret = krb5_store_int16(sp, p->name.name_string.len + 1); + else + ret = krb5_store_int16(sp, p->name.name_string.len); + if(ret) return ret; + ret = krb5_kt_store_string(sp, p->realm); + if(ret) return ret; + for(i = 0; i < p->name.name_string.len; i++){ + ret = krb5_kt_store_string(sp, p->name.name_string.val[i]); + if(ret) + return ret; + } + if(!krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE)) { + ret = krb5_store_int32(sp, p->name.name_type); + if(ret) + return ret; + } + + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fkt_resolve(krb5_context context, const char *name, krb5_keytab id) +{ + struct fkt_data *d; + + d = malloc(sizeof(*d)); + if(d == NULL) + return krb5_enomem(context); + d->filename = strdup(name); + if(d->filename == NULL) { + free(d); + return krb5_enomem(context); + } + d->flags = 0; + id->data = d; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fkt_resolve_java14(krb5_context context, const char *name, krb5_keytab id) +{ + krb5_error_code ret; + + ret = fkt_resolve(context, name, id); + if (ret == 0) { + struct fkt_data *d = id->data; + d->flags |= KRB5_KT_FL_JAVA; + } + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fkt_close(krb5_context context, krb5_keytab id) +{ + struct fkt_data *d = id->data; + free(d->filename); + free(d); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fkt_destroy(krb5_context context, krb5_keytab id) +{ + struct fkt_data *d = id->data; + _krb5_erase_file(context, d->filename); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fkt_get_name(krb5_context context, + krb5_keytab id, + char *name, + size_t namesize) +{ + /* This function is XXX */ + struct fkt_data *d = id->data; + strlcpy(name, d->filename, namesize); + return 0; +} + +static void +storage_set_flags(krb5_context context, krb5_storage *sp, int vno) +{ + int flags = 0; + switch(vno) { + case KRB5_KT_VNO_1: + flags |= KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS; + flags |= KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE; + flags |= KRB5_STORAGE_HOST_BYTEORDER; + break; + case KRB5_KT_VNO_2: + break; + default: + krb5_warnx(context, + "storage_set_flags called with bad vno (%d)", vno); + } + krb5_storage_set_flags(sp, flags); +} + +static krb5_error_code +fkt_start_seq_get_int(krb5_context context, + krb5_keytab id, + int flags, + int exclusive, + krb5_kt_cursor *c) +{ + int8_t pvno, tag; + krb5_error_code ret; + struct fkt_data *d = id->data; + const char *stdio_mode = "rb"; + + memset(c, 0, sizeof(*c)); + c->fd = open (d->filename, flags); + if (c->fd < 0) { + ret = errno; + krb5_set_error_message(context, ret, + N_("keytab %s open failed: %s", ""), + d->filename, strerror(ret)); + return ret; + } + rk_cloexec(c->fd); + ret = _krb5_xlock(context, c->fd, exclusive, d->filename); + if (ret) { + close(c->fd); + return ret; + } + if ((flags & O_ACCMODE) == O_RDWR && (flags & O_APPEND)) + stdio_mode = "ab+"; + else if ((flags & O_ACCMODE) == O_RDWR) + stdio_mode = "rb+"; + else if ((flags & O_ACCMODE) == O_WRONLY) + stdio_mode = "wb"; + c->sp = krb5_storage_stdio_from_fd(c->fd, stdio_mode); + if (c->sp == NULL) { + close(c->fd); + return krb5_enomem(context); + } + krb5_storage_set_eof_code(c->sp, KRB5_KT_END); + ret = krb5_ret_int8(c->sp, &pvno); + if(ret) { + krb5_storage_free(c->sp); + close(c->fd); + krb5_clear_error_message(context); + return ret; + } + if(pvno != 5) { + krb5_storage_free(c->sp); + close(c->fd); + krb5_clear_error_message (context); + return KRB5_KEYTAB_BADVNO; + } + ret = krb5_ret_int8(c->sp, &tag); + if (ret) { + krb5_storage_free(c->sp); + close(c->fd); + krb5_clear_error_message(context); + return ret; + } + id->version = tag; + storage_set_flags(context, c->sp, id->version); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fkt_start_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *c) +{ + return fkt_start_seq_get_int(context, id, O_RDONLY | O_BINARY | O_CLOEXEC, 0, c); +} + +static krb5_error_code +fkt_next_entry_int(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry, + krb5_kt_cursor *cursor, + off_t *start, + off_t *end) +{ + struct fkt_data *d = id->data; + int32_t len; + int ret; + int8_t tmp8; + int32_t tmp32; + uint32_t utmp32; + off_t pos, curpos; + + pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); +loop: + ret = krb5_ret_int32(cursor->sp, &len); + if (ret) + return ret; + if(len < 0) { + pos = krb5_storage_seek(cursor->sp, -len, SEEK_CUR); + goto loop; + } + ret = krb5_kt_ret_principal (context, d, cursor->sp, &entry->principal); + if (ret) + goto out; + ret = krb5_ret_uint32(cursor->sp, &utmp32); + entry->timestamp = utmp32; + if (ret) + goto out; + ret = krb5_ret_int8(cursor->sp, &tmp8); + if (ret) + goto out; + entry->vno = tmp8; + ret = krb5_kt_ret_keyblock (context, d, cursor->sp, &entry->keyblock); + if (ret) + goto out; + /* there might be a 32 bit kvno here + * if it's zero, assume that the 8bit one was right, + * otherwise trust the new value */ + curpos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); + if(len + 4 + pos - curpos >= 4) { + ret = krb5_ret_int32(cursor->sp, &tmp32); + if (ret == 0 && tmp32 != 0) + entry->vno = tmp32; + } + /* there might be a flags field here */ + if(len + 4 + pos - curpos >= 8) { + ret = krb5_ret_uint32(cursor->sp, &utmp32); + if (ret == 0) + entry->flags = utmp32; + } else + entry->flags = 0; + + entry->aliases = NULL; + + if(start) *start = pos; + if(end) *end = pos + 4 + len; + out: + if (ret) + krb5_kt_free_entry(context, entry); + krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fkt_next_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry, + krb5_kt_cursor *cursor) +{ + return fkt_next_entry_int(context, id, entry, cursor, NULL, NULL); +} + +static krb5_error_code KRB5_CALLCONV +fkt_end_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *cursor) +{ + krb5_storage_free(cursor->sp); + close(cursor->fd); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +fkt_setup_keytab(krb5_context context, + krb5_keytab id, + krb5_storage *sp) +{ + krb5_error_code ret; + ret = krb5_store_int8(sp, 5); + if(ret) + return ret; + if(id->version == 0) + id->version = KRB5_KT_VNO; + return krb5_store_int8 (sp, id->version); +} + +static krb5_error_code KRB5_CALLCONV +fkt_add_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + int ret; + int fd; + krb5_storage *sp; + krb5_ssize_t bytes; + struct fkt_data *d = id->data; + krb5_data keytab; + int32_t len; + + fd = open(d->filename, O_RDWR | O_BINARY | O_CLOEXEC); + if (fd < 0) { + fd = open(d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600); + if (fd < 0) { + ret = errno; + krb5_set_error_message(context, ret, + N_("open(%s): %s", ""), d->filename, + strerror(ret)); + return ret; + } + rk_cloexec(fd); + + ret = _krb5_xlock(context, fd, 1, d->filename); + if (ret) { + close(fd); + return ret; + } + sp = krb5_storage_stdio_from_fd(fd, "wb+"); + if (sp == NULL) { + close(fd); + return krb5_enomem(context); + } + krb5_storage_set_eof_code(sp, KRB5_KT_END); + ret = fkt_setup_keytab(context, id, sp); + if (ret) { + goto out; + } + storage_set_flags(context, sp, id->version); + } else { + int8_t pvno, tag; + + rk_cloexec(fd); + + ret = _krb5_xlock(context, fd, 1, d->filename); + if (ret) { + close(fd); + return ret; + } + sp = krb5_storage_stdio_from_fd(fd, "wb+"); + if (sp == NULL) { + (void) close(fd); + return ret; + } + krb5_storage_set_eof_code(sp, KRB5_KT_END); + ret = krb5_ret_int8(sp, &pvno); + if(ret) { + /* we probably have a zero byte file, so try to set it up + properly */ + ret = fkt_setup_keytab(context, id, sp); + if(ret) { + krb5_set_error_message(context, ret, + N_("%s: keytab is corrupted: %s", ""), + d->filename, strerror(ret)); + goto out; + } + storage_set_flags(context, sp, id->version); + } else { + if(pvno != 5) { + ret = KRB5_KEYTAB_BADVNO; + krb5_set_error_message(context, ret, + N_("Bad version in keytab %s", ""), + d->filename); + goto out; + } + ret = krb5_ret_int8 (sp, &tag); + if (ret) { + krb5_set_error_message(context, ret, + N_("failed reading tag from " + "keytab %s", ""), + d->filename); + goto out; + } + id->version = tag; + storage_set_flags(context, sp, id->version); + } + } + + { + krb5_storage *emem; + emem = krb5_storage_emem(); + if(emem == NULL) { + ret = krb5_enomem(context); + goto out; + } + ret = krb5_kt_store_principal(context, emem, entry->principal); + if(ret) { + krb5_set_error_message(context, ret, + N_("Failed storing principal " + "in keytab %s", ""), + d->filename); + krb5_storage_free(emem); + goto out; + } + ret = krb5_store_int32 (emem, entry->timestamp); + if(ret) { + krb5_set_error_message(context, ret, + N_("Failed storing timpstamp " + "in keytab %s", ""), + d->filename); + krb5_storage_free(emem); + goto out; + } + ret = krb5_store_int8 (emem, entry->vno % 256); + if(ret) { + krb5_set_error_message(context, ret, + N_("Failed storing kvno " + "in keytab %s", ""), + d->filename); + krb5_storage_free(emem); + goto out; + } + ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock); + if(ret) { + krb5_storage_free(emem); + goto out; + } + if ((d->flags & KRB5_KT_FL_JAVA) == 0) { + ret = krb5_store_int32 (emem, entry->vno); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed storing extended kvno " + "in keytab %s", ""), + d->filename); + krb5_storage_free(emem); + goto out; + } + ret = krb5_store_uint32 (emem, entry->flags); + if (ret) { + krb5_set_error_message(context, ret, + N_("Failed storing extended kvno " + "in keytab %s", ""), + d->filename); + krb5_storage_free(emem); + goto out; + } + } + + ret = krb5_storage_to_data(emem, &keytab); + krb5_storage_free(emem); + if(ret) { + krb5_set_error_message(context, ret, + N_("Failed converting keytab entry " + "to memory block for keytab %s", ""), + d->filename); + goto out; + } + } + + while(1) { + off_t here; + + here = krb5_storage_seek(sp, 0, SEEK_CUR); + if (here == -1) { + ret = errno; + krb5_set_error_message(context, ret, + N_("Failed writing keytab block " + "in keytab %s: %s", ""), + d->filename, strerror(ret)); + goto out; + } + ret = krb5_ret_int32(sp, &len); + if (ret) { + /* There could have been a partial length. Recover! */ + (void) krb5_storage_truncate(sp, here); + len = keytab.length; + break; + } + if(len < 0) { + len = -len; + if(len >= (int)keytab.length) { + krb5_storage_seek(sp, -4, SEEK_CUR); + break; + } + } + krb5_storage_seek(sp, len, SEEK_CUR); + } + ret = krb5_store_int32(sp, len); + if (ret != 0) + goto out; + bytes = krb5_storage_write(sp, keytab.data, keytab.length); + if (bytes != keytab.length) { + ret = bytes == -1 ? errno : KRB5_KT_END; + krb5_set_error_message(context, ret, + N_("Failed writing keytab block " + "in keytab %s: %s", ""), + d->filename, strerror(ret)); + } + memset(keytab.data, 0, keytab.length); + krb5_data_free(&keytab); + out: + if (ret == 0) + ret = krb5_storage_fsync(sp); + krb5_storage_free(sp); + close(fd); + return ret; +} + +static krb5_error_code KRB5_CALLCONV +fkt_remove_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + struct fkt_data *fkt = id->data; + krb5_ssize_t bytes; + krb5_keytab_entry e; + krb5_kt_cursor cursor; + off_t pos_start, pos_end; + int found = 0; + krb5_error_code ret; + + ret = fkt_start_seq_get_int(context, id, O_RDWR | O_BINARY | O_CLOEXEC, 1, &cursor); + if (ret != 0) { + const char *emsg = krb5_get_error_message(context, ret); + + krb5_set_error_message(context, ret, + N_("Could not open keytab file for write: %s: %s", ""), + fkt->filename, + emsg); + krb5_free_error_message(context, emsg); + return ret; + } + while (ret == 0 && + (ret = fkt_next_entry_int(context, id, &e, &cursor, + &pos_start, &pos_end)) == 0) { + if (krb5_kt_compare(context, &e, entry->principal, + entry->vno, entry->keyblock.keytype)) { + int32_t len; + unsigned char buf[128]; + found = 1; + krb5_storage_seek(cursor.sp, pos_start, SEEK_SET); + len = pos_end - pos_start - 4; + ret = krb5_store_int32(cursor.sp, -len); + memset(buf, 0, sizeof(buf)); + while (ret == 0 && len > 0) { + bytes = krb5_storage_write(cursor.sp, buf, + min((size_t)len, sizeof(buf))); + if (bytes != min((size_t)len, sizeof(buf))) { + ret = bytes == -1 ? errno : KRB5_KT_END; + break; + } + len -= min((size_t)len, sizeof(buf)); + } + } + krb5_kt_free_entry(context, &e); + } + (void) krb5_kt_end_seq_get(context, id, &cursor); + if (ret == KRB5_KT_END) + ret = 0; + if (ret) { + const char *emsg = krb5_get_error_message(context, ret); + + krb5_set_error_message(context, ret, + N_("Could not remove keytab entry from %s: %s", ""), + fkt->filename, + emsg); + krb5_free_error_message(context, emsg); + } else if (!found) { + krb5_clear_error_message(context); + return KRB5_KT_NOTFOUND; + } + return ret; +} + +const krb5_kt_ops krb5_fkt_ops = { + "FILE", + fkt_resolve, + fkt_get_name, + fkt_close, + fkt_destroy, + NULL, /* get */ + fkt_start_seq_get, + fkt_next_entry, + fkt_end_seq_get, + fkt_add_entry, + fkt_remove_entry, + NULL, + 0 +}; + +const krb5_kt_ops krb5_wrfkt_ops = { + "WRFILE", + fkt_resolve, + fkt_get_name, + fkt_close, + fkt_destroy, + NULL, /* get */ + fkt_start_seq_get, + fkt_next_entry, + fkt_end_seq_get, + fkt_add_entry, + fkt_remove_entry, + NULL, + 0 +}; + +const krb5_kt_ops krb5_javakt_ops = { + "JAVA14", + fkt_resolve_java14, + fkt_get_name, + fkt_close, + fkt_destroy, + NULL, /* get */ + fkt_start_seq_get, + fkt_next_entry, + fkt_end_seq_get, + fkt_add_entry, + fkt_remove_entry, + NULL, + 0 +}; diff --git a/third_party/heimdal/lib/krb5/keytab_keyfile.c b/third_party/heimdal/lib/krb5/keytab_keyfile.c new file mode 100644 index 0000000..af3ac86 --- /dev/null +++ b/third_party/heimdal/lib/krb5/keytab_keyfile.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1997 - 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +#ifndef HEIMDAL_SMALLER + +/* afs keyfile operations --------------------------------------- */ + +/* + * Minimum tools to handle the AFS KeyFile. + * + * Format of the KeyFile is: + * {[ ] * numkeys} + * + * It just adds to the end of the keyfile, deleting isn't implemented. + * Use your favorite text/hex editor to delete keys. + * + */ + +#define AFS_SERVERTHISCELL "/usr/afs/etc/ThisCell" +#define AFS_SERVERMAGICKRBCONF "/usr/afs/etc/krb.conf" + +struct akf_data { + uint32_t num_entries; + char *filename; + char *cell; + char *realm; +}; + +/* + * set `d->cell' and `d->realm' + */ + +static int +get_cell_and_realm (krb5_context context, struct akf_data *d) +{ + FILE *f; + char buf[BUFSIZ], *cp; + int ret; + + f = fopen (AFS_SERVERTHISCELL, "r"); + if (f == NULL) { + ret = errno; + krb5_set_error_message (context, ret, + N_("Open ThisCell %s: %s", ""), + AFS_SERVERTHISCELL, + strerror(ret)); + return ret; + } + if (fgets (buf, sizeof(buf), f) == NULL) { + fclose (f); + krb5_set_error_message (context, EINVAL, + N_("No cell in ThisCell file %s", ""), + AFS_SERVERTHISCELL); + return EINVAL; + } + buf[strcspn(buf, "\n")] = '\0'; + fclose(f); + + d->cell = strdup (buf); + if (d->cell == NULL) + return krb5_enomem(context); + + f = fopen (AFS_SERVERMAGICKRBCONF, "r"); + if (f != NULL) { + if (fgets (buf, sizeof(buf), f) == NULL) { + free (d->cell); + d->cell = NULL; + fclose (f); + krb5_set_error_message (context, EINVAL, + N_("No realm in ThisCell file %s", ""), + AFS_SERVERMAGICKRBCONF); + return EINVAL; + } + buf[strcspn(buf, "\n")] = '\0'; + fclose(f); + } + /* uppercase */ + for (cp = buf; *cp != '\0'; cp++) + *cp = toupper((unsigned char)*cp); + + d->realm = strdup (buf); + if (d->realm == NULL) { + free (d->cell); + d->cell = NULL; + return krb5_enomem(context); + } + return 0; +} + +/* + * init and get filename + */ + +static krb5_error_code KRB5_CALLCONV +akf_resolve(krb5_context context, const char *name, krb5_keytab id) +{ + int ret; + struct akf_data *d = calloc(1, sizeof (struct akf_data)); + + if (d == NULL) + return krb5_enomem(context); + + d->num_entries = 0; + ret = get_cell_and_realm (context, d); + if (ret) { + free (d); + return ret; + } + d->filename = strdup (name); + if (d->filename == NULL) { + free (d->cell); + free (d->realm); + free (d); + return krb5_enomem(context); + } + id->data = d; + + return 0; +} + +/* + * cleanup + */ + +static krb5_error_code KRB5_CALLCONV +akf_close(krb5_context context, krb5_keytab id) +{ + struct akf_data *d = id->data; + + free (d->filename); + free (d->cell); + free (d); + return 0; +} + +/* + * Return filename + */ + +static krb5_error_code KRB5_CALLCONV +akf_get_name(krb5_context context, + krb5_keytab id, + char *name, + size_t name_sz) +{ + struct akf_data *d = id->data; + + strlcpy (name, d->filename, name_sz); + return 0; +} + +/* + * Init + */ + +static krb5_error_code KRB5_CALLCONV +akf_start_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *c) +{ + int32_t ret; + struct akf_data *d = id->data; + + c->fd = open (d->filename, O_RDONLY | O_BINARY | O_CLOEXEC, 0600); + if (c->fd < 0) { + ret = errno; + krb5_set_error_message(context, ret, + N_("keytab afs keyfile open %s failed: %s", ""), + d->filename, strerror(ret)); + return ret; + } + + c->data = NULL; + c->sp = krb5_storage_from_fd(c->fd); + if (c->sp == NULL) { + close(c->fd); + krb5_clear_error_message (context); + return KRB5_KT_NOTFOUND; + } + krb5_storage_set_eof_code(c->sp, KRB5_KT_END); + + ret = krb5_ret_uint32(c->sp, &d->num_entries); + if(ret || d->num_entries > INT_MAX / 8) { + krb5_storage_free(c->sp); + close(c->fd); + krb5_clear_error_message (context); + if(ret == KRB5_KT_END) + return KRB5_KT_NOTFOUND; + return ret; + } + + return 0; +} + +static krb5_error_code KRB5_CALLCONV +akf_next_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry, + krb5_kt_cursor *cursor) +{ + struct akf_data *d = id->data; + int32_t kvno; + off_t pos; + int ret; + + pos = krb5_storage_seek(cursor->sp, 0, SEEK_CUR); + + if ((pos - 4) / (4 + 8) >= d->num_entries) + return KRB5_KT_END; + + ret = krb5_make_principal (context, &entry->principal, + d->realm, "afs", d->cell, NULL); + if (ret) + goto out; + + ret = krb5_ret_int32(cursor->sp, &kvno); + if (ret) { + krb5_free_principal (context, entry->principal); + goto out; + } + + entry->vno = kvno; + + if (cursor->data) + entry->keyblock.keytype = ETYPE_DES_CBC_MD5; + else + entry->keyblock.keytype = ETYPE_DES_CBC_CRC; + entry->keyblock.keyvalue.length = 8; + entry->keyblock.keyvalue.data = malloc (8); + if (entry->keyblock.keyvalue.data == NULL) { + krb5_free_principal (context, entry->principal); + ret = krb5_enomem(context); + goto out; + } + + ret = krb5_storage_read(cursor->sp, entry->keyblock.keyvalue.data, 8); + if(ret != 8) + ret = (ret < 0) ? errno : KRB5_KT_END; + else + ret = 0; + + entry->timestamp = time(NULL); + entry->flags = 0; + entry->aliases = NULL; + + out: + if (cursor->data) { + krb5_storage_seek(cursor->sp, pos + 4 + 8, SEEK_SET); + cursor->data = NULL; + } else + cursor->data = cursor; + return ret; +} + +static krb5_error_code KRB5_CALLCONV +akf_end_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *cursor) +{ + krb5_storage_free(cursor->sp); + close(cursor->fd); + cursor->data = NULL; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +akf_add_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + struct akf_data *d = id->data; + int fd, created = 0; + krb5_error_code ret; + int32_t len; + krb5_storage *sp; + + + if (entry->keyblock.keyvalue.length != 8) + return 0; + switch(entry->keyblock.keytype) { + case ETYPE_DES_CBC_CRC: + case ETYPE_DES_CBC_MD4: + case ETYPE_DES_CBC_MD5: + break; + default: + return 0; + } + + fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC); + if (fd < 0) { + fd = open (d->filename, + O_RDWR | O_BINARY | O_CREAT | O_EXCL | O_CLOEXEC, 0600); + if (fd < 0) { + ret = errno; + krb5_set_error_message(context, ret, + N_("open keyfile(%s): %s", ""), + d->filename, + strerror(ret)); + return ret; + } + created = 1; + } + + sp = krb5_storage_from_fd(fd); + if(sp == NULL) { + close(fd); + return krb5_enomem(context); + } + if (created) + len = 0; + else { + if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { + ret = errno; + krb5_storage_free(sp); + close(fd); + krb5_set_error_message(context, ret, + N_("seeking in keyfile: %s", ""), + strerror(ret)); + return ret; + } + + ret = krb5_ret_int32(sp, &len); + if(ret) { + krb5_storage_free(sp); + close(fd); + return ret; + } + } + + /* + * Make sure we don't add the entry twice, assumes the DES + * encryption types are all the same key. + */ + if (len > 0) { + int32_t kvno; + int i; + + for (i = 0; i < len; i++) { + ret = krb5_ret_int32(sp, &kvno); + if (ret) { + krb5_set_error_message (context, ret, + N_("Failed getting kvno from keyfile", "")); + goto out; + } + if(krb5_storage_seek(sp, 8, SEEK_CUR) < 0) { + ret = errno; + krb5_set_error_message (context, ret, + N_("Failed seeing in keyfile: %s", ""), + strerror(ret)); + goto out; + } + if (kvno == entry->vno) { + ret = 0; + goto out; + } + } + } + + len++; + + if(krb5_storage_seek(sp, 0, SEEK_SET) < 0) { + ret = errno; + krb5_set_error_message (context, ret, + N_("Failed seeing in keyfile: %s", ""), + strerror(ret)); + goto out; + } + + ret = krb5_store_int32(sp, len); + if(ret) { + ret = errno; + krb5_set_error_message (context, ret, + N_("keytab keyfile failed new length", "")); + goto out; + } + + if(krb5_storage_seek(sp, (len - 1) * (8 + 4), SEEK_CUR) < 0) { + ret = errno; + krb5_set_error_message (context, ret, + N_("seek to end: %s", ""), strerror(ret)); + goto out; + } + + ret = krb5_store_int32(sp, entry->vno); + if(ret) { + krb5_set_error_message(context, ret, + N_("keytab keyfile failed store kvno", "")); + goto out; + } + ret = krb5_storage_write(sp, entry->keyblock.keyvalue.data, + entry->keyblock.keyvalue.length); + if(ret != entry->keyblock.keyvalue.length) { + if (ret < 0) + ret = errno; + else + ret = ENOTTY; + krb5_set_error_message(context, ret, + N_("keytab keyfile failed to add key", "")); + goto out; + } + ret = 0; +out: + krb5_storage_free(sp); + close (fd); + return ret; +} + +const krb5_kt_ops krb5_akf_ops = { + "AFSKEYFILE", + akf_resolve, + akf_get_name, + akf_close, + NULL, /* destroy */ + NULL, /* get */ + akf_start_seq_get, + akf_next_entry, + akf_end_seq_get, + akf_add_entry, + NULL, /* remove */ + NULL, + 0 +}; + +#endif /* HEIMDAL_SMALLER */ diff --git a/third_party/heimdal/lib/krb5/keytab_memory.c b/third_party/heimdal/lib/krb5/keytab_memory.c new file mode 100644 index 0000000..87953a6 --- /dev/null +++ b/third_party/heimdal/lib/krb5/keytab_memory.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 1997 - 2001 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + +/* memory operations -------------------------------------------- */ + +struct mkt_data { + krb5_keytab_entry *entries; + int num_entries; + char *name; + int refcount; + struct mkt_data *next; +}; + +/* this mutex protects mkt_head, ->refcount, and ->next + * content is not protected (name is static and need no protection) + */ +static HEIMDAL_MUTEX mkt_mutex = HEIMDAL_MUTEX_INITIALIZER; +static struct mkt_data *mkt_head; + + +static krb5_error_code KRB5_CALLCONV +mkt_resolve(krb5_context context, const char *name, krb5_keytab id) +{ + struct mkt_data *d; + + HEIMDAL_MUTEX_lock(&mkt_mutex); + + for (d = mkt_head; d != NULL; d = d->next) + if (strcmp(d->name, name) == 0) + break; + if (d) { + if (d->refcount < 1) + krb5_abortx(context, "Double close on memory keytab, " + "refcount < 1 %d", d->refcount); + d->refcount++; + id->data = d; + HEIMDAL_MUTEX_unlock(&mkt_mutex); + return 0; + } + + d = calloc(1, sizeof(*d)); + if(d == NULL) { + HEIMDAL_MUTEX_unlock(&mkt_mutex); + return krb5_enomem(context); + } + d->name = strdup(name); + if (d->name == NULL) { + HEIMDAL_MUTEX_unlock(&mkt_mutex); + free(d); + return krb5_enomem(context); + } + d->entries = NULL; + d->num_entries = 0; + d->refcount = 1; + d->next = mkt_head; + mkt_head = d; + HEIMDAL_MUTEX_unlock(&mkt_mutex); + id->data = d; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +mkt_close(krb5_context context, krb5_keytab id) +{ + struct mkt_data *d = id->data, **dp; + int i; + + HEIMDAL_MUTEX_lock(&mkt_mutex); + if (d->refcount < 1) + krb5_abortx(context, + "krb5 internal error, memory keytab refcount < 1 on close"); + + if (--d->refcount > 0) { + HEIMDAL_MUTEX_unlock(&mkt_mutex); + return 0; + } + for (dp = &mkt_head; *dp != NULL; dp = &(*dp)->next) { + if (*dp == d) { + *dp = d->next; + break; + } + } + HEIMDAL_MUTEX_unlock(&mkt_mutex); + + free(d->name); + for(i = 0; i < d->num_entries; i++) + krb5_kt_free_entry(context, &d->entries[i]); + free(d->entries); + free(d); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +mkt_get_name(krb5_context context, + krb5_keytab id, + char *name, + size_t namesize) +{ + struct mkt_data *d = id->data; + strlcpy(name, d->name, namesize); + return 0; +} + +static krb5_error_code KRB5_CALLCONV +mkt_start_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *c) +{ + /* XXX */ + c->fd = 0; + return 0; +} + +static krb5_error_code KRB5_CALLCONV +mkt_next_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry, + krb5_kt_cursor *c) +{ + struct mkt_data *d = id->data; + if(c->fd >= d->num_entries) + return KRB5_KT_END; + return krb5_kt_copy_entry_contents(context, &d->entries[c->fd++], entry); +} + +static krb5_error_code KRB5_CALLCONV +mkt_end_seq_get(krb5_context context, + krb5_keytab id, + krb5_kt_cursor *cursor) +{ + return 0; +} + +static krb5_error_code KRB5_CALLCONV +mkt_add_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + struct mkt_data *d = id->data; + krb5_keytab_entry *tmp; + tmp = realloc(d->entries, (d->num_entries + 1) * sizeof(*d->entries)); + if (tmp == NULL) + return krb5_enomem(context); + d->entries = tmp; + return krb5_kt_copy_entry_contents(context, entry, + &d->entries[d->num_entries++]); +} + +static krb5_error_code KRB5_CALLCONV +mkt_remove_entry(krb5_context context, + krb5_keytab id, + krb5_keytab_entry *entry) +{ + struct mkt_data *d = id->data; + krb5_keytab_entry *e, *end; + int found = 0; + + if (d->num_entries == 0) { + krb5_clear_error_message(context); + return KRB5_KT_NOTFOUND; + } + + /* do this backwards to minimize copying */ + for(end = d->entries + d->num_entries, e = end - 1; e >= d->entries; e--) { + if(krb5_kt_compare(context, e, entry->principal, + entry->vno, entry->keyblock.keytype)) { + krb5_kt_free_entry(context, e); + memmove(e, e + 1, (end - e - 1) * sizeof(*e)); + memset(end - 1, 0, sizeof(*end)); + d->num_entries--; + end--; + found = 1; + } + } + if (!found) { + krb5_clear_error_message (context); + return KRB5_KT_NOTFOUND; + } + e = realloc(d->entries, d->num_entries * sizeof(*d->entries)); + if(e != NULL || d->num_entries == 0) + d->entries = e; + return 0; +} + +const krb5_kt_ops krb5_mkt_ops = { + "MEMORY", + mkt_resolve, + mkt_get_name, + mkt_close, + NULL, /* destroy */ + NULL, /* get */ + mkt_start_seq_get, + mkt_next_entry, + mkt_end_seq_get, + mkt_add_entry, + mkt_remove_entry, + NULL, + 0 +}; diff --git a/third_party/heimdal/lib/krb5/krb5-plugin.7 b/third_party/heimdal/lib/krb5/krb5-plugin.7 new file mode 100644 index 0000000..0b1e729 --- /dev/null +++ b/third_party/heimdal/lib/krb5/krb5-plugin.7 @@ -0,0 +1,359 @@ +.\" Copyright (c) 1999 - 2005 Kungliga Tekniska Högskolan +.\" (Royal Institute of Technology, Stockholm, Sweden). +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" 3. Neither the name of the Institute nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd December 21, 2011 +.Dt KRB5-PLUGIN 7 +.Os HEIMDAL +.Sh NAME +.Nm krb5-plugin +.Nd plugin interface for Heimdal +.Sh SYNOPSIS +.In krb5.h +.In krb5/an2ln_plugin.h +.In krb5/ccache_plugin.h +.In krb5/db_plugin.h +.In krb5/kuserok_plugin.h +.In krb5/locate_plugin.h +.In krb5/send_to_kdc_plugin.h +.Sh DESCRIPTION +Heimdal has a plugin interface. Plugins may be statically linked into +Heimdal and registered via the +.Xr krb5_plugin_register 3 +function, or they may be dynamically loaded from shared objects present +in the Heimdal plugins directories. +.Pp +Plugins consist of a C struct whose struct name is given in the +associated header file, such as, for example, +.Va krb5plugin_kuserok_ftable +and a pointer to which is either registered via +.Xr krb5_plugin_register 3 +or via a plugin load function exported by a shared object. +Plugin load functions should be named by concatenating the name defined in the +associated header file with the string "plugin_load" (e.g. +"krb5_plugin_kuserok_plugin_load" for the plugin for +.Xr krb5_kuserok 3 +). +The plugin load function must be of type +.Va heim_plugin_load_ft +which is: +.Bd -literal -offset indent +krb5_error_code HEIM_CALLCONV +my_plugin_load(heim_pcontext context, + krb5_get_instance_func_t *get_instance, + size_t *num_plugins, + heim_plugin_common_ftable_cp **plugins); + +.Ed +where +.Va HEIM_CALLCONV +is +.Va __stdcall +on Windows. +.Pp +The plugin should set the get_instance output parameter to the a +function that will return the instances of its library +dependencies. For example: +.Bd -literal -offset indent +static uintptr_t HEIM_LIB_CALL +my_plugin_get_instance(const char *name) +{ + if (strcmp(name, "krb5") == 0) + return krb5_get_instance(name); + return 0; +} +.Ed +.Pp +The +.Va get_instance +function is used to check that dynamically-linked plugins are +linked with the same Heimdal shared objects as the one loading +and running the plugin. +.Pp +The output parameters +.Va plugins +and +.Va n_plugins +output an array of pointers to function tabls, and the number of +those, respectively. +.Pp +The plugin structs for all plugin types always begin with the same three +common fields: +.Bl -enum -compact +.It +.Va minor_version +, an int. Plugin minor versions are defined in each plugin type's +associated header file. +.It +.Va init +, a pointer to a function with two arguments, a +.Va heim_pcontext +(which for krb5 plugins is actually a krb5_context), +and a +.Va void ** +, returning a heim_error_code. This function will be called to +initialize a plugin-specific context in the form of a +.Va void * +that will be output through the init function's second argument. +.It +.Va fini +, a pointer to a function of one argument, a +.Va void * +, consisting of the plugin's context to be destroyed, and +returning +.Va void. +.El +.Pp +Each plugin type may add fields to this struct following the above +three. Plugins are typically invoked in no particular order until one +succeeds or fails, or all return a special return value that indicates +that the plugin was not applicable. For krb5 plugins, +.Va KRB5_PLUGIN_NO_HANDLE +indicates that the plugin was not applicable. +.Pp +Heimdal plugin callers either invoke all plugins until one returns an +error or all return +.Va KRB5_PLUGIN_NO_HANDLE +, or invoke all plugins until one returns a value other than +.Va KRB5_PLUGIN_NO_HANDLE +with the expectation that only one plugin would return success and all +oters would return +.Va KRB5_PLUGIN_NO_HANDLE. +Thus Heimdal plugin invokation can be deterministic in spite of +non-deterministic invocation order. +.Pp +There is a database plugin system intended for many of the uses of +databases in Heimdal. The plugin is expected to call +.Xr heim_db_register 3 +from its +.Va init +entry point to register a DB type. The DB plugin's +.Va fini +function must do nothing, and the plugin must not provide any other +entry points. +.Pp +The krb5_kuserok plugin adds a single field to its struct: a pointer to +a function that implements kuserok functionality with the following +form: +.Bd -literal -offset indent +static krb5_error_code +kuserok(void *plug_ctx, krb5_context context, const char *rule, + unsigned int flags, const char *k5login_dir, + const char *luser, krb5_const_principal principal, + krb5_boolean *result) +.Ed +.Pp +The +.Va luser +, +.Va principal +and +.Va result +arguments are self-explanatory (see +.Xr krb5_kuserok 3 +). The +.Va plug_ctx +argument is the context output by the plugin's init function. The +.Va rule +argument is a kuserok rule from the krb5.conf file; each plugin is invoked once +for each rule until all plugins fail or one succeeds. The +.Va k5login_dir +argument provides an alternative k5login file location, if not NULL. +The +.Va flags +argument indicates whether the plugin may call +.Xr krb5_aname_to_localname 3 +(KUSEROK_ANAME_TO_LNAME_OK), and whether k5login databases are expected to be +authoritative (KUSEROK_K5LOGIN_IS_AUTHORITATIVE). +.Pp +The plugin for +.Xr krb5_aname_to_localname 3 +is named "an2ln" and has a single extra field for the plugin struct: +.Bd -literal -offset indent +typedef krb5_error_code (*set_result_f)(void *, const char *); + +static krb5_error_code +an2ln(void *plug_ctx, krb5_context context, const char *rule, + krb5_const_principal aname, set_result_f set_res_f, void *set_res_ctx) +.Ed +.Pp +The arguments for the +.Va an2ln +plugin are similar to those of the kuserok plugin, but the result, being +a string, is set by calling the +.Va set_res_f +function argument with the +.Va set_res_ctx +and result string as arguments. The +.Va set_res_f +function will make a copy of the string. +.Sh FILES +.Bl -tag -compact +.It Pa libdir/plugin/krb5/* +Shared objects containing plugins for Heimdal. +.El +.Sh EXAMPLES +.Pp +An example an2ln plugin that maps principals to a constant "nouser" +follows: +.Pp +.Bd -literal -offset indent +#include + +/* Note that `context' here is actually a krb5_context value */ +static krb5_error_code KRB5_CALLCONV +nouser_plug_init(heim_pcontext context, void **ctx) +{ + *ctx = NULL; + return 0; +} + +static void KRB5_CALLCONV nouser_plug_fini(void *ctx) { } + +static krb5_error_code KRB5_CALLCONV +nouser_plug_an2ln(void *plug_ctx, krb5_context context, + const char *rule, + krb5_const_principal aname, + set_result_f set_res_f, void *set_res_ctx) +{ + krb5_error_code ret; + + if (strcmp(rule, "NOUSER") != 0) + return KRB5_PLUGIN_NO_HANDLE; + + ret = set_res_f(set_res_ctx, "nouser"); + + return ret; +} + +krb5plugin_an2ln_ftable an2ln = { + KRB5_PLUGIN_AN2LN_VERSION_0, + nouser_plug_init, + nouser_plug_fini, + nouser_plug_an2ln, +}; + +static const krb5plugin_an2ln_ftable *const plugins[] = { + &an2ln +}; + +static uintptr_t +an2ln_get_instance(const char *libname) +{ + if (strcmp(libname, "krb5") == 0) + return krb5_get_instance(libname); + + return 0; +} + +/* Note that `context' here is actually a krb5_context value */ +krb5_error_code +an2ln_plugin_load(heim_pcontext context, + krb5_get_instance_func_t *get_instance, + size_t *num_plugins, + const krb5plugin_an2ln_ftable * const **pplugins) +{ + *get_instance = an2ln_get_instance; + *num_plugins = sizeof(plugins) / sizeof(plugins[0]); + *pplugins = plugins; + return 0; +} +.Ed +.Pp +An example kuserok plugin that rejects all requests follows. (Note that +there exists a built-in plugin with this functionality; see +.Xr krb5_kuserok 3 +). +.Pp +.Bd -literal -offset indent +#include + +static krb5_error_code KRB5_CALLCONV +reject_plug_init(heim_context context, void **ctx) +{ + *ctx = NULL; + return 0; +} + +static void KRB5_CALLCONV reject_plug_fini(void *ctx) { } + +static krb5_error_code KRB5_CALLCONV +reject_plug_kuserok(void *plug_ctx, krb5_context context, const char *rule, + unsigned int flags, const char *k5login_dir, + const char *luser, krb5_const_principal principal, + krb5_boolean *result) +{ + if (strcmp(rule, "REJECT") != 0) + return KRB5_PLUGIN_NO_HANDLE; + + *result = FALSE; + return 0; +} + +static krb5plugin_kuserok_ftable kuserok = { + KRB5_PLUGIN_KUSEROK_VERSION_0, + reject_plug_init, + reject_plug_fini, + reject_plug_kuserok, +}; + +static const krb5plugin_kuserok_ftable *const plugins[] = { + &kuserok +}; + +static uintptr_t +kuserok_get_instance(const char *libname) +{ + if (strcmp(libname, "krb5") == 0) + return krb5_get_instance(libname); + + return 0; +} + +krb5_error_code +krb5_plugin_kuserok_plugin_load( + heim_context context, + krb5_get_instance_func_t *get_instance, + size_t *num_plugins, + const krb5plugin_kuserok_ftable * const **pplugins) +{ + *krb5_instance = kuserok_get_instance; + *num_plugins = sizeof(plugins) / sizeof(plugins[0]); + *pplugins = plugins; + return 0; +} + +.Ed +.Sh SEE ALSO +.Xr krb5_plugin_register 3 +.Xr krb5_kuserok 3 +.Xr krb5_aname_to_localname 3 diff --git a/third_party/heimdal/lib/krb5/krb5.conf.5 b/third_party/heimdal/lib/krb5/krb5.conf.5 new file mode 100644 index 0000000..a10b572 --- /dev/null +++ b/third_party/heimdal/lib/krb5/krb5.conf.5 @@ -0,0 +1,1475 @@ +.\" Copyright (c) 1999 - 2005 Kungliga Tekniska Högskolan +.\" (Royal Institute of Technology, Stockholm, Sweden). +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" 3. Neither the name of the Institute nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id$ +.\" +.Dd May 4, 2005 +.Dt KRB5.CONF 5 +.Os HEIMDAL +.Sh NAME +.Nm krb5.conf +.Nd configuration file for Kerberos 5 +.Sh SYNOPSIS +.In krb5.h +.Sh DESCRIPTION +The +.Nm +file specifies several configuration parameters for the Kerberos 5 +library, as well as for some programs. +.Pp +The file consists of one or more sections, containing a number of +bindings. +The value of each binding can be either a string or a list of other +bindings. +The grammar looks like: +.Bd -literal -offset indent +file: + /* empty */ + sections + includes + +sections: + section sections + section + +section: + '[' section_name ']' bindings + +section_name: + STRING + +bindings: + binding bindings + binding + +binding: + name '=' STRING + name '=' '{' bindings '}' + +name: + STRING + +includes: + 'include' path + 'includedir' path + +path: STRING + +.Ed +.Li STRINGs +consists of one or more non-whitespace characters. +.Pp +Files and directories may be included by absolute path, with percent +token expansion (see the TOKEN EXPANSION section). Including a +directory causes all files in the directory to be included as if each +file had been included separately, but only files whose names consist of +alphanumeric, hyphen, and underscore are included, though they may also +end in '.conf'. +.Pp +STRINGs that are specified later in this man-page uses the following +notation. +.Bl -tag -width "xxx" -offset indent +.It boolean +values can be either yes/true or no/false. +.It time +values can be a list of year, month, day, hour, min, second. +Example: 1 month 2 days 30 min. +If no unit is given, seconds is assumed. +.It etypes +valid encryption types are: des-cbc-crc, des-cbc-md4, des-cbc-md5, +des3-cbc-sha1, arcfour-hmac-md5, aes128-cts-hmac-sha1-96, aes256-cts-hmac-sha1-96, +aes128-cts-hmac-sha256-128, and aes256-cts-hmac-sha384-192. +.It address +an address can be either a IPv4 or a IPv6 address. +.El +.Pp +Currently recognised sections and bindings are: +.Bl -tag -width "xxx" -offset indent +.It Li [appdefaults] +Specifies the default values to be used for Kerberos applications. +You can specify defaults per application, realm, or a combination of +these. +The preference order is: +.Bl -enum -compact +.It +.Va application Va realm Va option +.It +.Va application Va option +.It +.Va realm Va option +.It +.Va option +.El +.Pp +The supported options are: +.Bl -tag -width "xxx" -offset indent +.It Li forwardable = Va boolean +When obtaining initial credentials, make the credentials forwardable. +.It Li proxiable = Va boolean +When obtaining initial credentials, make the credentials proxiable. +.It Li no-addresses = Va boolean +When obtaining initial credentials, request them for an empty set of +addresses, making the tickets valid from any address. +.It Li ticket_lifetime = Va time +Default ticket lifetime. +.It Li renew_lifetime = Va time +Default renewable ticket lifetime. +.It Li encrypt = Va boolean +Use encryption, when available. +.It Li forward = Va boolean +Forward credentials to remote host (for +.Xr rsh 1 , +.Xr telnet 1 , +etc). +.It Li historical_anon_pkinit = Va boolean +Enable legacy anonymous pkinit command-line syntax. +With this option set to +.Li true, +the +.Xr kinit 1 +.Fl Fl anonymous +command with no principal argument specified will request an anonymous pkinit +ticket from the default realm. +If a principal argument is specified, it is used as an explicit realm name for +anonymous pkinit even without an +.Li @ +prefix. +.It Li delegate-destination-tgt = Va boolean +When forwarding credentials to a remote host, forward a TGT for the +realm of the destination host rather than a TGT for the user's realm. +This is useful when hosts in the remote realm should not or cannot +(e.g. firewalled from user realm's KDC) obtain tickets for services +in the user's realm. When the user's realm and the host's realm are +the same, this parameter has no effect. The setting can be applied +to a single realm as follows: +.Bd -literal -offset indent +EXAMPLE.COM = { + delegate-destination-tgt = true +} +.Ed +.It Li pkinit_pool = Va HX509-STORE +This is a multi-valued parameter naming one or more stores of +intermediate certification authority (CA) certificates for the +client's end entity certificate. +.It Li pkinit_anchors = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +anchors for PKINIT KDC certificates. +.It Li pkinit_revoke = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +CRLs for the issuers of PKINIT KDC certificates. +Only the first valid CRL for a particular issuer will be checked. +If no CRLs are configured, then CRLs will not be checked. +This is because hx509 currently lacks support. +.El +.It Li [libdefaults] +.Bl -tag -width "xxx" -offset indent +.It Li default_realm = Va REALM +Default realm to use, this is also known as your +.Dq local realm . +The default is the result of +.Fn krb5_get_host_realm "local hostname" . +.It Li allow_weak_crypto = Va boolean +are weak crypto algorithms allowed to be used, among others, DES is +considered weak. +.It Li clockskew = Va time +Maximum time differential (in seconds) allowed when comparing +times. +Default is 300 seconds (five minutes). +.It Li kdc_timeout = Va time +Maximum time to wait for a reply from the kdc, default is 3 seconds. +.It Li capath = { +.Bl -tag -width "xxx" -offset indent +.It Va destination-realm Li = Va next-hop-realm +.It ... +.It Li } +.El +This is deprecated, see the +.Li capaths +section below. +.It Li default_cc_type = Va cctype +sets the default credentials type. +.It Li default_cc_name = Va ccname +the default credentials cache name. +If you want to change the type only use +.Li default_cc_type . +The string can contain variables that are expanded at runtime. See the TOKEN +EXPANSION section. +.It Li default_file_cache_collections = Va FILE:/path/with/tokens ... +This multi-valued parameter allows more than one path to be +configured for the FILE credentials cache type to look in. The FILE +credentials cache type will also consider file names whose prefixes +match these and end in +.Va +name +as subsidiary caches in the collection. The values of this +parameter are subject to token expansion. See the TOKEN EXPANSION +section. +.It Li enable_file_cache_iteration = Va boolean +If enabled, the +.Va FILE +credential cache type will support iteration of all subsidiary +caches in the default collection, meaning that +.Xr kinit 1 +.Va -l +option will list them. This does require scanning the directory +containing a given +.Va FILE +ccache, which, if it is +.Va /tmp +may be a slow operation. Defaults to false. +.It Li default_etypes = Va etypes ... +A list of default encryption types to use. (Default: all enctypes if +allow_weak_crypto = TRUE, else all enctypes except single DES enctypes.) +.It Li default_as_etypes = Va etypes ... +A list of default encryption types to use in AS requests. (Default: the +value of default_etypes.) +.It Li default_tgs_etypes = Va etypes ... +A list of default encryption types to use in TGS requests. (Default: +the value of default_etypes.) +.It Li default_etypes_des = Va etypes ... +A list of default encryption types to use when requesting a DES credential. +.It Li default_keytab_name = Va keytab +The keytab to use if no other is specified, default is +.Dq FILE:/etc/krb5.keytab . +.It Li default_client_keytab_name = Va keytab +The keytab to use for client credential acquisition if no other is +specified, default is +.Dq FILE:%{LOCALSTATEDIR}/user/%{euid}/client.keytab . +See the TOKEN EXPANSION section. +.It Li dns_lookup_kdc = Va boolean +Use DNS SRV records to lookup KDC services location. +.It Li dns_lookup_realm = Va boolean +Use DNS TXT records to lookup domain to realm mappings. +.It Li enforce_ok_as_delegate = Va boolean +If this flag to true, GSSAPI credential delegation will be +disabled when the +.Ar ok-as-delegate +flag is not set in the service ticket. +If this flag is false, the +.Ar ok-as-delegate +ticket flag is only enforced when an application specifically +requests enforcement. +The default value is false. +.It Li kdc_timesync = Va boolean +Try to keep track of the time differential between the local machine +and the KDC, and then compensate for that when issuing requests. +.It Li max_retries = Va number +The max number of times to try to contact each KDC. +.It Li large_msg_size = Va number +The threshold where protocols with tiny maximum message sizes are not +considered usable to send messages to the KDC. +.It Li ticket_lifetime = Va time +Default ticket lifetime. +.It Li renew_lifetime = Va time +Default renewable ticket lifetime. +.It Li forwardable = Va boolean +When obtaining initial credentials, make the credentials forwardable. +This option is also valid in the [realms] section. +.It Li proxiable = Va boolean +When obtaining initial credentials, make the credentials proxiable. +This option is also valid in the [realms] section. +.It Li verify_ap_req_nofail = Va boolean +If enabled, failure to verify credentials against a local key is a +fatal error. +The application has to be able to read the corresponding service key +for this to work. +Some applications, like +.Xr su 1 , +enable this option unconditionally. +.It Li warn_pwexpire = Va time +How soon to warn for expiring password. +Default is seven days. +.It Li http_proxy = Va proxy-spec +A HTTP-proxy to use when talking to the KDC via HTTP. +.It Li dns_proxy = Va proxy-spec +Enable using DNS via HTTP. +.It Li extra_addresses = Va address ... +A list of addresses to get tickets for along with all local addresses. +.It Li time_format = Va string +How to print time strings in logs, this string is passed to +.Xr strftime 3 . +.It Li date_format = Va string +How to print date strings in logs, this string is passed to +.Xr strftime 3 . +.It Li log_utc = Va boolean +Write log-entries using UTC instead of your local time zone. +.It Li scan_interfaces = Va boolean +Scan all network interfaces for addresses, as opposed to simply using +the address associated with the system's host name. +.It Li fcache_version = Va int +Use file credential cache format version specified. +.It Li fcc-mit-ticketflags = Va boolean +Use MIT compatible format for file credential cache. +It's the field ticketflags that is stored in reverse bit order for +older than Heimdal 0.7. +Setting this flag to +.Dv TRUE +makes it store the MIT way, this is default for Heimdal 0.7. +.It Li check-rd-req-server +If set to "ignore", the framework will ignore any of the server input to +.Xr krb5_rd_req 3 , +this is very useful when the GSS-API server input the +wrong server name into the gss_accept_sec_context call. +.It Li k5login_directory = Va directory +Alternative location for user .k5login files. This option is provided +for compatibility with MIT krb5 configuration files. This path is +subject to percent token expansion (see TOKEN EXPANSION). +.It Li k5login_authoritative = Va boolean +If true then if a principal is not found in k5login files then +.Xr krb5_userok 3 +will not fallback on principal to username mapping. This option is +provided for compatibility with MIT krb5 configuration files. +.It Li kuserok = Va rule ... +Specifies +.Xr krb5_userok 3 +behavior. If multiple values are given, then +.Xr krb5_userok 3 +will evaluate them in order until one succeeds or all fail. Rules are +implemented by plugins, with three built-in plugins +described below. Default: USER-K5LOGIN SIMPLE DENY. +.It Li kuserok = Va DENY +If set and evaluated then +.Xr krb5_userok 3 +will deny access to the given username no matter what the principal name +might be. +.It Li kuserok = Va SIMPLE +If set and evaluated then +.Xr krb5_userok 3 +will use principal to username mapping (see auth_to_local below). If +the principal maps to the requested username then access is allowed. +.It Li kuserok = Va SYSTEM-K5LOGIN[:directory] +If set and evaluated then +.Xr krb5_userok 3 +will use k5login files named after the +.Va luser +argument to +.Xr krb5_userok 3 +in the given directory or in +.Pa /etc/k5login.d/ . +K5login files are text files, with each line containing just a principal +name; principals apearing in a user's k5login file are permitted access +to the user's account. Note: this rule performs no ownership nor +permissions checks on k5login files; proper ownership and +permissions/ACLs are expected due to the k5login location being a +system location. +.It Li kuserok = Va USER-K5LOGIN +If set and evaluated then +.Xr krb5_userok 3 +will use +.Pa ~luser/.k5login +and +.Pa ~luser/.k5login.d/* . +User k5login files and directories must be owned by the user and must +not have world nor group write permissions. +.It Li aname2lname-text-db = Va filename +The named file must be a sorted (in increasing order) text file where +every line consists of an unparsed principal name optionally followed by +whitespace and a username. The aname2lname function will do a binary +search on this file, if configured, looking for lines that match the +given principal name, and if found the given username will be used, or, +if the username is missing, an error will be returned. If the file +doesn't exist, or if no matching line is found then other plugins will +be allowed to run. +.It Li fcache_strict_checking +strict checking in FILE credential caches that owner, no symlink and +permissions is correct. +.It Li moduli = Va FILE +Names a file that contains acceptable modular Diffie-Hellman +groups for PKINIT. +The given file should contain lines with whitespace-separated +fields in this order: +.Va name, nbits, p, g, q . +Lines starting with a +.Va # +are comments. +.It Li pkinit_dh_min_bits = Va NUMBER +PKINIT client's minimum acceptable modular Diffie-Hellman public +key size in bits. +.It Li enable-kx509 = Va boolean +Enable use of kx509 so that every TGT that can has a corresponding +PKIX certificate. Default: false. +.It Li kx509_gen_key_type = Va public-key-type +Type of public key for kx509 private key generation. Defaults to +.Va rsa +and currently only +.Va rsa +is supported. +.It Li kx509_gen_rsa_key_size = Va number-of-bits +RSA key size for kx509. Defaults to 2048. +.It Li kx509_store = path +A file path into which to write a certificate obtained with +kx509, and its private key, when attempting kx509 optimistically +using credentials from a default ccache. Tokens will be +expanded. +.It Li kx509_hostname = Va hostname +If set, then the kx509 client will use this hostname for the +kx509 service. This can also be set in the +.Li [realm] +section on a per-realm basis. If not set then a TGS name will be +used. +.It Li name_canon_rules = Va rules +One or more service principal name canonicalization rules. Each rule +consists of one or more tokens separated by colon (':'). Currently +these rules are used only for hostname canonicalization (usually when +getting a service ticket, from a ccache or a TGS, but also when +acquiring GSS initiator credentials from a keytab). These rules can be +used to implement DNS resolver-like search lists without having to use +DNS. +.Pp +NOTE: Name canonicalization rules are an experimental feature. +.Pp +The first token is a rule type, one of: +.Va as-is, +.Va qualify, or +.Va nss. +.Pp +Any remaining tokens must be options tokens: +.Va use_fast +(use FAST to protect TGS exchanges; currently not supported), +.Va use_dnssec +(use DNSSEC to protect hostname lookups; currently not supported), +.Va ccache_only +, +.Va use_referrals, +.Va no_referrals, +.Va lookup_realm, +.Va mindots=N, +.Va maxdots=N, +.Va order=N, +domain= +.Va domain, +realm= +.Va realm, +match_domain= +.Va domain, +and match_realm= +.Va realm. +.Pp +When trying to obtain a service ticket for a host-based service +principal name, name canonicalization rules are applied to that name in +the order given, one by one, until one succeds (a service ticket is +obtained), or all fail. Similarly when acquiring GSS initiator +credentials from a keytab, and when comparing a non-canonical GSS name +to a canonical one. +.Pp +For each rule the system checks that the hostname has at least +.Va mindots +periods (if given) in it, at most +.Va maxdots +periods (if given), that the hostname ends in the given +.Va match_domain +(if given), +and that the realm of the principal matches the +.Va match_realm +(if given). +.Pp +.Va As-is +rules leave the hostname unmodified but may set a realm. +.Va Qualify +rules qualify the hostname with the given +.Va domain +and also may set the realm. +The +.Va nss +rule uses the system resolver to lookup the host's canonical name and is +usually not secure. Note that using the +.Va nss +rule type implies having to have principal aliases in the HDB (though +not necessarily in keytabs). +.Pp +The empty realm denotes "ask the client's realm's TGS". The empty realm +may be set as well as matched. +.Pp +The order in which rules are applied is as follows: first all the rules +with explicit +.Va order +then all other rules in the order in which they appear. If any two +rules have the same explicit +.Va order , +their order of appearance in krb5.conf breaks the tie. Explicitly +specifying order can be useful where tools read and write the +configuration file without preserving parameter order. +.Pp +Malformed rules are ignored. +.It Li allow_hierarchical_capaths = Va boolean +When validating cross-realm transit paths, absent any explicit capath from the +client realm to the server realm, allow a hierarchical transit path via the +common ancestor domain of the two realms. +Defaults to true. +Note, absent an explicit setting, hierarchical capaths are always used by +the KDC when generating a referral to a destination with which is no direct +trust. +.It Li client_aware_channel_bindings = Va boolean +If this flag is true, then all application protocol authentication +requests will be flagged to indicate that the application supports +channel bindings when operating over a secure channel. +The default value is false. +.It Li check_pac = Va boolean +If this flag is true and a Windows Privilege Attribute Certificate (PAC) +is present in the ticket authorization data, then +.Xr krb5_rd_req 3 +will validate the PAC before returning success. The default value is true. +.It Li report_canonical_client_name = Va boolean +If this flag is true, then the canonical client name from the PAC will +be used instead of the client name in the ticket. The default value is false. +Note that setting it to true implicitly sets +.Va check_pac +to true. +.El +.It Li [domain_realm] +This is a list of mappings from DNS domain to Kerberos realm. +.Pp +It is used by the client and the TGS both to determine the realm +of host-based service principal names based on the principal's +hostname component. +.Pp +The client may try DNS to determine a host's realm; see the +`dns_lookup_realm' parameter, and see below. +.Pp +The TGS will issue a referral when a host-based service does not +exist in the requested realm but can be mapped with these rules +to a different realm. +The TGS will also issue a referral when a host-based service +exists in the requested realm as an alias of a service in another +realm. +.Pp +Each binding in this section looks like: +.Pp +.Dl domain = realm +.Pp +The domain can be either a full name of a host or a trailing +component, in the latter case the domain-string should start with a +period. +The trailing component only matches hosts that are in the same domain, ie +.Dq .example.com +matches +.Dq foo.example.com , +but not +.Dq foo.test.example.com . +.Pp +The realm may be the token `dns_locate', in which case the actual +realm will be determined using DNS (independently of the setting +of the `dns_lookup_realm' option). +.It Li [realms] +.Bl -tag -width "xxx" -offset indent +.It Va REALM Li = { +.Bl -tag -width "xxx" -offset indent +.It Li kdc = Va [service/]host[:port] +Specifies a list of kdcs for this realm. +If the optional +.Va port +is absent, the +default value for the +.Dq kerberos/udp +.Dq kerberos/tcp , +and +.Dq http/tcp +port (depending on service) will be used. +The kdcs will be used in the order that they are specified. +.Pp +The optional +.Va service +specifies over what medium the kdc should be +contacted. +Possible services are +.Dq udp , +.Dq tcp , +and +.Dq http . +Http can also be written as +.Dq http:// . +Default service is +.Dq udp +and +.Dq tcp . +.It Li admin_server = Va host[:port] +Specifies the admin server for this realm, where all the modifications +to the database are performed. +.It Li kpasswd_server = Va host[:port] +Points to the server where all the password changes are performed. +If there is no such entry, the kpasswd port on the admin_server host +will be tried. +.It Li tgs_require_subkey +a boolan variable that defaults to false. +Old DCE secd (pre 1.1) might need this to be true. +.It Li auth_to_local_names = { +.Bl -tag -width "xxx" -offset indent +.It Va principal_name = Va username +The given +.Va principal_name +will be mapped to the given +.Va username +if the +.Va REALM +is a default realm. +.El +.It Li } +.It Li auth_to_local = HEIMDAL_DEFAULT +Use the Heimdal default principal to username mapping. +Applies to principals from the +.Va REALM +if and only if +.Va REALM +is a default realm. +.It Li auth_to_local = DEFAULT +Use the MIT default principal to username mapping. +Applies to principals from the +.Va REALM +if and only if +.Va REALM +is a default realm. +.It Li auth_to_local = DB:/path/to/db.txt +Use a binary search of the given DB. The DB must be a flat-text +file sortedf in the "C" locale, with each record being a line +(separated by either LF or CRLF) consisting of a principal name +followed by whitespace followed by a username. +Applies to principals from the +.Va REALM +if and only if +.Va REALM +is a default realm. +.It Li auth_to_local = DB:/path/to/db +Use the given DB, if there's a plugin for it. +Applies to principals from the +.Va REALM +if and only if +.Va REALM +is a default realm. +.It Li auth_to_local = RULE:... +Use the given rule, if there's a plugin for it. +Applies to principals from the +.Va REALM +if and only if +.Va REALM +is a default realm. +.It Li auth_to_local = NONE +No additional principal to username mapping is done. Note that +.Va auth_to_local_names +and any preceding +.Va auth_to_local +rules have precedence. +.It Li pkinit_require_eku = BOOL +If +.Va true +then the KDC PKINIT Extended Key Usage (EKU) OID (1.3.6.5.2.3.5) +must be present in KDCs' PKINIT certificates. +Defaults to +.Va true . +.It Li pkinit_require_krbtgt_otherName = BOOL +If +.Va true +then the PKINIT Subject Alternative Name (SAN) for the TGS must +be present in KDCs' PKINIT certificates, and must match their +realm. +Defaults to +.Va true . +.It Li pkinit_require_hostname_match = BOOL +If +.Va true +then KDCs' PKINIT certificates must match their hostnames. +Defaults to +.Va false . +.It Li pkinit_trustedCertifiers = BOOL +If +.Va true +then PKINIT client will tell KDCs which trust anchors it trusts. +Defaults to +.Va true . +.It Li disable_pac = BOOL +If +.Va true +then the KDC will not sign tickets with PAC, which disables S4U2Proxy support. +Defaults to +.Va false . +.El +.It Li } +.El +.It Li [capaths] +.Bl -tag -width "xxx" -offset indent +.It Va client-realm Li = { +.Bl -tag -width "xxx" -offset indent +.It Va server-realm Li = Va hop-realm ... +This serves two purposes. First the first listed +.Va hop-realm +tells a client which realm it should contact in order to ultimately +obtain credentials for a service in the +.Va server-realm . +Secondly, it tells the KDC (and other servers) which realms are +allowed in a multi-hop traversal from +.Va client-realm +to +.Va server-realm . +Except for the client case, the order of the realms are not important. +.El +.It Va } +.El +.It Li [logging] +.Bl -tag -width "xxx" -offset indent +.It Va entity Li = Va destination +Specifies that +.Va entity +should use the specified +.Li destination +for logging. +See the +.Xr krb5_openlog 3 +manual page for a list of defined destinations. +.El +.It Li [kdc] +.Bl -tag -width "xxx" -offset indent +.It Li database Li = { +.Bl -tag -width "xxx" -offset indent +.It Li dbname Li = Va [DATBASETYPE:]DATABASENAME +Use this database for this realm. The +.Va DATABASETYPE +should be one of 'lmdb', 'db3', 'db1', 'db', 'sqlite', or 'ldap'. +See the info documetation how to configure different database backends. +.It Li realm Li = Va REALM +Specifies the realm that will be stored in this database. +It realm isn't set, it will used as the default database, there can +only be one entry that doesn't have a +.Li realm +stanza. +.It Li mkey_file Li = Pa FILENAME +Use this keytab file for the master key of this database. +If not specified +.Va DATABASENAME Ns .mkey +will be used. +.It Li acl_file Li = PA FILENAME +Use this file for the ACL list of this database. +.It Li log_file Li = Pa FILENAME +Use this file as the log of changes performed to the database. +This file is used by +.Nm ipropd-master +for propagating changes to slaves. It is also used by +.Nm kadmind +and +.Nm kadmin +(when used with the +.Li -l +option), and by all applications using +.Nm libkadm5 +with the local backend, for two-phase commit functionality. Slaves also +use this. Setting this to +.Nm /dev/null +disables two-phase commit and incremental propagation. Use +.Nm iprop-log +to show the contents of this log file. +.It Li log-max-size = Pa number +When the log reaches this size (in bytes), the log will be truncated, +saving some entries, and keeping the latest version number so as to not +disrupt incremental propagation. If set to a negative value then +automatic log truncation will be disabled. Defaults to 52428800 (50MB). +.El +.It Li } +.It Li max-request = Va SIZE +Maximum size of a kdc request. +.It Li require-preauth = Va BOOL +If set pre-authentication is required. +.It Li ports = Va "list of ports" +List of ports the kdc should listen to. +.It Li addresses = Va "list of interfaces" +List of addresses the kdc should bind to. +.It Li enable-http = Va BOOL +Should the kdc answer kdc-requests over http. +.It Li tgt-use-strongest-session-key = Va BOOL +If this is TRUE then the KDC will prefer the strongest key from the +client's AS-REQ or TGS-REQ enctype list for the ticket session key that +is supported by the KDC and the target principal when the target +principal is a krbtgt principal. Else it will prefer the first key from +the client's AS-REQ enctype list that is also supported by the KDC and +the target principal. Defaults to FALSE. +.It Li svc-use-strongest-session-key = Va BOOL +Like tgt-use-strongest-session-key, but applies to the session key +enctype of tickets for services other than krbtgt principals. Defaults +to FALSE. +.It Li preauth-use-strongest-session-key = Va BOOL +If TRUE then select the strongest possible enctype from the client's +AS-REQ for PA-ETYPE-INFO2 (i.e., for password-based pre-authentication). +Else pick the first supported enctype from the client's AS-REQ. Defaults +to FALSE. +.It Li use-strongest-server-key = Va BOOL +If TRUE then the KDC picks, for the ticket encrypted part's key, the +first supported enctype from the target service principal's hdb entry's +current keyset. Else the KDC picks the first supported enctype from the +target service principal's hdb entry's current keyset. Defaults to TRUE. +.It Li check-ticket-addresses = Va BOOL +Verify the addresses in the tickets used in tgs requests. +.\" XXX +.It Li warn_ticket_addresses = Va BOOL +Warn about, but allow, usage of tickets from hosts that don't match the +addresses in the tickets. +.It Li allow-null-ticket-addresses = Va BOOL +Allow address-less tickets. +.\" XXX +.It Li disable_pac = Va BOOL +Do not include a PAC in service tickets. +However, if a service has the +.Li auth-data-reqd +attribute then the KDC will include a PAC anyways. +.It Li enable_fast = Va BOOL +Enable RFC 6113 FAST support, this is enabled by default. +.It Li enable_fast_cookie = Va BOOL +If FAST is enabled, enable support for the FAST cookie +and mechanisms that require it. +.It Li enable_armored_pa_enc_timestamp = Va BOOL +Enable armored encrypted timestamp pre-authentication with key +strengthening. +RFC 6113 says not to use PA-ENC-TIMESTAMP in FAST armored tunnels +as there is a newer replacement, PA-ENC-CHALLENGE, but for +interoperability with earlier versions of Heimdal, this is +enabled by default for now. +.It Li enable_unarmored_pa_enc_timestamp = Va BOOL +Enable unarmored encrypted timestamp pre-authentication. +Enabled by default for now, but in a future release will be +disabled. +.It Li enable-pkinit = Va BOOL +Enable PKINIT (disabled by default). +.It Li require-pkinit-freshness = Va BOOL +If PKINIT is enabled, require that PKINIT requests contain a +freshness token proving recent possession of the private key. +Disabled by default. +.It Li allow-anonymous = Va BOOL +If the kdc is allowed to hand out anonymous tickets. +.It Li synthetic_clients = Va BOOL +If enabled then the KDC will issue tickets for clients that don't +exist in the HDB provided that they use PKINIT, that PKINIT is +enabled, and that the client's have certificates with PKINIT +subject alternative names (SANs). +.It Li synthetic_clients_max_life = Va TIME +Maximum ticket lifetime for synthetic clients. +Default: 5 minutes. +.It Li synthetic_clients_max_renew = Va TIME +Maximum ticket renewable lifetime for synthetic clients. +Default: 5 minutes. +.It Li pkinit_identity = Va HX509-STORE +This is an HX509 store containing the KDC's PKINIT credential +(private key and end-entity certificate). +This is single valued, though multiple stores can be specified by +separating them with commas. +An +.Va HX509-STORE +is of the form +.Va TYPE:name +where +.Va TYPE +is one of +.Va FILE, Va PEM-FILE, Va DER-FILE, Va PKCS12, Va PKCS11, +or on OX X, +.Va KEYCHAIN . +The form of the +.Va name +depends on the +.Va TYPE . +For +.Va FILE, Va PEM-FILE, Va DER-FILE, +and +.Va PKCS12 +the +.Va name +is a file path. +See the Heimdal hx509 documentation for more information. +.It Li pkinit_pool = Va HX509-STORE +This is a multi-valued parameter naming one or more stores of +intermediate certification authority (CA) certificates for the +KDC's end entity certificate. +.It Li pkinit_anchors = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +anchors for PKINIT client certificates. +Note that the +.Va DIR +type of +.Va HX509-STORE +is also supported here. +.Va DIR +type stores are OpenSSL-style CA certificate hash directories. +.It Li pkinit_revoke = Va HX509-STORE ... +This is a multi-valued parameter naming one or more stores of +CRLs for the issuers of PKINIT client certificates. +Only the first valid CRL for a particular issuer will be checked. +If no CRLs are configured, then CRLs will not be checked. +This is because the KDC will not dereference CRL distribution +points nor request OCSP responses. +.It Li pkinit_kdc_ocsp = Va PATH +This names a file whose contents is the DER encoding of an +OCSPResponse for the KDC's end entity certificate. +.It Li pkinit_kdc_friendly_name = Va NAME +This is an optional friendly name of the KDC's end entity +certificate. +This is only helpful when the +.Li pkinit_identity +store contains many credentials. +.It Li pkinit_principal_in_certificate = Va BOOL +If set to +.Va true +then the KDC will match AS-REQ client principal names to the +PKINIT +.Va subjectAlternativeName +values from the clients' certificates. +Defaults to +.Va true . +.It Li pkinit_dh_min_bits = Va NUMBER +Minimum acceptable modular Diffie-Hellman public key size in +bits. +.It Li pkinit_max_life_from_cert_extension = Va BOOL +If set to +.Va true +then the KDC will override the +.Va max_life +attribute of the client principal's HDB record with a maximum +ticket life taken from a certificate extension with OID +.Va { iso(1) member-body(2) se(752) su(43) heim-pkix(16) 4 } +and the DER encoding of an +.Va INTEGER +number of seconds. +Alternatively, if the extended key usage OID +.Va { iso(1) member-body(2) se(752) su(43) heim-pkix(16) 3 } +is included in the client's certificate, then the +.Va notAfter +minus the current time will be used. +.It Li pkinit_max_life_bound = Va TIME +If set, this will be a hard bound on the maximum ticket lifetime +taken from the client's certificate. +As usual, +.Va TIME +can be given as a number followed by a unit, such as +.Dq 2d +for +.Dq two days . +.It Li pkinit_max_life_from_cert = Va TIME +If set, this will override the +.Va max_life +attribute of the client principal's HDB record with the +.Va notAfter +of the client's certificate minus the current time, bounded to +the given relative +.Va TIME +unless the +.Li pkinit_max_life_from_cert_extension +parameter is set and the client's certificate has that extension. +As usual, +.Va TIME +can be given as a number followed by a unit, such as +.Dq 2d +for +.Dq two days . +.It Li enable_gss_preauth = Va boolean +Enables pre-authentication using a GSS-API mechanism supported by the client and KDC. +The GSS-API initiator and AS request client names must match, unless the +.Li WELLKNOWN/FEDERATED +name was used in the AS request, in which case the AS reply will contain the +GSS-API initiator name. Authorization and mapping behavior may be customized +by plugins. If synthetic clients are enabled, then the GSS-API initiator need +not exist in the local database. GSS-API pre-authentication is disabled by +default. +.It Li enable_gss_auth_data = Va boolean +When using GSS-API pre-authentication, includes a Kerberos authorization data +element containing naming attributes associated with the GSS-API initiator. This +is disabled by default as it may significantly increase the size of returned +tickets. +.It Li gss_mechanisms_allowed = Va mechs ... +A list of GSS-API mechanisms that may be used for GSS-API pre-authentication. +.It Li gss_cross_realm_mechanisms_allowed = Va mechs ... +A list of GSS-API mechanisms that, when using the default authorization +mechanism, will be permitted to map Kerberos principals in foreign realms. The +list is empty by default. Initiator names from mechanisms not on this list will +be mapped to an enterprise principal in the AS-REQ realm. This option is +intended to avoid conflating GSS-API pre-authentication and Kerberos +cross-realm authentication. The behavior is provided by the default +authorization mechanism and will be overridden by an authorization plugin. +Mechanisms may be identified by dot-separated OID or a short name. +.It Li historical_anon_realm = Va boolean +Enables pre-7.0 non-RFC-comformant KDC behavior. +With this option set to +.Li true +the client realm in anonymous pkinit AS replies will be the requested realm, +rather than the RFC-conformant +.Li WELLKNOWN:ANONYMOUS +realm. +This can have a security impact on servers that expect to grant access to +anonymous-but-authenticated to the KDC users of the realm in question: +they would also grant access to unauthenticated anonymous users. +As such, it is not recommend to set this option to +.Li true. +.It Li encode_as_rep_as_tgs_rep = Va BOOL +Encode as-rep as tgs-rep to be compatible with mistakes older DCE secd did. +.\" XXX +.It Li kdc_warn_pwexpire = Va TIME +The time before expiration that the user should be warned that her +password is about to expire. +.It Li logging = Va Logging +What type of logging the kdc should use, see also [logging]/kdc. +.It Li hdb-ldap-structural-object Va structural object +If the LDAP backend is used for storing principals, this is the +structural object that will be used when creating and when reading +objects. +The default value is account . +.It Li hdb-ldap-create-base Va creation dn +is the dn that will be appended to the principal when creating entries. +Default value is the search dn. +.It Li enable-digest = Va BOOL +Should the kdc answer digest requests. The default is FALSE. +.It Li digests_allowed = Va list of digests +Specifies the digests the kdc will reply to. The default is +.Li ntlm-v2 . +.It Li enable-kx509 = Va boolean +Enables kx509 service. +.Pp +The kx509 service is configurable for a number of cases: +.Bl -tag -width "" -offset indent +.It Li default certificates for user or service principals, +.It Li non-default certificate requests including subject alternative names (SAN) and extended key usage (EKU) certificate extensions, for either client, server, or mixed usage. +.El +.Pp +Distinct configurations are supported for all of these cases as +shown below: +.Bd -literal -offset indent +[kdc] + enable-kx509 = yes | no + require_csr = yes | no + require_initial_kca_tickets = yes | no + realm = { + = { + kx509 = { +