summaryrefslogtreecommitdiffstats
path: root/third_party/heimdal/lib/krb5
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/heimdal/lib/krb5')
-rw-r--r--third_party/heimdal/lib/krb5/Makefile.am454
-rw-r--r--third_party/heimdal/lib/krb5/NTMakefile542
-rw-r--r--third_party/heimdal/lib/krb5/acache.c1129
-rw-r--r--third_party/heimdal/lib/krb5/acl.c293
-rw-r--r--third_party/heimdal/lib/krb5/add_et_list.c54
-rw-r--r--third_party/heimdal/lib/krb5/addr_families.c1574
-rw-r--r--third_party/heimdal/lib/krb5/aes-test.c1058
-rw-r--r--third_party/heimdal/lib/krb5/an2ln_plugin.h91
-rw-r--r--third_party/heimdal/lib/krb5/aname_to_localname.c471
-rw-r--r--third_party/heimdal/lib/krb5/appdefault.c140
-rw-r--r--third_party/heimdal/lib/krb5/asn1_glue.c162
-rw-r--r--third_party/heimdal/lib/krb5/auth_context.c621
-rw-r--r--third_party/heimdal/lib/krb5/authdata.c124
-rw-r--r--third_party/heimdal/lib/krb5/build_ap_req.c68
-rw-r--r--third_party/heimdal/lib/krb5/build_auth.c267
-rw-r--r--third_party/heimdal/lib/krb5/cache.c2300
-rw-r--r--third_party/heimdal/lib/krb5/ccache_plugin.h46
-rw-r--r--third_party/heimdal/lib/krb5/changepw.c860
-rw-r--r--third_party/heimdal/lib/krb5/codec.c214
-rw-r--r--third_party/heimdal/lib/krb5/config_file.c758
-rw-r--r--third_party/heimdal/lib/krb5/constants.c67
-rw-r--r--third_party/heimdal/lib/krb5/context.c1506
-rw-r--r--third_party/heimdal/lib/krb5/convert_creds.c92
-rw-r--r--third_party/heimdal/lib/krb5/copy_host_realm.c72
-rw-r--r--third_party/heimdal/lib/krb5/crc.c69
-rw-r--r--third_party/heimdal/lib/krb5/creds.c282
-rw-r--r--third_party/heimdal/lib/krb5/crypto-aes-sha1.c177
-rw-r--r--third_party/heimdal/lib/krb5/crypto-aes-sha2.c199
-rw-r--r--third_party/heimdal/lib/krb5/crypto-algs.c94
-rw-r--r--third_party/heimdal/lib/krb5/crypto-arcfour.c368
-rw-r--r--third_party/heimdal/lib/krb5/crypto-des-common.c158
-rw-r--r--third_party/heimdal/lib/krb5/crypto-des.c403
-rw-r--r--third_party/heimdal/lib/krb5/crypto-des3.c292
-rw-r--r--third_party/heimdal/lib/krb5/crypto-evp.c674
-rw-r--r--third_party/heimdal/lib/krb5/crypto-null.c103
-rw-r--r--third_party/heimdal/lib/krb5/crypto-pk.c295
-rw-r--r--third_party/heimdal/lib/krb5/crypto-rand.c152
-rw-r--r--third_party/heimdal/lib/krb5/crypto-stubs.c102
-rw-r--r--third_party/heimdal/lib/krb5/crypto.c3248
-rw-r--r--third_party/heimdal/lib/krb5/crypto.h231
-rw-r--r--third_party/heimdal/lib/krb5/data.c228
-rw-r--r--third_party/heimdal/lib/krb5/db_plugin.c41
-rw-r--r--third_party/heimdal/lib/krb5/db_plugin.h68
-rw-r--r--third_party/heimdal/lib/krb5/dcache.c854
-rw-r--r--third_party/heimdal/lib/krb5/deprecated.c725
-rw-r--r--third_party/heimdal/lib/krb5/derived-key-test.c145
-rw-r--r--third_party/heimdal/lib/krb5/digest.c1165
-rw-r--r--third_party/heimdal/lib/krb5/dll.c76
-rw-r--r--third_party/heimdal/lib/krb5/doxygen.c700
-rw-r--r--third_party/heimdal/lib/krb5/eai_to_heim_errno.c118
-rw-r--r--third_party/heimdal/lib/krb5/enomem.c42
-rw-r--r--third_party/heimdal/lib/krb5/error_string.c236
-rw-r--r--third_party/heimdal/lib/krb5/expand_hostname.c177
-rw-r--r--third_party/heimdal/lib/krb5/expand_path.c94
-rw-r--r--third_party/heimdal/lib/krb5/fast.c963
-rw-r--r--third_party/heimdal/lib/krb5/fcache.c1693
-rw-r--r--third_party/heimdal/lib/krb5/free.c51
-rw-r--r--third_party/heimdal/lib/krb5/free_host_realm.c59
-rw-r--r--third_party/heimdal/lib/krb5/generate_seq_number.c48
-rw-r--r--third_party/heimdal/lib/krb5/generate_subkey.c73
-rw-r--r--third_party/heimdal/lib/krb5/get_addrs.c283
-rw-r--r--third_party/heimdal/lib/krb5/get_cred.c2044
-rw-r--r--third_party/heimdal/lib/krb5/get_default_principal.c95
-rw-r--r--third_party/heimdal/lib/krb5/get_default_realm.c80
-rw-r--r--third_party/heimdal/lib/krb5/get_for_creds.c367
-rw-r--r--third_party/heimdal/lib/krb5/get_host_realm.c308
-rw-r--r--third_party/heimdal/lib/krb5/get_in_tkt.c557
-rw-r--r--third_party/heimdal/lib/krb5/get_port.c52
-rw-r--r--third_party/heimdal/lib/krb5/init_creds.c482
-rw-r--r--third_party/heimdal/lib/krb5/init_creds_pw.c4052
-rw-r--r--third_party/heimdal/lib/krb5/k524_err.et20
-rw-r--r--third_party/heimdal/lib/krb5/k5e1_err.et13
-rw-r--r--third_party/heimdal/lib/krb5/kcm.c1505
-rw-r--r--third_party/heimdal/lib/krb5/kcm.h87
-rw-r--r--third_party/heimdal/lib/krb5/kerberos.8115
-rw-r--r--third_party/heimdal/lib/krb5/keyblock.c203
-rw-r--r--third_party/heimdal/lib/krb5/keytab.c976
-rw-r--r--third_party/heimdal/lib/krb5/keytab_any.c260
-rw-r--r--third_party/heimdal/lib/krb5/keytab_file.c856
-rw-r--r--third_party/heimdal/lib/krb5/keytab_keyfile.c456
-rw-r--r--third_party/heimdal/lib/krb5/keytab_memory.c231
-rw-r--r--third_party/heimdal/lib/krb5/krb5-plugin.7359
-rw-r--r--third_party/heimdal/lib/krb5/krb5.conf.51475
-rw-r--r--third_party/heimdal/lib/krb5/krb5.h1073
-rw-r--r--third_party/heimdal/lib/krb5/krb5.moduli3
-rw-r--r--third_party/heimdal/lib/krb5/krb524_convert_creds_kdc.386
-rw-r--r--third_party/heimdal/lib/krb5/krb5_425_conv_principal.3224
-rw-r--r--third_party/heimdal/lib/krb5/krb5_acl_match_file.3111
-rw-r--r--third_party/heimdal/lib/krb5/krb5_aname_to_localname.380
-rw-r--r--third_party/heimdal/lib/krb5/krb5_appdefault.388
-rw-r--r--third_party/heimdal/lib/krb5/krb5_auth_context.3395
-rw-r--r--third_party/heimdal/lib/krb5/krb5_c_make_checksum.3297
-rw-r--r--third_party/heimdal/lib/krb5/krb5_ccapi.h239
-rw-r--r--third_party/heimdal/lib/krb5/krb5_check_transited.3106
-rw-r--r--third_party/heimdal/lib/krb5/krb5_create_checksum.3226
-rw-r--r--third_party/heimdal/lib/krb5/krb5_creds.3119
-rw-r--r--third_party/heimdal/lib/krb5/krb5_digest.3260
-rw-r--r--third_party/heimdal/lib/krb5/krb5_eai_to_heim_errno.368
-rw-r--r--third_party/heimdal/lib/krb5/krb5_encrypt.3278
-rw-r--r--third_party/heimdal/lib/krb5/krb5_err.et317
-rw-r--r--third_party/heimdal/lib/krb5/krb5_find_padata.387
-rw-r--r--third_party/heimdal/lib/krb5/krb5_generate_random_block.357
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_all_client_addrs.374
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_credentials.3181
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_creds.3173
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_forwarded_creds.379
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_in_cred.3274
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_init_creds.3403
-rw-r--r--third_party/heimdal/lib/krb5/krb5_get_krbhst.386
-rw-r--r--third_party/heimdal/lib/krb5/krb5_getportbyname.367
-rw-r--r--third_party/heimdal/lib/krb5/krb5_init_context.3308
-rw-r--r--third_party/heimdal/lib/krb5/krb5_is_thread_safe.358
-rw-r--r--third_party/heimdal/lib/krb5/krb5_krbhst_init.3174
-rw-r--r--third_party/heimdal/lib/krb5/krb5_locl.h491
-rw-r--r--third_party/heimdal/lib/krb5/krb5_mk_req.3187
-rw-r--r--third_party/heimdal/lib/krb5/krb5_mk_safe.382
-rw-r--r--third_party/heimdal/lib/krb5/krb5_openlog.3305
-rw-r--r--third_party/heimdal/lib/krb5/krb5_parse_name.367
-rw-r--r--third_party/heimdal/lib/krb5/krb5_principal.3371
-rw-r--r--third_party/heimdal/lib/krb5/krb5_rcache.3163
-rw-r--r--third_party/heimdal/lib/krb5/krb5_rd_error.398
-rw-r--r--third_party/heimdal/lib/krb5/krb5_rd_safe.381
-rw-r--r--third_party/heimdal/lib/krb5/krb5_set_default_realm.3164
-rw-r--r--third_party/heimdal/lib/krb5/krb5_set_password.3143
-rw-r--r--third_party/heimdal/lib/krb5/krb5_string_to_key.3156
-rw-r--r--third_party/heimdal/lib/krb5/krb5_timeofday.3118
-rw-r--r--third_party/heimdal/lib/krb5/krb5_verify_init_creds.3103
-rw-r--r--third_party/heimdal/lib/krb5/krb5_verify_user.3241
-rw-r--r--third_party/heimdal/lib/krb5/krb_err.et63
-rw-r--r--third_party/heimdal/lib/krb5/krbhst-test.c109
-rw-r--r--third_party/heimdal/lib/krb5/krbhst.c1282
-rw-r--r--third_party/heimdal/lib/krb5/krcache.c2075
-rw-r--r--third_party/heimdal/lib/krb5/kuserok.c753
-rw-r--r--third_party/heimdal/lib/krb5/kuserok_plugin.h91
-rw-r--r--third_party/heimdal/lib/krb5/kx509.c1323
-rw-r--r--third_party/heimdal/lib/krb5/kx509_err.et39
-rw-r--r--third_party/heimdal/lib/krb5/libkrb5-exports.def.in901
-rw-r--r--third_party/heimdal/lib/krb5/locate_plugin.h81
-rw-r--r--third_party/heimdal/lib/krb5/log.c252
-rw-r--r--third_party/heimdal/lib/krb5/mcache.c623
-rw-r--r--third_party/heimdal/lib/krb5/misc.c111
-rw-r--r--third_party/heimdal/lib/krb5/mit_glue.c434
-rw-r--r--third_party/heimdal/lib/krb5/mk_cred.c324
-rw-r--r--third_party/heimdal/lib/krb5/mk_error.c117
-rw-r--r--third_party/heimdal/lib/krb5/mk_priv.c151
-rw-r--r--third_party/heimdal/lib/krb5/mk_rep.c119
-rw-r--r--third_party/heimdal/lib/krb5/mk_req.c114
-rw-r--r--third_party/heimdal/lib/krb5/mk_req_ext.c151
-rw-r--r--third_party/heimdal/lib/krb5/mk_safe.c139
-rw-r--r--third_party/heimdal/lib/krb5/n-fold-test.c119
-rw-r--r--third_party/heimdal/lib/krb5/n-fold.c150
-rw-r--r--third_party/heimdal/lib/krb5/net_read.c53
-rw-r--r--third_party/heimdal/lib/krb5/net_write.c118
-rw-r--r--third_party/heimdal/lib/krb5/pac.c2159
-rw-r--r--third_party/heimdal/lib/krb5/padata.c62
-rw-r--r--third_party/heimdal/lib/krb5/parse-name-test.c192
-rw-r--r--third_party/heimdal/lib/krb5/pcache.c81
-rw-r--r--third_party/heimdal/lib/krb5/pkinit-ec.c314
-rw-r--r--third_party/heimdal/lib/krb5/pkinit.c2670
-rw-r--r--third_party/heimdal/lib/krb5/plugin.c208
-rw-r--r--third_party/heimdal/lib/krb5/principal.c2220
-rw-r--r--third_party/heimdal/lib/krb5/prog_setup.c64
-rw-r--r--third_party/heimdal/lib/krb5/prompter_posix.c72
-rw-r--r--third_party/heimdal/lib/krb5/pseudo-random-test.c112
-rw-r--r--third_party/heimdal/lib/krb5/rd_cred.c348
-rw-r--r--third_party/heimdal/lib/krb5/rd_error.c125
-rw-r--r--third_party/heimdal/lib/krb5/rd_priv.c184
-rw-r--r--third_party/heimdal/lib/krb5/rd_rep.c121
-rw-r--r--third_party/heimdal/lib/krb5/rd_req.c1107
-rw-r--r--third_party/heimdal/lib/krb5/rd_safe.c214
-rw-r--r--third_party/heimdal/lib/krb5/read_message.c104
-rw-r--r--third_party/heimdal/lib/krb5/recvauth.c269
-rw-r--r--third_party/heimdal/lib/krb5/replay.c328
-rw-r--r--third_party/heimdal/lib/krb5/salt-aes-sha1.c102
-rw-r--r--third_party/heimdal/lib/krb5/salt-aes-sha2.c136
-rw-r--r--third_party/heimdal/lib/krb5/salt-arcfour.c107
-rw-r--r--third_party/heimdal/lib/krb5/salt-des.c223
-rw-r--r--third_party/heimdal/lib/krb5/salt-des3.c147
-rw-r--r--third_party/heimdal/lib/krb5/salt.c362
-rw-r--r--third_party/heimdal/lib/krb5/scache.c1602
-rw-r--r--third_party/heimdal/lib/krb5/send_to_kdc.c1357
-rw-r--r--third_party/heimdal/lib/krb5/send_to_kdc_plugin.h70
-rw-r--r--third_party/heimdal/lib/krb5/sendauth.c257
-rw-r--r--third_party/heimdal/lib/krb5/set_default_realm.c85
-rw-r--r--third_party/heimdal/lib/krb5/sock_principal.c68
-rwxr-xr-xthird_party/heimdal/lib/krb5/sp800-108-kdf.c100
-rw-r--r--third_party/heimdal/lib/krb5/store-int.c67
-rw-r--r--third_party/heimdal/lib/krb5/store-int.h50
-rw-r--r--third_party/heimdal/lib/krb5/store-test.c117
-rw-r--r--third_party/heimdal/lib/krb5/store.c2058
-rw-r--r--third_party/heimdal/lib/krb5/store_emem.c222
-rw-r--r--third_party/heimdal/lib/krb5/store_fd.c200
-rw-r--r--third_party/heimdal/lib/krb5/store_mem.c212
-rw-r--r--third_party/heimdal/lib/krb5/store_sock.c163
-rw-r--r--third_party/heimdal/lib/krb5/store_stdio.c271
-rw-r--r--third_party/heimdal/lib/krb5/string-to-key-test.c140
-rw-r--r--third_party/heimdal/lib/krb5/test_acl.c118
-rw-r--r--third_party/heimdal/lib/krb5/test_addr.c239
-rw-r--r--third_party/heimdal/lib/krb5/test_alname.c219
-rw-r--r--third_party/heimdal/lib/krb5/test_ap-req.c228
-rwxr-xr-xthird_party/heimdal/lib/krb5/test_canon.c177
-rw-r--r--third_party/heimdal/lib/krb5/test_cc.c1218
-rw-r--r--third_party/heimdal/lib/krb5/test_config.c246
-rw-r--r--third_party/heimdal/lib/krb5/test_config_strings.cfg12
-rw-r--r--third_party/heimdal/lib/krb5/test_crypto.c216
-rw-r--r--third_party/heimdal/lib/krb5/test_crypto_wrapping.c168
-rw-r--r--third_party/heimdal/lib/krb5/test_expand_toks.c104
-rw-r--r--third_party/heimdal/lib/krb5/test_forward.c134
-rw-r--r--third_party/heimdal/lib/krb5/test_fx.c253
-rw-r--r--third_party/heimdal/lib/krb5/test_get_addrs.c111
-rw-r--r--third_party/heimdal/lib/krb5/test_gic.c148
-rw-r--r--third_party/heimdal/lib/krb5/test_hostname.c150
-rw-r--r--third_party/heimdal/lib/krb5/test_keytab.c291
-rw-r--r--third_party/heimdal/lib/krb5/test_kuserok.c109
-rw-r--r--third_party/heimdal/lib/krb5/test_mem.c71
-rw-r--r--third_party/heimdal/lib/krb5/test_mkforwardable.c191
-rw-r--r--third_party/heimdal/lib/krb5/test_pac.c1270
-rw-r--r--third_party/heimdal/lib/krb5/test_pkinit_dh2key.c216
-rw-r--r--third_party/heimdal/lib/krb5/test_pknistkdf.c373
-rw-r--r--third_party/heimdal/lib/krb5/test_plugin.c131
-rw-r--r--third_party/heimdal/lib/krb5/test_prf.c100
-rw-r--r--third_party/heimdal/lib/krb5/test_princ.c365
-rw-r--r--third_party/heimdal/lib/krb5/test_renew.c119
-rw-r--r--third_party/heimdal/lib/krb5/test_rfc3961.c522
-rw-r--r--third_party/heimdal/lib/krb5/test_set_kvno0.c182
-rw-r--r--third_party/heimdal/lib/krb5/test_store.c364
-rw-r--r--third_party/heimdal/lib/krb5/test_time.c85
-rw-r--r--third_party/heimdal/lib/krb5/test_x500.c110
-rw-r--r--third_party/heimdal/lib/krb5/ticket.c967
-rw-r--r--third_party/heimdal/lib/krb5/time.c138
-rw-r--r--third_party/heimdal/lib/krb5/transited.c699
-rw-r--r--third_party/heimdal/lib/krb5/verify_init.c246
-rw-r--r--third_party/heimdal/lib/krb5/verify_krb5_conf-version.rc36
-rw-r--r--third_party/heimdal/lib/krb5/verify_krb5_conf.895
-rw-r--r--third_party/heimdal/lib/krb5/verify_krb5_conf.c795
-rw-r--r--third_party/heimdal/lib/krb5/verify_user.c258
-rw-r--r--third_party/heimdal/lib/krb5/version-script.map892
-rw-r--r--third_party/heimdal/lib/krb5/version.c39
-rw-r--r--third_party/heimdal/lib/krb5/warn.c330
-rw-r--r--third_party/heimdal/lib/krb5/write_message.c87
240 files changed, 92587 insertions, 0 deletions
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 <krb5_ccapi.h>
+
+#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 <fnmatch.h>
+
+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 <hex.h>
+#include <err.h>
+#include <assert.h>
+
+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 <heimbase-svc.h>
+
+#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 <string.h>
+
+/* 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]
+ * <realm-name> = {
+ * auth_to_local_names = {
+ * <unparsed-principal-name> = <username>
+ * }
+ * }
+ *
+ * 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(&reg_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 <krb5.h>
+
+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 : "<out of memory>");
+ 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 <krb5.h>
+#include <common_plugin.h>
+
+#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 <CoreFoundation/CoreFoundation.h>
+#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, &section);
+ 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, &section);
+ 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, &section);
+ 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, &section);
+ 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 <assert.h>
+#include <com_err.h>
+
+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 <pkinit_asn1.h>
+
+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 <krb5_locl.h>
+
+/* 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 <heimbase-svc.h>
+
+#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 <err.h>
+
+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<windows.h>
+
+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 <krb5.h>
+ *
+ * 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 <stdarg.h>
+
+/**
+ * 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 <heim-ipc.h>
+#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 <net/if.h>
+#endif
+#include <ifaddrs.h>
+
+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 <assert.h>
+
+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
+ * <username>/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 <user>/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 <resolve.h>
+
+/* 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 <heimbasepriv.h>
+
+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, &timestamp, &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 = &empty;
+ }
+
+ 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 <heim-ipc.h>
+
+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}:<subsidiary>
+ * - <something not starting with %{UID}:>
+ * - <empty string>
+ * - <NULL>
+ *
+ * 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 <IPC-name>:<cache-name>
+ * - col should be <IPC-name>
+ * - sub should be <cache-name>
+ */
+ 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 <drive>:<path> 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 : "<unknown>",
+ 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:
+ * <int32_t numkeys> {[<int32_t kvno> <char[8] deskey>] * 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 <krb5/an2ln_plugin.h>
+
+/* 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 <krb5/kuserok_plugin.h>
+
+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 = {
+ <REALM> = {
+ kx509 = {
+ <label> = {
+ <param> = <value>
+ }
+ hostbased_service = {
+ <service> = {
+ <param> = <value>
+ }
+ }
+ }
+ }
+ }
+.Ed
+where
+.Va label
+is one of:
+.Bl -tag -width "xxx" -offset indent
+.It Li user
+for default certificates for user principals,
+.It Li root_user
+for default certificates for root user principals,
+.It Li admin_user
+for default certificates for admin user principals,
+.It Li hostbased_service
+for default certificates for host-based service principals, in which case the
+service name is used as shown above,
+.It Li client
+for non-default client certificates,
+.It Li server
+for non-default server certificates,
+.It Li mixed
+for non-default client and server certificates.
+.El
+and where the parameters are as follows:
+.Bl -tag -width "xxx" -offset indent
+.It Li ca = Va file
+Specifies the PEM credentials for the kx509 / bx509d certification
+authority.
+If not specified for any specific use-case, then that use-case
+will be disabled.
+.It Li max_cert_lifetime = Va NUMunit
+Specifies the maximum certificate lifetime as a decimal number
+and an optional unit (the default unit is
+.Dq day
+).
+.It Li force_cert_lifetime = Va NUMunit
+Specifies a minimum certificate lifetime as a decimal number and
+an optional unit (the default unit is
+.Dq day
+).
+.It Li allow_extra_lifetime = Va boolean
+Indicates whether a client may request longer lifetimes than
+their authentication credentials.
+Defaults to false.
+.It Li require_initial_kca_tickets = Va boolean
+Specified whether to require that tickets for the
+.Li kca_service
+service principal be INITIAL.
+This may be set on a per-realm basis as well as globally.
+Defaults to true for the global setting.
+.It Li include_pkinit_san = Va boolean
+If true then the kx509 client principal's name and realm will be
+included in an
+.Li id-pkinit-san
+subject alternative name certificate extension.
+This can be set on a per-realm basis as well as globally.
+Defaults to true for the global setting.
+.It Li email_domain = Va domain
+If set then the kx509 client user principal's name at the given
+domain will be included in an
+.Li rfc822Name
+subject alternative name certificate extension.
+This can be set on a per-realm basis as well as globally.
+Defaults to false for the global setting.
+.It Li include_dnsname_san = Va boolean
+If true then a kx509 host-based or domain-based client
+principal's hostname will be included in an
+.Li dNSName
+subject alternative name certificate extension, with the
+downcased realm as the domainname. This can be set on a
+per-realm basis as well as
+globally. Defaults to false for the global setting.
+.It Li ekus = Va OID
+List of OIDs to include as EKUs.
+.It Li subject_name = Va DN
+Specifies a subject name that should either be empty or contain
+variable interpolation as described below for
+.Va template_cert .
+The subject may be the empty string, causing the issued
+certificates' subject names to be empty.
+.It Li template_cert = Va store
+Specifies the hx509 store (e.g.,
+.Va PEM-FILE:path )
+with a template
+for the certificates to be issued to kx509 clients whose
+principal names have one component (i.e., are user principals).
+A template is a certificate with variables to be interpolated in
+the subjectName. The following variables can be interpolated in
+the subject name using
+.Va ${variable}
+syntax:
+.Bl -tag -width "xxx" -offset indent
+.It principal-name
+The full name of the kx509 client principal.
+.It principal-name-without-realm
+The full name of the kx509 client principal, excluding the realm name.
+.It principal-name-realm
+The name of the client principal's realm.
+.It principal-component0
+The first component of the client principal.
+.It principal-component1
+The second component of the client principal.
+.It principal-component2
+The third component of the client principal.
+.It principal-service-name
+The name of the service.
+.It principal-host-name
+The name of the host.
+.El
+.Pp
+If a template and subject name are not specified and no default
+SANs are configured, then no certificate will be issued.
+Otherwise if a template and subject name are not specified, then
+subject of the certificate will be empty.
+.El
+.El
+.Pp
+.It Li [hdb]
+.Bl -tag -width "xxx" -offset indent
+.It Li db-dir = Va path
+This parameter defines a directory that can contain:
+.Bl -tag -width "xxx" -offset indent
+.It Va kdc.conf
+A configuration file with the same format as krb5.conf that will
+be included.
+.It Va m-key
+The master key file.
+.It Va kdc.log
+The default logfile for the KDC when a logfile is not specified in
+.Li [logging]
+.It Va kadm5.acl
+The access controls for
+.Nm kadmind .
+.It Va log
+The (binary) log of transactions used for
+.Nm HDB
+replication via the
+.Nm iprop
+protocol.
+See
+.Nm iprop-log(1)
+for more detail.
+.It Va pki-mapping
+The default PKINIT mapping file if one is not specified in
+.Va [kdc] pkinit_mappings_file .
+.El
+and other files related to
+.Nm iprop
+operation.
+.It Li new_service_key_delay = Va time
+Sets a bias such that new keys are not taken into service until
+after the given time has passed since they were set.
+This is useful for key rotation on concrete principals shared by
+multiple instances of an application: set this time to twice or
+more the keytab fetch period used by applications.
+.It Li enable_virtual_hostbased_princs = Va boolean
+Heimdal supports a notion of virtual host-based service
+principals whose keys are derived from those of a base namespace
+principal of the form
+.Nm WELLKNOWN/HOSTBASED-NAMESPACE/svc/hostname .
+The service name can be wild-carded as
+.Va _ .
+Non-wildcarded services have to be listed in the
+.Li virtual_hostbased_princ_svcs
+parameter (see below).
+This parameter enables this feature, which is disabled by
+default.
+.It Li virtual_hostbased_princ_ndots = Va Integer
+Minimum number of label-separating periods in virtual host-based
+service principals' hostname component.
+.It Li virtual_hostbased_princ_maxdots = Va Integer
+Maximum number of label-separating periods in namespaces'
+hostname component.
+.It Li virtual_hostbased_princ_svcs = Va service-name
+This multi-valued parameter lists service names not to wildcard
+when searching for a namespace for a virtual host-based service
+principal.
+Other service names will have keys derived from a matching
+namespace with a wild-carded service name.
+This allows one to have different attributes for different
+services.
+For example, the
+.Nm "host"
+service can be configured to have the ok-as-delegate flag while
+all others do not.
+.El
+.Pp
+.It Li [bx509]
+This section contains online certification authority configuration, much
+like
+.Li kx509
+in the
+.Li [kdc]
+section, but with the
+.Li kx509
+layer removed.
+.Bd -literal -offset indent
+[kdc]
+ realm = {
+ <REALM> = {
+ ...
+ }
+ }
+.Ed
+.It Li [get-tgt]
+.Bl -tag -width "xxx" -offset indent
+.It Li no_addresses = Va BOOL
+If set to
+.Va true
+then the
+.Va /get-tgt
+end-point of the
+.Xr bx509d 8
+service will issue address-less TGTs.
+If set to
+.Va false
+then the
+.Va /get-tgt
+end-point of the
+.Xr bx509d 8
+service will include the client's IP address in the TGT it issues
+it.
+Defaults to
+.Va true .
+.It Li allow_addresses = Va BOOL
+If set to
+.Va true
+then the
+.Va /get-tgt
+end-point of the
+.Xr bx509d 8
+service will add arbitrary addresses requested by clients to the
+TGTs it issues them.
+Defaults to
+.Va false .
+.El
+.Pp
+Certification authority related parameters are as for
+.Va bx509 .
+.It Li [kadmin]
+.Bl -tag -width "xxx" -offset indent
+.It Li password_lifetime = Va time
+If a principal already have its password set for expiration, this is
+the time it will be valid for after a change. A value of 0 will clear
+the password expiration after a successful password change.
+.It Li default_keys = Va keytypes...
+For each entry in
+.Va default_keys
+try to parse it as a sequence of
+.Va etype:salttype:salt
+syntax of this if something like:
+.Pp
+[(des|des3|etype):](pw-salt|afs3-salt)[:string]
+.Pp
+If
+.Ar etype
+is omitted it means everything, and if string is omitted it means the
+default salt string (for that principal and encryption type).
+Additional special values of keytypes are:
+.Bl -tag -width "xxx" -offset indent
+.It Li v5
+The Kerberos 5 salt
+.Va pw-salt
+.El
+.It Li default_key_rules = Va {
+.Bl -tag -width "xxx" -offset indent
+.It Va globing-rule Li = Va keytypes...
+a globbing rule to matching a principal, and when true, use the
+keytypes as specified the same format as [kadmin]default_keys .
+.El
+.It Li }
+.It Li prune-key-history = Va BOOL
+When adding keys to the key history, drop keys that are too old to match
+unexpired tickets (based on the principal's maximum ticket lifetime).
+If the KDC keystore is later compromised traffic protected with the
+discarded older keys may remain protected. This also keeps the HDB
+records for principals with key history from growing without bound.
+The default (backwards compatible) value is "false".
+.It Li use_v4_salt = Va BOOL
+When true, this is the same as
+.Pp
+.Va default_keys = Va des3:pw-salt Va v4
+.Pp
+and is only left for backwards compatibility.
+.It Li [password_quality]
+Check the Password quality assurance in the info documentation for
+more information.
+.Bl -tag -width "xxx" -offset indent
+.It Li check_library = Va library-name
+Library name that contains the password check_function
+.It Li check_function = Va function-name
+Function name for checking passwords in check_library
+.It Li policy_libraries = Va library1 ... libraryN
+List of libraries that can do password policy checks
+.It Li policies = Va policy1 ... policyN
+List of policy names to apply to the password. Builtin policies are
+among other minimum-length, character-class, external-check.
+.El
+.El
+.El
+.Sh TOKEN EXPANSION
+The values of some parameters are subject to percent token expansion.
+Expansions supported on all platforms:
+.Bl -tag -width "xxx" -offset indent
+.It %{LIBDIR}
+The install location of Heimdal libraries.
+.It %{BINDIR}
+The install location of Heimdal user programs.
+.It %{LIBEXEC}
+The install location of Heimdal services.
+.It %{SBINDIR}
+The install location of Heimdal admin programs.
+.It %{username}
+The current username.
+.It %{TEMP}
+A temporary directory.
+.It %{USERID}
+The current user's SID (Windows) or effective user ID (POSIX).
+.It %{uid}
+The current user's SID (Windows) or real user ID (POSIX). On POSIX it is best
+to use the
+.Va %{euid}
+token instead (see below).
+.It %{null}
+The empty string.
+.El
+.Pp
+Expansions supported on POSIX-like platforms:
+.Bl -tag -width "xxx" -offset indent
+.It %{euid}
+The current effective user ID.
+.It %{loginname}
+The username of the logged-in user for this terminal.
+.It %{LOCALSTATEDIR}
+The install location of Heimdal databases.
+.El
+.Pp
+On Windows, several additional tokens can also be expanded:
+.Bl -tag -width "xxx" -offset indent
+.It %{APPDATA}
+Roaming application data (for current user).
+.It %{COMMON_APPDATA}
+Application data (all users).
+.It %{LOCAL_APPDATA}
+Local application data (for current user).
+.It %{SYSTEM}
+Windows System folder.
+.It %{WINDOWS}
+Windows folder.
+.It %{USERCONFIG}
+Per user Heimdal configuration file path.
+.It %{COMMONCONFIG}
+Common Heimdal configuration file path.
+.El
+.Sh ENVIRONMENT
+.Ev KRB5_CONFIG
+points to the configuration file to read.
+.Sh FILES
+.Bl -tag -width "/etc/krb5.conf"
+.It Pa /etc/krb5.conf
+configuration file for Kerberos 5.
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+[libdefaults]
+ default_realm = FOO.SE
+ name_canon_rules = as-is:realm=FOO.SE
+ name_canon_rules = qualify:domain=foo.se:realm=FOO.SE
+ name_canon_rules = qualify:domain=bar.se:realm=FOO.SE
+ name_canon_rules = nss
+[domain_realm]
+ .foo.se = FOO.SE
+ .bar.se = FOO.SE
+[realms]
+ FOO.SE = {
+ kdc = kerberos.foo.se
+ default_domain = foo.se
+ }
+[logging]
+ kdc = FILE:/var/heimdal/kdc.log
+ kdc = SYSLOG:INFO
+ default = SYSLOG:INFO:USER
+[kadmin]
+ default_key_rules = {
+ */ppp@* = arcfour-hmac-md5:pw-salt
+ }
+.Ed
+.Sh DIAGNOSTICS
+Since
+.Nm
+is read and parsed by the krb5 library, there is not a lot of
+opportunities for programs to report parsing errors in any useful
+format.
+To help overcome this problem, there is a program
+.Nm verify_krb5_conf
+that reads
+.Nm
+and tries to emit useful diagnostics from parsing errors.
+Note that this program does not have any way of knowing what options
+are actually used and thus cannot warn about unknown or misspelled
+ones.
+.Sh SEE ALSO
+.Xr kinit 1 ,
+.Xr krb5_openlog 3 ,
+.Xr strftime 3 ,
+.Xr verify_krb5_conf 8
diff --git a/third_party/heimdal/lib/krb5/krb5.h b/third_party/heimdal/lib/krb5/krb5.h
new file mode 100644
index 0000000..c291f3d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5.h
@@ -0,0 +1,1073 @@
+/*
+ * Copyright (c) 1997 - 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.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_H__
+#define __KRB5_H__
+
+#include <time.h>
+#include <krb5-types.h>
+
+#include <asn1_err.h>
+#include <krb5_err.h>
+#include <heim_err.h>
+#include <k524_err.h>
+#include <k5e1_err.h>
+
+#include <krb5_asn1.h>
+typedef Krb5Int32 krb5int32;
+typedef Krb5UInt32 krb5uint32;
+
+/* name confusion with MIT */
+#ifndef KRB5KDC_ERR_KEY_EXP
+#define KRB5KDC_ERR_KEY_EXP KRB5KDC_ERR_KEY_EXPIRED
+#endif
+
+#ifdef _WIN32
+#define KRB5_CALLCONV __stdcall
+#define KRB5_LIB_CALL __stdcall
+#else
+#define KRB5_CALLCONV
+#define KRB5_LIB_CALL
+#endif
+
+/* simple constants */
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+typedef int krb5_boolean;
+
+typedef int32_t krb5_error_code;
+
+typedef int32_t krb5_kvno;
+
+typedef uint32_t krb5_flags;
+
+typedef void *krb5_pointer;
+typedef const void *krb5_const_pointer;
+
+struct krb5_crypto_data;
+typedef struct krb5_crypto_data *krb5_crypto;
+
+struct krb5_get_creds_opt_data;
+typedef struct krb5_get_creds_opt_data *krb5_get_creds_opt;
+
+struct krb5_digest_data;
+typedef struct krb5_digest_data *krb5_digest;
+struct krb5_ntlm_data;
+typedef struct krb5_ntlm_data *krb5_ntlm;
+
+struct krb5_pac_data;
+typedef struct krb5_pac_data *krb5_pac;
+typedef const struct krb5_pac_data *krb5_const_pac;
+
+typedef struct krb5_rd_req_in_ctx_data *krb5_rd_req_in_ctx;
+typedef struct krb5_rd_req_out_ctx_data *krb5_rd_req_out_ctx;
+
+typedef CKSUMTYPE krb5_cksumtype;
+
+typedef Checksum krb5_checksum;
+
+typedef ENCTYPE krb5_enctype;
+
+typedef struct krb5_get_init_creds_ctx *krb5_init_creds_context;
+
+typedef heim_octet_string krb5_data;
+
+/* PKINIT related forward declarations */
+struct ContentInfo;
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+
+/* krb5_enc_data is a mit compat structure */
+typedef struct krb5_enc_data {
+ krb5_enctype enctype;
+ krb5_kvno kvno;
+ krb5_data ciphertext;
+} krb5_enc_data;
+
+/* alternative names */
+#define ENCTYPE_NULL KRB5_ENCTYPE_NULL
+#define ENCTYPE_DES_CBC_CRC KRB5_ENCTYPE_DES_CBC_CRC
+#define ENCTYPE_DES_CBC_MD4 KRB5_ENCTYPE_DES_CBC_MD4
+#define ENCTYPE_DES_CBC_MD5 KRB5_ENCTYPE_DES_CBC_MD5
+#define ENCTYPE_DES3_CBC_MD5 KRB5_ENCTYPE_DES3_CBC_MD5
+#define ENCTYPE_OLD_DES3_CBC_SHA1 KRB5_ENCTYPE_OLD_DES3_CBC_SHA1
+#define ENCTYPE_SIGN_DSA_GENERATE KRB5_ENCTYPE_SIGN_DSA_GENERATE
+#define ENCTYPE_ENCRYPT_RSA_PRIV KRB5_ENCTYPE_ENCRYPT_RSA_PRIV
+#define ENCTYPE_ENCRYPT_RSA_PUB KRB5_ENCTYPE_ENCRYPT_RSA_PUB
+#define ENCTYPE_DES3_CBC_SHA1 KRB5_ENCTYPE_DES3_CBC_SHA1
+#define ENCTYPE_AES128_CTS_HMAC_SHA1_96 KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+#define ENCTYPE_AES256_CTS_HMAC_SHA1_96 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+#define ENCTYPE_ARCFOUR_HMAC KRB5_ENCTYPE_ARCFOUR_HMAC_MD5
+#define ENCTYPE_ARCFOUR_HMAC_MD5 KRB5_ENCTYPE_ARCFOUR_HMAC_MD5
+#define ENCTYPE_ARCFOUR_HMAC_MD5_56 KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56
+#define ENCTYPE_ENCTYPE_PK_CROSS KRB5_ENCTYPE_ENCTYPE_PK_CROSS
+#define ENCTYPE_DES_CBC_NONE KRB5_ENCTYPE_DES_CBC_NONE
+#define ENCTYPE_DES3_CBC_NONE KRB5_ENCTYPE_DES3_CBC_NONE
+#define ENCTYPE_DES_CFB64_NONE KRB5_ENCTYPE_DES_CFB64_NONE
+#define ENCTYPE_DES_PCBC_NONE KRB5_ENCTYPE_DES_PCBC_NONE
+#define ETYPE_NULL KRB5_ENCTYPE_NULL
+#define ETYPE_DES_CBC_CRC KRB5_ENCTYPE_DES_CBC_CRC
+#define ETYPE_DES_CBC_MD4 KRB5_ENCTYPE_DES_CBC_MD4
+#define ETYPE_DES_CBC_MD5 KRB5_ENCTYPE_DES_CBC_MD5
+#define ETYPE_DES3_CBC_MD5 KRB5_ENCTYPE_DES3_CBC_MD5
+#define ETYPE_OLD_DES3_CBC_SHA1 KRB5_ENCTYPE_OLD_DES3_CBC_SHA1
+#define ETYPE_SIGN_DSA_GENERATE KRB5_ENCTYPE_SIGN_DSA_GENERATE
+#define ETYPE_ENCRYPT_RSA_PRIV KRB5_ENCTYPE_ENCRYPT_RSA_PRIV
+#define ETYPE_ENCRYPT_RSA_PUB KRB5_ENCTYPE_ENCRYPT_RSA_PUB
+#define ETYPE_DES3_CBC_SHA1 KRB5_ENCTYPE_DES3_CBC_SHA1
+#define ETYPE_AES128_CTS_HMAC_SHA1_96 KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96
+#define ETYPE_AES256_CTS_HMAC_SHA1_96 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96
+#define ETYPE_AES128_CTS_HMAC_SHA256_128 KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128
+#define ETYPE_AES256_CTS_HMAC_SHA384_192 KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192
+#define ETYPE_ARCFOUR_HMAC_MD5 KRB5_ENCTYPE_ARCFOUR_HMAC_MD5
+#define ETYPE_ARCFOUR_HMAC_MD5_56 KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56
+#define ETYPE_ENCTYPE_PK_CROSS KRB5_ENCTYPE_ENCTYPE_PK_CROSS
+#define ETYPE_ARCFOUR_MD4 KRB5_ENCTYPE_ARCFOUR_MD4
+#define ETYPE_ARCFOUR_HMAC_OLD KRB5_ENCTYPE_ARCFOUR_HMAC_OLD
+#define ETYPE_ARCFOUR_HMAC_OLD_EXP KRB5_ENCTYPE_ARCFOUR_HMAC_OLD_EXP
+#define ETYPE_DES_CBC_NONE KRB5_ENCTYPE_DES_CBC_NONE
+#define ETYPE_DES3_CBC_NONE KRB5_ENCTYPE_DES3_CBC_NONE
+#define ETYPE_DES_CFB64_NONE KRB5_ENCTYPE_DES_CFB64_NONE
+#define ETYPE_DES_PCBC_NONE KRB5_ENCTYPE_DES_PCBC_NONE
+#define ETYPE_DIGEST_MD5_NONE KRB5_ENCTYPE_DIGEST_MD5_NONE
+#define ETYPE_CRAM_MD5_NONE KRB5_ENCTYPE_CRAM_MD5_NONE
+#define DOMAIN_X500_COMPRESS domain_X500_Compress
+
+/* PDU types */
+typedef enum krb5_pdu {
+ KRB5_PDU_ERROR = 0,
+ KRB5_PDU_TICKET = 1,
+ KRB5_PDU_AS_REQUEST = 2,
+ KRB5_PDU_AS_REPLY = 3,
+ KRB5_PDU_TGS_REQUEST = 4,
+ KRB5_PDU_TGS_REPLY = 5,
+ KRB5_PDU_AP_REQUEST = 6,
+ KRB5_PDU_AP_REPLY = 7,
+ KRB5_PDU_KRB_SAFE = 8,
+ KRB5_PDU_KRB_PRIV = 9,
+ KRB5_PDU_KRB_CRED = 10,
+ KRB5_PDU_NONE = 11 /* See krb5_get_permitted_enctypes() */
+} krb5_pdu;
+
+typedef PADATA_TYPE krb5_preauthtype;
+
+typedef enum krb5_key_usage {
+ KRB5_KU_PA_ENC_TIMESTAMP = 1,
+ /* AS-REQ PA-ENC-TIMESTAMP padata timestamp, encrypted with the
+ client key (section 5.4.1) */
+ KRB5_KU_TICKET = 2,
+ /* AS-REP Ticket and TGS-REP Ticket (includes tgs session key or
+ application session key), encrypted with the service key
+ (section 5.4.2) */
+ KRB5_KU_AS_REP_ENC_PART = 3,
+ /* AS-REP encrypted part (includes tgs session key or application
+ session key), encrypted with the client key (section 5.4.2) */
+ KRB5_KU_TGS_REQ_AUTH_DAT_SESSION = 4,
+ /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs
+ session key (section 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH_DAT_SUBKEY = 5,
+ /* TGS-REQ KDC-REQ-BODY AuthorizationData, encrypted with the tgs
+ authenticator subkey (section 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH_CKSUM = 6,
+ /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator cksum, keyed
+ with the tgs session key (sections 5.3.2, 5.4.1) */
+ KRB5_KU_TGS_REQ_AUTH = 7,
+ /* TGS-REQ PA-TGS-REQ padata AP-REQ Authenticator (includes tgs
+ authenticator subkey), encrypted with the tgs session key
+ (section 5.3.2) */
+ KRB5_KU_TGS_REP_ENC_PART_SESSION = 8,
+ /* TGS-REP encrypted part (includes application session key),
+ encrypted with the tgs session key (section 5.4.2) */
+ KRB5_KU_TGS_REP_ENC_PART_SUB_KEY = 9,
+ /* TGS-REP encrypted part (includes application session key),
+ encrypted with the tgs authenticator subkey (section 5.4.2) */
+ KRB5_KU_AP_REQ_AUTH_CKSUM = 10,
+ /* AP-REQ Authenticator cksum, keyed with the application session
+ key (section 5.3.2) */
+ KRB5_KU_AP_REQ_AUTH = 11,
+ /* AP-REQ Authenticator (includes application authenticator
+ subkey), encrypted with the application session key (section
+ 5.3.2) */
+ KRB5_KU_AP_REQ_ENC_PART = 12,
+ /* AP-REP encrypted part (includes application session subkey),
+ encrypted with the application session key (section 5.5.2) */
+ KRB5_KU_KRB_PRIV = 13,
+ /* KRB-PRIV encrypted part, encrypted with a key chosen by the
+ application (section 5.7.1) */
+ KRB5_KU_KRB_CRED = 14,
+ /* KRB-CRED encrypted part, encrypted with a key chosen by the
+ application (section 5.8.1) */
+ KRB5_KU_KRB_SAFE_CKSUM = 15,
+ /* KRB-SAFE cksum, keyed with a key chosen by the application
+ (section 5.6.1) */
+ KRB5_KU_OTHER_ENCRYPTED = 16,
+ /* Data which is defined in some specification outside of
+ Kerberos to be encrypted using an RFC1510 encryption type. */
+ KRB5_KU_OTHER_CKSUM = 17,
+ /* Data which is defined in some specification outside of
+ Kerberos to be checksummed using an RFC1510 checksum type. */
+ KRB5_KU_KRB_ERROR = 18,
+ /* Krb-error checksum */
+ KRB5_KU_AD_KDC_ISSUED = 19,
+ /* AD-KDCIssued checksum */
+ KRB5_KU_MANDATORY_TICKET_EXTENSION = 20,
+ /* Checksum for Mandatory Ticket Extensions */
+ KRB5_KU_AUTH_DATA_TICKET_EXTENSION = 21,
+ /* Checksum in Authorization Data in Ticket Extensions */
+ KRB5_KU_USAGE_SEAL = 22,
+ /* seal in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_SIGN = 23,
+ /* sign in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_SEQ = 24,
+ /* SEQ in GSSAPI krb5 mechanism */
+ KRB5_KU_USAGE_ACCEPTOR_SEAL = 22,
+ /* acceptor sign in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_ACCEPTOR_SIGN = 23,
+ /* acceptor seal in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_INITIATOR_SEAL = 24,
+ /* initiator sign in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_USAGE_INITIATOR_SIGN = 25,
+ /* initiator seal in GSSAPI CFX krb5 mechanism */
+ KRB5_KU_PA_SERVER_REFERRAL_DATA = 22,
+ /* encrypted server referral data */
+ KRB5_KU_SAM_CHECKSUM = 25,
+ /* Checksum for the SAM-CHECKSUM field */
+ KRB5_KU_SAM_ENC_TRACK_ID = 26,
+ /* Encryption of the SAM-TRACK-ID field */
+ KRB5_KU_PA_SERVER_REFERRAL = 26,
+ /* Keyusage for the server referral in a TGS req */
+ KRB5_KU_SAM_ENC_NONCE_SAD = 27,
+ /* Defined in [MS-SFU] */
+ KRB5_KU_PA_S4U_X509_USER_REQUEST = 26,
+ /* Defined in [MS-SFU] */
+ KRB5_KU_PA_S4U_X509_USER_REPLY = 27,
+ /* Encryption of the SAM-NONCE-OR-SAD field */
+ KRB5_KU_PA_PKINIT_KX = 44,
+ /* Encryption type of the kdc session contribution in pk-init */
+ KRB5_KU_AS_REQ = 56,
+ /* Checksum of over the AS-REQ send by the KDC in PA-REQ-ENC-PA-REP */
+ KRB5_KU_FAST_REQ_CHKSUM = 50,
+ /* FAST armor checksum */
+ KRB5_KU_FAST_ENC = 51,
+ /* FAST armor encryption */
+ KRB5_KU_FAST_REP = 52,
+ /* FAST armor reply */
+ KRB5_KU_FAST_FINISHED = 53,
+ /* FAST finished checksum */
+ KRB5_KU_ENC_CHALLENGE_CLIENT = 54,
+ /* fast challenge from client */
+ KRB5_KU_ENC_CHALLENGE_KDC = 55,
+ /* fast challenge from kdc */
+ KRB5_KU_AS_FRESHNESS = 60,
+ /* Freshness token from KDC */
+ KRB5_KU_DIGEST_ENCRYPT = -18,
+ /* Encryption key usage used in the digest encryption field */
+ KRB5_KU_DIGEST_OPAQUE = -19,
+ /* Checksum key usage used in the digest opaque field */
+ KRB5_KU_KRB5SIGNEDPATH = -21,
+ /* Checksum key usage on KRB5SignedPath */
+ KRB5_KU_CANONICALIZED_NAMES = -23,
+ /* Checksum key usage on PA-CANONICALIZED */
+ KRB5_KU_H5L_COOKIE = -25
+ /* encrypted foo */
+} krb5_key_usage;
+
+typedef krb5_key_usage krb5_keyusage;
+
+typedef enum krb5_salttype {
+ KRB5_PW_SALT = KRB5_PADATA_PW_SALT,
+ KRB5_AFS3_SALT = KRB5_PADATA_AFS3_SALT
+}krb5_salttype;
+
+typedef struct krb5_salt {
+ krb5_salttype salttype;
+ krb5_data saltvalue;
+} krb5_salt;
+
+typedef ETYPE_INFO krb5_preauthinfo;
+
+typedef struct {
+ krb5_preauthtype type;
+ krb5_preauthinfo info; /* list of preauthinfo for this type */
+} krb5_preauthdata_entry;
+
+typedef struct krb5_preauthdata {
+ unsigned len;
+ krb5_preauthdata_entry *val;
+}krb5_preauthdata;
+
+typedef enum krb5_address_type {
+ KRB5_ADDRESS_INET = 2,
+ KRB5_ADDRESS_NETBIOS = 20,
+ KRB5_ADDRESS_INET6 = 24,
+ KRB5_ADDRESS_ADDRPORT = 256,
+ KRB5_ADDRESS_IPPORT = 257
+} krb5_address_type;
+
+enum {
+ AP_OPTS_USE_SESSION_KEY = 1,
+ AP_OPTS_MUTUAL_REQUIRED = 2,
+ AP_OPTS_USE_SUBKEY = 4 /* library internal */
+};
+
+typedef HostAddress krb5_address;
+
+typedef HostAddresses krb5_addresses;
+
+typedef krb5_enctype krb5_keytype;
+
+enum krb5_keytype_old {
+ KEYTYPE_NULL = ETYPE_NULL,
+ KEYTYPE_DES = ETYPE_DES_CBC_CRC,
+ KEYTYPE_DES3 = ETYPE_OLD_DES3_CBC_SHA1,
+ KEYTYPE_AES128 = ETYPE_AES128_CTS_HMAC_SHA1_96,
+ KEYTYPE_AES256 = ETYPE_AES256_CTS_HMAC_SHA1_96,
+ KEYTYPE_ARCFOUR = ETYPE_ARCFOUR_HMAC_MD5,
+ KEYTYPE_ARCFOUR_56 = ETYPE_ARCFOUR_HMAC_MD5_56
+};
+
+typedef EncryptionKey krb5_keyblock;
+
+typedef AP_REQ krb5_ap_req;
+
+struct krb5_cc_ops;
+
+#define KRB5_DEFAULT_CCFILE_ROOT "%{TEMP}/krb5cc_"
+
+#define KRB5_DEFAULT_CCROOT "FILE:" KRB5_DEFAULT_CCFILE_ROOT
+
+#define KRB5_ACCEPT_NULL_ADDRESSES(C) \
+ krb5_config_get_bool_default((C), NULL, TRUE, \
+ "libdefaults", "accept_null_addresses", \
+ NULL)
+
+typedef void *krb5_cc_cursor;
+typedef struct krb5_cccol_cursor_data *krb5_cccol_cursor;
+
+typedef struct krb5_ccache_data {
+ const struct krb5_cc_ops *ops;
+ krb5_data data;
+ unsigned int cc_initialized:1; /* if 1: krb5_cc_initialize() called */
+ unsigned int cc_need_start_realm:1;
+ unsigned int cc_start_tgt_stored:1;
+ unsigned int cc_kx509_done:1;
+}krb5_ccache_data;
+
+typedef struct krb5_ccache_data *krb5_ccache;
+
+typedef struct krb5_context_data *krb5_context;
+
+typedef Realm krb5_realm;
+typedef const char *krb5_const_realm; /* stupid language */
+
+#define krb5_realm_length(r) strlen(r)
+#define krb5_realm_data(r) (r)
+
+typedef Principal krb5_principal_data;
+typedef struct Principal *krb5_principal;
+typedef const struct Principal *krb5_const_principal;
+typedef struct Principals *krb5_principals;
+
+typedef time_t krb5_deltat;
+typedef time_t krb5_timestamp;
+
+typedef struct krb5_times {
+ krb5_timestamp authtime;
+ krb5_timestamp starttime;
+ krb5_timestamp endtime;
+ krb5_timestamp renew_till;
+} krb5_times;
+
+typedef union {
+ TicketFlags b;
+ krb5_flags i;
+} krb5_ticket_flags;
+
+/* options for krb5_get_in_tkt() */
+#define KDC_OPT_FORWARDABLE (1 << 1)
+#define KDC_OPT_FORWARDED (1 << 2)
+#define KDC_OPT_PROXIABLE (1 << 3)
+#define KDC_OPT_PROXY (1 << 4)
+#define KDC_OPT_ALLOW_POSTDATE (1 << 5)
+#define KDC_OPT_POSTDATED (1 << 6)
+#define KDC_OPT_RENEWABLE (1 << 8)
+#define KDC_OPT_REQUEST_ANONYMOUS (1 << 14)
+#define KDC_OPT_DISABLE_TRANSITED_CHECK (1 << 26)
+#define KDC_OPT_RENEWABLE_OK (1 << 27)
+#define KDC_OPT_ENC_TKT_IN_SKEY (1 << 28)
+#define KDC_OPT_RENEW (1 << 30)
+#define KDC_OPT_VALIDATE (1 << 31)
+
+typedef union {
+ KDCOptions b;
+ krb5_flags i;
+} krb5_kdc_flags;
+
+/* flags for krb5_verify_ap_req */
+
+#define KRB5_VERIFY_AP_REQ_IGNORE_INVALID (1 << 0)
+#define KRB5_VERIFY_AP_REQ_IGNORE_ADDRS (1 << 1)
+
+#define KRB5_GC_CACHED (1U << 0)
+#define KRB5_GC_USER_USER (1U << 1)
+#define KRB5_GC_EXPIRED_OK (1U << 2)
+#define KRB5_GC_NO_STORE (1U << 3)
+#define KRB5_GC_FORWARDABLE (1U << 4)
+#define KRB5_GC_NO_TRANSIT_CHECK (1U << 5)
+#define KRB5_GC_CONSTRAINED_DELEGATION (1U << 6)
+#define KRB5_GC_CANONICALIZE (1U << 7)
+#define KRB5_GC_ANONYMOUS (1U << 8)
+
+/* constants for compare_creds (and cc_retrieve_cred) */
+#define KRB5_TC_DONT_MATCH_REALM (1U << 31)
+#define KRB5_TC_MATCH_KEYTYPE (1U << 30)
+#define KRB5_TC_MATCH_KTYPE KRB5_TC_MATCH_KEYTYPE /* MIT name */
+#define KRB5_TC_MATCH_SRV_NAMEONLY (1 << 29)
+#define KRB5_TC_MATCH_FLAGS_EXACT (1 << 28)
+#define KRB5_TC_MATCH_FLAGS (1 << 27)
+#define KRB5_TC_MATCH_TIMES_EXACT (1 << 26)
+#define KRB5_TC_MATCH_TIMES (1 << 25)
+#define KRB5_TC_MATCH_AUTHDATA (1 << 24)
+#define KRB5_TC_MATCH_2ND_TKT (1 << 23)
+#define KRB5_TC_MATCH_IS_SKEY (1 << 22)
+
+/* constants for get_flags and set_flags */
+#define KRB5_TC_OPENCLOSE 0x00000001
+#define KRB5_TC_NOTICKET 0x00000002
+
+typedef AuthorizationData krb5_authdata;
+
+typedef KRB_ERROR krb5_error;
+
+typedef struct krb5_creds {
+ krb5_principal client;
+ krb5_principal server;
+ krb5_keyblock session;
+ krb5_times times;
+ krb5_data ticket;
+ krb5_data second_ticket;
+ krb5_authdata authdata;
+ krb5_addresses addresses;
+ krb5_ticket_flags flags;
+} krb5_creds;
+
+typedef struct krb5_cc_cache_cursor_data *krb5_cc_cache_cursor;
+
+#define KRB5_CC_OPS_VERSION_0 0
+#define KRB5_CC_OPS_VERSION_1 1
+#define KRB5_CC_OPS_VERSION_2 2
+#define KRB5_CC_OPS_VERSION_3 3
+#define KRB5_CC_OPS_VERSION_5 5
+
+/* Only extend the structure. Do not change signatures. */
+typedef struct krb5_cc_ops {
+ /* Version 0 */
+ int version;
+ const char *prefix;
+ const char* (KRB5_CALLCONV * get_name)(krb5_context, krb5_ccache);
+ krb5_error_code (KRB5_CALLCONV * resolve)(krb5_context, krb5_ccache *, const char *);
+ krb5_error_code (KRB5_CALLCONV * gen_new)(krb5_context, krb5_ccache *);
+ krb5_error_code (KRB5_CALLCONV * init)(krb5_context, krb5_ccache, krb5_principal);
+ krb5_error_code (KRB5_CALLCONV * destroy)(krb5_context, krb5_ccache);
+ krb5_error_code (KRB5_CALLCONV * close)(krb5_context, krb5_ccache);
+ krb5_error_code (KRB5_CALLCONV * store)(krb5_context, krb5_ccache, krb5_creds*);
+ krb5_error_code (KRB5_CALLCONV * retrieve)(krb5_context, krb5_ccache,
+ krb5_flags, const krb5_creds*, krb5_creds *);
+ krb5_error_code (KRB5_CALLCONV * get_princ)(krb5_context, krb5_ccache, krb5_principal*);
+ krb5_error_code (KRB5_CALLCONV * get_first)(krb5_context, krb5_ccache, krb5_cc_cursor *);
+ krb5_error_code (KRB5_CALLCONV * get_next)(krb5_context, krb5_ccache,
+ krb5_cc_cursor*, krb5_creds*);
+ krb5_error_code (KRB5_CALLCONV * end_get)(krb5_context, krb5_ccache, krb5_cc_cursor*);
+ krb5_error_code (KRB5_CALLCONV * remove_cred)(krb5_context, krb5_ccache,
+ krb5_flags, krb5_creds*);
+ krb5_error_code (KRB5_CALLCONV * set_flags)(krb5_context, krb5_ccache, krb5_flags);
+ int (KRB5_CALLCONV * get_version)(krb5_context, krb5_ccache);
+ krb5_error_code (KRB5_CALLCONV * get_cache_first)(krb5_context, krb5_cc_cursor *);
+ krb5_error_code (KRB5_CALLCONV * get_cache_next)(krb5_context, krb5_cc_cursor,
+ krb5_ccache *);
+ krb5_error_code (KRB5_CALLCONV * end_cache_get)(krb5_context, krb5_cc_cursor);
+ krb5_error_code (KRB5_CALLCONV * move)(krb5_context, krb5_ccache, krb5_ccache);
+ krb5_error_code (KRB5_CALLCONV * get_default_name)(krb5_context, char **);
+ /* Version 1 */
+ krb5_error_code (KRB5_CALLCONV * set_default)(krb5_context, krb5_ccache);
+ /* Version 2 */
+ krb5_error_code (KRB5_CALLCONV * lastchange)(krb5_context, krb5_ccache, krb5_timestamp *);
+ /* Version 3 */
+ krb5_error_code (KRB5_CALLCONV * set_kdc_offset)(krb5_context, krb5_ccache, krb5_deltat);
+ krb5_error_code (KRB5_CALLCONV * get_kdc_offset)(krb5_context, krb5_ccache, krb5_deltat *);
+ /* Version 5 */
+ krb5_error_code (KRB5_CALLCONV * get_name_2)(krb5_context, krb5_ccache,
+ const char **id, const char **res,
+ const char **sub);
+ krb5_error_code (KRB5_CALLCONV * resolve_2)(krb5_context, krb5_ccache *id, const char *res,
+ const char *sub);
+ /* Add new functions here for versions 6 and above */
+} krb5_cc_ops;
+
+/*
+ * krb5_config_binding is identical to struct heim_config_binding
+ * within heimbase.h. Its format is public and used by callers of
+ * krb5_config_get_list() and krb5_config_vget_list().
+ */
+enum krb5_config_type {
+ krb5_config_string,
+ krb5_config_list,
+};
+struct krb5_config_binding {
+ enum krb5_config_type type;
+ char *name;
+ struct krb5_config_binding *next;
+ union {
+ char *string;
+ struct krb5_config_binding *list;
+ void *generic;
+ } u;
+};
+
+typedef struct krb5_config_binding krb5_config_binding;
+typedef krb5_config_binding krb5_config_section;
+
+typedef struct krb5_ticket {
+ EncTicketPart ticket;
+ krb5_principal client;
+ krb5_principal server;
+} krb5_ticket;
+
+typedef Authenticator krb5_authenticator_data;
+
+typedef krb5_authenticator_data *krb5_authenticator;
+
+struct krb5_rcache_data;
+typedef struct krb5_rcache_data *krb5_rcache;
+typedef Authenticator krb5_donot_replay;
+
+#define KRB5_STORAGE_HOST_BYTEORDER 0x01 /* old */
+#define KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS 0x02
+#define KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE 0x04
+#define KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE 0x08
+#define KRB5_STORAGE_BYTEORDER_MASK 0x70
+#define KRB5_STORAGE_BYTEORDER_BE 0x00 /* default */
+#define KRB5_STORAGE_BYTEORDER_PACKED 0x10
+#define KRB5_STORAGE_BYTEORDER_LE 0x20
+#define KRB5_STORAGE_BYTEORDER_HOST 0x40
+#define KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER 0x80
+
+struct krb5_storage_data;
+typedef struct krb5_storage_data krb5_storage;
+
+typedef struct krb5_keytab_entry {
+ krb5_principal principal;
+ krb5_kvno vno;
+ krb5_keyblock keyblock;
+ uint32_t timestamp;
+ uint32_t flags;
+ krb5_principals aliases;
+} krb5_keytab_entry;
+
+typedef struct krb5_kt_cursor {
+ int fd;
+ krb5_storage *sp;
+ void *data;
+} krb5_kt_cursor;
+
+struct krb5_keytab_data;
+
+typedef struct krb5_keytab_data *krb5_keytab;
+
+#define KRB5_KT_PREFIX_MAX_LEN 30
+
+struct krb5_keytab_data {
+ const char *prefix;
+ krb5_error_code (KRB5_CALLCONV * resolve)(krb5_context, const char*, krb5_keytab);
+ krb5_error_code (KRB5_CALLCONV * get_name)(krb5_context, krb5_keytab, char*, size_t);
+ krb5_error_code (KRB5_CALLCONV * close)(krb5_context, krb5_keytab);
+ krb5_error_code (KRB5_CALLCONV * destroy)(krb5_context, krb5_keytab);
+ krb5_error_code (KRB5_CALLCONV * get)(krb5_context, krb5_keytab, krb5_const_principal,
+ krb5_kvno, krb5_enctype, krb5_keytab_entry*);
+ krb5_error_code (KRB5_CALLCONV * start_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*);
+ krb5_error_code (KRB5_CALLCONV * next_entry)(krb5_context, krb5_keytab,
+ krb5_keytab_entry*, krb5_kt_cursor*);
+ krb5_error_code (KRB5_CALLCONV * end_seq_get)(krb5_context, krb5_keytab, krb5_kt_cursor*);
+ krb5_error_code (KRB5_CALLCONV * add)(krb5_context, krb5_keytab, krb5_keytab_entry*);
+ krb5_error_code (KRB5_CALLCONV * remove)(krb5_context, krb5_keytab, krb5_keytab_entry*);
+ void *data;
+ int32_t version;
+};
+
+typedef struct krb5_keytab_data krb5_kt_ops;
+
+struct krb5_keytab_key_proc_args {
+ krb5_keytab keytab;
+ krb5_principal principal;
+};
+
+typedef struct krb5_keytab_key_proc_args krb5_keytab_key_proc_args;
+
+typedef struct krb5_replay_data {
+ krb5_timestamp timestamp;
+ int32_t usec;
+ uint32_t seq;
+} krb5_replay_data;
+
+/* flags for krb5_auth_con_setflags */
+enum {
+ KRB5_AUTH_CONTEXT_DO_TIME = 1,
+ KRB5_AUTH_CONTEXT_RET_TIME = 2,
+ KRB5_AUTH_CONTEXT_DO_SEQUENCE = 4,
+ KRB5_AUTH_CONTEXT_RET_SEQUENCE = 8,
+ KRB5_AUTH_CONTEXT_PERMIT_ALL = 16,
+ KRB5_AUTH_CONTEXT_USE_SUBKEY = 32,
+ KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED = 64
+};
+
+/* flags for krb5_auth_con_genaddrs */
+enum {
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR = 1,
+ KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR = 3,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR = 4,
+ KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR = 12
+};
+
+typedef struct krb5_auth_context_data {
+ unsigned int flags;
+
+ krb5_address *local_address;
+ krb5_address *remote_address;
+ int16_t local_port;
+ int16_t remote_port;
+ krb5_keyblock *keyblock;
+ krb5_keyblock *local_subkey;
+ krb5_keyblock *remote_subkey;
+
+ uint32_t local_seqnumber;
+ uint32_t remote_seqnumber;
+
+ krb5_authenticator authenticator;
+
+ krb5_pointer i_vector;
+
+ krb5_rcache rcache;
+
+ krb5_keytype keytype; /* ¿requested key type ? */
+ krb5_cksumtype cksumtype; /* ¡requested checksum type! */
+
+ AuthorizationData *auth_data;
+
+}krb5_auth_context_data, *krb5_auth_context;
+
+typedef struct {
+ KDC_REP kdc_rep;
+ EncKDCRepPart enc_part;
+ KRB_ERROR error;
+} krb5_kdc_rep;
+
+extern const char *const heimdal_version, *const heimdal_long_version;
+
+typedef void (KRB5_CALLCONV * krb5_log_log_func_t)(krb5_context,
+ const char*,
+ const char*,
+ void*);
+typedef void (KRB5_CALLCONV * krb5_log_close_func_t)(void*);
+
+typedef struct heim_log_facility_s krb5_log_facility;
+
+typedef EncAPRepPart krb5_ap_rep_enc_part;
+
+#define KRB5_RECVAUTH_IGNORE_VERSION 1
+
+#define KRB5_SENDAUTH_VERSION "KRB5_SENDAUTH_V1.0"
+
+#define KRB5_TGS_NAME_SIZE (6)
+#define KRB5_TGS_NAME ("krbtgt")
+#define KRB5_WELLKNOWN_NAME ("WELLKNOWN")
+#define KRB5_ANON_NAME ("ANONYMOUS")
+#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS")
+#define KRB5_FEDERATED_NAME ("FEDERATED")
+#define KRB5_FEDERATED_REALM ("WELLKNOWN:FEDERATED")
+#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L")
+#define KRB5_DIGEST_NAME ("digest")
+
+#define KRB5_PKU2U_REALM_NAME ("WELLKNOWN:PKU2U")
+#define KRB5_LKDC_REALM_NAME ("WELLKNOWN:COM.APPLE.LKDC")
+
+#define KRB5_GSS_HOSTBASED_SERVICE_NAME ("WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE")
+#define KRB5_GSS_REFERALS_REALM_NAME ("WELLKNOWN:ORG.H5L.REFERALS-REALM")
+
+typedef enum {
+ KRB5_PROMPT_TYPE_PASSWORD = 0x1,
+ KRB5_PROMPT_TYPE_NEW_PASSWORD = 0x2,
+ KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN = 0x3,
+ KRB5_PROMPT_TYPE_PREAUTH = 0x4,
+ KRB5_PROMPT_TYPE_INFO = 0x5
+} krb5_prompt_type;
+
+typedef struct _krb5_prompt {
+ const char *prompt;
+ int hidden;
+ krb5_data *reply;
+ krb5_prompt_type type;
+} krb5_prompt;
+
+typedef int (KRB5_CALLCONV * krb5_prompter_fct)(krb5_context /*context*/,
+ void * /*data*/,
+ const char * /*name*/,
+ const char * /*banner*/,
+ int /*num_prompts*/,
+ krb5_prompt /*prompts*/[]);
+typedef krb5_error_code (KRB5_CALLCONV * krb5_key_proc)(krb5_context /*context*/,
+ krb5_enctype /*type*/,
+ krb5_salt /*salt*/,
+ krb5_const_pointer /*keyseed*/,
+ krb5_keyblock ** /*key*/);
+typedef krb5_error_code (KRB5_CALLCONV * krb5_decrypt_proc)(krb5_context /*context*/,
+ krb5_keyblock * /*key*/,
+ krb5_key_usage /*usage*/,
+ krb5_const_pointer /*decrypt_arg*/,
+ krb5_kdc_rep * /*dec_rep*/);
+typedef krb5_error_code (KRB5_CALLCONV * krb5_s2k_proc)(krb5_context /*context*/,
+ krb5_enctype /*type*/,
+ krb5_const_pointer /*keyseed*/,
+ krb5_salt /*salt*/,
+ krb5_data * /*s2kparms*/,
+ krb5_keyblock ** /*key*/);
+
+struct _krb5_get_init_creds_opt_private;
+
+struct _krb5_get_init_creds_opt {
+ krb5_flags flags;
+ krb5_deltat tkt_life;
+ krb5_deltat renew_life;
+ int forwardable;
+ int proxiable;
+ int anonymous;
+ int change_password_prompt;
+ krb5_enctype *etype_list;
+ int etype_list_length;
+ krb5_addresses *address_list;
+ /* XXX the next three should not be used, as they may be
+ removed later */
+ krb5_preauthtype *preauth_list;
+ int preauth_list_length;
+ krb5_data *salt;
+ struct _krb5_get_init_creds_opt_private *opt_private;
+};
+
+typedef struct _krb5_get_init_creds_opt krb5_get_init_creds_opt;
+
+#define KRB5_GET_INIT_CREDS_OPT_TKT_LIFE 0x0001
+#define KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE 0x0002
+#define KRB5_GET_INIT_CREDS_OPT_FORWARDABLE 0x0004
+#define KRB5_GET_INIT_CREDS_OPT_PROXIABLE 0x0008
+#define KRB5_GET_INIT_CREDS_OPT_ETYPE_LIST 0x0010
+#define KRB5_GET_INIT_CREDS_OPT_ADDRESS_LIST 0x0020
+#define KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST 0x0040
+#define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080 /* no supported */
+#define KRB5_GET_INIT_CREDS_OPT_ANONYMOUS 0x0100
+#define KRB5_GET_INIT_CREDS_OPT_DISABLE_TRANSITED_CHECK 0x0200
+#define KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT 0x0400
+
+#define KRB5_FAST_REQUIRED 0x0001 /* fast required by action of caller */
+
+/* krb5_init_creds_step flags argument */
+#define KRB5_INIT_CREDS_STEP_FLAG_CONTINUE 0x0001
+
+typedef struct _krb5_verify_init_creds_opt {
+ krb5_flags flags;
+ int ap_req_nofail;
+} krb5_verify_init_creds_opt;
+
+#define KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL 0x0001
+
+typedef struct krb5_verify_opt {
+ unsigned int flags;
+ krb5_ccache ccache;
+ krb5_keytab keytab;
+ krb5_boolean secure;
+ const char *service;
+} krb5_verify_opt;
+
+#define KRB5_VERIFY_LREALMS 1
+#define KRB5_VERIFY_NO_ADDRESSES 2
+
+#define KRB5_KPASSWD_VERS_CHANGEPW 1
+#define KRB5_KPASSWD_VERS_SETPW 0xff80
+
+#define KRB5_KPASSWD_SUCCESS 0
+#define KRB5_KPASSWD_MALFORMED 1
+#define KRB5_KPASSWD_HARDERROR 2
+#define KRB5_KPASSWD_AUTHERROR 3
+#define KRB5_KPASSWD_SOFTERROR 4
+#define KRB5_KPASSWD_ACCESSDENIED 5
+#define KRB5_KPASSWD_BAD_VERSION 6
+#define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
+
+#define KPASSWD_PORT 464
+
+/* types for the new krbhst interface */
+struct krb5_krbhst_data;
+typedef struct krb5_krbhst_data *krb5_krbhst_handle;
+
+#define KRB5_KRBHST_KDC 1
+#define KRB5_KRBHST_ADMIN 2
+#define KRB5_KRBHST_CHANGEPW 3
+#define KRB5_KRBHST_KRB524 4
+#define KRB5_KRBHST_KCA 5
+#define KRB5_KRBHST_READONLY_ADMIN 6
+#define KRB5_KRBHST_TKTBRIDGEAP 7
+
+typedef struct krb5_krbhst_info {
+ enum { KRB5_KRBHST_UDP,
+ KRB5_KRBHST_TCP,
+ KRB5_KRBHST_HTTP } proto;
+ unsigned short port;
+ unsigned short def_port;
+ struct addrinfo *ai;
+ struct krb5_krbhst_info *next;
+ char hostname[1]; /* has to come last */
+} krb5_krbhst_info;
+
+/* flags for krb5_krbhst_init_flags (and krb5_send_to_kdc_flags) */
+enum {
+ KRB5_KRBHST_FLAGS_MASTER = 1,
+ KRB5_KRBHST_FLAGS_LARGE_MSG = 2
+};
+
+typedef krb5_error_code (*krb5_sendto_prexmit)(krb5_context, int, void *, int, krb5_data *);
+typedef krb5_error_code
+(KRB5_CALLCONV * krb5_send_to_kdc_func)(krb5_context, void *, krb5_krbhst_info *, time_t,
+ const krb5_data *, krb5_data *);
+
+/** flags for krb5_parse_name_flags */
+enum {
+ KRB5_PRINCIPAL_PARSE_NO_REALM = 1, /**< Require that there are no realm */
+ KRB5_PRINCIPAL_PARSE_REQUIRE_REALM = 2, /**< Require a realm present */
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE = 4, /**< Parse as a NT-ENTERPRISE name */
+ KRB5_PRINCIPAL_PARSE_IGNORE_REALM = 8, /**< Ignore realm if present */
+ KRB5_PRINCIPAL_PARSE_NO_DEF_REALM = 16 /**< Don't default the realm */
+};
+
+/** flags for krb5_unparse_name_flags */
+enum {
+ KRB5_PRINCIPAL_UNPARSE_SHORT = 1, /**< No realm if it is the default realm */
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM = 2, /**< No realm */
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY = 4 /**< No quoting */
+};
+
+typedef struct krb5_sendto_ctx_data *krb5_sendto_ctx;
+
+#define KRB5_SENDTO_DONE 0
+#define KRB5_SENDTO_RESET 1
+#define KRB5_SENDTO_CONTINUE 2
+#define KRB5_SENDTO_TIMEOUT 3
+#define KRB5_SENDTO_INITIAL 4
+#define KRB5_SENDTO_FILTER 5
+#define KRB5_SENDTO_FAILED 6
+#define KRB5_SENDTO_KRBHST 7
+
+typedef krb5_error_code
+(KRB5_CALLCONV * krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *,
+ const krb5_data *, int *);
+
+enum krb5_plugin_type {
+ PLUGIN_TYPE_DATA = 1,
+ PLUGIN_TYPE_FUNC /* no longer supported */
+};
+
+/*
+ * Since <krb5/common_plugin.h> is new with Heimdal 8, users looking to write
+ * portable plugins across Heimdal 7 and 8 need a conditional compilation
+ * predicate from a header file that does exist in both major releases. This
+ * is as good a place as any to define a plugin source-compatibility version
+ * number.
+ *
+ * When this macro is defined and is equal to 1, the Heimdal 8 plugin source
+ * API, and <krb5/common_plugin.h> header are available and should be used.
+ *
+ * In Heimdal 7, this macro is not defined, and <krb5/common_plugin.h> may not
+ * be available.
+ */
+#define KRB5_PLUGIN_COMMON_SPI_VERSION 1
+
+#define KRB5_PLUGIN_INVOKE_ALL 1
+
+typedef uintptr_t
+(KRB5_LIB_CALL *krb5_get_instance_func_t)(const char *);
+
+struct credentials; /* this is to keep the compiler happy */
+struct getargs;
+struct sockaddr;
+
+/**
+ * Semi private, not stable yet
+ */
+
+typedef struct krb5_crypto_iov {
+ unsigned int flags;
+ /* ignored */
+#define KRB5_CRYPTO_TYPE_EMPTY 0
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_HEADER) */
+#define KRB5_CRYPTO_TYPE_HEADER 1
+ /* IN and OUT */
+#define KRB5_CRYPTO_TYPE_DATA 2
+ /* IN */
+#define KRB5_CRYPTO_TYPE_SIGN_ONLY 3
+ /* (only for encryption) OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */
+#define KRB5_CRYPTO_TYPE_PADDING 4
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_TRAILER) */
+#define KRB5_CRYPTO_TYPE_TRAILER 5
+ /* OUT krb5_crypto_length(KRB5_CRYPTO_TYPE_CHECKSUM) */
+#define KRB5_CRYPTO_TYPE_CHECKSUM 6
+ krb5_data data;
+} krb5_crypto_iov;
+
+
+/* Glue for MIT */
+
+typedef struct {
+ int32_t lr_type;
+ krb5_timestamp value;
+} krb5_last_req_entry;
+
+typedef krb5_error_code
+(KRB5_CALLCONV * krb5_gic_process_last_req)(krb5_context, krb5_last_req_entry **, void *);
+
+typedef struct {
+ krb5_enctype ks_enctype;
+ krb5int32 ks_salttype;
+}krb5_key_salt_tuple;
+
+/*
+ * Name canonicalization rule options
+ */
+
+typedef enum krb5_name_canon_rule_options {
+ KRB5_NCRO_GC_ONLY = 1 << 0,
+ KRB5_NCRO_USE_REFERRALS = 1 << 1,
+ KRB5_NCRO_NO_REFERRALS = 1 << 2,
+ KRB5_NCRO_USE_FAST = 1 << 3,
+ KRB5_NCRO_USE_DNSSEC = 1 << 4,
+ KRB5_NCRO_LOOKUP_REALM = 1 << 5
+} krb5_name_canon_rule_options;
+
+typedef struct krb5_name_canon_rule_data *krb5_name_canon_rule;
+typedef const struct krb5_name_canon_rule_data *krb5_const_name_canon_rule;
+typedef struct krb5_name_canon_iterator_data *krb5_name_canon_iterator;
+
+/*
+ * krb5_get_init_creds_opt_set_pkinit flags
+ */
+
+#define KRB5_GIC_OPT_PKINIT_USE_ENCKEY 2 /* use RSA, not DH */
+#define KRB5_GIC_OPT_PKINIT_ANONYMOUS 4 /* anonymous PKINIT */
+#define KRB5_GIC_OPT_PKINIT_BTMM 8 /* reserved by Apple */
+#define KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR 16 /* do not authenticate KDC */
+
+/*
+ * _krb5_principal_is_anonymous() flags
+ */
+#define KRB5_ANON_MATCH_AUTHENTICATED 1 /* authenticated with anon flag */
+#define KRB5_ANON_MATCH_UNAUTHENTICATED 2 /* anonymous PKINIT */
+#define KRB5_ANON_IGNORE_NAME_TYPE 4 /* don't check the name type */
+#define KRB5_ANON_MATCH_ANY ( KRB5_ANON_MATCH_AUTHENTICATED | \
+ KRB5_ANON_MATCH_UNAUTHENTICATED )
+#define KRB5_ANON_MATCH_ANY_NONT ( KRB5_ANON_MATCH_ANY | \
+ KRB5_ANON_IGNORE_NAME_TYPE )
+
+/*
+ *
+ */
+
+struct hx509_certs_data;
+typedef struct krb5_kx509_req_ctx_data *krb5_kx509_req_ctx;
+
+#include <krb5-protos.h>
+
+/* variables */
+
+extern KRB5_LIB_VARIABLE const char *const krb5_config_file;
+extern KRB5_LIB_VARIABLE const char *const krb5_defkeyname;
+
+
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_akcm_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_krcc_ops;
+
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_fkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_wrfkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_javakt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_mkt_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_akf_ops;
+extern KRB5_LIB_VARIABLE const krb5_kt_ops krb5_any_ops;
+
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_api;
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_file;
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_memory;
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_kcm;
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_scc;
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_dcc;
+extern KRB5_LIB_VARIABLE const char *const krb5_cc_type_keyring;
+
+/* clang analyzer workarounds */
+
+#ifdef __clang_analyzer__
+/*
+ * The clang analyzer (lint) can't know that krb5_enomem() always returns
+ * non-zero, so code like:
+ *
+ * if ((x = malloc(...)) == NULL)
+ * ret = krb5_enomem(context)
+ * if (ret == 0)
+ * *x = ...;
+ *
+ * causes false positives.
+ *
+ * The fix is to make krb5_enomem() a macro that always evaluates to ENOMEM.
+ */
+#define krb5_enomem(c) (krb5_enomem(c), ENOMEM)
+#endif
+
+#endif /* __KRB5_H__ */
+
diff --git a/third_party/heimdal/lib/krb5/krb5.moduli b/third_party/heimdal/lib/krb5/krb5.moduli
new file mode 100644
index 0000000..a27bae5
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5.moduli
@@ -0,0 +1,3 @@
+# $Id$
+# comment security-bits-decimal secure-prime(p)-hex generator(g)-hex (q)-hex
+rfc3526-MODP-group14 1760 FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF 02 7FFFFFFFFFFFFFFFE487ED5110B4611A62633145C06E0E68948127044533E63A0105DF531D89CD9128A5043CC71A026EF7CA8CD9E69D218D98158536F92F8A1BA7F09AB6B6A8E122F242DABB312F3F637A262174D31BF6B585FFAE5B7A035BF6F71C35FDAD44CFD2D74F9208BE258FF324943328F6722D9EE1003E5C50B1DF82CC6D241B0E2AE9CD348B1FD47E9267AFC1B2AE91EE51D6CB0E3179AB1042A95DCF6A9483B84B4B36B3861AA7255E4C0278BA3604650C10BE19482F23171B671DF1CF3B960C074301CD93C1D17603D147DAE2AEF837A62964EF15E5FB4AAC0B8C1CCAA4BE754AB5728AE9130C4C7D02880AB9472D455655347FFFFFFFFFFFFFFF
diff --git a/third_party/heimdal/lib/krb5/krb524_convert_creds_kdc.3 b/third_party/heimdal/lib/krb5/krb524_convert_creds_kdc.3
new file mode 100644
index 0000000..511eabd
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb524_convert_creds_kdc.3
@@ -0,0 +1,86 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd March 20, 2004
+.Dt KRB524_CONVERT_CREDS_KDC 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb524_convert_creds_kdc ,
+.Nm krb524_convert_creds_kdc_ccache
+.Nd converts Kerberos 5 credentials to Kerberos 4 credentials
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb524_convert_creds_kdc
+.Fa "krb5_context context"
+.Fa "krb5_creds *in_cred"
+.Fa "struct credentials *v4creds"
+.Fc
+.Ft krb5_error_code
+.Fo krb524_convert_creds_kdc_ccache
+.Fa "krb5_context context"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_creds *in_cred"
+.Fa "struct credentials *v4creds"
+.Fc
+.Sh DESCRIPTION
+Convert the Kerberos 5 credential to Kerberos 4 credential.
+This is done by sending them to the 524 service in the KDC.
+.Pp
+.Fn krb524_convert_creds_kdc
+converts the Kerberos 5 credential in
+.Fa in_cred
+to Kerberos 4 credential that is stored in
+.Fa credentials .
+.Pp
+.Fn krb524_convert_creds_kdc_ccache
+is different from
+.Fn krb524_convert_creds_kdc
+in that way that if
+.Fa in_cred
+doesn't contain a DES session key, then a new one is fetched from the
+KDC and stored in the cred cache
+.Fa ccache ,
+and then the KDC is queried to convert the credential.
+.Pp
+This interfaces are used to make the migration to Kerberos 5 from
+Kerberos 4 easier.
+There are few services that still need Kerberos 4, and this is mainly
+for compatibility for those services.
+Some services, like AFS, really have Kerberos 5 supports, but still
+uses the 524 interface to make the migration easier.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_425_conv_principal.3 b/third_party/heimdal/lib/krb5/krb5_425_conv_principal.3
new file mode 100644
index 0000000..49028f4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_425_conv_principal.3
@@ -0,0 +1,224 @@
+.\" 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.
+.\"
+.\" $Id$
+.\"
+.Dd September 3, 2003
+.Dt KRB5_425_CONV_PRINCIPAL 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_425_conv_principal ,
+.Nm krb5_425_conv_principal_ext ,
+.Nm krb5_524_conv_principal
+.Nd converts to and from version 4 principals
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fn krb5_425_conv_principal "krb5_context context" "const char *name" "const char *instance" "const char *realm" "krb5_principal *principal"
+.Ft krb5_error_code
+.Fn krb5_425_conv_principal_ext "krb5_context context" "const char *name" "const char *instance" "const char *realm" "krb5_boolean (*func)(krb5_context, krb5_principal)" "krb5_boolean resolve" "krb5_principal *principal"
+.Ft krb5_error_code
+.Fn krb5_524_conv_principal "krb5_context context" "const krb5_principal principal" "char *name" "char *instance" "char *realm"
+.Sh DESCRIPTION
+Converting between version 4 and version 5 principals can at best be
+described as a mess.
+.Pp
+A version 4 principal consists of a name, an instance, and a realm. A
+version 5 principal consists of one or more components, and a
+realm. In some cases also the first component/name will differ between
+version 4 and version 5. Furthermore the second component of a host
+principal will be the fully qualified domain name of the host in
+question, while the instance of a version 4 principal will only
+contain the first part (short hostname). Because of these problems
+the conversion between principals will have to be site customized.
+.Pp
+.Fn krb5_425_conv_principal_ext
+will try to convert a version 4 principal, given by
+.Fa name ,
+.Fa instance ,
+and
+.Fa realm ,
+to a version 5 principal. This can result in several possible
+principals, and if
+.Fa func
+is non-NULL, it will be called for each candidate principal.
+.Fa func
+should return true if the principal was
+.Dq good .
+To accomplish this,
+.Fn krb5_425_conv_principal_ext
+will look up the name in
+.Pa krb5.conf .
+It first looks in the
+.Li v4_name_convert/host
+subsection, which should contain a list of version 4 names whose
+instance should be treated as a hostname. This list can be specified
+for each realm (in the
+.Li realms
+section), or in the
+.Li libdefaults
+section. If the name is found the resulting name of the principal
+will be the value of this binding. The instance is then first looked
+up in
+.Li v4_instance_convert
+for the specified realm. If found the resulting value will be used as
+instance (this can be used for special cases), no further attempts
+will be made to find a conversion if this fails (with
+.Fa func ) .
+If the
+.Fa resolve
+parameter is true, the instance will be looked up with
+.Fn gethostbyname .
+This can be a time consuming, error prone, and unsafe operation. Next
+a list of hostnames will be created from the instance and the
+.Li v4_domains
+variable, which should contain a list of possible domains for the
+specific realm.
+.Pp
+On the other hand, if the name is not found in a
+.Li host
+section, it is looked up in a
+.Li v4_name_convert/plain
+binding. If found here the name will be converted, but the instance
+will be untouched.
+.Pp
+This list of default host-type conversions is compiled-in:
+.Bd -literal -offset indent
+v4_name_convert = {
+ host = {
+ ftp = ftp
+ hprop = hprop
+ imap = imap
+ pop = pop
+ rcmd = host
+ smtp = smtp
+ }
+}
+.Ed
+.Pp
+It will only be used if there isn't an entry for these names in the
+config file, so you can override these defaults.
+.Pp
+.Fn krb5_425_conv_principal
+will call
+.Fn krb5_425_conv_principal_ext
+with
+.Dv NULL
+as
+.Fa func ,
+and the value of
+.Li v4_instance_resolve
+(from the
+.Li libdefaults
+section) as
+.Fa resolve .
+.Pp
+.Fn krb5_524_conv_principal
+basically does the opposite of
+.Fn krb5_425_conv_principal ,
+it just doesn't have to look up any names, but will instead truncate
+instances found to belong to a host principal. The
+.Fa name ,
+.Fa instance ,
+and
+.Fa realm
+should be at least 40 characters long.
+.Sh EXAMPLES
+Since this is confusing an example is in place.
+.Pp
+Assume that we have the
+.Dq foo.com ,
+and
+.Dq bar.com
+domains that have shared a single version 4 realm, FOO.COM. The version 4
+.Pa krb.realms
+file looked like:
+.Bd -literal -offset indent
+foo.com FOO.COM
+\&.foo.com FOO.COM
+\&.bar.com FOO.COM
+.Ed
+.Pp
+A
+.Pa krb5.conf
+file that covers this case might look like:
+.Bd -literal -offset indent
+[libdefaults]
+ v4_instance_resolve = yes
+[realms]
+ FOO.COM = {
+ kdc = kerberos.foo.com
+ v4_instance_convert = {
+ foo = foo.com
+ }
+ v4_domains = foo.com
+ }
+.Ed
+.Pp
+With this setup and the following host table:
+.Bd -literal -offset indent
+foo.com
+a-host.foo.com
+b-host.bar.com
+.Ed
+the following conversions will be made:
+.Bd -literal -offset indent
+rcmd.a-host -\*(Gt host/a-host.foo.com
+ftp.b-host -\*(Gt ftp/b-host.bar.com
+pop.foo -\*(Gt pop/foo.com
+ftp.other -\*(Gt ftp/other.foo.com
+other.a-host -\*(Gt other/a-host
+.Ed
+.Pp
+The first three are what you expect. If you remove the
+.Dq v4_domains ,
+the fourth entry will result in an error (since the host
+.Dq other
+can't be found). Even if
+.Dq a-host
+is a valid host name, the last entry will not be converted, since the
+.Dq other
+name is not known to represent a host-type principal.
+If you turn off
+.Dq v4_instance_resolve
+the second example will result in
+.Dq ftp/b-host.foo.com
+(because of the default domain). And all of this is of course only
+valid if you have working name resolving.
+.Sh SEE ALSO
+.Xr krb5_build_principal 3 ,
+.Xr krb5_free_principal 3 ,
+.Xr krb5_parse_name 3 ,
+.Xr krb5_sname_to_principal 3 ,
+.Xr krb5_unparse_name 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_acl_match_file.3 b/third_party/heimdal/lib/krb5/krb5_acl_match_file.3
new file mode 100644
index 0000000..a1eb1e4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_acl_match_file.3
@@ -0,0 +1,111 @@
+.\" Copyright (c) 2004, 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$
+.\"
+.Dd May 12, 2006
+.Dt KRB5_ACL_MATCH_FILE 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_acl_match_file ,
+.Nm krb5_acl_match_string
+.Nd ACL matching functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.Ft krb5_error_code
+.Fo krb5_acl_match_file
+.Fa "krb5_context context"
+.Fa "const char *file"
+.Fa "const char *format"
+.Fa "..."
+.Fc
+.Ft krb5_error_code
+.Fo krb5_acl_match_string
+.Fa "krb5_context context"
+.Fa "const char *string"
+.Fa "const char *format"
+.Fa "..."
+.Fc
+.Sh DESCRIPTION
+.Nm krb5_acl_match_file
+matches ACL format against each line in a file.
+Lines starting with # are treated like comments and ignored.
+.Pp
+.Nm krb5_acl_match_string
+matches ACL format against a string.
+.Pp
+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 " " and "\et" as a delimiter; multiple
+" " and "\et" in a row are considered to be the same.
+.Pp
+.Bl -tag -width "fXX" -offset indent
+.It s
+Matches a string using
+.Xr strcmp 3
+(case sensitive).
+.It f
+Matches the string with
+.Xr fnmatch 3 .
+The
+.Fa flags
+argument (the last argument) passed to the fnmatch function is 0.
+.It r
+Returns a copy of the string in the char ** passed in; the copy must be
+freed with
+.Xr free 3 .
+There is no need to
+.Xr free 3
+the string on error: the function will clean up and set the pointer to
+.Dv NULL .
+.El
+.Pp
+All unknown format specifiers cause an error.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+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);
+.Ed
+.Sh SEE ALSO
+.Xr krb5 3
diff --git a/third_party/heimdal/lib/krb5/krb5_aname_to_localname.3 b/third_party/heimdal/lib/krb5/krb5_aname_to_localname.3
new file mode 100644
index 0000000..a50e935
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_aname_to_localname.3
@@ -0,0 +1,80 @@
+.\" Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd February 18, 2006
+.Dt KRB5_ANAME_TO_LOCALNAME 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_aname_to_localname
+.Nd converts a principal to a system local name
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_boolean
+.Fo krb5_aname_to_localname
+.Fa "krb5_context context"
+.Fa "krb5_const_principal name"
+.Fa "size_t lnsize"
+.Fa "char *lname"
+.Fc
+.Sh DESCRIPTION
+This function takes a principal
+.Fa name ,
+verifies that it is in the local realm (using
+.Fn krb5_get_default_realms )
+and then returns the local name of the principal.
+.Pp
+If
+.Fa name
+isn't in one of the local realms an error is returned.
+.Pp
+If the size
+.Fa ( lnsize )
+of the local name
+.Fa ( lname )
+is too small, an error is returned.
+.Pp
+.Fn krb5_aname_to_localname
+should only be use by an application that implements protocols that
+don't transport the login name and thus needs to convert a principal
+to a local name.
+.Pp
+Protocols should be designed so that they authenticate using
+Kerberos, send over the login name and then verify the principal
+that is authenticated is allowed to login and the login name.
+A way to check if a user is allowed to login is using the function
+.Fn krb5_kuserok .
+.Sh SEE ALSO
+.Xr krb5_get_default_realms 3 ,
+.Xr krb5_kuserok 3
diff --git a/third_party/heimdal/lib/krb5/krb5_appdefault.3 b/third_party/heimdal/lib/krb5/krb5_appdefault.3
new file mode 100644
index 0000000..780c2ff
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_appdefault.3
@@ -0,0 +1,88 @@
+.\" 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 July 25, 2000
+.Dt KRB5_APPDEFAULT 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_appdefault_boolean ,
+.Nm krb5_appdefault_string ,
+.Nm krb5_appdefault_time
+.Nd get application configuration value
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft void
+.Fn krb5_appdefault_boolean "krb5_context context" "const char *appname" "krb5_realm realm" "const char *option" "krb5_boolean def_val" "krb5_boolean *ret_val"
+.Ft void
+.Fn krb5_appdefault_string "krb5_context context" "const char *appname" "krb5_realm realm" "const char *option" "const char *def_val" "char **ret_val"
+.Ft void
+.Fn krb5_appdefault_time "krb5_context context" "const char *appname" "krb5_realm realm" "const char *option" "time_t def_val" "time_t *ret_val"
+.Sh DESCRIPTION
+These functions get application defaults from the
+.Dv appdefaults
+section of the
+.Xr krb5.conf 5
+configuration file. These defaults can be specified per application,
+and/or per realm.
+.Pp
+These values will be looked for in
+.Xr krb5.conf 5 ,
+in order of descending importance.
+.Bd -literal -offset indent
+[appdefaults]
+ appname = {
+ realm = {
+ option = value
+ }
+ }
+ appname = {
+ option = value
+ }
+ realm = {
+ option = value
+ }
+ option = value
+.Ed
+.Fa appname
+is the name of the application, and
+.Fa realm
+is the realm name. If the realm is omitted it will not be used for
+resolving values.
+.Fa def_val
+is the value to return if no value is found in
+.Xr krb5.conf 5 .
+.Sh SEE ALSO
+.Xr krb5_config 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_auth_context.3 b/third_party/heimdal/lib/krb5/krb5_auth_context.3
new file mode 100644
index 0000000..b627e48
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_auth_context.3
@@ -0,0 +1,395 @@
+.\" Copyright (c) 2001 - 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 17, 2005
+.Dt KRB5_AUTH_CONTEXT 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_auth_con_addflags ,
+.Nm krb5_auth_con_free ,
+.Nm krb5_auth_con_genaddrs ,
+.Nm krb5_auth_con_generatelocalsubkey ,
+.Nm krb5_auth_con_getaddrs ,
+.Nm krb5_auth_con_getauthenticator ,
+.Nm krb5_auth_con_getflags ,
+.Nm krb5_auth_con_getkey ,
+.Nm krb5_auth_con_getlocalsubkey ,
+.Nm krb5_auth_con_getrcache ,
+.Nm krb5_auth_con_getremotesubkey ,
+.Nm krb5_auth_con_getuserkey ,
+.Nm krb5_auth_con_init ,
+.Nm krb5_auth_con_initivector ,
+.Nm krb5_auth_con_removeflags ,
+.Nm krb5_auth_con_setaddrs ,
+.Nm krb5_auth_con_setaddrs_from_fd ,
+.Nm krb5_auth_con_setflags ,
+.Nm krb5_auth_con_setivector ,
+.Nm krb5_auth_con_setkey ,
+.Nm krb5_auth_con_setlocalsubkey ,
+.Nm krb5_auth_con_setrcache ,
+.Nm krb5_auth_con_setremotesubkey ,
+.Nm krb5_auth_con_setuserkey ,
+.Nm krb5_auth_context ,
+.Nm krb5_auth_getcksumtype ,
+.Nm krb5_auth_getkeytype ,
+.Nm krb5_auth_getlocalseqnumber ,
+.Nm krb5_auth_getremoteseqnumber ,
+.Nm krb5_auth_setcksumtype ,
+.Nm krb5_auth_setkeytype ,
+.Nm krb5_auth_setlocalseqnumber ,
+.Nm krb5_auth_setremoteseqnumber ,
+.Nm krb5_free_authenticator
+.Nd manage authentication on connection level
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_auth_con_init
+.Fa "krb5_context context"
+.Fa "krb5_auth_context *auth_context"
+.Fc
+.Ft void
+.Fo krb5_auth_con_free
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_setflags
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "int32_t flags"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_getflags
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "int32_t *flags"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_addflags
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "int32_t addflags"
+.Fa "int32_t *flags"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_removeflags
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "int32_t removelags"
+.Fa "int32_t *flags"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_setaddrs
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "krb5_address *local_addr"
+.Fa "krb5_address *remote_addr"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_getaddrs
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "krb5_address **local_addr"
+.Fa "krb5_address **remote_addr"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_genaddrs
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "int fd"
+.Fa "int flags"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_setaddrs_from_fd
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "void *p_fd"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_getkey
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "krb5_keyblock **keyblock"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_getlocalsubkey
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "krb5_keyblock **keyblock"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_getremotesubkey
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "krb5_keyblock **keyblock"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_generatelocalsubkey
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_initivector
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_auth_con_setivector
+.Fa "krb5_context context"
+.Fa "krb5_auth_context *auth_context"
+.Fa "krb5_pointer ivector"
+.Fc
+.Ft void
+.Fo krb5_free_authenticator
+.Fa "krb5_context context"
+.Fa "krb5_authenticator *authenticator"
+.Fc
+.Sh DESCRIPTION
+The
+.Nm krb5_auth_context
+structure holds all context related to an authenticated connection, in
+a similar way to
+.Nm krb5_context
+that holds the context for the thread or process.
+.Nm 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-type.
+.Pp
+.Fn krb5_auth_con_init
+allocates and initializes the
+.Nm krb5_auth_context
+structure. Default values can be changed with
+.Fn krb5_auth_con_setcksumtype
+and
+.Fn krb5_auth_con_setflags .
+The
+.Nm auth_context
+structure must be freed by
+.Fn krb5_auth_con_free .
+.Pp
+.Fn krb5_auth_con_getflags ,
+.Fn krb5_auth_con_setflags ,
+.Fn krb5_auth_con_addflags
+and
+.Fn krb5_auth_con_removeflags
+gets and modifies the flags for a
+.Nm krb5_auth_context
+structure. Possible flags to set are:
+.Bl -tag -width Ds
+.It Dv KRB5_AUTH_CONTEXT_DO_SEQUENCE
+Generate and check sequence-number on each packet.
+.It Dv KRB5_AUTH_CONTEXT_DO_TIME
+Check timestamp on incoming packets.
+.It Dv KRB5_AUTH_CONTEXT_RET_SEQUENCE , Dv KRB5_AUTH_CONTEXT_RET_TIME
+Return sequence numbers and time stamps in the outdata parameters.
+.It Dv KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED
+will force
+.Fn krb5_get_forwarded_creds
+and
+.Fn krb5_fwd_tgt_creds
+to create unencrypted )
+.Dv KRB5_ENCTYPE_NULL )
+credentials.
+This is for use with old MIT server and JAVA based servers as
+they can't handle encrypted
+.Dv KRB-CRED .
+Note that sending such
+.Dv KRB-CRED
+is clear exposes crypto keys and tickets and is insecure,
+make sure the packet is encrypted in the protocol.
+.Xr krb5_rd_cred 3 ,
+.Xr krb5_rd_priv 3 ,
+.Xr krb5_rd_safe 3 ,
+.Xr krb5_mk_priv 3
+and
+.Xr krb5_mk_safe 3 .
+Setting this flag requires that parameter to be passed to these
+functions.
+.Pp
+The flags
+.Dv KRB5_AUTH_CONTEXT_DO_TIME
+also modifies the behavior the function
+.Fn krb5_get_forwarded_creds
+by removing the timestamp in the forward credential message, this have
+backward compatibility problems since not all versions of the heimdal
+supports timeless credentional messages.
+Is very useful since it always the sender of the message to cache
+forward message and thus avoiding a round trip to the KDC for each
+time a credential is forwarded.
+The same functionality can be obtained by using address-less tickets.
+.\".It Dv KRB5_AUTH_CONTEXT_PERMIT_ALL
+.El
+.Pp
+.Fn krb5_auth_con_setaddrs ,
+.Fn krb5_auth_con_setaddrs_from_fd
+and
+.Fn krb5_auth_con_getaddrs
+gets and sets the addresses that are checked when a packet is received.
+It is mandatory to set an address for the remote
+host. If the local address is not set, it iss deduced from the underlaying
+operating system.
+.Fn krb5_auth_con_getaddrs
+will call
+.Fn krb5_free_address
+on any address that is passed in
+.Fa local_addr
+or
+.Fa remote_addr .
+.Fn krb5_auth_con_setaddr
+allows passing in a
+.Dv NULL
+pointer as
+.Fa local_addr
+and
+.Fa remote_addr ,
+in that case it will just not set that address.
+.Pp
+.Fn krb5_auth_con_setaddrs_from_fd
+fetches the addresses from a file descriptor.
+.Pp
+.Fn krb5_auth_con_genaddrs
+fetches the address information from the given file descriptor
+.Fa fd
+depending on the bitmap argument
+.Fa flags .
+.Pp
+Possible values on
+.Fa flags
+are:
+.Bl -tag -width Ds
+.It Va KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR
+fetches the local address from
+.Fa fd .
+.It Va KRB5_AUTH_CONTEXT_GENERATE_REMOTE_ADDR
+fetches the remote address from
+.Fa fd .
+.El
+.Pp
+.Fn krb5_auth_con_setkey ,
+.Fn krb5_auth_con_setuserkey
+and
+.Fn krb5_auth_con_getkey
+gets and sets the key used for this auth context. The keyblock returned by
+.Fn krb5_auth_con_getkey
+should be freed with
+.Fn krb5_free_keyblock .
+The keyblock send into
+.Fn krb5_auth_con_setkey
+is copied into the
+.Nm krb5_auth_context ,
+and thus no special handling is needed.
+.Dv NULL
+is not a valid keyblock to
+.Fn krb5_auth_con_setkey .
+.Pp
+.Fn krb5_auth_con_setuserkey
+is only useful when doing user to user authentication.
+.Fn krb5_auth_con_setkey
+is equivalent to
+.Fn krb5_auth_con_setuserkey .
+.Pp
+.Fn krb5_auth_con_getlocalsubkey ,
+.Fn krb5_auth_con_setlocalsubkey ,
+.Fn krb5_auth_con_getremotesubkey
+and
+.Fn krb5_auth_con_setremotesubkey
+gets and sets the keyblock for the local and remote subkey.
+The keyblock returned by
+.Fn krb5_auth_con_getlocalsubkey
+and
+.Fn krb5_auth_con_getremotesubkey
+must be freed with
+.Fn krb5_free_keyblock .
+.Pp
+.Fn krb5_auth_setcksumtype
+and
+.Fn krb5_auth_getcksumtype
+sets and gets the checksum type that should be used for this
+connection.
+.Pp
+.Fn krb5_auth_con_generatelocalsubkey
+generates a local subkey that have the same encryption type as
+.Fa key .
+.Pp
+.Fn krb5_auth_getremoteseqnumber
+.Fn krb5_auth_setremoteseqnumber ,
+.Fn krb5_auth_getlocalseqnumber
+and
+.Fn krb5_auth_setlocalseqnumber
+gets and sets the sequence-number for the local and remote
+sequence-number counter.
+.Pp
+.Fn krb5_auth_setkeytype
+and
+.Fn krb5_auth_getkeytype
+gets and gets the keytype of the keyblock in
+.Nm krb5_auth_context .
+.Pp
+.Fn krb5_auth_con_getauthenticator
+Retrieves the authenticator that was used during mutual
+authentication. The
+.Dv authenticator
+returned should be freed by calling
+.Fn krb5_free_authenticator .
+.Pp
+.Fn krb5_auth_con_getrcache
+and
+.Fn krb5_auth_con_setrcache
+gets and sets the replay-cache.
+.Pp
+.Fn krb5_auth_con_initivector
+allocates memory for and zeros the initial vector in the
+.Fa auth_context
+keyblock.
+.Pp
+.Fn krb5_auth_con_setivector
+sets the i_vector portion of
+.Fa auth_context
+to
+.Fa ivector .
+.Pp
+.Fn krb5_free_authenticator
+free the content of
+.Fa authenticator
+and
+.Fa authenticator
+itself.
+.Sh SEE ALSO
+.Xr krb5_context 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_c_make_checksum.3 b/third_party/heimdal/lib/krb5/krb5_c_make_checksum.3
new file mode 100644
index 0000000..66402d4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_c_make_checksum.3
@@ -0,0 +1,297 @@
+.\" Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd Nov 17, 2006
+.Dt KRB5_C_MAKE_CHECKSUM 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_c_block_size ,
+.Nm krb5_c_decrypt ,
+.Nm krb5_c_encrypt ,
+.Nm krb5_c_encrypt_length ,
+.Nm krb5_c_enctype_compare ,
+.Nm krb5_c_get_checksum ,
+.Nm krb5_c_is_coll_proof_cksum ,
+.Nm krb5_c_is_keyed_cksum ,
+.Nm krb5_c_keylength ,
+.Nm krb5_c_make_checksum ,
+.Nm krb5_c_make_random_key ,
+.Nm krb5_c_set_checksum ,
+.Nm krb5_c_valid_cksumtype ,
+.Nm krb5_c_valid_enctype ,
+.Nm krb5_c_verify_checksum ,
+.Nm krb5_c_checksum_length
+.Nd Kerberos 5 crypto API
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Ft krb5_error_code
+.Fo krb5_c_block_size
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "size_t *blocksize"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_decrypt
+.Fa "krb5_context context"
+.Fa "const krb5_keyblock key"
+.Fa "krb5_keyusage usage"
+.Fa "const krb5_data *ivec"
+.Fa "krb5_enc_data *input"
+.Fa "krb5_data *output"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_encrypt
+.Fa "krb5_context context"
+.Fa "const krb5_keyblock *key"
+.Fa "krb5_keyusage usage"
+.Fa "const krb5_data *ivec"
+.Fa "const krb5_data *input"
+.Fa "krb5_enc_data *output"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_encrypt_length
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "size_t inputlen"
+.Fa "size_t *length"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_enctype_compare
+.Fa "krb5_context context"
+.Fa "krb5_enctype e1"
+.Fa "krb5_enctype e2"
+.Fa "krb5_boolean *similar"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_make_random_key
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "krb5_keyblock *random_key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_make_checksum
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype cksumtype"
+.Fa "const krb5_keyblock *key"
+.Fa "krb5_keyusage usage"
+.Fa "const krb5_data *input"
+.Fa "krb5_checksum *cksum"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_verify_checksum
+.Fa "krb5_context context"
+.Fa "const krb5_keyblock *key"
+.Fa "krb5_keyusage usage"
+.Fa "const krb5_data *data"
+.Fa "const krb5_checksum *cksum"
+.Fa "krb5_boolean *valid"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_checksum_length
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype cksumtype"
+.Fa "size_t *length"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_get_checksum
+.Fa "krb5_context context"
+.Fa "const krb5_checksum *cksum"
+.Fa "krb5_cksumtype *type"
+.Fa "krb5_data **data"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_set_checksum
+.Fa "krb5_context context"
+.Fa "krb5_checksum *cksum"
+.Fa "krb5_cksumtype type"
+.Fa "const krb5_data *data"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_c_valid_enctype
+.Fa krb5_enctype etype"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_c_valid_cksumtype
+.Fa "krb5_cksumtype ctype"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_c_is_coll_proof_cksum
+.Fa "krb5_cksumtype ctype"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_c_is_keyed_cksum
+.Fa "krb5_cksumtype ctype"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_c_keylengths
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "size_t *inlength"
+.Fa "size_t *keylength"
+.Fc
+.Sh DESCRIPTION
+The functions starting with krb5_c are compat functions with MIT kerberos.
+.Pp
+The
+.Li krb5_enc_data
+structure holds and encrypted data.
+There are two public accessible members of
+.Li krb5_enc_data .
+.Li enctype
+that holds the encryption type of the data encrypted and
+.Li ciphertext
+that is a
+.Ft krb5_data
+that might contain the encrypted data.
+.Pp
+.Fn krb5_c_block_size
+returns the blocksize of the encryption type.
+.Pp
+.Fn krb5_c_decrypt
+decrypts
+.Fa input
+and store the data in
+.Fa output.
+If
+.Fa ivec
+is
+.Dv NULL
+the default initialization vector for that encryption type will be used.
+.Pp
+.Fn krb5_c_encrypt
+encrypts the plaintext in
+.Fa input
+and store the ciphertext in
+.Fa output .
+.Pp
+.Fn krb5_c_encrypt_length
+returns the length the encrypted data given the plaintext length.
+.Pp
+.Fn krb5_c_enctype_compare
+compares to encryption types and returns if they use compatible
+encryption key types.
+.Pp
+.Fn krb5_c_make_checksum
+creates a checksum
+.Fa cksum
+with the checksum type
+.Fa cksumtype
+of the data in
+.Fa data .
+.Fa key
+and
+.Fa usage
+are used if the checksum is a keyed checksum type.
+Returns 0 or an error code.
+.Pp
+.Fn krb5_c_verify_checksum
+verifies the checksum
+of
+.Fa data
+in
+.Fa cksum
+that was created with
+.Fa key
+using the key usage
+.Fa usage .
+.Fa verify
+is set to non-zero if the checksum verifies correctly and zero if not.
+Returns 0 or an error code.
+.Pp
+.Fn krb5_c_checksum_length
+returns the length of the checksum.
+.Pp
+.Fn krb5_c_set_checksum
+sets the
+.Li krb5_checksum
+structure given
+.Fa type
+and
+.Fa data .
+The content of
+.Fa cksum
+should be freeed with
+.Fn krb5_c_free_checksum_contents .
+.Pp
+.Fn krb5_c_get_checksum
+retrieves the components of the
+.Li krb5_checksum .
+structure.
+.Fa data
+should be free with
+.Fn krb5_free_data .
+If some either of
+.Fa data
+or
+.Fa checksum
+is not needed for the application,
+.Dv NULL
+can be passed in.
+.Pp
+.Fn krb5_c_valid_enctype
+returns true if
+.Fa etype
+is a valid encryption type.
+.Pp
+.Fn krb5_c_valid_cksumtype
+returns true if
+.Fa ctype
+is a valid checksum type.
+.Pp
+.Fn krb5_c_is_keyed_cksum
+return true if
+.Fa ctype
+is a keyed checksum type.
+.Pp
+.Fn krb5_c_is_coll_proof_cksum
+returns true if
+.Fa ctype
+is a collision proof checksum type.
+.Pp
+.Fn krb5_c_keylengths
+return the minimum length
+.Fa ( inlength )
+bytes needed to create a key and the
+length
+.Fa ( keylength )
+of the resulting key
+for the
+.Fa enctype .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_create_checksum 3 ,
+.Xr krb5_free_data 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_ccapi.h b/third_party/heimdal/lib/krb5/krb5_ccapi.h
new file mode 100644
index 0000000..ed7b848
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_ccapi.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef KRB5_CCAPI_H
+#define KRB5_CCAPI_H 1
+
+#include <krb5-types.h>
+
+#if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__))
+#pragma pack(push,2)
+#endif
+
+enum {
+ cc_credentials_v5 = 2
+};
+
+enum {
+ ccapi_version_3 = 3,
+ ccapi_version_4 = 4
+};
+
+enum {
+ ccNoError = 0,
+
+ ccIteratorEnd = 201,
+ ccErrBadParam,
+ ccErrNoMem,
+ ccErrInvalidContext,
+ ccErrInvalidCCache,
+
+ ccErrInvalidString, /* 206 */
+ ccErrInvalidCredentials,
+ ccErrInvalidCCacheIterator,
+ ccErrInvalidCredentialsIterator,
+ ccErrInvalidLock,
+
+ ccErrBadName, /* 211 */
+ ccErrBadCredentialsVersion,
+ ccErrBadAPIVersion,
+ ccErrContextLocked,
+ ccErrContextUnlocked,
+
+ ccErrCCacheLocked, /* 216 */
+ ccErrCCacheUnlocked,
+ ccErrBadLockType,
+ ccErrNeverDefault,
+ ccErrCredentialsNotFound,
+
+ ccErrCCacheNotFound, /* 221 */
+ ccErrContextNotFound,
+ ccErrServerUnavailable,
+ ccErrServerInsecure,
+ ccErrServerCantBecomeUID,
+
+ ccErrTimeOffsetNotSet /* 226 */
+};
+
+typedef int32_t cc_int32;
+typedef uint32_t cc_uint32;
+typedef struct cc_context_t *cc_context_t;
+typedef struct cc_ccache_t *cc_ccache_t;
+typedef struct cc_ccache_iterator_t *cc_ccache_iterator_t;
+typedef struct cc_credentials_v5_t cc_credentials_v5_t;
+typedef struct cc_credentials_t *cc_credentials_t;
+typedef struct cc_credentials_iterator_t *cc_credentials_iterator_t;
+typedef struct cc_string_t *cc_string_t;
+typedef cc_uint32 cc_time_t;
+
+typedef struct cc_data {
+ cc_uint32 type;
+ cc_uint32 length;
+ void *data;
+} cc_data;
+
+struct cc_credentials_v5_t {
+ char *client;
+ char *server;
+ cc_data keyblock;
+ cc_time_t authtime;
+ cc_time_t starttime;
+ cc_time_t endtime;
+ cc_time_t renew_till;
+ cc_uint32 is_skey;
+ cc_uint32 ticket_flags;
+#define KRB5_CCAPI_TKT_FLG_FORWARDABLE 0x40000000
+#define KRB5_CCAPI_TKT_FLG_FORWARDED 0x20000000
+#define KRB5_CCAPI_TKT_FLG_PROXIABLE 0x10000000
+#define KRB5_CCAPI_TKT_FLG_PROXY 0x08000000
+#define KRB5_CCAPI_TKT_FLG_MAY_POSTDATE 0x04000000
+#define KRB5_CCAPI_TKT_FLG_POSTDATED 0x02000000
+#define KRB5_CCAPI_TKT_FLG_INVALID 0x01000000
+#define KRB5_CCAPI_TKT_FLG_RENEWABLE 0x00800000
+#define KRB5_CCAPI_TKT_FLG_INITIAL 0x00400000
+#define KRB5_CCAPI_TKT_FLG_PRE_AUTH 0x00200000
+#define KRB5_CCAPI_TKT_FLG_HW_AUTH 0x00100000
+#define KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000
+#define KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE 0x00040000
+#define KRB5_CCAPI_TKT_FLG_ANONYMOUS 0x00020000
+ cc_data **addresses;
+ cc_data ticket;
+ cc_data second_ticket;
+ cc_data **authdata;
+};
+
+
+typedef struct cc_string_functions {
+ cc_int32 (*release)(cc_string_t);
+} cc_string_functions;
+
+struct cc_string_t {
+ const char *data;
+ const cc_string_functions *func;
+};
+
+typedef struct cc_credentials_union {
+ cc_int32 version;
+ union {
+ cc_credentials_v5_t* credentials_v5;
+ } credentials;
+} cc_credentials_union;
+
+struct cc_credentials_functions {
+ cc_int32 (*release)(cc_credentials_t);
+ cc_int32 (*compare)(cc_credentials_t, cc_credentials_t, cc_uint32*);
+};
+
+struct cc_credentials_t {
+ const cc_credentials_union* data;
+ const struct cc_credentials_functions* func;
+};
+
+struct cc_credentials_iterator_functions {
+ cc_int32 (*release)(cc_credentials_iterator_t);
+ cc_int32 (*next)(cc_credentials_iterator_t, cc_credentials_t*);
+};
+
+struct cc_credentials_iterator_t {
+ const struct cc_credentials_iterator_functions *func;
+};
+
+struct cc_ccache_iterator_functions {
+ cc_int32 (*release) (cc_ccache_iterator_t);
+ cc_int32 (*next)(cc_ccache_iterator_t, cc_ccache_t*);
+};
+
+struct cc_ccache_iterator_t {
+ const struct cc_ccache_iterator_functions* func;
+};
+
+typedef struct cc_ccache_functions {
+ cc_int32 (*release)(cc_ccache_t);
+ cc_int32 (*destroy)(cc_ccache_t);
+ cc_int32 (*set_default)(cc_ccache_t);
+ cc_int32 (*get_credentials_version)(cc_ccache_t, cc_uint32*);
+ cc_int32 (*get_name)(cc_ccache_t, cc_string_t*);
+ cc_int32 (*get_principal)(cc_ccache_t, cc_uint32, cc_string_t*);
+ cc_int32 (*set_principal)(cc_ccache_t, cc_uint32, const char*);
+ cc_int32 (*store_credentials)(cc_ccache_t, const cc_credentials_union*);
+ cc_int32 (*remove_credentials)(cc_ccache_t, cc_credentials_t);
+ cc_int32 (*new_credentials_iterator)(cc_ccache_t,
+ cc_credentials_iterator_t*);
+ cc_int32 (*move)(cc_ccache_t, cc_ccache_t);
+ cc_int32 (*lock)(cc_ccache_t, cc_uint32, cc_uint32);
+ cc_int32 (*unlock)(cc_ccache_t);
+ cc_int32 (*get_last_default_time)(cc_ccache_t, cc_time_t*);
+ cc_int32 (*get_change_time)(cc_ccache_t, cc_time_t*);
+ cc_int32 (*compare)(cc_ccache_t, cc_ccache_t, cc_uint32*);
+ cc_int32 (*get_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t *);
+ cc_int32 (*set_kdc_time_offset)(cc_ccache_t, cc_int32, cc_time_t);
+ cc_int32 (*clear_kdc_time_offset)(cc_ccache_t, cc_int32);
+} cc_ccache_functions;
+
+struct cc_ccache_t {
+ const cc_ccache_functions *func;
+};
+
+struct cc_context_functions {
+ cc_int32 (*release)(cc_context_t);
+ cc_int32 (*get_change_time)(cc_context_t, cc_time_t *);
+ cc_int32 (*get_default_ccache_name)(cc_context_t, cc_string_t*);
+ cc_int32 (*open_ccache)(cc_context_t, const char*, cc_ccache_t *);
+ cc_int32 (*open_default_ccache)(cc_context_t, cc_ccache_t*);
+ cc_int32 (*create_ccache)(cc_context_t,const char*, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*create_default_ccache)(cc_context_t, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*create_new_ccache)(cc_context_t, cc_uint32,
+ const char*, cc_ccache_t*);
+ cc_int32 (*new_ccache_iterator)(cc_context_t, cc_ccache_iterator_t*);
+ cc_int32 (*lock)(cc_context_t, cc_uint32, cc_uint32);
+ cc_int32 (*unlock)(cc_context_t);
+ cc_int32 (*compare)(cc_context_t, cc_context_t, cc_uint32*);
+};
+
+struct cc_context_t {
+ const struct cc_context_functions* func;
+};
+
+typedef cc_int32
+(*cc_initialize_func)(cc_context_t*, cc_int32, cc_int32 *, char const **);
+
+#if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__))
+#pragma pack(pop)
+#endif
+
+
+#endif /* KRB5_CCAPI_H */
diff --git a/third_party/heimdal/lib/krb5/krb5_check_transited.3 b/third_party/heimdal/lib/krb5/krb5_check_transited.3
new file mode 100644
index 0000000..4d4dae3
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_check_transited.3
@@ -0,0 +1,106 @@
+.\" Copyright (c) 2004, 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$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_CHECK_TRANSITED 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_check_transited ,
+.Nm krb5_check_transited_realms ,
+.Nm krb5_domain_x500_decode ,
+.Nm krb5_domain_x500_encode
+.Nd realm transit verification and encoding/decoding functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_check_transited
+.Fa "krb5_context context"
+.Fa "krb5_const_realm client_realm"
+.Fa "krb5_const_realm server_realm"
+.Fa "krb5_realm *realms"
+.Fa "int num_realms"
+.Fa "int *bad_realm"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_check_transited_realms
+.Fa "krb5_context context"
+.Fa "const char *const *realms"
+.Fa "int num_realms"
+.Fa "int *bad_realm"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_domain_x500_decode
+.Fa "krb5_context context"
+.Fa "krb5_data tr"
+.Fa "char ***realms"
+.Fa "int *num_realms"
+.Fa "const char *client_realm"
+.Fa "const char *server_realm"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_domain_x500_encode
+.Fa "char **realms"
+.Fa "int num_realms"
+.Fa "krb5_data *encoding"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_check_transited
+checks the path from
+.Fa client_realm
+to
+.Fa server_realm
+where
+.Fa realms
+and
+.Fa num_realms
+is the realms between them.
+If the function returns an error value,
+.Fa bad_realm
+will be set to the realm in the list causing the error.
+.Fn krb5_check_transited
+is used internally by the KDC and libkrb5 and should not be called by
+client applications.
+.Pp
+.Fn krb5_check_transited_realms
+is deprecated.
+.Pp
+.Fn krb5_domain_x500_encode
+and
+.Fn krb5_domain_x500_decode
+encodes and decodes the realm names in the X500 format that Kerberos
+uses to describe the transited realms in krbtgts.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_create_checksum.3 b/third_party/heimdal/lib/krb5/krb5_create_checksum.3
new file mode 100644
index 0000000..06d806e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_create_checksum.3
@@ -0,0 +1,226 @@
+.\" 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 August 12, 2005
+.Dt NAME 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_checksum ,
+.Nm krb5_checksum_disable ,
+.Nm krb5_checksum_is_collision_proof ,
+.Nm krb5_checksum_is_keyed ,
+.Nm krb5_checksumsize ,
+.Nm krb5_cksumtype_valid ,
+.Nm krb5_copy_checksum ,
+.Nm krb5_create_checksum ,
+.Nm krb5_crypto_get_checksum_type
+.Nm krb5_free_checksum ,
+.Nm krb5_free_checksum_contents ,
+.Nm krb5_hmac ,
+.Nm krb5_verify_checksum
+.Nd creates, handles and verifies checksums
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li "typedef Checksum krb5_checksum;"
+.Ft void
+.Fo krb5_checksum_disable
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype type"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_checksum_is_collision_proof
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype type"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_checksum_is_keyed
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype type"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_cksumtype_valid
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype ctype"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_checksumsize
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype type"
+.Fa "size_t *size"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_create_checksum
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "krb5_key_usage usage"
+.Fa "int type"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "Checksum *result"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_verify_checksum
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "krb5_key_usage usage"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "Checksum *cksum"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_crypto_get_checksum_type
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "krb5_cksumtype *type"
+.Fc
+.Ft void
+.Fo krb5_free_checksum
+.Fa "krb5_context context"
+.Fa "krb5_checksum *cksum"
+.Fc
+.Ft void
+.Fo krb5_free_checksum_contents
+.Fa "krb5_context context"
+.Fa "krb5_checksum *cksum"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_hmac
+.Fa "krb5_context context"
+.Fa "krb5_cksumtype cktype"
+.Fa "const void *data"
+.Fa "size_t len"
+.Fa "unsigned usage"
+.Fa "krb5_keyblock *key"
+.Fa "Checksum *result"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_copy_checksum
+.Fa "krb5_context context"
+.Fa "const krb5_checksum *old"
+.Fa "krb5_checksum **new"
+.Fc
+.Sh DESCRIPTION
+The
+.Li krb5_checksum
+structure holds a Kerberos checksum.
+There is no component inside
+.Li krb5_checksum
+that is directly referable.
+.Pp
+The functions are used to create and verify checksums.
+.Fn krb5_create_checksum
+creates a checksum of the specified data, and puts it in
+.Fa result .
+If
+.Fa crypto
+is
+.Dv NULL ,
+.Fa usage_or_type
+specifies the checksum type to use; it must not be keyed. Otherwise
+.Fa crypto
+is an encryption context created by
+.Fn krb5_crypto_init ,
+and
+.Fa usage_or_type
+specifies a key-usage.
+.Pp
+.Fn krb5_verify_checksum
+verifies the
+.Fa checksum
+against the provided data.
+.Pp
+.Fn krb5_checksum_is_collision_proof
+returns true is the specified checksum is collision proof (that it's
+very unlikely that two strings has the same hash value, and that it's
+hard to find two strings that has the same hash). Examples of
+collision proof checksums are MD5, and SHA1, while CRC32 is not.
+.Pp
+.Fn krb5_checksum_is_keyed
+returns true if the specified checksum type is keyed (that the hash
+value is a function of both the data, and a separate key). Examples of
+keyed hash algorithms are HMAC-SHA1-DES3, and RSA-MD5-DES. The
+.Dq plain
+hash functions MD5, and SHA1 are not keyed.
+.Pp
+.Fn krb5_crypto_get_checksum_type
+returns the checksum type that will be used when creating a checksum for the given
+.Fa crypto
+context.
+This function is useful in combination with
+.Fn krb5_checksumsize
+when you want to know the size a checksum will
+use when you create it.
+.Pp
+.Fn krb5_cksumtype_valid
+returns 0 or an error if the checksumtype is implemented and not
+currently disabled in this kerberos library.
+.Pp
+.Fn krb5_checksumsize
+returns the size of the outdata of checksum function.
+.Pp
+.Fn krb5_copy_checksum
+returns a copy of the checksum
+.Fn krb5_free_checksum
+should use used to free the
+.Fa new
+checksum.
+.Pp
+.Fn krb5_free_checksum
+free the checksum and the content of the checksum.
+.Pp
+.Fn krb5_free_checksum_contents
+frees the content of checksum in
+.Fa cksum .
+.Pp
+.Fn krb5_hmac
+calculates the HMAC over
+.Fa data
+(with length
+.Fa len )
+using the keyusage
+.Fa usage
+and keyblock
+.Fa key .
+Note that keyusage is not always used in checksums.
+.Pp
+.Nm krb5_checksum_disable
+globally disables the checksum type.
+.\" .Sh EXAMPLE
+.\" .Sh BUGS
+.Sh SEE ALSO
+.Xr krb5_crypto_init 3 ,
+.Xr krb5_c_encrypt 3 ,
+.Xr krb5_encrypt 3
diff --git a/third_party/heimdal/lib/krb5/krb5_creds.3 b/third_party/heimdal/lib/krb5/krb5_creds.3
new file mode 100644
index 0000000..b22550e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_creds.3
@@ -0,0 +1,119 @@
+.\" Copyright (c) 2004, 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$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_CREDS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_creds ,
+.Nm krb5_copy_creds ,
+.Nm krb5_copy_creds_contents ,
+.Nm krb5_free_creds ,
+.Nm krb5_free_cred_contents
+.Nd Kerberos 5 credential handling functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_copy_creds
+.Fa "krb5_context context"
+.Fa "const krb5_creds *incred"
+.Fa "krb5_creds **outcred"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_copy_creds_contents
+.Fa "krb5_context context"
+.Fa "const krb5_creds *incred"
+.Fa "krb5_creds *outcred"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_free_creds
+.Fa "krb5_context context"
+.Fa "krb5_creds *outcred"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_free_cred_contents
+.Fa "krb5_context context"
+.Fa "krb5_creds *cred"
+.Fc
+.Sh DESCRIPTION
+.Vt krb5_creds
+holds Kerberos credentials:
+.Bd -literal -offset
+typedef struct krb5_creds {
+ krb5_principal client;
+ krb5_principal server;
+ krb5_keyblock session;
+ krb5_times times;
+ krb5_data ticket;
+ krb5_data second_ticket;
+ krb5_authdata authdata;
+ krb5_addresses addresses;
+ krb5_ticket_flags flags;
+} krb5_creds;
+.Ed
+.Pp
+.Fn krb5_copy_creds
+makes a copy of
+.Fa incred
+to
+.Fa outcred .
+.Fa outcred
+should be freed with
+.Fn krb5_free_creds
+by the caller.
+.Pp
+.Fn krb5_copy_creds_contents
+makes a copy of the content of
+.Fa incred
+to
+.Fa outcreds .
+.Fa outcreds
+should be freed by the called with
+.Fn krb5_free_creds_contents .
+.Pp
+.Fn krb5_free_creds
+frees the content of the
+.Fa cred
+structure and the structure itself.
+.Pp
+.Fn krb5_free_cred_contents
+frees the content of the
+.Fa cred
+structure.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_compare_creds 3 ,
+.Xr krb5_get_init_creds 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_digest.3 b/third_party/heimdal/lib/krb5/krb5_digest.3
new file mode 100644
index 0000000..11d4db4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_digest.3
@@ -0,0 +1,260 @@
+.\" Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd February 18, 2007
+.Dt KRB5_DIGEST 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_digest ,
+.Nm krb5_digest_alloc ,
+.Nm krb5_digest_free ,
+.Nm krb5_digest_set_server_cb ,
+.Nm krb5_digest_set_type ,
+.Nm krb5_digest_set_hostname ,
+.Nm krb5_digest_get_server_nonce ,
+.Nm krb5_digest_set_server_nonce ,
+.Nm krb5_digest_get_opaque ,
+.Nm krb5_digest_set_opaque ,
+.Nm krb5_digest_get_identifier ,
+.Nm krb5_digest_set_identifier ,
+.Nm krb5_digest_init_request ,
+.Nm krb5_digest_set_client_nonce ,
+.Nm krb5_digest_set_digest ,
+.Nm krb5_digest_set_username ,
+.Nm krb5_digest_set_authid ,
+.Nm krb5_digest_set_authentication_user ,
+.Nm krb5_digest_set_realm ,
+.Nm krb5_digest_set_method ,
+.Nm krb5_digest_set_uri ,
+.Nm krb5_digest_set_nonceCount ,
+.Nm krb5_digest_set_qop ,
+.Nm krb5_digest_request ,
+.Nm krb5_digest_get_responseData ,
+.Nm krb5_digest_get_rsp ,
+.Nm krb5_digest_get_tickets ,
+.Nm krb5_digest_get_client_binding ,
+.Nm krb5_digest_get_a1_hash
+.Nd remote digest (HTTP-DIGEST, SASL, CHAP) support
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li "typedef struct krb5_digest *krb5_digest;"
+.Pp
+.Ft krb5_error_code
+.Fo krb5_digest_alloc
+.Fa "krb5_context context"
+.Fa "krb5_digest *digest"
+.Fc
+.Ft void
+.Fo krb5_digest_free
+.Fa "krb5_digest digest"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_type
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *type"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_server_cb
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *type"
+.Fa "const char *binding"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_hostname
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *hostname"
+.Fc
+.Ft "const char *"
+.Fo krb5_digest_get_server_nonce
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_server_nonce
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *nonce"
+.Fc
+.Ft "const char *"
+.Fo krb5_digest_get_opaque
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_opaque
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *opaque"
+.Fc
+.Ft "const char *"
+.Fo krb5_digest_get_identifier
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_identifier
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *id"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_init_request
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "krb5_realm realm"
+.Fa "krb5_ccache ccache"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_client_nonce
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *nonce"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_digest
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *dgst"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_username
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *username"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_authid
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *authid"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_authentication_user
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "krb5_principal authentication_user"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_realm
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *realm"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_method
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *method"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_uri
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *uri"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_nonceCount
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *nonce_count"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_set_qop
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "const char *qop"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_request
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "krb5_realm realm"
+.Fa "krb5_ccache ccache"
+.Fc
+.Ft "const char *"
+.Fo krb5_digest_get_responseData
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fc
+.Ft "const char *"
+.Fo krb5_digest_get_rsp
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_get_tickets
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "Ticket **tickets"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_get_client_binding
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "char **type"
+.Fa "char **binding"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_digest_get_a1_hash
+.Fa "krb5_context context"
+.Fa "krb5_digest digest"
+.Fa "krb5_data *data"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn krb5_digest_alloc
+function allocatates the
+.Fa digest
+structure. The structure should be freed with
+.Fn krb5_digest_free
+when it is no longer being used.
+.Pp
+.Fn krb5_digest_alloc
+returns 0 to indicate success.
+Otherwise an kerberos code is returned and the pointer that
+.Fa digest
+points to is set to
+.Dv NULL .
+.Pp
+.Fn krb5_digest_free
+free the structure
+.Fa digest .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_eai_to_heim_errno.3 b/third_party/heimdal/lib/krb5/krb5_eai_to_heim_errno.3
new file mode 100644
index 0000000..0c83a33
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_eai_to_heim_errno.3
@@ -0,0 +1,68 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd April 13, 2004
+.Dt KRB5_EAI_TO_HEIM_ERRNO 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_eai_to_heim_errno ,
+.Nm krb5_h_errno_to_heim_errno
+.Nd convert resolver error code to com_err error codes
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_eai_to_heim_errno
+.Fa "int eai_errno"
+.Fa "int system_error"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_h_errno_to_heim_errno
+.Fa "int eai_errno"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_eai_to_heim_errno
+and
+.Fn krb5_h_errno_to_heim_errno
+convert
+.Xr getaddrinfo 3 ,
+.Xr getnameinfo 3 ,
+and
+.Xr h_errno 3
+to com_err error code that are used by Heimdal, this is useful for for
+function returning kerberos errors and needs to communicate failures
+from resolver function.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_encrypt.3 b/third_party/heimdal/lib/krb5/krb5_encrypt.3
new file mode 100644
index 0000000..2b1f587
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_encrypt.3
@@ -0,0 +1,278 @@
+.\" Copyright (c) 1999 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd March 20, 2004
+.Dt KRB5_ENCRYPT 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_crypto_getblocksize ,
+.Nm krb5_crypto_getconfoundersize
+.Nm krb5_crypto_getenctype ,
+.Nm krb5_crypto_getpadsize ,
+.Nm krb5_crypto_overhead ,
+.Nm krb5_decrypt ,
+.Nm krb5_decrypt_EncryptedData ,
+.Nm krb5_decrypt_ivec ,
+.Nm krb5_decrypt_ticket ,
+.Nm krb5_encrypt ,
+.Nm krb5_encrypt_EncryptedData ,
+.Nm krb5_encrypt_ivec ,
+.Nm krb5_enctype_disable ,
+.Nm krb5_enctype_keysize ,
+.Nm krb5_enctype_to_string ,
+.Nm krb5_enctype_valid ,
+.Nm krb5_get_wrapped_length ,
+.Nm krb5_string_to_enctype
+.Nd "encrypt and decrypt data, set and get encryption type parameters"
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_encrypt
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "unsigned usage"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "krb5_data *result"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_encrypt_EncryptedData
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "unsigned usage"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "int kvno"
+.Fa "EncryptedData *result"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_encrypt_ivec
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "unsigned usage"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "krb5_data *result"
+.Fa "void *ivec"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_decrypt
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "unsigned usage"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "krb5_data *result"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_decrypt_EncryptedData
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "unsigned usage"
+.Fa "EncryptedData *e"
+.Fa "krb5_data *result"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_decrypt_ivec
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "unsigned usage"
+.Fa "void *data"
+.Fa "size_t len"
+.Fa "krb5_data *result"
+.Fa "void *ivec"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_decrypt_ticket
+.Fa "krb5_context context"
+.Fa "Ticket *ticket"
+.Fa "krb5_keyblock *key"
+.Fa "EncTicketPart *out"
+.Fa "krb5_flags flags"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_crypto_getblocksize
+.Fa "krb5_context context"
+.Fa "size_t *blocksize"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_crypto_getenctype
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "krb5_enctype *enctype"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_crypto_getpadsize
+.Fa "krb5_context context"
+.Fa size_t *padsize"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_crypto_getconfoundersize
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa size_t *confoundersize"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_enctype_keysize
+.Fa "krb5_context context"
+.Fa "krb5_enctype type"
+.Fa "size_t *keysize"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_crypto_overhead
+.Fa "krb5_context context"
+.Fa size_t *padsize"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_enctype
+.Fa "krb5_context context"
+.Fa "const char *string"
+.Fa "krb5_enctype *etype"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_enctype_to_string
+.Fa "krb5_context context"
+.Fa "krb5_enctype etype"
+.Fa "char **string"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_enctype_valid
+.Fa "krb5_context context"
+.Fa "krb5_enctype etype"
+.Fc
+.Ft void
+.Fo krb5_enctype_disable
+.Fa "krb5_context context"
+.Fa "krb5_enctype etype"
+.Fc
+.Ft size_t
+.Fo krb5_get_wrapped_length
+.Fa "krb5_context context"
+.Fa "krb5_crypto crypto"
+.Fa "size_t data_len"
+.Fc
+.Sh DESCRIPTION
+These functions are used to encrypt and decrypt data.
+.Pp
+.Fn krb5_encrypt_ivec
+puts the encrypted version of
+.Fa data
+(of size
+.Fa len )
+in
+.Fa result .
+If the encryption type supports using derived keys,
+.Fa usage
+should be the appropriate key-usage.
+.Fa ivec
+is a pointer to a initial IV, it is modified to the end IV at the end of
+the round.
+Ivec should be the size of
+If
+.Dv NULL
+is passed in, the default IV is used.
+.Fn krb5_encrypt
+does the same as
+.Fn krb5_encrypt_ivec
+but with
+.Fa ivec
+being
+.Dv NULL .
+.Fn krb5_encrypt_EncryptedData
+does the same as
+.Fn krb5_encrypt ,
+but it puts the encrypted data in a
+.Fa EncryptedData
+structure instead. If
+.Fa kvno
+is not zero, it will be put in the (optional)
+.Fa kvno
+field in the
+.Fa EncryptedData .
+.Pp
+.Fn krb5_decrypt_ivec ,
+.Fn krb5_decrypt ,
+and
+.Fn krb5_decrypt_EncryptedData
+works similarly.
+.Pp
+.Fn krb5_decrypt_ticket
+decrypts the encrypted part of
+.Fa ticket
+with
+.Fa key .
+.Fn krb5_decrypt_ticket
+also verifies the timestamp in the ticket, invalid flag and if the KDC
+haven't verified the transited path, the transit path.
+.Pp
+.Fn krb5_enctype_keysize ,
+.Fn krb5_crypto_getconfoundersize ,
+.Fn krb5_crypto_getblocksize ,
+.Fn krb5_crypto_getenctype ,
+.Fn krb5_crypto_getpadsize ,
+.Fn krb5_crypto_overhead
+all returns various (sometimes) useful information from a crypto context.
+.Fn krb5_crypto_overhead
+is the combination of krb5_crypto_getconfoundersize,
+krb5_crypto_getblocksize and krb5_crypto_getpadsize and return the
+maximum overhead size.
+.Pp
+.Fn krb5_enctype_to_string
+converts a encryption type number to a string that can be printable
+and stored. The strings returned should be freed with
+.Xr free 3 .
+.Pp
+.Fn krb5_string_to_enctype
+converts a encryption type strings to a encryption type number that
+can use used for other Kerberos crypto functions.
+.Pp
+.Fn krb5_enctype_valid
+returns 0 if the encrypt is supported and not disabled, otherwise and
+error code is returned.
+.Pp
+.Fn krb5_enctype_disable
+(globally, for all contextes) disables the
+.Fa enctype .
+.Pp
+.Fn krb5_get_wrapped_length
+returns the size of an encrypted packet by
+.Fa crypto
+of length
+.Fa data_len .
+.\" .Sh EXAMPLE
+.\" .Sh BUGS
+.Sh SEE ALSO
+.Xr krb5_create_checksum 3 ,
+.Xr krb5_crypto_init 3
diff --git a/third_party/heimdal/lib/krb5/krb5_err.et b/third_party/heimdal/lib/krb5/krb5_err.et
new file mode 100644
index 0000000..2b4062e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_err.et
@@ -0,0 +1,317 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+# Do try to keep this in sync with MIT's.
+#
+id "$Id$"
+
+error_table krb5
+
+prefix KRB5KDC_ERR
+error_code NONE, "No error"
+error_code NAME_EXP, "Client's entry in database has expired"
+error_code SERVICE_EXP, "Server's entry in database has expired"
+error_code BAD_PVNO, "Requested protocol version not supported"
+error_code C_OLD_MAST_KVNO, "Client's key is encrypted in an old master key"
+error_code S_OLD_MAST_KVNO, "Server's key is encrypted in an old master key"
+error_code C_PRINCIPAL_UNKNOWN, "Client not found in Kerberos database"
+error_code S_PRINCIPAL_UNKNOWN, "Server not found in Kerberos database"
+error_code PRINCIPAL_NOT_UNIQUE,"Principal has multiple entries in Kerberos database"
+error_code NULL_KEY, "Client or server has a null key"
+error_code CANNOT_POSTDATE, "Ticket is ineligible for postdating"
+error_code NEVER_VALID, "Requested effective lifetime is negative or too short"
+error_code POLICY, "KDC policy rejects request"
+error_code BADOPTION, "KDC can't fulfill requested option"
+error_code ETYPE_NOSUPP, "KDC has no support for encryption type"
+error_code SUMTYPE_NOSUPP, "KDC has no support for checksum type"
+error_code PADATA_TYPE_NOSUPP, "KDC has no support for padata type"
+error_code TRTYPE_NOSUPP, "KDC has no support for transited type"
+error_code CLIENT_REVOKED, "Clients credentials have been revoked"
+error_code SERVICE_REVOKED, "Credentials for server have been revoked"
+error_code TGT_REVOKED, "TGT has been revoked"
+error_code CLIENT_NOTYET, "Client not yet valid - try again later"
+error_code SERVICE_NOTYET, "Server not yet valid - try again later"
+error_code KEY_EXPIRED, "Password has expired"
+error_code PREAUTH_FAILED, "Preauthentication failed"
+error_code PREAUTH_REQUIRED, "Additional pre-authentication required"
+error_code SERVER_NOMATCH, "Requested server and ticket don't match"
+error_code KDC_ERR_MUST_USE_USER2USER, "Server principal valid for user2user only"
+error_code PATH_NOT_ACCEPTED, "KDC Policy rejects transited path"
+error_code SVC_UNAVAILABLE, "A service is not available"
+
+index 31
+prefix KRB5KRB_AP
+error_code ERR_BAD_INTEGRITY, "Decrypt integrity check failed"
+error_code ERR_TKT_EXPIRED, "Ticket expired"
+error_code ERR_TKT_NYV, "Ticket not yet valid"
+error_code ERR_REPEAT, "Request is a replay"
+error_code ERR_NOT_US, "The ticket isn't for us"
+error_code ERR_BADMATCH, "Ticket/authenticator don't match"
+error_code ERR_SKEW, "Clock skew too great"
+error_code ERR_BADADDR, "Incorrect net address"
+error_code ERR_BADVERSION, "Protocol version mismatch"
+error_code ERR_MSG_TYPE, "Invalid message type"
+error_code ERR_MODIFIED, "Message stream modified"
+error_code ERR_BADORDER, "Message out of order"
+error_code ERR_ILL_CR_TKT, "Invalid cross-realm ticket"
+error_code ERR_BADKEYVER, "Key version is not available"
+error_code ERR_NOKEY, "Service key not available"
+error_code ERR_MUT_FAIL, "Mutual authentication failed"
+error_code ERR_BADDIRECTION, "Incorrect message direction"
+error_code ERR_METHOD, "Alternative authentication method required"
+error_code ERR_BADSEQ, "Incorrect sequence number in message"
+error_code ERR_INAPP_CKSUM, "Inappropriate type of checksum in message"
+error_code PATH_NOT_ACCEPTED, "Policy rejects transited path"
+
+prefix KRB5KRB_ERR
+error_code RESPONSE_TOO_BIG, "Response too big for UDP, retry with TCP"
+# 53-59 are reserved
+index 60
+error_code GENERIC, "Generic error (see e-text)"
+error_code FIELD_TOOLONG, "Field is too long for this implementation"
+
+# pkinit
+index 62
+prefix KRB5_KDC_ERR
+error_code CLIENT_NOT_TRUSTED, "Client not trusted"
+error_code KDC_NOT_TRUSTED, "KDC not trusted"
+error_code INVALID_SIG, "Invalid signature"
+error_code DH_KEY_PARAMETERS_NOT_ACCEPTED, "DH parameters not accepted"
+
+index 68
+prefix KRB5_KDC_ERR
+error_code WRONG_REALM, "Wrong realm"
+
+index 69
+prefix KRB5_AP_ERR
+error_code USER_TO_USER_REQUIRED, "User to user required"
+
+index 70
+prefix KRB5_KDC_ERR
+error_code CANT_VERIFY_CERTIFICATE, "Cannot verify certificate"
+error_code INVALID_CERTIFICATE, "Certificate invalid"
+error_code REVOKED_CERTIFICATE, "Certificate revoked"
+error_code REVOCATION_STATUS_UNKNOWN, "Revocation status unknown"
+error_code REVOCATION_STATUS_UNAVAILABLE, "Revocation status unavailable"
+error_code CLIENT_NAME_MISMATCH, "Client name mismatch in certificate"
+error_code INCONSISTENT_KEY_PURPOSE, "Inconsistent key purpose"
+error_code DIGEST_IN_CERT_NOT_ACCEPTED, "Digest in certificate not accepted"
+error_code PA_CHECKSUM_MUST_BE_INCLUDED, "paChecksum must be included"
+error_code DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signedData not accepted"
+error_code PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
+
+## these are never used
+#index 85
+#prefix KRB5_IAKERB
+#error_code ERR_KDC_NOT_FOUND, "IAKERB proxy could not find a KDC"
+#error_code ERR_KDC_NO_RESPONSE, "IAKERB proxy never received a response from a KDC"
+
+index 90
+error_code PREAUTH_EXPIRED, "Pre-authentication data expired"
+
+index 91
+error_code MORE_PREAUTH_DATA_REQUIRED, "More pre-authentication data required"
+
+index 93
+error_code UNKNOWN_CRITICAL_FAST_OPTIONS, "Unknown critical FAST options"
+
+index 94
+error_code INVALID_HASH_ALG, "Invalid OTP digest algorithm"
+error_code INVALID_ITERATION_COUNT, "Invalid OTP iteration count"
+
+# 97-99 are reserved
+
+index 100
+error_code NO_ACCEPTABLE_KDF, "No acceptable KDF offered"
+
+# 101-127 are reserved
+
+index 128
+prefix
+error_code KRB5_ERR_RCSID, "$Id$"
+
+error_code KRB5_LIBOS_BADLOCKFLAG, "Invalid flag for file lock mode"
+error_code KRB5_LIBOS_CANTREADPWD, "Cannot read password"
+error_code KRB5_LIBOS_BADPWDMATCH, "Password mismatch"
+error_code KRB5_LIBOS_PWDINTR, "Password read interrupted"
+
+error_code KRB5_PARSE_ILLCHAR, "Invalid character in component name"
+error_code KRB5_PARSE_MALFORMED, "Malformed representation of principal"
+
+error_code KRB5_CONFIG_CANTOPEN, "Can't open/find configuration file"
+error_code KRB5_CONFIG_BADFORMAT, "Improper format of configuration file"
+error_code KRB5_CONFIG_NOTENUFSPACE, "Insufficient space to return complete information"
+
+error_code KRB5_BADMSGTYPE, "Invalid message type specified for encoding"
+
+error_code KRB5_CC_BADNAME, "Credential cache name malformed"
+error_code KRB5_CC_UNKNOWN_TYPE, "Unknown credential cache type"
+error_code KRB5_CC_NOTFOUND, "Matching credential not found"
+error_code KRB5_CC_END, "End of credential cache reached"
+
+error_code KRB5_NO_TKT_SUPPLIED, "Request did not supply a ticket"
+
+error_code KRB5KRB_AP_WRONG_PRINC, "Wrong principal in request"
+error_code KRB5KRB_AP_ERR_TKT_INVALID, "Ticket has invalid flag set"
+
+error_code KRB5_PRINC_NOMATCH, "Requested principal and ticket don't match"
+error_code KRB5_KDCREP_MODIFIED, "KDC reply did not match expectations"
+error_code KRB5_KDCREP_SKEW, "Clock skew too great in KDC reply"
+error_code KRB5_IN_TKT_REALM_MISMATCH, "Client/server realm mismatch in initial ticket request"
+
+error_code KRB5_PROG_ETYPE_NOSUPP, "Program lacks support for encryption type"
+error_code KRB5_PROG_KEYTYPE_NOSUPP, "Program lacks support for key type"
+error_code KRB5_WRONG_ETYPE, "Requested encryption type not used in message"
+error_code KRB5_PROG_SUMTYPE_NOSUPP, "Program lacks support for checksum type"
+
+error_code KRB5_REALM_UNKNOWN, "Cannot find KDC for requested realm"
+error_code KRB5_SERVICE_UNKNOWN, "Kerberos service unknown"
+error_code KRB5_KDC_UNREACH, "Cannot contact any KDC for requested realm"
+error_code KRB5_NO_LOCALNAME, "No local name found for principal name"
+
+error_code KRB5_MUTUAL_FAILED, "Mutual authentication failed"
+
+# some of these should be combined/supplanted by system codes
+
+error_code KRB5_RC_TYPE_EXISTS, "Replay cache type is already registered"
+error_code KRB5_RC_MALLOC, "No more memory to allocate (in replay cache code)"
+error_code KRB5_RC_TYPE_NOTFOUND, "Replay cache type is unknown"
+error_code KRB5_RC_UNKNOWN, "Generic unknown RC error"
+error_code KRB5_RC_REPLAY, "Message is a replay"
+error_code KRB5_RC_IO, "Replay I/O operation failed XXX"
+error_code KRB5_RC_NOIO, "Replay cache type does not support non-volatile storage"
+error_code KRB5_RC_PARSE, "Replay cache name parse/format error"
+
+error_code KRB5_RC_IO_EOF, "End-of-file on replay cache I/O"
+error_code KRB5_RC_IO_MALLOC, "No more memory to allocate (in replay cache I/O code)"
+error_code KRB5_RC_IO_PERM, "Permission denied in replay cache code"
+error_code KRB5_RC_IO_IO, "I/O error in replay cache i/o code"
+error_code KRB5_RC_IO_UNKNOWN, "Generic unknown RC/IO error"
+error_code KRB5_RC_IO_SPACE, "Insufficient system space to store replay information"
+
+error_code KRB5_TRANS_CANTOPEN, "Can't open/find realm translation file"
+error_code KRB5_TRANS_BADFORMAT, "Improper format of realm translation file"
+
+error_code KRB5_LNAME_CANTOPEN, "Can't open/find lname translation database"
+error_code KRB5_LNAME_NOTRANS, "No translation available for requested principal"
+error_code KRB5_LNAME_BADFORMAT, "Improper format of translation database entry"
+
+error_code KRB5_CRYPTO_INTERNAL, "Cryptosystem internal error"
+
+error_code KRB5_KT_BADNAME, "Key table name malformed"
+error_code KRB5_KT_UNKNOWN_TYPE, "Unknown Key table type"
+error_code KRB5_KT_NOTFOUND, "Key table entry not found"
+error_code KRB5_KT_END, "End of key table reached"
+error_code KRB5_KT_NOWRITE, "Cannot write to specified key table"
+error_code KRB5_KT_IOERR, "Error writing to key table"
+
+error_code KRB5_NO_TKT_IN_RLM, "Cannot find ticket for requested realm"
+error_code KRB5DES_BAD_KEYPAR, "DES key has bad parity"
+error_code KRB5DES_WEAK_KEY, "DES key is a weak key"
+
+error_code KRB5_BAD_ENCTYPE, "Bad encryption type"
+error_code KRB5_BAD_KEYSIZE, "Key size is incompatible with encryption type"
+error_code KRB5_BAD_MSIZE, "Message size is incompatible with encryption type"
+
+error_code KRB5_CC_TYPE_EXISTS, "Credentials cache type is already registered."
+error_code KRB5_KT_TYPE_EXISTS, "Key table type is already registered."
+
+error_code KRB5_CC_IO, "Credentials cache I/O operation failed XXX"
+error_code KRB5_FCC_PERM, "Credentials cache file permissions incorrect"
+error_code KRB5_FCC_NOFILE, "No credentials cache file found"
+error_code KRB5_FCC_INTERNAL, "Internal file credentials cache error"
+error_code KRB5_CC_WRITE, "Error writing to credentials cache file"
+error_code KRB5_CC_NOMEM, "No more memory to allocate (in credentials cache code)"
+error_code KRB5_CC_FORMAT, "Bad format in credentials cache"
+error_code KRB5_CC_NOT_KTYPE, "No credentials found with supported encryption types"
+
+# errors for dual tgt library calls
+error_code KRB5_INVALID_FLAGS, "Invalid KDC option combination (library internal error)"
+error_code KRB5_NO_2ND_TKT, "Request missing second ticket"
+
+error_code KRB5_NOCREDS_SUPPLIED, "No credentials supplied to library routine"
+
+# errors for sendauth (and recvauth)
+
+error_code KRB5_SENDAUTH_BADAUTHVERS, "Bad sendauth version was sent"
+error_code KRB5_SENDAUTH_BADAPPLVERS, "Bad application version was sent (via sendauth)"
+error_code KRB5_SENDAUTH_BADRESPONSE, "Bad response (during sendauth exchange)"
+error_code KRB5_SENDAUTH_REJECTED, "Server rejected authentication (during sendauth exchange)"
+
+# errors for preauthentication
+
+error_code KRB5_PREAUTH_BAD_TYPE, "Unsupported preauthentication type"
+error_code KRB5_PREAUTH_NO_KEY, "Required preauthentication key not supplied"
+error_code KRB5_PREAUTH_FAILED, "Generic preauthentication failure"
+
+# version number errors
+
+error_code KRB5_RCACHE_BADVNO, "Unsupported replay cache format version number"
+error_code KRB5_CCACHE_BADVNO, "Unsupported credentials cache format version number"
+error_code KRB5_KEYTAB_BADVNO, "Unsupported key table format version number"
+
+#
+#
+
+error_code KRB5_PROG_ATYPE_NOSUPP, "Program lacks support for address type"
+error_code KRB5_RC_REQUIRED, "Message replay detection requires rcache parameter"
+error_code KRB5_ERR_BAD_HOSTNAME, "Hostname cannot be canonicalized"
+error_code KRB5_ERR_HOST_REALM_UNKNOWN, "Cannot determine realm for host"
+error_code KRB5_SNAME_UNSUPP_NAMETYPE, "Conversion to service principal undefined for name type"
+
+error_code KRB5KRB_AP_ERR_V4_REPLY, "Initial Ticket response appears to be Version 4"
+error_code KRB5_REALM_CANT_RESOLVE, "Cannot resolve KDC for requested realm"
+error_code KRB5_TKT_NOT_FORWARDABLE, "Requesting ticket can't get forwardable tickets"
+error_code KRB5_FWD_BAD_PRINCIPAL, "Bad principal name while trying to forward credentials"
+
+error_code KRB5_GET_IN_TKT_LOOP, "Looping detected inside krb5_get_in_tkt"
+error_code KRB5_CONFIG_NODEFREALM, "Configuration file does not specify default realm"
+
+error_code KRB5_SAM_UNSUPPORTED, "Bad SAM flags in obtain_sam_padata"
+error_code KRB5_SAM_INVALID_ETYPE, "Invalid encryption type in SAM challenge"
+error_code KRB5_SAM_NO_CHECKSUM, "Missing checksum in SAM challenge"
+error_code KRB5_SAM_BAD_CHECKSUM, "Bad checksum in SAM challenge"
+
+error_code KRB5_KT_NAME_TOOLONG, "Keytab name too long"
+error_code KRB5_KT_KVNONOTFOUND, "Key version number for principal in key table is incorrect"
+error_code KRB5_APPL_EXPIRED, "This application has expired"
+error_code KRB5_LIB_EXPIRED, "This Krb5 library has expired"
+
+error_code KRB5_CHPW_PWDNULL, "New password cannot be zero length"
+error_code KRB5_CHPW_FAIL, "Password change failed"
+error_code KRB5_KT_FORMAT, "Bad format in keytab"
+
+error_code KRB5_NOPERM_ETYPE, "Encryption type not permitted"
+error_code KRB5_CONFIG_ETYPE_NOSUPP, "No supported encryption types (config file error?)"
+
+error_code KRB5_OBSOLETE_FN, "Program called an obsolete, deleted function"
+
+error_code KRB5_EAI_FAIL, "unknown getaddrinfo failure"
+error_code KRB5_EAI_NODATA, "no data available for host/domain name"
+error_code KRB5_EAI_NONAME, "host/domain name not found"
+error_code KRB5_EAI_SERVICE, "service name unknown"
+
+error_code KRB5_ERR_NUMERIC_REALM, "Cannot determine realm for numeric host address"
+
+error_code KRB5_ERR_BAD_S2K_PARAMS, "Invalid key generation parameters from KDC"
+
+error_code KRB5_ERR_NO_SERVICE, "Service not available"
+
+index 247
+error_code KRB5_CC_NOSUPP, "Credential cache function not supported"
+error_code KRB5_DELTAT_BADFORMAT, "Invalid format of Kerberos lifetime or clock skew string"
+error_code KRB5_PLUGIN_NO_HANDLE, "Supplied data not handled by this plugin"
+error_code KRB5_PLUGIN_OP_NOTSUPP, "Plugin does not support the operaton"
+
+error_code KRB5_ERR_INVALID_UTF8, "Invalid UTF-8 string"
+error_code KRB5_ERR_FAST_REQUIRED, "FAST protected pre-authentication required but not supported by KDC"
+
+error_code KRB5_LOCAL_ADDR_REQUIRED, "Auth context must contain local address"
+error_code KRB5_REMOTE_ADDR_REQUIRED, "Auth context must contain remote address"
+
+error_code KRB5_TRACE_NOSUPP, "Tracing unsupported"
+
+
+end
diff --git a/third_party/heimdal/lib/krb5/krb5_find_padata.3 b/third_party/heimdal/lib/krb5/krb5_find_padata.3
new file mode 100644
index 0000000..377a2cb
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_find_padata.3
@@ -0,0 +1,87 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd March 21, 2004
+.Dt KRB5_FIND_PADATA 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_find_padata ,
+.Nm krb5_padata_add
+.Nd Kerberos 5 pre-authentication data handling functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Ft "PA_DATA *"
+.Fo krb5_find_padata
+.Fa "PA_DATA *val"
+.Fa "unsigned len"
+.Fa "int type"
+.Fa "int *index"
+.Fc
+.Ft int
+.Fo krb5_padata_add
+.Fa "krb5_context context"
+.Fa "METHOD_DATA *md"
+.Fa "int type"
+.Fa "void *buf"
+.Fa "size_t len"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_find_padata
+tries to find the pre-authentication data entry of type
+.Fa type
+in the array
+.Fa val
+of length
+.Fa len .
+The search is started at entry pointed out by
+.Fa *index
+(zero based indexing).
+If the type isn't found,
+.Dv NULL
+is returned.
+.Pp
+.Fn krb5_padata_add
+adds a pre-authentication data entry of type
+.Fa type
+pointed out by
+.Fa buf
+and
+.Fa len
+to
+.Fa md .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_generate_random_block.3 b/third_party/heimdal/lib/krb5/krb5_generate_random_block.3
new file mode 100644
index 0000000..8fad949
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_generate_random_block.3
@@ -0,0 +1,57 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd March 21, 2004
+.Dt KRB5_GENERATE_RANDOM_BLOCK 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_generate_random_block
+.Nd Kerberos 5 random functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft void
+.Fo krb5_generate_random_block
+.Fa "void *buf"
+.Fa "size_t len"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_generate_random_block
+generates a cryptographically strong pseudo-random block into the buffer
+.Fa buf
+of length
+.Fa len .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_get_all_client_addrs.3 b/third_party/heimdal/lib/krb5/krb5_get_all_client_addrs.3
new file mode 100644
index 0000000..56a5322
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_all_client_addrs.3
@@ -0,0 +1,74 @@
+.\" 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 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 July 1, 2001
+.Dt KRB5_GET_ADDRS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_all_client_addrs ,
+.Nm krb5_get_all_server_addrs
+.Nd return local addresses
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft "krb5_error_code"
+.Fn krb5_get_all_client_addrs "krb5_context context" "krb5_addresses *addrs"
+.Ft "krb5_error_code"
+.Fn krb5_get_all_server_addrs "krb5_context context" "krb5_addresses *addrs"
+.Sh DESCRIPTION
+These functions return in
+.Fa addrs
+a list of addresses associated with the local
+host.
+.Pp
+The server variant returns all configured interface addresses (if
+possible), including loop-back addresses. This is useful if you want
+to create sockets to listen to.
+.Pp
+The client version will also scan local interfaces (can be turned off
+by setting
+.Li libdefaults/scan_interfaces
+to false in
+.Pa krb5.conf ) ,
+but will not include loop-back addresses, unless there are no other
+addresses found. It will remove all addresses included in
+.Li libdefaults/ignore_addresses
+but will unconditionally include addresses in
+.Li libdefaults/extra_addresses .
+.Pp
+The returned addresses should be freed by calling
+.Fn krb5_free_addresses .
+.\".Sh EXAMPLE
+.Sh SEE ALSO
+.Xr krb5_free_addresses 3
diff --git a/third_party/heimdal/lib/krb5/krb5_get_credentials.3 b/third_party/heimdal/lib/krb5/krb5_get_credentials.3
new file mode 100644
index 0000000..112f308
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_credentials.3
@@ -0,0 +1,181 @@
+.\" Copyright (c) 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.
+.\"
+.\" $Id$
+.\"
+.Dd July 26, 2004
+.Dt KRB5_GET_CREDENTIALS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_credentials ,
+.Nm krb5_get_credentials_with_flags ,
+.Nm krb5_get_kdc_cred ,
+.Nm krb5_get_renewed_creds
+.Nd get credentials from the KDC using krbtgt
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_get_credentials
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_creds *in_creds"
+.Fa "krb5_creds **out_creds"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_credentials_with_flags
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "krb5_kdc_flags flags"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_creds *in_creds"
+.Fa "krb5_creds **out_creds"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_kdc_cred
+.Fa "krb5_context context"
+.Fa "krb5_ccache id"
+.Fa "krb5_kdc_flags flags"
+.Fa "krb5_addresses *addresses"
+.Fa "Ticket *second_ticket"
+.Fa "krb5_creds *in_creds"
+.Fa "krb5_creds **out_creds"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_renewed_creds
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "krb5_const_principal client"
+.Fa "krb5_ccache ccache"
+.Fa "const char *in_tkt_service"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_get_credentials_with_flags
+get credentials specified by
+.Fa in_creds->server
+and
+.Fa in_creds->client
+(the rest of the
+.Fa in_creds
+structure is ignored)
+by first looking in the
+.Fa ccache
+and if doesn't exists or is expired, fetch the credential from the KDC
+using the krbtgt in
+.Fa ccache .
+The credential is returned in
+.Fa out_creds
+and should be freed using the function
+.Fn krb5_free_creds .
+.Pp
+Valid flags to pass into
+.Fa options
+argument are:
+.Pp
+.Bl -tag -width "KRB5_GC_EXPIRED_OK" -compact
+.It KRB5_GC_CACHED
+Only check the
+.Fa ccache ,
+don't got out on network to fetch credential.
+.It KRB5_GC_USER_USER
+Request a user to user ticket.
+This option doesn't store the resulting user to user credential in
+the
+.Fa ccache .
+.It KRB5_GC_EXPIRED_OK
+returns the credential even if it is expired, default behavior is trying
+to refetch the credential from the KDC.
+.El
+.Pp
+.Fa Flags
+are KDCOptions, note the caller must fill in the bit-field and not
+use the integer associated structure.
+.Pp
+.Fn krb5_get_credentials
+works the same way as
+.Fn krb5_get_credentials_with_flags
+except that the
+.Fa flags
+field is missing.
+.Pp
+.Fn krb5_get_kdc_cred
+does the same as the functions above, but the caller must fill in all
+the information andits closer to the wire protocol.
+.Pp
+.Fn krb5_get_renewed_creds
+renews a credential given by
+.Fa in_tkt_service
+(if
+.Dv NULL
+the default
+.Li krbtgt )
+using the credential cache
+.Fa ccache .
+The result is stored in
+.Fa creds
+and should be freed using
+.Fa krb5_free_creds .
+.Sh EXAMPLES
+Here is a example function that get a credential from a credential cache
+.Fa id
+or the KDC and returns it to the caller.
+.Bd -literal
+#include <krb5.h>
+
+int
+getcred(krb5_context context, krb5_ccache id, krb5_creds **creds)
+{
+ krb5_error_code ret;
+ krb5_creds in;
+
+ ret = krb5_parse_name(context, "client@EXAMPLE.COM",
+ &in.client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_parse_name(context, "host/server.example.com@EXAMPLE.COM",
+ &in.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_get_credentials(context, 0, id, &in, creds);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_credentials");
+
+ return 0;
+}
+.Ed
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_get_forwarded_creds 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_get_creds.3 b/third_party/heimdal/lib/krb5/krb5_get_creds.3
new file mode 100644
index 0000000..35e861e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_creds.3
@@ -0,0 +1,173 @@
+.\" 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$
+.\"
+.Dd June 15, 2006
+.Dt KRB5_GET_CREDS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_creds ,
+.Nm krb5_get_creds_opt_add_options ,
+.Nm krb5_get_creds_opt_alloc ,
+.Nm krb5_get_creds_opt_free ,
+.Nm krb5_get_creds_opt_set_enctype ,
+.Nm krb5_get_creds_opt_set_impersonate ,
+.Nm krb5_get_creds_opt_set_options ,
+.Nm krb5_get_creds_opt_set_ticket
+.Nd get credentials from the KDC
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_get_creds
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_const_principal inprinc"
+.Fa "krb5_creds **out_creds"
+.Fc
+.Ft void
+.Fo krb5_get_creds_opt_add_options
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fa "krb5_flags options"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_creds_opt_alloc
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt *opt"
+.Fc
+.Ft void
+.Fo krb5_get_creds_opt_free
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fc
+.Ft void
+.Fo krb5_get_creds_opt_set_enctype
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fa "krb5_enctype enctype"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_creds_opt_set_impersonate
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fa "krb5_const_principal self"
+.Fc
+.Ft void
+.Fo krb5_get_creds_opt_set_options
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fa "krb5_flags options"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_creds_opt_set_ticket
+.Fa "krb5_context context"
+.Fa "krb5_get_creds_opt opt"
+.Fa "const Ticket *ticket"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_get_creds
+fetches credentials specified by
+.Fa opt
+by first looking in the
+.Fa ccache ,
+and then it doesn't exists, fetch the credential from the KDC
+using the krbtgts in
+.Fa ccache .
+The credential is returned in
+.Fa out_creds
+and should be freed using the function
+.Fn krb5_free_creds .
+.Pp
+The structure
+.Li krb5_get_creds_opt
+controls the behavior of
+.Fn krb5_get_creds .
+The structure is opaque to consumers that can set the content of the
+structure with accessors functions. All accessor functions make copies
+of the data that is passed into accessor functions, so external
+consumers free the memory before calling
+.Fn krb5_get_creds .
+.Pp
+The structure
+.Li krb5_get_creds_opt
+is allocated with
+.Fn krb5_get_creds_opt_alloc
+and freed with
+.Fn krb5_get_creds_opt_free .
+The free function also frees the content of the structure set by the
+accessor functions.
+.Pp
+.Fn krb5_get_creds_opt_add_options
+and
+.Fn krb5_get_creds_opt_set_options
+adds and sets options to the
+.Li krb5_get_creds_opt
+structure .
+The possible options to set are
+.Bl -tag -width "KRB5_GC_USER_USER" -compact
+.It KRB5_GC_CACHED
+Only check the
+.Fa ccache ,
+don't got out on network to fetch credential.
+.It KRB5_GC_USER_USER
+request a user to user ticket.
+This options doesn't store the resulting user to user credential in
+the
+.Fa ccache .
+.It KRB5_GC_EXPIRED_OK
+returns the credential even if it is expired, default behavior is trying
+to refetch the credential from the KDC.
+.It KRB5_GC_NO_STORE
+Do not store the resulting credentials in the
+.Fa ccache .
+.El
+.Pp
+.Fn krb5_get_creds_opt_set_enctype
+sets the preferred encryption type of the application. Don't set this
+unless you have to since if there is no match in the KDC, the function
+call will fail.
+.Pp
+.Fn krb5_get_creds_opt_set_impersonate
+sets the principal to impersonate., Returns a ticket that have the
+impersonation principal as a client and the requestor as the
+service. Note that the requested principal have to be the same as the
+client principal in the krbtgt.
+.Pp
+.Fn krb5_get_creds_opt_set_ticket
+sets the extra ticket used in user-to-user or contrained delegation use case.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_get_credentials 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_get_forwarded_creds.3 b/third_party/heimdal/lib/krb5/krb5_get_forwarded_creds.3
new file mode 100644
index 0000000..a648317
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_forwarded_creds.3
@@ -0,0 +1,79 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd July 26, 2004
+.Dt KRB5_GET_FORWARDED_CREDS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_forwarded_creds ,
+.Nm krb5_fwd_tgt_creds
+.Nd get forwarded credentials from the KDC
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_get_forwarded_creds
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_flags flags"
+.Fa "const char *hostname"
+.Fa "krb5_creds *in_creds"
+.Fa "krb5_data *out_data"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_fwd_tgt_creds
+.Fa "krb5_context context"
+.Fa "krb5_auth_context auth_context"
+.Fa "const char *hostname"
+.Fa "krb5_principal client"
+.Fa "krb5_principal server"
+.Fa "krb5_ccache ccache"
+.Fa "int forwardable"
+.Fa "krb5_data *out_data"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_get_forwarded_creds
+and
+.Fn krb5_fwd_tgt_creds
+get tickets forwarded to
+.Fa hostname.
+If the tickets that are forwarded are address-less, the forwarded
+tickets will also be address-less, otherwise
+.Fa hostname
+will be used for figure out the address to forward the ticket too.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_get_credentials 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_get_in_cred.3 b/third_party/heimdal/lib/krb5/krb5_get_in_cred.3
new file mode 100644
index 0000000..c415aec
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_in_cred.3
@@ -0,0 +1,274 @@
+.\" Copyright (c) 2003 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 31, 2003
+.Dt KRB5_GET_IN_TKT 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_in_tkt ,
+.Nm krb5_get_in_cred ,
+.Nm krb5_get_in_tkt_with_password ,
+.Nm krb5_get_in_tkt_with_keytab ,
+.Nm krb5_get_in_tkt_with_skey ,
+.Nm krb5_free_kdc_rep ,
+.Nm krb5_password_key_proc
+.Nd deprecated initial authentication functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Ft krb5_error_code
+.Fo krb5_get_in_tkt
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "const krb5_addresses *addrs"
+.Fa "const krb5_enctype *etypes"
+.Fa "const krb5_preauthtype *ptypes"
+.Fa "krb5_key_proc key_proc"
+.Fa "krb5_const_pointer keyseed"
+.Fa "krb5_decrypt_proc decrypt_proc"
+.Fa "krb5_const_pointer decryptarg"
+.Fa "krb5_creds *creds"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_kdc_rep *ret_as_reply"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_in_cred
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "const krb5_addresses *addrs"
+.Fa "const krb5_enctype *etypes"
+.Fa "const krb5_preauthtype *ptypes"
+.Fa "const krb5_preauthdata *preauth"
+.Fa "krb5_key_proc key_proc"
+.Fa "krb5_const_pointer keyseed"
+.Fa "krb5_decrypt_proc decrypt_proc"
+.Fa "krb5_const_pointer decryptarg"
+.Fa "krb5_creds *creds"
+.Fa "krb5_kdc_rep *ret_as_reply"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_in_tkt_with_password
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "krb5_addresses *addrs"
+.Fa "const krb5_enctype *etypes"
+.Fa "const krb5_preauthtype *pre_auth_types"
+.Fa "const char *password"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_creds *creds"
+.Fa "krb5_kdc_rep *ret_as_reply"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_in_tkt_with_keytab
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "krb5_addresses *addrs"
+.Fa "const krb5_enctype *etypes"
+.Fa "const krb5_preauthtype *pre_auth_types"
+.Fa "krb5_keytab keytab"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_creds *creds"
+.Fa "krb5_kdc_rep *ret_as_reply"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_in_tkt_with_skey
+.Fa "krb5_context context"
+.Fa "krb5_flags options"
+.Fa "krb5_addresses *addrs"
+.Fa "const krb5_enctype *etypes"
+.Fa "const krb5_preauthtype *pre_auth_types"
+.Fa "const krb5_keyblock *key"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_creds *creds"
+.Fa "krb5_kdc_rep *ret_as_reply"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_free_kdc_rep
+.Fa "krb5_context context"
+.Fa "krb5_kdc_rep *rep"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_password_key_proc
+.Fa "krb5_context context"
+.Fa "krb5_enctype type"
+.Fa "krb5_salt salt"
+.Fa "krb5_const_pointer keyseed"
+.Fa "krb5_keyblock **key"
+.Fc
+.Sh DESCRIPTION
+.Bf Em
+All the functions in this manual page are deprecated in the MIT
+implementation, and will soon be deprecated in Heimdal too, don't use them.
+.Ef
+.Pp
+Getting initial credential ticket for a principal.
+.Nm krb5_get_in_cred
+is the function all other krb5_get_in function uses to fetch tickets.
+The other krb5_get_in function are more specialized and therefor
+somewhat easier to use.
+.Pp
+If your need is only to verify a user and password, consider using
+.Xr krb5_verify_user 3
+instead, it have a much simpler interface.
+.Pp
+.Nm krb5_get_in_tkt
+and
+.Nm krb5_get_in_cred
+fetches initial credential, queries after key using the
+.Fa key_proc
+argument.
+The differences between the two function is that
+.Nm krb5_get_in_tkt
+stores the credential in a
+.Li krb5_creds
+while
+.Nm krb5_get_in_cred
+stores the credential in a
+.Li krb5_ccache .
+.Pp
+.Nm krb5_get_in_tkt_with_password ,
+.Nm krb5_get_in_tkt_with_keytab ,
+and
+.Nm krb5_get_in_tkt_with_skey
+does the same work as
+.Nm krb5_get_in_cred
+but are more specialized.
+.Pp
+.Nm krb5_get_in_tkt_with_password
+uses the clients password to authenticate.
+If the password argument is
+.Dv NULL
+the user user queried with the default password query function.
+.Pp
+.Nm krb5_get_in_tkt_with_keytab
+searches the given keytab for a service entry for the client principal.
+If the keytab is
+.Dv NULL
+the default keytab is used.
+.Pp
+.Nm krb5_get_in_tkt_with_skey
+uses a key to get the initial credential.
+.Pp
+There are some common arguments to the krb5_get_in functions, these are:
+.Pp
+.Fa options
+are the
+.Dv KDC_OPT
+flags.
+.Pp
+.Fa etypes
+is a
+.Dv NULL
+terminated array of encryption types that the client approves.
+.Pp
+.Fa addrs
+a list of the addresses that the initial ticket.
+If it is
+.Dv NULL
+the list will be generated by the library.
+.Pp
+.Fa pre_auth_types
+a
+.Dv NULL
+terminated array of pre-authentication types.
+If
+.Fa pre_auth_types
+is
+.Dv NULL
+the function will try without pre-authentication and return those
+pre-authentication that the KDC returned.
+.Pp
+.Fa ret_as_reply
+will (if not
+.Dv NULL )
+be filled in with the response of the KDC and should be free with
+.Fn krb5_free_kdc_rep .
+.Pp
+.Fa key_proc
+is a pointer to a function that should return a key salted appropriately.
+Using
+.Dv NULL
+will use the default password query function.
+.Pp
+.Fa decrypt_proc
+Using
+.Dv NULL
+will use the default decryption function.
+.Pp
+.Fa decryptarg
+will be passed to the decryption function
+.Fa decrypt_proc .
+.Pp
+.Fa creds
+creds should be filled in with the template for a credential that
+should be requested.
+The client and server elements of the creds structure must be filled in.
+Upon return of the function it will be contain the content of the
+requested credential
+.Fa ( krb5_get_in_cred ) ,
+or it will be freed with
+.Xr krb5_free_creds 3
+(all the other krb5_get_in functions).
+.Pp
+.Fa ccache
+will store the credential in the credential cache
+.Fa ccache .
+The credential cache will not be initialized, thats up the the caller.
+.Pp
+.Nm krb5_password_key_proc
+is a library function that is suitable using as the
+.Fa krb5_key_proc
+argument to
+.Nm krb5_get_in_cred
+or
+.Nm krb5_get_in_tkt .
+.Fa keyseed
+should be a pointer to a
+.Dv NUL
+terminated string or
+.Dv NULL .
+.Nm krb5_password_key_proc
+will query the user for the pass on the console if the password isn't
+given as the argument
+.Fa keyseed .
+.Pp
+.Fn krb5_free_kdc_rep
+frees the content of
+.Fa rep .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_verify_user 3 ,
+.Xr krb5.conf 5 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_get_init_creds.3 b/third_party/heimdal/lib/krb5/krb5_get_init_creds.3
new file mode 100644
index 0000000..11257b9
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_init_creds.3
@@ -0,0 +1,403 @@
+.\" Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd Sep 16, 2006
+.Dt KRB5_GET_INIT_CREDS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_init_creds ,
+.Nm krb5_get_init_creds_keytab ,
+.Nm krb5_get_init_creds_opt ,
+.Nm krb5_get_init_creds_opt_alloc ,
+.Nm krb5_get_init_creds_opt_free ,
+.Nm krb5_get_init_creds_opt_init ,
+.Nm krb5_get_init_creds_opt_set_address_list ,
+.Nm krb5_get_init_creds_opt_set_addressless ,
+.Nm krb5_get_init_creds_opt_set_anonymous ,
+.Nm krb5_get_init_creds_opt_set_default_flags ,
+.Nm krb5_get_init_creds_opt_set_etype_list ,
+.Nm krb5_get_init_creds_opt_set_forwardable ,
+.Nm krb5_get_init_creds_opt_set_pa_password ,
+.Nm krb5_get_init_creds_opt_set_paq_request ,
+.Nm krb5_get_init_creds_opt_set_preauth_list ,
+.Nm krb5_get_init_creds_opt_set_proxiable ,
+.Nm krb5_get_init_creds_opt_set_renew_life ,
+.Nm krb5_get_init_creds_opt_set_salt ,
+.Nm krb5_get_init_creds_opt_set_tkt_life ,
+.Nm krb5_get_init_creds_opt_set_canonicalize ,
+.Nm krb5_get_init_creds_opt_set_win2k ,
+.Nm krb5_get_init_creds_password ,
+.Nm krb5_prompt ,
+.Nm krb5_prompter_posix
+.Nd Kerberos 5 initial authentication functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Ft krb5_get_init_creds_opt;
+.Pp
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_opt_alloc
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt **opt"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_free
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_init
+.Fa "krb5_get_init_creds_opt *opt"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_address_list
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_addresses *addresses"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_addressless
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_boolean addressless"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_anonymous
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "int anonymous"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_change_password_prompt
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "int change_password_prompt"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_default_flags
+.Fa "krb5_context context"
+.Fa "const char *appname"
+.Fa "krb5_const_realm realm"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_etype_list
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_enctype *etype_list"
+.Fa "int etype_list_length"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_forwardable
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "int forwardable"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_opt_set_pa_password
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "const char *password"
+.Fa "krb5_s2k_proc key_proc"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_opt_set_paq_request
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_boolean req_pac"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_opt_set_pkinit
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "const char *cert_file"
+.Fa "const char *key_file"
+.Fa "const char *x509_anchors"
+.Fa "int flags"
+.Fa "char *password"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_preauth_list
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_preauthtype *preauth_list"
+.Fa "int preauth_list_length"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_proxiable
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "int proxiable"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_renew_life
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_deltat renew_life"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_salt
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_data *salt"
+.Fc
+.Ft void
+.Fo krb5_get_init_creds_opt_set_tkt_life
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_deltat tkt_life"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_opt_set_canonicalize
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_boolean req"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_opt_set_win2k
+.Fa "krb5_context context"
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "krb5_boolean req"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "krb5_principal client"
+.Fa "krb5_prompter_fct prompter"
+.Fa "void *prompter_data"
+.Fa "krb5_deltat start_time"
+.Fa "const char *in_tkt_service"
+.Fa "krb5_get_init_creds_opt *options"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_password
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "krb5_principal client"
+.Fa "const char *password"
+.Fa "krb5_prompter_fct prompter"
+.Fa "void *prompter_data"
+.Fa "krb5_deltat start_time"
+.Fa "const char *in_tkt_service"
+.Fa "krb5_get_init_creds_opt *in_options"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_init_creds_keytab
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "krb5_principal client"
+.Fa "krb5_keytab keytab"
+.Fa "krb5_deltat start_time"
+.Fa "const char *in_tkt_service"
+.Fa "krb5_get_init_creds_opt *options"
+.Fc
+.Ft int
+.Fo krb5_prompter_posix
+.Fa "krb5_context context"
+.Fa "void *data"
+.Fa "const char *name"
+.Fa "const char *banner"
+.Fa "int num_prompts"
+.Fa "krb5_prompt prompts[]"
+.Fc
+.Sh DESCRIPTION
+Getting initial credential ticket for a principal.
+That may include changing an expired password, and doing preauthentication.
+This interface that replaces the deprecated
+.Fa krb5_in_tkt
+and
+.Fa krb5_in_cred
+functions.
+.Pp
+If you only want to verify a username and password, consider using
+.Xr krb5_verify_user 3
+instead, since it also verifies that initial credentials with using a
+keytab to make sure the response was from the KDC.
+.Pp
+First a
+.Li krb5_get_init_creds_opt
+structure is initialized
+with
+.Fn krb5_get_init_creds_opt_alloc
+or
+.Fn krb5_get_init_creds_opt_init .
+.Fn krb5_get_init_creds_opt_alloc
+allocates a extendible structures that needs to be freed with
+.Fn krb5_get_init_creds_opt_free .
+The structure may be modified by any of the
+.Fn krb5_get_init_creds_opt_set
+functions to change request parameters and authentication information.
+.Pp
+If the caller want to use the default options,
+.Dv NULL
+can be passed instead.
+.Pp
+The the actual request to the KDC is done by any of the
+.Fn krb5_get_init_creds ,
+.Fn krb5_get_init_creds_password ,
+or
+.Fn krb5_get_init_creds_keytab
+functions.
+.Fn krb5_get_init_creds
+is the least specialized function and can, with the right in data,
+behave like the latter two.
+The latter two are there for compatibility with older releases and
+they are slightly easier to use.
+.Pp
+.Li krb5_prompt
+is a structure containing the following elements:
+.Bd -literal
+typedef struct {
+ const char *prompt;
+ int hidden;
+ krb5_data *reply;
+ krb5_prompt_type type
+} krb5_prompt;
+.Ed
+.Pp
+.Fa prompt
+is the prompt that should shown to the user
+If
+.Fa hidden
+is set, the prompter function shouldn't echo the output to the display
+device.
+.Fa reply
+must be preallocated; it will not be allocated by the prompter
+function.
+Possible values for the
+.Fa type
+element are:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It KRB5_PROMPT_TYPE_PASSWORD
+.It KRB5_PROMPT_TYPE_NEW_PASSWORD
+.It KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN
+.It KRB5_PROMPT_TYPE_PREAUTH
+.It KRB5_PROMPT_TYPE_INFO
+.El
+.Pp
+.Fn krb5_prompter_posix
+is the default prompter function in a POSIX environment.
+It matches the
+.Fa krb5_prompter_fct
+and can be used in the
+.Fa krb5_get_init_creds
+functions.
+.Fn krb5_prompter_posix
+doesn't require
+.Fa prompter_data.
+.Pp
+If the
+.Fa start_time
+is zero, then the requested ticket will be valid
+beginning immediately.
+Otherwise, the
+.Fa start_time
+indicates how far in the future the ticket should be postdated.
+.Pp
+If the
+.Fa in_tkt_service
+name is
+.Dv non-NULL ,
+that principal name will be
+used as the server name for the initial ticket request.
+The realm of the name specified will be ignored and will be set to the
+realm of the client name.
+If no in_tkt_service name is specified,
+krbtgt/CLIENT-REALM@CLIENT-REALM will be used.
+.Pp
+For the rest of arguments, a configuration or library default will be
+used if no value is specified in the options structure.
+.Pp
+.Fn krb5_get_init_creds_opt_set_address_list
+sets the list of
+.Fa addresses
+that is should be stored in the ticket.
+.Pp
+.Fn krb5_get_init_creds_opt_set_addressless
+controls if the ticket is requested with addresses or not,
+.Fn krb5_get_init_creds_opt_set_address_list
+overrides this option.
+.Pp
+.Fn krb5_get_init_creds_opt_set_anonymous
+make the request anonymous if the
+.Fa anonymous
+parameter is non-zero.
+.Pp
+.Fn krb5_get_init_creds_opt_set_default_flags
+sets the default flags using the configuration file.
+.Pp
+.Fn krb5_get_init_creds_opt_set_etype_list
+set a list of enctypes that the client is willing to support in the
+request.
+.Pp
+.Fn krb5_get_init_creds_opt_set_forwardable
+request a forwardable ticket.
+.Pp
+.Fn krb5_get_init_creds_opt_set_pa_password
+set the
+.Fa password
+and
+.Fa key_proc
+that is going to be used to get a new ticket.
+.Fa password
+or
+.Fa key_proc
+can be
+.Dv NULL
+if the caller wants to use the default values.
+If the
+.Fa password
+is unset and needed, the user will be prompted for it.
+.Pp
+.Fn krb5_get_init_creds_opt_set_paq_request
+sets the password that is going to be used to get a new ticket.
+.Pp
+.Fn krb5_get_init_creds_opt_set_preauth_list
+sets the list of client-supported preauth types.
+.Pp
+.Fn krb5_get_init_creds_opt_set_proxiable
+makes the request proxiable.
+.Pp
+.Fn krb5_get_init_creds_opt_set_renew_life
+sets the requested renewable lifetime.
+.Pp
+.Fn krb5_get_init_creds_opt_set_salt
+sets the salt that is going to be used in the request.
+.Pp
+.Fn krb5_get_init_creds_opt_set_tkt_life
+sets requested ticket lifetime.
+.Pp
+.Fn krb5_get_init_creds_opt_set_canonicalize
+requests that the KDC canonicalize the client principal if possible.
+.Pp
+.Fn krb5_get_init_creds_opt_set_win2k
+turns on compatibility with Windows 2000.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_creds 3 ,
+.Xr krb5_verify_user 3 ,
+.Xr krb5.conf 5 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_get_krbhst.3 b/third_party/heimdal/lib/krb5/krb5_get_krbhst.3
new file mode 100644
index 0000000..55fb8f2
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_get_krbhst.3
@@ -0,0 +1,86 @@
+.\" 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 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 April 24, 2005
+.Dt KRB5_GET_KRBHST 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_krbhst ,
+.Nm krb5_get_krb_admin_hst ,
+.Nm krb5_get_krb_changepw_hst ,
+.Nm krb5_get_krb524hst ,
+.Nm krb5_free_krbhst
+.Nd lookup Kerberos KDC hosts
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fn krb5_get_krbhst "krb5_context context" "const krb5_realm *realm" "char ***hostlist"
+.Ft krb5_error_code
+.Fn krb5_get_krb_admin_hst "krb5_context context" "const krb5_realm *realm" "char ***hostlist"
+.Ft krb5_error_code
+.Fn krb5_get_krb_changepw_hst "krb5_context context" "const krb5_realm *realm" "char ***hostlist"
+.Ft krb5_error_code
+.Fn krb5_get_krb524hst "krb5_context context" "const krb5_realm *realm" "char ***hostlist"
+.Ft krb5_error_code
+.Fn krb5_free_krbhst "krb5_context context" "char **hostlist"
+.Sh DESCRIPTION
+These functions implement the old API to get a list of Kerberos hosts,
+and are thus similar to the
+.Fn krb5_krbhst_init
+functions. However, since these functions returns
+.Em all
+hosts in one go, they potentially have to do more lookups than
+necessary. These functions remain for compatibility reasons.
+.Pp
+After a call to one of these functions,
+.Fa hostlist
+is a
+.Dv NULL
+terminated list of strings, pointing to the requested Kerberos hosts. These should be freed with
+.Fn krb5_free_krbhst
+when done with.
+.Sh EXAMPLES
+The following code will print the KDCs of the realm
+.Dq MY.REALM .
+.Bd -literal -offset indent
+char **hosts, **p;
+krb5_get_krbhst(context, "MY.REALM", &hosts);
+for(p = hosts; *p; p++)
+ printf("%s\\n", *p);
+krb5_free_krbhst(context, hosts);
+.Ed
+.\" .Sh BUGS
+.Sh SEE ALSO
+.Xr krb5_krbhst_init 3
diff --git a/third_party/heimdal/lib/krb5/krb5_getportbyname.3 b/third_party/heimdal/lib/krb5/krb5_getportbyname.3
new file mode 100644
index 0000000..0e9b7cb
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_getportbyname.3
@@ -0,0 +1,67 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd August 15, 2004
+.Dt NAME 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_getportbyname
+.Nd get port number by name
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft int
+.Fo krb5_getportbyname
+.Fa "krb5_context context"
+.Fa "const char *service"
+.Fa "const char *proto"
+.Fa "int default_port"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_getportbyname
+gets the port number for
+.Fa service /
+.Fa proto
+pair from the global service table for and returns it in network order.
+If it isn't found in the global table, the
+.Fa default_port
+(given in host order)
+is returned.
+.Sh EXAMPLE
+.Bd -literal
+int port = krb5_getportbyname(context, "kerberos", "tcp", 88);
+.Ed
+.\" .Sh BUGS
+.Sh SEE ALSO
+.Xr krb5 3
diff --git a/third_party/heimdal/lib/krb5/krb5_init_context.3 b/third_party/heimdal/lib/krb5/krb5_init_context.3
new file mode 100644
index 0000000..3a6199b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_init_context.3
@@ -0,0 +1,308 @@
+.\" Copyright (c) 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.
+.\"
+.\" $Id$
+.\"
+.Dd December 8, 2004
+.Dt KRB5_CONTEXT 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_add_et_list ,
+.Nm krb5_add_extra_addresses ,
+.Nm krb5_add_ignore_addresses ,
+.Nm krb5_context ,
+.Nm krb5_free_config_files ,
+.Nm krb5_free_context ,
+.Nm krb5_get_default_config_files ,
+.Nm krb5_get_dns_canonize_hostname ,
+.Nm krb5_get_extra_addresses ,
+.Nm krb5_get_fcache_version ,
+.Nm krb5_get_ignore_addresses ,
+.Nm krb5_get_kdc_sec_offset ,
+.Nm krb5_get_max_time_skew ,
+.Nm krb5_get_use_admin_kdc
+.Nm krb5_init_context ,
+.Nm krb5_init_ets ,
+.Nm krb5_prepend_config_files ,
+.Nm krb5_prepend_config_files_default ,
+.Nm krb5_set_config_files ,
+.Nm krb5_set_dns_canonize_hostname ,
+.Nm krb5_set_extra_addresses ,
+.Nm krb5_set_fcache_version ,
+.Nm krb5_set_ignore_addresses ,
+.Nm krb5_set_max_time_skew ,
+.Nm krb5_set_use_admin_kdc ,
+.Nd create, modify and delete krb5_context structures
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li "struct krb5_context;"
+.Pp
+.Ft krb5_error_code
+.Fo krb5_init_context
+.Fa "krb5_context *context"
+.Fc
+.Ft void
+.Fo krb5_free_context
+.Fa "krb5_context context"
+.Fc
+.Ft void
+.Fo krb5_init_ets
+.Fa "krb5_context context"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_add_et_list
+.Fa "krb5_context context"
+.Fa "void (*func)(struct et_list **)"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_add_extra_addresses
+.Fa "krb5_context context"
+.Fa "krb5_addresses *addresses"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_extra_addresses
+.Fa "krb5_context context"
+.Fa "const krb5_addresses *addresses"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_extra_addresses
+.Fa "krb5_context context"
+.Fa "krb5_addresses *addresses"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_add_ignore_addresses
+.Fa "krb5_context context"
+.Fa "krb5_addresses *addresses"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_ignore_addresses
+.Fa "krb5_context context"
+.Fa "const krb5_addresses *addresses"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_ignore_addresses
+.Fa "krb5_context context"
+.Fa "krb5_addresses *addresses"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_fcache_version
+.Fa "krb5_context context"
+.Fa "int version"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_fcache_version
+.Fa "krb5_context context"
+.Fa "int *version"
+.Fc
+.Ft void
+.Fo krb5_set_dns_canonize_hostname
+.Fa "krb5_context context"
+.Fa "krb5_boolean flag"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_get_dns_canonize_hostname
+.Fa "krb5_context context"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_kdc_sec_offset
+.Fa "krb5_context context"
+.Fa "int32_t *sec"
+.Fa "int32_t *usec"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_config_files
+.Fa "krb5_context context"
+.Fa "char **filenames"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_prepend_config_files
+.Fa "const char *filelist"
+.Fa "char **pq"
+.Fa "char ***ret_pp"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_prepend_config_files_default
+.Fa "const char *filelist"
+.Fa "char ***pfilenames"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_default_config_files
+.Fa "char ***pfilenames"
+.Fc
+.Ft void
+.Fo krb5_free_config_files
+.Fa "char **filenames"
+.Fc
+.Ft void
+.Fo krb5_set_use_admin_kdc
+.Fa "krb5_context context"
+.Fa "krb5_boolean flag"
+.Fc
+.Ft krb5_boolean
+.Fo krb5_get_use_admin_kdc
+.Fa "krb5_context context"
+.Fc
+.Ft time_t
+.Fo krb5_get_max_time_skew
+.Fa "krb5_context context"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_max_time_skew
+.Fa "krb5_context context"
+.Fa "time_t time"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn krb5_init_context
+function initializes the
+.Fa context
+structure and reads the configuration file
+.Pa /etc/krb5.conf .
+.Pp
+The structure should be freed by calling
+.Fn krb5_free_context
+when it is no longer being used.
+.Pp
+.Fn krb5_init_context
+returns 0 to indicate success.
+Otherwise an errno code is returned.
+Failure means either that something bad happened during initialization
+(typically
+.Bq ENOMEM )
+or that Kerberos should not be used
+.Bq ENXIO .
+.Pp
+.Fn krb5_init_ets
+adds all
+.Xr com_err 3
+libs to
+.Fa context .
+This is done by
+.Fn krb5_init_context .
+.Pp
+.Fn krb5_add_et_list
+adds a
+.Xr com_err 3
+error-code handler
+.Fa func
+to the specified
+.Fa context .
+The error handler must generated by the the re-rentrant version of the
+.Xr compile_et 1
+program.
+.Fn krb5_add_extra_addresses
+add a list of addresses that should be added when requesting tickets.
+.Pp
+.Fn krb5_add_ignore_addresses
+add a list of addresses that should be ignored when requesting tickets.
+.Pp
+.Fn krb5_get_extra_addresses
+get the list of addresses that should be added when requesting tickets.
+.Pp
+.Fn krb5_get_ignore_addresses
+get the list of addresses that should be ignored when requesting tickets.
+.Pp
+.Fn krb5_set_ignore_addresses
+set the list of addresses that should be ignored when requesting tickets.
+.Pp
+.Fn krb5_set_extra_addresses
+set the list of addresses that should be added when requesting tickets.
+.Pp
+.Fn krb5_set_fcache_version
+sets the version of file credentials caches that should be used.
+.Pp
+.Fn krb5_get_fcache_version
+gets the version of file credentials caches that should be used.
+.Pp
+.Fn krb5_set_dns_canonize_hostname
+sets if the context is configured to canonicalize hostnames using DNS.
+.Pp
+.Fn krb5_get_dns_canonize_hostname
+returns if the context is configured to canonicalize hostnames using DNS.
+.Pp
+.Fn krb5_get_kdc_sec_offset
+returns the offset between the localtime and the KDC's time.
+.Fa sec
+and
+.Fa usec
+are both optional argument and
+.Dv NULL
+can be passed in.
+.Pp
+.Fn krb5_set_config_files
+set the list of configuration files to use and re-initialize the
+configuration from the files.
+.Pp
+.Fn krb5_prepend_config_files
+parse the
+.Fa filelist
+and prepend the result to the already existing list
+.Fa pq
+The result is returned in
+.Fa ret_pp
+and should be freed with
+.Fn krb5_free_config_files .
+.Pp
+.Fn krb5_prepend_config_files_default
+parse the
+.Fa filelist
+and append that to the default
+list of configuration files.
+.Pp
+.Fn krb5_get_default_config_files
+get a list of default configuration files.
+.Pp
+.Fn krb5_free_config_files
+free a list of configuration files returned by
+.Fn krb5_get_default_config_files ,
+.Fn krb5_prepend_config_files_default ,
+or
+.Fn krb5_prepend_config_files .
+.Pp
+.Fn krb5_set_use_admin_kdc
+sets if all KDC requests should go admin KDC.
+.Pp
+.Fn krb5_get_use_admin_kdc
+gets if all KDC requests should go admin KDC.
+.Pp
+.Fn krb5_get_max_time_skew
+and
+.Fn krb5_set_max_time_skew
+get and sets the maximum allowed time skew between client and server.
+.Sh SEE ALSO
+.Xr errno 2 ,
+.Xr krb5 3 ,
+.Xr krb5_config 3 ,
+.Xr krb5_context 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_is_thread_safe.3 b/third_party/heimdal/lib/krb5/krb5_is_thread_safe.3
new file mode 100644
index 0000000..1a73e85
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_is_thread_safe.3
@@ -0,0 +1,58 @@
+.\" Copyright (c) 2005 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 5, 2006
+.Dt KRB5_IS_THREAD_SAFE 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_is_thread_safe
+.Nd "is the Kerberos library compiled with multithread support"
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_boolean
+.Fn krb5_is_thread_safe "void"
+.Sh DESCRIPTION
+.Nm
+returns
+.Dv TRUE
+if the library was compiled with with multithread support.
+If the library isn't compiled, the consumer have to use a global lock
+to make sure Kerboros functions are not called at the same time by
+different threads.
+.\" .Sh EXAMPLE
+.\" .Sh BUGS
+.Sh SEE ALSO
+.Xr krb5_create_checksum 3 ,
+.Xr krb5_encrypt 3
diff --git a/third_party/heimdal/lib/krb5/krb5_krbhst_init.3 b/third_party/heimdal/lib/krb5/krb5_krbhst_init.3
new file mode 100644
index 0000000..8abb00f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_krbhst_init.3
@@ -0,0 +1,174 @@
+.\" Copyright (c) 2001-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 10, 2005
+.Dt KRB5_KRBHST_INIT 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_krbhst_init ,
+.Nm krb5_krbhst_init_flags ,
+.Nm krb5_krbhst_next ,
+.Nm krb5_krbhst_next_as_string ,
+.Nm krb5_krbhst_reset ,
+.Nm krb5_krbhst_free ,
+.Nm krb5_krbhst_format_string ,
+.Nm krb5_krbhst_get_addrinfo
+.Nd lookup Kerberos KDC hosts
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fn krb5_krbhst_init "krb5_context context" "const char *realm" "unsigned int type" "krb5_krbhst_handle *handle"
+.Ft krb5_error_code
+.Fn krb5_krbhst_init_flags "krb5_context context" "const char *realm" "unsigned int type" "int flags" "krb5_krbhst_handle *handle"
+.Ft krb5_error_code
+.Fn "krb5_krbhst_next" "krb5_context context" "krb5_krbhst_handle handle" "krb5_krbhst_info **host"
+.Ft krb5_error_code
+.Fn krb5_krbhst_next_as_string "krb5_context context" "krb5_krbhst_handle handle" "char *hostname" "size_t hostlen"
+.Ft void
+.Fn krb5_krbhst_reset "krb5_context context" "krb5_krbhst_handle handle"
+.Ft void
+.Fn krb5_krbhst_free "krb5_context context" "krb5_krbhst_handle handle"
+.Ft krb5_error_code
+.Fn krb5_krbhst_format_string "krb5_context context" "const krb5_krbhst_info *host" "char *hostname" "size_t hostlen"
+.Ft krb5_error_code
+.Fn krb5_krbhst_get_addrinfo "krb5_context context" "krb5_krbhst_info *host" "struct addrinfo **ai"
+.Sh DESCRIPTION
+These functions are used to sequence through all Kerberos hosts of a
+particular realm and service. The service type can be the KDCs, the
+administrative servers, the password changing servers, or the servers
+for Kerberos 4 ticket conversion.
+.Pp
+First a handle to a particular service is obtained by calling
+.Fn krb5_krbhst_init
+(or
+.Fn krb5_krbhst_init_flags )
+with the
+.Fa realm
+of interest and the type of service to lookup. The
+.Fa type
+can be one of:
+.Pp
+.Bl -tag -width Ds -compact -offset indent
+.It KRB5_KRBHST_KDC
+.It KRB5_KRBHST_ADMIN
+.It KRB5_KRBHST_CHANGEPW
+.It KRB5_KRBHST_KRB524
+.El
+.Pp
+The
+.Fa handle
+is returned to the caller, and should be passed to the other
+functions.
+.Pp
+The
+.Fa flag
+argument to
+.Nm krb5_krbhst_init_flags
+is the same flags as
+.Fn krb5_send_to_kdc_flags
+uses.
+Possible values are:
+.Pp
+.Bl -tag -width KRB5_KRBHST_FLAGS_LARGE_MSG -compact -offset indent
+.It KRB5_KRBHST_FLAGS_MASTER
+only talk to master (readwrite) KDC
+.It KRB5_KRBHST_FLAGS_LARGE_MSG
+this is a large message, so use transport that can handle that.
+.El
+.Pp
+For each call to
+.Fn krb5_krbhst_next
+information on a new host is returned. The former function returns in
+.Fa host
+a pointer to a structure containing information about the host, such
+as protocol, hostname, and port:
+.Bd -literal -offset indent
+typedef struct krb5_krbhst_info {
+ enum { KRB5_KRBHST_UDP,
+ KRB5_KRBHST_TCP,
+ KRB5_KRBHST_HTTP } proto;
+ unsigned short port;
+ struct addrinfo *ai;
+ struct krb5_krbhst_info *next;
+ char hostname[1];
+} krb5_krbhst_info;
+.Ed
+.Pp
+The related function,
+.Fn krb5_krbhst_next_as_string ,
+return the same information as a URL-like string.
+.Pp
+When there are no more hosts, these functions return
+.Dv KRB5_KDC_UNREACH .
+.Pp
+To re-iterate over all hosts, call
+.Fn krb5_krbhst_reset
+and the next call to
+.Fn krb5_krbhst_next
+will return the first host.
+.Pp
+When done with the handle,
+.Fn krb5_krbhst_free
+should be called.
+.Pp
+To use a
+.Va krb5_krbhst_info ,
+there are two functions:
+.Fn krb5_krbhst_format_string
+that will return a printable representation of that struct
+and
+.Fn krb5_krbhst_get_addrinfo
+that will return a
+.Va struct addrinfo
+that can then be used for communicating with the server mentioned.
+.Sh EXAMPLES
+The following code will print the KDCs of the realm
+.Dq MY.REALM :
+.Bd -literal -offset indent
+krb5_krbhst_handle handle;
+char host[MAXHOSTNAMELEN];
+krb5_krbhst_init(context, "MY.REALM", KRB5_KRBHST_KDC, &handle);
+while(krb5_krbhst_next_as_string(context, handle,
+ host, sizeof(host)) == 0)
+ printf("%s\\n", host);
+krb5_krbhst_free(context, handle);
+.Ed
+.\" .Sh BUGS
+.Sh SEE ALSO
+.Xr getaddrinfo 3 ,
+.Xr krb5_get_krbhst 3 ,
+.Xr krb5_send_to_kdc_flags 3
+.Sh HISTORY
+These functions first appeared in Heimdal 0.3g.
diff --git a/third_party/heimdal/lib/krb5/krb5_locl.h b/third_party/heimdal/lib/krb5/krb5_locl.h
new file mode 100644
index 0000000..57e7819
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_locl.h
@@ -0,0 +1,491 @@
+/*
+ * Copyright (c) 1997-2016 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.
+ */
+
+/* $Id$ */
+
+#ifndef __KRB5_LOCL_H__
+#define __KRB5_LOCL_H__
+
+#include <config.h>
+#include <roken.h>
+
+#include <ctype.h>
+
+#ifdef HAVE_POLL_H
+#include <sys/poll.h>
+#endif
+
+#include <krb5-types.h>
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
+#include <sys/ioctl.h>
+#endif
+#ifdef HAVE_PWD_H
+#undef _POSIX_PTHREAD_SEMANTICS
+/* This gets us the 5-arg getpwnam_r on Solaris 9. */
+#define _POSIX_PTHREAD_SEMANTICS
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#ifdef _AIX
+struct mbuf;
+#endif
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+#include <com_err.h>
+
+#include <heimbase.h>
+#include "heimbase-atomics.h"
+
+#define HEIMDAL_TEXTDOMAIN "heimdal_krb5"
+
+#ifdef LIBINTL
+#include <libintl.h>
+#undef N_
+#define N_(x,y) dgettext(HEIMDAL_TEXTDOMAIN, x)
+#else
+#undef N_
+#define N_(x,y) (x)
+#define bindtextdomain(package, localedir)
+#endif
+
+
+#ifdef HAVE_CRYPT_H
+#undef des_encrypt
+#define des_encrypt wingless_pigs_mostly_fail_to_fly
+#include <crypt.h>
+#undef des_encrypt
+#endif
+
+#ifdef HAVE_DOOR_CREATE
+#include <door.h>
+#endif
+
+#include <parse_time.h>
+#include <base64.h>
+
+#include <wind.h>
+
+/*
+ * We use OpenSSL for EC, but to do this we need to disable cross-references
+ * between OpenSSL and hcrypto bn.h and such. Source files that use OpenSSL EC
+ * must define HEIM_NO_CRYPTO_HDRS before including this file.
+ */
+#define HC_DEPRECATED_CRYPTO
+#ifndef HEIM_NO_CRYPTO_HDRS
+#include "crypto-headers.h"
+#endif
+
+
+#include <krb5_asn1.h>
+typedef Krb5Int32 krb5int32;
+typedef Krb5UInt32 krb5uint32;
+#include <pkinit_asn1.h>
+
+struct send_to_kdc;
+
+/* XXX glue for pkinit */
+struct hx509_certs_data;
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+struct ContentInfo;
+struct AlgorithmIdentifier;
+typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
+struct krb5_dh_moduli;
+struct krb5_fast_state;
+struct krb5_gss_init_ctx_data;
+
+/* v4 glue */
+struct _krb5_krb_auth_data;
+
+struct krb5_gss_init_ctx_data;
+typedef struct krb5_gss_init_ctx_data *krb5_gss_init_ctx;
+
+struct gss_ctx_id_t_desc_struct;
+struct gss_cred_id_t_desc_struct;
+struct gss_OID_desc_struct;
+
+#include <der.h>
+
+#include <krb5.h>
+#include <krb5_err.h>
+#include <k5e1_err.h>
+#include <asn1_err.h>
+#ifdef PKINIT
+#include <hx509.h>
+#endif
+
+#include "crypto.h"
+
+typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_step)(
+ krb5_context,
+ krb5_gss_init_ctx,
+ const krb5_creds *,
+ struct gss_ctx_id_t_desc_struct **,
+ KDCOptions options,
+ krb5_data *,
+ krb5_data *,
+ krb5_data *);
+
+typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_finish)(
+ krb5_context,
+ krb5_gss_init_ctx,
+ const krb5_creds *,
+ struct gss_ctx_id_t_desc_struct *,
+ krb5int32,
+ krb5_enctype,
+ krb5_principal *,
+ krb5_keyblock **);
+
+typedef void (KRB5_LIB_CALL *krb5_gssic_release_cred)(
+ krb5_context,
+ krb5_gss_init_ctx,
+ struct gss_cred_id_t_desc_struct *);
+
+typedef void (KRB5_LIB_CALL *krb5_gssic_delete_sec_context)(
+ krb5_context,
+ krb5_gss_init_ctx,
+ struct gss_ctx_id_t_desc_struct *);
+
+#define KRB5_GSS_IC_FLAG_RELEASE_CRED 1
+
+#include <krb5-private.h>
+
+#include "heim_threads.h"
+
+extern const char _krb5_wellknown_lkdc[];
+
+#define ALLOC(X, N) (X) = calloc((N), sizeof(*(X)))
+#define ALLOC_SEQ(X, N) do { (X)->len = (N); ALLOC((X)->val, (N)); } while(0)
+
+#define krb5_einval(context, argnum) _krb5_einval((context), __func__, (argnum))
+
+#ifndef PATH_SEP
+#define PATH_SEP ":"
+#endif
+
+/* should this be public? */
+#define KEYTAB_DEFAULT "FILE:" SYSCONFDIR "/krb5.keytab"
+#define KEYTAB_DEFAULT_MODIFY "FILE:" SYSCONFDIR "/krb5.keytab"
+
+#ifndef CLIENT_KEYTAB_DEFAULT
+#define CLIENT_KEYTAB_DEFAULT "FILE:" LOCALSTATEDIR "/user/%{euid}/client.keytab";
+#endif
+
+#define MODULI_FILE SYSCONFDIR "/krb5.moduli"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+
+#define KRB5_BUFSIZ 2048
+
+typedef enum {
+ KRB5_INIT_CREDS_TRISTATE_UNSET = 0,
+ KRB5_INIT_CREDS_TRISTATE_TRUE,
+ KRB5_INIT_CREDS_TRISTATE_FALSE
+} krb5_get_init_creds_tristate;
+
+struct _krb5_get_init_creds_opt_private {
+ int refcount;
+ /* ENC_TIMESTAMP */
+ const char *password;
+ krb5_s2k_proc key_proc;
+ /* PA_PAC_REQUEST */
+ krb5_get_init_creds_tristate req_pac;
+ /* PKINIT */
+ krb5_pk_init_ctx pk_init_ctx;
+ krb5_get_init_creds_tristate addressless;
+ int flags;
+#define KRB5_INIT_CREDS_DONE 1
+#define KRB5_INIT_CREDS_CANONICALIZE 2
+#define KRB5_INIT_CREDS_NO_C_CANON_CHECK 4
+#define KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK 8
+#define KRB5_INIT_CREDS_PKINIT_KX_VALID 32
+#define KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK 64
+ struct {
+ krb5_gic_process_last_req func;
+ void *ctx;
+ } lr;
+
+ krb5_flags fast_flags;
+ char *fast_armor_ccache_name;
+};
+
+typedef uint32_t krb5_enctype_set;
+
+/*
+ * Do not remove or reorder the fields of this structure.
+ * Fields that are no longer used should be marked "deprecated".
+ * New fields should always be appended to the end of the
+ * structure.
+ *
+ * Although this structure is internal it is shared with
+ * plugins and such changes will result in data corruption
+ * if plugins are not built with a matching version.
+ */
+typedef struct krb5_context_data {
+ heim_context hcontext;
+ krb5_enctype *etypes;
+ krb5_enctype *cfg_etypes;
+ krb5_enctype *etypes_des;/* deprecated */
+ krb5_enctype *as_etypes;
+ krb5_enctype *tgs_etypes;
+ krb5_enctype *permitted_enctypes;
+ char **default_realms;
+ time_t max_skew;
+ time_t kdc_timeout;
+ time_t host_timeout;
+ unsigned max_retries;
+ int32_t kdc_sec_offset;
+ int32_t kdc_usec_offset;
+ krb5_config_section *cf;
+ const krb5_cc_ops **cc_ops;
+ int num_cc_ops;
+ const char *http_proxy;
+ const char *time_fmt;
+ krb5_boolean log_utc;
+ const char *default_keytab;
+ const char *default_keytab_modify;
+ krb5_boolean use_admin_kdc;
+ krb5_addresses *extra_addresses;
+ krb5_boolean scan_interfaces; /* `ifconfig -a' */
+ krb5_boolean srv_lookup; /* do SRV lookups */
+ krb5_boolean srv_try_txt; /* try TXT records also */
+ int32_t fcache_vno; /* create cache files w/ this
+ version */
+ int num_kt_types; /* # of registered keytab types */
+ struct krb5_keytab_data *kt_types; /* registered keytab types */
+ const char *date_fmt;
+ krb5_error_code error_code;
+ krb5_addresses *ignore_addresses;
+ char *default_cc_name;
+ char *default_cc_name_env;
+ char *configured_default_cc_name;
+ int default_cc_name_set;
+ int large_msg_size;
+ int max_msg_size;
+ int tgs_negative_timeout; /* timeout for TGS negative cache */
+ int flags;
+#define KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME 1
+#define KRB5_CTX_F_CHECK_PAC 2
+#define KRB5_CTX_F_HOMEDIR_ACCESS 4
+#define KRB5_CTX_F_SOCKETS_INITIALIZED 8
+#define KRB5_CTX_F_RD_REQ_IGNORE 16
+#define KRB5_CTX_F_FCACHE_STRICT_CHECKING 32
+#define KRB5_CTX_F_ENFORCE_OK_AS_DELEGATE 64
+#define KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME 128
+ struct send_to_kdc *send_to_kdc;
+#ifdef PKINIT
+ hx509_context hx509ctx;
+#endif
+ unsigned int num_kdc_requests;
+ krb5_name_canon_rule name_canon_rules;
+ size_t config_include_depth;
+ krb5_boolean no_ticket_store; /* Don't store service tickets */
+} krb5_context_data;
+
+#define KRB5_DEFAULT_CCNAME_FILE "FILE:%{TEMP}/krb5cc_%{uid}"
+#define KRB5_DEFAULT_CCNAME_DIR "DIR:%{TEMP}/krb5cc_%{uid}_dir/"
+#define KRB5_DEFAULT_CCNAME_API "API:"
+#define KRB5_DEFAULT_CCNAME_KCM_KCM "KCM:%{uid}"
+#define KRB5_DEFAULT_CCNAME_KCM_API "API:%{uid}"
+
+#define EXTRACT_TICKET_ALLOW_CNAME_MISMATCH 1
+#define EXTRACT_TICKET_ALLOW_SERVER_MISMATCH 2
+#define EXTRACT_TICKET_MATCH_REALM 4
+#define EXTRACT_TICKET_AS_REQ 8
+#define EXTRACT_TICKET_TIMESYNC 16
+#define EXTRACT_TICKET_MATCH_ANON 32
+
+/*
+ * Configurable options
+ */
+
+#ifndef KRB5_DEFAULT_CCTYPE
+#ifdef __APPLE__
+#define KRB5_DEFAULT_CCTYPE (&krb5_acc_ops)
+#else
+#define KRB5_DEFAULT_CCTYPE (&krb5_fcc_ops)
+#endif
+#endif
+
+#ifndef KRB5_ADDRESSLESS_DEFAULT
+#define KRB5_ADDRESSLESS_DEFAULT TRUE
+#endif
+
+#ifndef KRB5_FORWARDABLE_DEFAULT
+#define KRB5_FORWARDABLE_DEFAULT TRUE
+#endif
+
+#ifndef KRB5_CONFIGURATION_CHANGE_NOTIFY_NAME
+#define KRB5_CONFIGURATION_CHANGE_NOTIFY_NAME "org.h5l.Kerberos.configuration-changed"
+#endif
+
+#ifndef KRB5_FALLBACK_DEFAULT
+#define KRB5_FALLBACK_DEFAULT TRUE
+#endif
+
+#ifndef KRB5_TKT_LIFETIME_DEFAULT
+# define KRB5_TKT_LIFETIME_DEFAULT 15778800 /* seconds */
+#endif
+
+#ifndef KRB5_TKT_RENEW_LIFETIME_DEFAULT
+# define KRB5_TKT_RENEW_LIFETIME_DEFAULT 15778800 /* seconds */
+#endif
+
+#ifdef PKINIT
+
+struct krb5_pk_identity {
+ hx509_verify_ctx verify_ctx;
+ hx509_certs certs;
+ hx509_cert cert;
+ hx509_certs anchors;
+ hx509_certs certpool;
+ hx509_revoke_ctx revokectx;
+ int flags;
+#define PKINIT_BTMM 1
+#define PKINIT_NO_KDC_ANCHOR 2
+};
+
+enum krb5_pk_type {
+ PKINIT_WIN2K = 1,
+ PKINIT_27 = 2
+};
+
+enum keyex_enum { USE_RSA, USE_DH, USE_ECDH };
+
+struct krb5_pk_init_ctx_data {
+ struct krb5_pk_identity *id;
+ enum keyex_enum keyex;
+ union {
+ DH *dh;
+ void *eckey;
+ } u;
+ krb5_data *clientDHNonce;
+ struct krb5_dh_moduli **m;
+ hx509_peer_info peer;
+ enum krb5_pk_type type;
+ unsigned int require_binding:1;
+ unsigned int require_eku:1;
+ unsigned int require_krbtgt_otherName:1;
+ unsigned int require_hostname_match:1;
+ unsigned int trustedCertifiers:1;
+ unsigned int anonymous:1;
+ unsigned int kdc_verified:1;
+};
+
+#endif /* PKINIT */
+
+struct krb5_fast_state {
+ enum PA_FX_FAST_REQUEST_enum type;
+ unsigned int flags;
+#define KRB5_FAST_PUBLIC_FLAGS 0x0000ff
+/* #define KRB5_FAST_REQUIRED 0x000001 - fast required by action of caller defined in krb5.h*/
+
+#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 0x000100
+#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 0x000200
+#define KRB5_FAST_KDC_REPLY_KEY_REPLACED 0x000400
+#define KRB5_FAST_REPLY_REPLY_VERIFIED 0x000800
+#define KRB5_FAST_STRONG 0x001000
+#define KRB5_FAST_EXPECTED 0x002000 /* in exchange with KDC, fast was discovered */
+#define KRB5_FAST_DISABLED 0x008000
+
+#define KRB5_FAST_AP_ARMOR_SERVICE 0x010000
+#define KRB5_FAST_OPTIMISTIC 0x020000 /* Optimistic try, like Anon + PKINIT or service fast bit */
+#define KRB5_FAST_REQUIRE_ENC_PA 0x040000
+
+#define KRB5_FAST_AS_REQ 0x100000
+#define KRB5_FAST_ANON_PKINIT_ARMOR 0x200000
+#define KRB5_FAST_KDC_VERIFIED 0x400000
+
+ krb5_keyblock *reply_key;
+ krb5_ccache armor_ccache;
+ krb5_auth_context armor_ac;
+ KrbFastArmor *armor_data;
+ krb5_principal armor_service;
+ krb5_crypto armor_crypto;
+ krb5_keyblock armor_key;
+ krb5_keyblock *strengthen_key;
+
+ /* KRB5_FAST_ANON_PKINIT_ARMOR */
+ krb5_get_init_creds_opt *anon_pkinit_opt;
+ krb5_init_creds_context anon_pkinit_ctx;
+};
+
+struct krb5_decrypt_tkt_with_subkey_state {
+ krb5_keyblock *subkey;
+ struct krb5_fast_state *fast_state;
+};
+
+#define ISTILDE(x) (x == '~')
+#ifdef _WIN32
+# define ISPATHSEP(x) (x == '/' || x =='\\')
+#else
+# define ISPATHSEP(x) (x == '/')
+#endif
+
+/* Flag in KRB5_AUTHDATA_AP_OPTIONS */
+#define KERB_AP_OPTIONS_CBT 0x00004000
+
+/* Flag in PAC_ATTRIBUTES_INFO */
+#define KRB5_PAC_WAS_REQUESTED 0x1
+#define KRB5_PAC_WAS_GIVEN_IMPLICITLY 0x2
+
+#endif /* __KRB5_LOCL_H__ */
diff --git a/third_party/heimdal/lib/krb5/krb5_mk_req.3 b/third_party/heimdal/lib/krb5/krb5_mk_req.3
new file mode 100644
index 0000000..41701b0
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_mk_req.3
@@ -0,0 +1,187 @@
+.\" Copyright (c) 2005 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd August 27, 2005
+.Dt KRB5_MK_REQ 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_mk_req ,
+.Nm krb5_mk_req_exact ,
+.Nm krb5_mk_req_extended ,
+.Nm krb5_rd_req ,
+.Nm krb5_rd_req_with_keyblock ,
+.Nm krb5_mk_rep ,
+.Nm krb5_mk_rep_exact ,
+.Nm krb5_mk_rep_extended ,
+.Nm krb5_rd_rep ,
+.Nm krb5_build_ap_req ,
+.Nm krb5_verify_ap_req
+.Nd create and read application authentication request
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_mk_req
+.Fa "krb5_context context"
+.Fa "krb5_auth_context *auth_context"
+.Fa "const krb5_flags ap_req_options"
+.Fa "const char *service"
+.Fa "const char *hostname"
+.Fa "krb5_data *in_data"
+.Fa "krb5_ccache ccache"
+.Fa "krb5_data *outbuf"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_mk_req_extended
+.Fa "krb5_context context"
+.Fa "krb5_auth_context *auth_context"
+.Fa "const krb5_flags ap_req_options"
+.Fa "krb5_data *in_data"
+.Fa "krb5_creds *in_creds"
+.Fa "krb5_data *outbuf"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rd_req
+.Fa "krb5_context context"
+.Fa "krb5_auth_context *auth_context"
+.Fa "const krb5_data *inbuf"
+.Fa "krb5_const_principal server"
+.Fa "krb5_keytab keytab"
+.Fa "krb5_flags *ap_req_options"
+.Fa "krb5_ticket **ticket"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_build_ap_req
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "krb5_creds *cred"
+.Fa "krb5_flags ap_options"
+.Fa "krb5_data authenticator"
+.Fa "krb5_data *retdata"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_verify_ap_req
+.Fa "krb5_context context"
+.Fa "krb5_auth_context *auth_context"
+.Fa "krb5_ap_req *ap_req"
+.Fa "krb5_const_principal server"
+.Fa "krb5_keyblock *keyblock"
+.Fa "krb5_flags flags"
+.Fa "krb5_flags *ap_req_options"
+.Fa "krb5_ticket **ticket"
+.Fc
+.Sh DESCRIPTION
+The functions documented in this manual page document the functions
+that facilitates the exchange between a Kerberos client and server.
+They are the core functions used in the authentication exchange
+between the client and the server.
+.Pp
+The
+.Nm krb5_mk_req
+and
+.Nm krb5_mk_req_extended
+creates the Kerberos message
+.Dv KRB_AP_REQ
+that is sent from the client to the server as the first packet in a client/server exchange. The result that should be sent to server is stored in
+.Fa outbuf .
+.Pp
+.Fa auth_context
+should be allocated with
+.Fn krb5_auth_con_init
+or
+.Dv NULL
+passed in, in that case, it will be allocated and freed internally.
+.Pp
+The input data
+.Fa in_data
+will have a checksum calculated over it and checksum will be
+transported in the message to the server.
+.Pp
+.Fa ap_req_options
+can be set to one or more of the following flags:
+.Pp
+.Bl -tag -width indent
+.It Dv AP_OPTS_USE_SESSION_KEY
+Use the session key when creating the request, used for user to user
+authentication.
+.It Dv AP_OPTS_MUTUAL_REQUIRED
+Mark the request as mutual authenticate required so that the receiver
+returns a mutual authentication packet.
+.El
+.Pp
+The
+.Nm krb5_rd_req
+read the AP_REQ in
+.Fa inbuf
+and verify and extract the content.
+If
+.Fa server
+is specified, that server will be fetched from the
+.Fa keytab
+and used unconditionally.
+If
+.Fa server
+is
+.Dv NULL ,
+the
+.Fa keytab
+will be search for a matching principal.
+.Pp
+The
+.Fa keytab
+argument specifies what keytab to search for receiving principals.
+The arguments
+.Fa ap_req_options
+and
+.Fa ticket
+returns the content.
+.Pp
+When the AS-REQ is a user to user request, neither of
+.Fa keytab
+or
+.Fa principal
+are used, instead
+.Fn krb5_rd_req
+expects the session key to be set in
+.Fa auth_context .
+.Pp
+The
+.Nm krb5_verify_ap_req
+and
+.Nm krb5_build_ap_req
+both constructs and verify the AP_REQ message, should not be used by
+external code.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_mk_safe.3 b/third_party/heimdal/lib/krb5/krb5_mk_safe.3
new file mode 100644
index 0000000..e8f4559
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_mk_safe.3
@@ -0,0 +1,82 @@
+.\" Copyright (c) 2003 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_MK_SAFE 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_mk_safe ,
+.Nm krb5_mk_priv
+.Nd generates integrity protected and/or encrypted messages
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Ft krb5_error_code
+.Fn krb5_mk_priv "krb5_context context" "krb5_auth_context auth_context" "const krb5_data *userdata" "krb5_data *outbuf" "krb5_replay_data *outdata"
+.Ft krb5_error_code
+.Fn krb5_mk_safe "krb5_context context" "krb5_auth_context auth_context" "const krb5_data *userdata" "krb5_data *outbuf" "krb5_replay_data *outdata"
+.Sh DESCRIPTION
+.Fn krb5_mk_safe
+and
+.Fn krb5_mk_priv
+formats
+.Li KRB-SAFE
+(integrity protected)
+and
+.Li KRB-PRIV
+(also encrypted)
+messages into
+.Fa outbuf .
+The actual message data is taken from
+.Fa userdata .
+If the
+.Dv KRB5_AUTH_CONTEXT_DO_SEQUENCE
+or
+.Dv KRB5_AUTH_CONTEXT_DO_TIME
+flags are set in the
+.Fa auth_context ,
+sequence numbers and time stamps are generated.
+If the
+.Dv KRB5_AUTH_CONTEXT_RET_SEQUENCE
+or
+.Dv KRB5_AUTH_CONTEXT_RET_TIME
+flags are set
+they are also returned in the
+.Fa outdata
+parameter.
+.Sh SEE ALSO
+.Xr krb5_auth_con_init 3 ,
+.Xr krb5_rd_priv 3 ,
+.Xr krb5_rd_safe 3
diff --git a/third_party/heimdal/lib/krb5/krb5_openlog.3 b/third_party/heimdal/lib/krb5/krb5_openlog.3
new file mode 100644
index 0000000..09de9d0
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_openlog.3
@@ -0,0 +1,305 @@
+.\" Copyright (c) 1997, 1999, 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.
+.\"
+.\" $Id$
+.Dd August 6, 1997
+.Dt KRB5_OPENLOG 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_initlog ,
+.Nm krb5_openlog ,
+.Nm krb5_closelog ,
+.Nm krb5_addlog_dest ,
+.Nm krb5_addlog_func ,
+.Nm krb5_log ,
+.Nm krb5_vlog ,
+.Nm krb5_log_msg ,
+.Nm krb5_vlog_msg
+.Nd Heimdal logging functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft "typedef void"
+.Fn "\*(lp*krb5_log_log_func_t\*(rp" "const char *time" "const char *message" "void *data"
+.Ft "typedef void"
+.Fn "\*(lp*krb5_log_close_func_t\*(rp" "void *data"
+.Ft krb5_error_code
+.Fn krb5_addlog_dest "krb5_context context" "krb5_log_facility *facility" "const char *destination"
+.Ft krb5_error_code
+.Fn krb5_addlog_func "krb5_context context" "krb5_log_facility *facility" "int min" "int max" "krb5_log_log_func_t log" "krb5_log_close_func_t close" "void *data"
+.Ft krb5_error_code
+.Fn krb5_closelog "krb5_context context" "krb5_log_facility *facility"
+.Ft krb5_error_code
+.Fn krb5_initlog "krb5_context context" "const char *program" "krb5_log_facility **facility"
+.Ft krb5_error_code
+.Fn krb5_log "krb5_context context" "krb5_log_facility *facility" "int level" "const char *format" "..."
+.Ft krb5_error_code
+.Fn krb5_log_msg "krb5_context context" "krb5_log_facility *facility" "char **reply" "int level" "const char *format" "..."
+.Ft krb5_error_code
+.Fn krb5_openlog "krb5_context context" "const char *program" "krb5_log_facility **facility"
+.Ft krb5_error_code
+.Fn krb5_vlog "krb5_context context" "krb5_log_facility *facility" "int level" "const char *format" "va_list arglist"
+.Ft krb5_error_code
+.Fn krb5_vlog_msg "krb5_context context" "krb5_log_facility *facility" "char **reply" "int level" "const char *format" "va_list arglist"
+.Sh DESCRIPTION
+These functions logs messages to one or more destinations.
+.Pp
+The
+.Fn krb5_openlog
+function creates a logging
+.Fa facility ,
+that is used to log messages. A facility consists of one or more
+destinations (which can be files or syslog or some other device). The
+.Fa program
+parameter should be the generic name of the program that is doing the
+logging. This name is used to lookup which destinations to use. This
+information is contained in the
+.Li logging
+section of the
+.Pa krb5.conf
+configuration file. If no entry is found for
+.Fa program ,
+the entry for
+.Li default
+is used, or if that is missing too,
+.Li SYSLOG
+will be used as destination.
+.Pp
+To close a logging facility, use the
+.Fn krb5_closelog
+function.
+.Pp
+To log a message to a facility use one of the functions
+.Fn krb5_log ,
+.Fn krb5_log_msg ,
+.Fn krb5_vlog ,
+or
+.Fn krb5_vlog_msg .
+The functions ending in
+.Li _msg
+return in
+.Fa reply
+a pointer to the message that just got logged. This string is allocated,
+and should be freed with
+.Fn free .
+The
+.Fa format
+is a standard
+.Fn printf
+style format string (but see the BUGS section).
+.Pp
+If you want better control of where things gets logged, you can instead of using
+.Fn krb5_openlog
+call
+.Fn krb5_initlog ,
+which just initializes a facility, but doesn't define any actual logging
+destinations. You can then add destinations with the
+.Fn krb5_addlog_dest
+and
+.Fn krb5_addlog_func
+functions. The first of these takes a string specifying a logging
+destination, and adds this to the facility. If you want to do some
+non-standard logging you can use the
+.Fn krb5_addlog_func
+function, which takes a function to use when logging.
+The
+.Fa log
+function is called for each message with
+.Fa time
+being a string specifying the current time, and
+.Fa message
+the message to log.
+.Fa close
+is called when the facility is closed. You can pass application specific data in the
+.Fa data
+parameter. The
+.Fa min
+and
+.Fa max
+parameter are the same as in a destination (defined below). To specify a
+max of infinity, pass -1.
+.Pp
+.Fn krb5_openlog
+calls
+.Fn krb5_initlog
+and then calls
+.Fn krb5_addlog_dest
+for each destination found.
+.Ss Destinations
+The defined destinations (as specified in
+.Pa krb5.conf )
+follows:
+.Bl -tag -width "xxx" -offset indent
+.It Li STDERR
+This logs to the program's stderr.
+.It Li EFILE: Ns Pa /file
+Log to the specified file if it exists, otherwise do nothing.
+All writes will be appended to the end of the file and the file
+will be re-opened for each new write.
+Non-existence of the file is cached for 1 second which reduces
+the potential performance impact significantly.
+This is useful for defining a trace file which can be enabled
+without restarting a server.
+.It Li FILE: Ns Pa /file
+Log to the specified file.
+All writes will be appended to the end of the file and the file
+will be re-opened for each new write.
+.It Li FILE= Ns Pa /file
+On the first write, this form will
+.Xr truncate 2
+the file and then append all subsequent messages whilst keeping the
+file descriptor open.
+This form is mainly for compatibility with MIT libkrb5.
+.It Li DEVICE= Ns Pa /device
+This logs to the specified device, at present this is the same as
+.Li FILE:/device .
+.It Li CONSOLE
+Log to the console, this is the same as
+.Li DEVICE=/dev/console .
+.It Li SYSLOG Ns Op :priority Ns Op :facility
+Send messages to the syslog system, using priority, and facility. To
+get the name for one of these, you take the name of the macro passed
+to
+.Xr syslog 3 ,
+and remove the leading
+.Li LOG_
+.No ( Li LOG_NOTICE
+becomes
+.Li NOTICE ) .
+The default values (as well as the values used for unrecognised
+values), are
+.Li ERR ,
+and
+.Li AUTH ,
+respectively. See
+.Xr syslog 3
+for a list of priorities and facilities.
+.El
+.Pp
+Each destination may optionally be prepended with a range of logging
+levels, specified as
+.Li min-max/ .
+If the
+.Fa level
+parameter to
+.Fn krb5_log
+is within this range (inclusive) the message gets logged to this
+destination, otherwise not. Either of the min and max valued may be
+omitted, in this case min is assumed to be 0, and max is assumed to
+be 3.
+If you don't include a dash, both min and max get set to the
+specified value.
+.Pp
+The paths specified are subject to token expansion.
+For the purposes of logging, the most interesting token
+expansion is
+.ar %{strftime:<string>}
+which calls
+.Xr strftime 3
+on
+.Ar <string>
+with the localised current time of day.
+.Ss Levels
+Each log message has a level as follows:
+.Bl -tag -width "xxx" -offset indent
+.It 0
+Critical conditions.
+This is a condition that should be corrected immediately, such as a
+corrupted Kerberos database.
+.It 1
+Errors.
+These are errors that occur in the normal processing of requests.
+.It 2
+Warning messages.
+On the KDC, this includes malformed requests and requests that
+are out of policy.
+.It 3
+Informational messages.
+.It 4-6
+Debugging messages with increasing obscurity as the level rises.
+.It 7
+Tracing messages.
+These messages may be high volume and are likely to impact
+performance significantly.
+Notably, tracing messages may be emitted whilst locks are held.
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+[logging]
+ kdc = 0/FILE:/var/log/kdc.log
+ kdc = 1-/SYSLOG:INFO:USER
+ default = STDERR
+.Ed
+.Pp
+This will log all messages from the
+.Nm kdc
+program with level 0 to
+.Pa /var/log/kdc.log ,
+other messages will be logged to syslog with priority
+.Li LOG_INFO ,
+and facility
+.Li LOG_USER .
+.Bd -literal -offset indent
+[logging]
+ kdc = FILE:/var/log/kdc-%{strftime:%Y%m%d%H}
+ kdc = 4-/EFILE:/tmp/kdc-trace
+.Ed
+.Pp
+This will log all messages from the
+.Nm kdc
+program with level 0 to 3 (inclusively) to a file whose
+name is generated using
+.Xr strftime 3 .
+As the file is
+.Xr open 2 ed
+each time a log message is written, this can be used to write
+automatically rotating log files.
+All of the KDC debugging messages will be written into
+.Pa /tmp/kdc-trace
+but only if it exists.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr krb5.conf 5
+.Sh BUGS
+These functions use
+.Fn asprintf
+to format the message. If your operating system does not have a working
+.Fn asprintf ,
+a replacement will be used. At present this replacement does not handle
+some correct conversion specifications (like floating point numbers). Until
+this is fixed, the use of these conversions should be avoided.
+.Pp
+If logging is done to the syslog facility, these functions might not be
+thread-safe, depending on the implementation of
+.Fn openlog ,
+and
+.Fn syslog .
diff --git a/third_party/heimdal/lib/krb5/krb5_parse_name.3 b/third_party/heimdal/lib/krb5/krb5_parse_name.3
new file mode 100644
index 0000000..85acc72
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_parse_name.3
@@ -0,0 +1,67 @@
+.\" 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.
+.\"
+.\" $Id$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_PARSE_NAME 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_parse_name
+.Nd string to principal conversion
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fn krb5_parse_name "krb5_context context" "const char *name" "krb5_principal *principal"
+.Sh DESCRIPTION
+.Fn krb5_parse_name
+converts a string representation of a principal name to
+.Nm krb5_principal .
+The
+.Fa principal
+will point to allocated data that should be freed with
+.Fn krb5_free_principal .
+.Pp
+The string should consist of one or more name components separated with slashes
+.Pq Dq / ,
+optionally followed with an
+.Dq @
+and a realm name. A slash or @ may be contained in a name component by
+quoting it with a backslash
+.Pq Dq \e .
+A realm should not contain slashes or colons.
+.Sh SEE ALSO
+.Xr krb5_build_principal 3 ,
+.Xr krb5_free_principal 3 ,
+.Xr krb5_sname_to_principal 3 ,
+.Xr krb5_unparse_name 3
diff --git a/third_party/heimdal/lib/krb5/krb5_principal.3 b/third_party/heimdal/lib/krb5/krb5_principal.3
new file mode 100644
index 0000000..c8d2fec
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_principal.3
@@ -0,0 +1,371 @@
+.\" Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_PRINCIPAL 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_get_default_principal ,
+.Nm krb5_principal ,
+.Nm krb5_build_principal ,
+.Nm krb5_build_principal_ext ,
+.Nm krb5_build_principal_va ,
+.Nm krb5_build_principal_va_ext ,
+.Nm krb5_copy_principal ,
+.Nm krb5_free_principal ,
+.Nm krb5_make_principal ,
+.Nm krb5_parse_name ,
+.Nm krb5_parse_name_flags ,
+.Nm krb5_parse_nametype ,
+.Nm krb5_princ_set_realm ,
+.Nm krb5_principal_compare ,
+.Nm krb5_principal_compare_any_realm ,
+.Nm krb5_principal_get_comp_string ,
+.Nm krb5_principal_get_realm ,
+.Nm krb5_principal_get_type ,
+.Nm krb5_principal_match ,
+.Nm krb5_principal_set_type ,
+.Nm krb5_realm_compare ,
+.Nm krb5_sname_to_principal ,
+.Nm krb5_sock_to_principal ,
+.Nm krb5_unparse_name ,
+.Nm krb5_unparse_name_flags ,
+.Nm krb5_unparse_name_fixed ,
+.Nm krb5_unparse_name_fixed_flags ,
+.Nm krb5_unparse_name_fixed_short ,
+.Nm krb5_unparse_name_short
+.Nd Kerberos 5 principal handling functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li krb5_principal ;
+.Ft void
+.Fn krb5_free_principal "krb5_context context" "krb5_principal principal"
+.Ft krb5_error_code
+.Fn krb5_parse_name "krb5_context context" "const char *name" "krb5_principal *principal"
+.Ft krb5_error_code
+.Fn krb5_parse_name_flags "krb5_context context" "const char *name" "int flags" "krb5_principal *principal"
+.Ft krb5_error_code
+.Fn "krb5_unparse_name" "krb5_context context" "krb5_const_principal principal" "char **name"
+.Ft krb5_error_code
+.Fn "krb5_unparse_name_flags" "krb5_context context" "krb5_const_principal principal" "int flags" "char **name"
+.Ft krb5_error_code
+.Fn krb5_unparse_name_fixed "krb5_context context" "krb5_const_principal principal" "char *name" "size_t len"
+.Ft krb5_error_code
+.Fn krb5_unparse_name_fixed_flags "krb5_context context" "krb5_const_principal principal" "int flags" "char *name" "size_t len"
+.Ft krb5_error_code
+.Fn "krb5_unparse_name_short" "krb5_context context" "krb5_const_principal principal" "char **name"
+.Ft krb5_error_code
+.Fn krb5_unparse_name_fixed_short "krb5_context context" "krb5_const_principal principal" "char *name" "size_t len"
+.Ft void
+.Fn krb5_princ_set_realm "krb5_context context" "krb5_principal principal" "krb5_realm *realm"
+.Ft krb5_error_code
+.Fn krb5_build_principal "krb5_context context" "krb5_principal *principal" "int rlen" "krb5_const_realm realm" "..."
+.Ft krb5_error_code
+.Fn krb5_build_principal_va "krb5_context context" "krb5_principal *principal" "int rlen" "krb5_const_realm realm" "va_list ap"
+.Ft krb5_error_code
+.Fn "krb5_build_principal_ext" "krb5_context context" "krb5_principal *principal" "int rlen" "krb5_const_realm realm" "..."
+.Ft krb5_error_code
+.Fn krb5_build_principal_va_ext "krb5_context context" "krb5_principal *principal" "int rlen" "krb5_const_realm realm" "va_list ap"
+.Ft krb5_error_code
+.Fn krb5_make_principal "krb5_context context" "krb5_principal *principal" "krb5_const_realm realm" "..."
+.Ft krb5_error_code
+.Fn krb5_copy_principal "krb5_context context" "krb5_const_principal inprinc" "krb5_principal *outprinc"
+.Ft krb5_boolean
+.Fn krb5_principal_compare "krb5_context context" "krb5_const_principal princ1" "krb5_const_principal princ2"
+.Ft krb5_boolean
+.Fn krb5_principal_compare_any_realm "krb5_context context" "krb5_const_principal princ1" "krb5_const_principal princ2"
+.Ft "const char *"
+.Fn krb5_principal_get_comp_string "krb5_context context" "krb5_const_principal principal" "unsigned int component"
+.Ft "const char *"
+.Fn krb5_principal_get_realm "krb5_context context" "krb5_const_principal principal"
+.Ft int
+.Fn krb5_principal_get_type "krb5_context context" "krb5_const_principal principal"
+.Ft krb5_boolean
+.Fn krb5_principal_match "krb5_context context" "krb5_const_principal principal" "krb5_const_principal pattern"
+.Ft void
+.Fn krb5_principal_set_type "krb5_context context" "krb5_principal principal" "int type"
+.Ft krb5_boolean
+.Fn krb5_realm_compare "krb5_context context" "krb5_const_principal princ1" "krb5_const_principal princ2"
+.Ft krb5_error_code
+.Fn krb5_sname_to_principal "krb5_context context" "const char *hostname" "const char *sname" "int32_t type" "krb5_principal *ret_princ"
+.Ft krb5_error_code
+.Fn krb5_sock_to_principal "krb5_context context" "int socket" "const char *sname" "int32_t type" "krb5_principal *principal"
+.Ft krb5_error_code
+.Fn krb5_get_default_principal "krb5_context context" "krb5_principal *princ"
+.Ft krb5_error_code
+.Fn krb5_parse_nametype "krb5_context context" "const char *str" "int32_t *type"
+.Sh DESCRIPTION
+.Li krb5_principal
+holds the name of a user or service in Kerberos.
+.Pp
+A principal has two parts, a
+.Li PrincipalName
+and a
+.Li realm .
+The PrincipalName consists of one or more components. In printed form,
+the components are separated by /.
+The PrincipalName also has a name-type.
+.Pp
+Examples of a principal are
+.Li nisse/root@EXAMPLE.COM
+and
+.Li host/datan.kth.se@KTH.SE .
+.Fn krb5_parse_name
+and
+.Fn krb5_parse_name_flags
+passes a principal name in
+.Fa name
+to the kerberos principal structure.
+.Fn krb5_parse_name_flags
+takes an extra
+.Fa flags
+argument the following flags can be passed in
+.Bl -tag -width Ds
+.It Dv KRB5_PRINCIPAL_PARSE_NO_REALM
+requires the input string to be without a realm, and no realm is
+stored in the
+.Fa principal
+return argument.
+.It Dv KRB5_PRINCIPAL_PARSE_REQUIRE_REALM
+requires the input string to with a realm.
+.El
+.Pp
+.Fn krb5_unparse_name
+and
+.Fn krb5_unparse_name_flags
+prints the principal
+.Fa princ
+to the string
+.Fa name .
+.Fa name
+should be freed with
+.Xr free 3 .
+To the
+.Fa flags
+argument the following flags can be passed in
+.Bl -tag -width Ds
+.It Dv KRB5_PRINCIPAL_UNPARSE_SHORT
+no realm if the realm is one of the local realms.
+.It Dv KRB5_PRINCIPAL_UNPARSE_NO_REALM
+never include any realm in the principal name.
+.It Dv KRB5_PRINCIPAL_UNPARSE_DISPLAY
+don't quote
+.El
+On failure
+.Fa name
+is set to
+.Dv NULL .
+.Fn krb5_unparse_name_fixed
+and
+.Fn krb5_unparse_name_fixed_flags
+behaves just like
+.Fn krb5_unparse ,
+but instead unparses the principal into a fixed size buffer.
+.Pp
+.Fn krb5_unparse_name_short
+just returns the principal without the realm if the principal is
+in the default realm. If the principal isn't, the full name is
+returned.
+.Fn krb5_unparse_name_fixed_short
+works just like
+.Fn krb5_unparse_name_short
+but on a fixed size buffer.
+.Pp
+.Fn krb5_build_principal
+builds a principal from the realm
+.Fa realm
+that has the length
+.Fa rlen .
+The following arguments form the components of the principal.
+The list of components is terminated with
+.Dv NULL .
+.Pp
+.Fn krb5_build_principal_va
+works like
+.Fn krb5_build_principal
+using vargs.
+.Pp
+.Fn krb5_build_principal_ext
+and
+.Fn krb5_build_principal_va_ext
+take a list of length-value pairs, the list is terminated with a zero
+length.
+.Pp
+.Fn krb5_make_principal
+works the same way as
+.Fn krb5_build_principal ,
+except it figures out the length of the realm itself.
+.Pp
+.Fn krb5_copy_principal
+makes a copy of a principal.
+The copy needs to be freed with
+.Fn krb5_free_principal .
+.Pp
+.Fn krb5_principal_compare
+compares the two principals, including realm of the principals and returns
+.Dv TRUE
+if they are the same and
+.Dv FALSE
+if not.
+.Pp
+.Fn krb5_principal_compare_any_realm
+works the same way as
+.Fn krb5_principal_compare
+but doesn't compare the realm component of the principal.
+.Pp
+.Fn krb5_realm_compare
+compares the realms of the two principals and returns
+.Dv TRUE
+is they are the same, and
+.Dv FALSE
+if not.
+.Pp
+.Fn krb5_principal_match
+matches a
+.Fa principal
+against a
+.Fa pattern .
+The pattern is a globbing expression, where each component (separated
+by /) is matched against the corresponding component of the principal.
+.Pp
+The
+.Fn krb5_principal_get_realm
+and
+.Fn krb5_principal_get_comp_string
+functions return parts of the
+.Fa principal ,
+either the realm or a specific component.
+Both functions return string pointers to data inside the principal, so
+they are valid only as long as the principal exists.
+.Pp
+The
+.Fa component
+argument to
+.Fn krb5_principal_get_comp_string
+is the index of the component to return, from zero to the total number of
+components minus one. If the index is out of range
+.Dv NULL
+is returned.
+.Pp
+.Fn krb5_principal_get_realm
+and
+.Fn krb5_principal_get_comp_string
+are replacements for
+.Fn krb5_princ_component
+and related macros, described as internal in the MIT API
+specification.
+Unlike the macros, these functions return strings, not
+.Dv krb5_data .
+A reason to return
+.Dv krb5_data
+was that it was believed that principal components could contain
+binary data, but this belief was unfounded, and it has been decided
+that principal components are infact UTF8, so it's safe to use zero
+terminated strings.
+.Pp
+It's generally not necessary to look at the components of a principal.
+.Pp
+.Fn krb5_principal_get_type
+and
+.Fn krb5_principal_set_type
+get and sets the name type for a principal.
+Name type handling is tricky and not often needed,
+don't use this unless you know what you do.
+.Pp
+.Fn krb5_sname_to_principal
+and
+.Fn krb5_sock_to_principal
+are for easy creation of
+.Dq service
+principals that can, for instance, be used to lookup a key in a keytab.
+For both functions the
+.Fa sname
+parameter will be used for the first component of the created principal.
+If
+.Fa sname
+is
+.Dv NULL ,
+.Dq host
+will be used instead.
+.Pp
+.Fn krb5_sname_to_principal
+will use the passed
+.Fa hostname
+for the second component.
+If
+.Fa type
+is
+.Dv KRB5_NT_SRV_HST
+this name will be looked up with
+.Fn gethostbyname .
+If
+.Fa hostname
+is
+.Dv NULL ,
+the local hostname will be used.
+.Pp
+.Fn krb5_sock_to_principal
+will use the
+.Dq sockname
+of the passed
+.Fa socket ,
+which should be a bound
+.Dv AF_INET
+or
+.Dv AF_INET6
+socket.
+There must be a mapping between the address and
+.Dq sockname .
+The function may try to resolve the name in DNS.
+.Pp
+.Fn krb5_get_default_principal
+tries to find out what's a reasonable default principal by looking at
+the environment it is running in.
+.Pp
+.Fn krb5_parse_nametype
+parses and returns the name type integer value in
+.Fa type .
+On failure the function returns an error code and set the error
+string.
+.\" .Sh EXAMPLES
+.Sh SEE ALSO
+.Xr krb5_config 3 ,
+.Xr krb5.conf 5
+.Sh BUGS
+You can not have a NUL in a component in some of the variable argument
+functions above.
+Until someone can give a good example of where it would be a good idea
+to have NUL's in a component, this will not be fixed.
diff --git a/third_party/heimdal/lib/krb5/krb5_rcache.3 b/third_party/heimdal/lib/krb5/krb5_rcache.3
new file mode 100644
index 0000000..5b121b8
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_rcache.3
@@ -0,0 +1,163 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_RCACHE 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_rcache ,
+.Nm krb5_rc_close ,
+.Nm krb5_rc_default ,
+.Nm krb5_rc_default_name ,
+.Nm krb5_rc_default_type ,
+.Nm krb5_rc_destroy ,
+.Nm krb5_rc_expunge ,
+.Nm krb5_rc_get_lifespan ,
+.Nm krb5_rc_get_name ,
+.Nm krb5_rc_get_type ,
+.Nm krb5_rc_initialize ,
+.Nm krb5_rc_recover ,
+.Nm krb5_rc_resolve ,
+.Nm krb5_rc_resolve_full ,
+.Nm krb5_rc_resolve_type ,
+.Nm krb5_rc_store ,
+.Nm krb5_get_server_rcache
+.Nd Kerberos 5 replay cache
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li "struct krb5_rcache;"
+.Pp
+.Ft krb5_error_code
+.Fo krb5_rc_close
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_default
+.Fa "krb5_context context"
+.Fa "krb5_rcache *id"
+.Fc
+.Ft "const char *"
+.Fo krb5_rc_default_name
+.Fa "krb5_context context"
+.Fc
+.Ft "const char *"
+.Fo krb5_rc_default_type
+.Fa "krb5_context context"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_destroy
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_expunge
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_get_lifespan
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fa "krb5_deltat *auth_lifespan"
+.Fc
+.Ft "const char*"
+.Fo krb5_rc_get_name
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fc
+.Ft "const char*"
+.Fo "krb5_rc_get_type"
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_initialize
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fa "krb5_deltat auth_lifespan"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_recover
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_resolve
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fa "const char *name"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_resolve_full
+.Fa "krb5_context context"
+.Fa "krb5_rcache *id"
+.Fa "const char *string_name"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_resolve_type
+.Fa "krb5_context context"
+.Fa "krb5_rcache *id"
+.Fa "const char *type"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_rc_store
+.Fa "krb5_context context"
+.Fa "krb5_rcache id"
+.Fa "krb5_donot_replay *rep"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_server_rcache
+.Fa "krb5_context context"
+.Fa "const krb5_data *piece"
+.Fa "krb5_rcache *id"
+.Fc
+.Sh DESCRIPTION
+The
+.Li krb5_rcache
+structure holds a storage element that is used for data manipulation.
+The structure contains no public accessible elements.
+.Pp
+.Fn krb5_rc_initialize
+Creates the reply cache
+.Fa id
+and sets it lifespan to
+.Fa auth_lifespan .
+If the cache already exists, the content is destroyed.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_data 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_rd_error.3 b/third_party/heimdal/lib/krb5/krb5_rd_error.3
new file mode 100644
index 0000000..19c092e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_rd_error.3
@@ -0,0 +1,98 @@
+.\" Copyright (c) 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd July 26, 2004
+.Dt KRB5_RD_ERROR 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_rd_error ,
+.Nm krb5_free_error ,
+.Nm krb5_free_error_contents ,
+.Nm krb5_error_from_rd_error
+.Nd parse, free and read error from KRB-ERROR message
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_rd_error
+.Fa "krb5_context context"
+.Fa "const krb5_data *msg"
+.Fa "KRB_ERROR *result"
+.Fc
+.Ft void
+.Fo krb5_free_error
+.Fa "krb5_context context"
+.Fa "krb5_error *error"
+.Fc
+.Ft void
+.Fo krb5_free_error_contents
+.Fa "krb5_context context"
+.Fa "krb5_error *error"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_error_from_rd_error
+.Fa "krb5_context context"
+.Fa "const krb5_error *error"
+.Fa "const krb5_creds *creds"
+.Fc
+.Sh DESCRIPTION
+Usually applications never needs to parse and understand Kerberos
+error messages since higher level functions will parse and push up the
+error in the krb5_context.
+These functions are described for completeness.
+.Pp
+.Fn krb5_rd_error
+parses and returns the kerboeros error message, the structure should be freed with
+.Fn krb5_free_error_contents
+when the caller is done with the structure.
+.Pp
+.Fn krb5_free_error
+frees the content and the memory region holding the structure iself.
+.Pp
+.Fn krb5_free_error_contents
+free the content of the KRB-ERROR message.
+.Pp
+.Fn krb5_error_from_rd_error
+will parse the error message and set the error buffer in krb5_context
+to the error string passed back or the matching error code in the
+KRB-ERROR message.
+Caller should pick up the message with
+.Fn krb5_get_error_string 3
+(don't forget to free the returned string with
+.Fn krb5_free_error_string ) .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_set_error_string 3 ,
+.Xr krb5_get_error_string 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_rd_safe.3 b/third_party/heimdal/lib/krb5/krb5_rd_safe.3
new file mode 100644
index 0000000..bb6294e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_rd_safe.3
@@ -0,0 +1,81 @@
+.\" Copyright (c) 2003 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_RD_SAFE 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_rd_safe ,
+.Nm krb5_rd_priv
+.Nd verifies authenticity of messages
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Ft krb5_error_code
+.Fn krb5_rd_priv "krb5_context context" "krb5_auth_context auth_context" "const krb5_data *inbuf" "krb5_data *outbuf" "krb5_replay_data *outdata"
+.Ft krb5_error_code
+.Fn krb5_rd_safe "krb5_context context" "krb5_auth_context auth_context" "const krb5_data *inbuf" "krb5_data *outbuf" "krb5_replay_data *outdata"
+.Sh DESCRIPTION
+.Fn krb5_rd_safe
+and
+.Fn krb5_rd_priv
+parses
+.Li KRB-SAFE
+and
+.Li KRB-PRIV
+messages (as generated by
+.Xr krb5_mk_safe 3
+and
+.Xr krb5_mk_priv 3 )
+from
+.Fa inbuf
+and verifies its integrity. The user data part of the message in put
+in
+.Fa outbuf .
+The encryption state, including keyblocks and addresses, is taken from
+.Fa auth_context .
+If the
+.Dv KRB5_AUTH_CONTEXT_RET_SEQUENCE
+or
+.Dv KRB5_AUTH_CONTEXT_RET_TIME
+flags are set in the
+.Fa auth_context
+the sequence number and time are returned in the
+.Fa outdata
+parameter.
+.Sh SEE ALSO
+.Xr krb5_auth_con_init 3 ,
+.Xr krb5_mk_priv 3 ,
+.Xr krb5_mk_safe 3
diff --git a/third_party/heimdal/lib/krb5/krb5_set_default_realm.3 b/third_party/heimdal/lib/krb5/krb5_set_default_realm.3
new file mode 100644
index 0000000..867da30
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_set_default_realm.3
@@ -0,0 +1,164 @@
+.\" Copyright (c) 2003 - 2005 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd April 24, 2005
+.Dt KRB5_SET_DEFAULT_REALM 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_copy_host_realm ,
+.Nm krb5_free_host_realm ,
+.Nm krb5_get_default_realm ,
+.Nm krb5_get_default_realms ,
+.Nm krb5_get_host_realm ,
+.Nm krb5_set_default_realm
+.Nd default and host realm read and manipulation routines
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_copy_host_realm
+.Fa "krb5_context context"
+.Fa "const krb5_realm *from"
+.Fa "krb5_realm **to"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_free_host_realm
+.Fa "krb5_context context"
+.Fa "krb5_realm *realmlist"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_default_realm
+.Fa "krb5_context context"
+.Fa "krb5_realm *realm"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_default_realms
+.Fa "krb5_context context"
+.Fa "krb5_realm **realm"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_host_realm
+.Fa "krb5_context context"
+.Fa "const char *host"
+.Fa "krb5_realm **realms"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_default_realm
+.Fa "krb5_context context"
+.Fa "const char *realm"
+.Fc
+.Sh DESCRIPTION
+.Fn krb5_copy_host_realm
+copies the list of realms from
+.Fa from
+to
+.Fa to .
+.Fa to
+should be freed by the caller using
+.Fa krb5_free_host_realm .
+.Pp
+.Fn krb5_free_host_realm
+frees all memory allocated by
+.Fa realmlist .
+.Pp
+.Fn krb5_get_default_realm
+returns the first default realm for this host.
+The realm returned should be freed with
+.Fn krb5_xfree .
+.Pp
+.Fn krb5_get_default_realms
+returns a
+.Dv NULL
+terminated list of default realms for this context.
+Realms returned by
+.Fn krb5_get_default_realms
+should be freed with
+.Fn krb5_free_host_realm .
+.Pp
+.Fn krb5_get_host_realm
+returns a
+.Dv NULL
+terminated list of realms for
+.Fa host
+by looking up the information in the
+.Li [domain_realm]
+in
+.Pa krb5.conf
+or in
+.Li DNS .
+If the mapping in
+.Li [domain_realm]
+results in the string
+.Li dns_locate ,
+DNS is used to lookup the realm.
+.Pp
+When using
+.Li DNS
+to a resolve the domain for the host a.b.c,
+.Fn krb5_get_host_realm
+looks for a
+.Dv TXT
+resource record named
+.Li _kerberos.a.b.c ,
+and if not found, it strips off the first component and tries a again
+(_kerberos.b.c) until it reaches the root.
+.Pp
+If there is no configuration or DNS information found,
+.Fn krb5_get_host_realm
+assumes it can use the domain part of the
+.Fa host
+to form a realm.
+Caller must free
+.Fa realmlist
+with
+.Fn krb5_free_host_realm .
+.Pp
+.Fn krb5_set_default_realm
+sets the default realm for the
+.Fa context .
+If
+.Dv NULL
+is used as a
+.Fa realm ,
+the
+.Li [libdefaults]default_realm
+stanza in
+.Pa krb5.conf
+is used.
+If there is no such stanza in the configuration file, the
+.Fn krb5_get_host_realm
+function is used to form a default realm.
+.Sh SEE ALSO
+.Xr free 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_set_password.3 b/third_party/heimdal/lib/krb5/krb5_set_password.3
new file mode 100644
index 0000000..14cd299
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_set_password.3
@@ -0,0 +1,143 @@
+.\" Copyright (c) 2003 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd July 15, 2004
+.Dt KRB5_SET_PASSWORD 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_change_password ,
+.Nm krb5_set_password ,
+.Nm krb5_set_password_using_ccache ,
+.Nm krb5_passwd_result_to_string
+.Nd change password functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_change_password
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "char *newpw"
+.Fa "int *result_code"
+.Fa "krb5_data *result_code_string"
+.Fa "krb5_data *result_string"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_password
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "char *newpw"
+.Fa "krb5_principal targprinc"
+.Fa "int *result_code"
+.Fa "krb5_data *result_code_string"
+.Fa "krb5_data *result_string"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_set_password_using_ccache
+.Fa "krb5_context context"
+.Fa "krb5_ccache ccache"
+.Fa "char *newpw"
+.Fa "krb5_principal targprinc"
+.Fa "int *result_code"
+.Fa "krb5_data *result_code_string"
+.Fa "krb5_data *result_string"
+.Fc
+.Ft "const char *"
+.Fo krb5_passwd_result_to_string
+.Fa "krb5_context context"
+.Fa "int result"
+.Fc
+.Sh DESCRIPTION
+These functions change the password for a given principal.
+.Pp
+.Fn krb5_set_password
+and
+.Fn krb5_set_password_using_ccache
+are the newer of the three functions, and use a newer version of the
+protocol (and also fall back to the older set-password protocol if the
+newer protocol doesn't work).
+.Pp
+.Fn krb5_change_password
+sets the password
+.Fa newpasswd
+for the client principal in
+.Fa creds .
+The server principal of creds must be
+.Li kadmin/changepw .
+.Pp
+.Fn krb5_set_password
+and
+.Fn krb5_set_password_using_ccache
+change the password for the principal
+.Fa targprinc .
+.Pp
+.Fn krb5_set_password
+requires that the credential for
+.Li kadmin/changepw@REALM
+is in
+.Fa creds .
+If the user caller isn't an administrator, this credential
+needs to be an initial credential, see
+.Xr krb5_get_init_creds 3
+how to get such credentials.
+.Pp
+.Fn krb5_set_password_using_ccache
+will get the credential from
+.Fa ccache .
+.Pp
+If
+.Fa targprinc
+is
+.Dv NULL ,
+.Fn krb5_set_password_using_ccache
+uses the the default principal in
+.Fa ccache
+and
+.Fn krb5_set_password
+uses the global the default principal.
+.Pp
+All three functions return an error in
+.Fa result_code
+and maybe an error string to print in
+.Fa result_string .
+.Pp
+.Fn krb5_passwd_result_to_string
+returns an human readable string describing the error code in
+.Fa result_code
+from the
+.Fn krb5_set_password
+functions.
+.Sh SEE ALSO
+.Xr krb5_ccache 3 ,
+.Xr krb5_init_context 3
diff --git a/third_party/heimdal/lib/krb5/krb5_string_to_key.3 b/third_party/heimdal/lib/krb5/krb5_string_to_key.3
new file mode 100644
index 0000000..c77d459
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_string_to_key.3
@@ -0,0 +1,156 @@
+.\" Copyright (c) 2004 - 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$
+.\"
+.Dd July 10, 2006
+.Dt KRB5_STRING_TO_KEY 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_string_to_key ,
+.Nm krb5_string_to_key_data ,
+.Nm krb5_string_to_key_data_salt ,
+.Nm krb5_string_to_key_data_salt_opaque ,
+.Nm krb5_string_to_key_salt ,
+.Nm krb5_string_to_key_salt_opaque ,
+.Nm krb5_get_pw_salt ,
+.Nm krb5_free_salt
+.Nd turns a string to a Kerberos key
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fo krb5_string_to_key
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "const char *password"
+.Fa "krb5_principal principal"
+.Fa "krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_key_data
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "krb5_data password"
+.Fa "krb5_principal principal"
+.Fa "krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_key_data_salt
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "krb5_data password"
+.Fa "krb5_salt salt"
+.Fa "krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_key_data_salt_opaque
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "krb5_data password"
+.Fa "krb5_salt salt"
+.Fa "krb5_data opaque"
+.Fa "krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_key_salt
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "const char *password"
+.Fa "krb5_salt salt"
+.Fa "krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_key_salt_opaque
+.Fa "krb5_context context"
+.Fa "krb5_enctype enctype"
+.Fa "const char *password"
+.Fa "krb5_salt salt"
+.Fa "krb5_data opaque"
+.Fa "krb5_keyblock *key"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_get_pw_salt
+.Fa "krb5_context context"
+.Fa "krb5_const_principal principal"
+.Fa "krb5_salt *salt"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_free_salt
+.Fa "krb5_context context"
+.Fa "krb5_salt salt"
+.Fc
+.Sh DESCRIPTION
+The string to key functions convert a string to a kerberos key.
+.Pp
+.Fn krb5_string_to_key_data_salt_opaque
+is the function that does all the work, the rest of the functions are
+just wrappers around
+.Fn krb5_string_to_key_data_salt_opaque
+that calls it with default values.
+.Pp
+.Fn krb5_string_to_key_data_salt_opaque
+transforms the
+.Fa password
+with the given salt-string
+.Fa salt
+and the opaque, encryption type specific parameter
+.Fa opaque
+to a encryption key
+.Fa key
+according to the string to key function associated with
+.Fa enctype .
+.Pp
+The
+.Fa key
+should be freed with
+.Fn krb5_free_keyblock_contents .
+.Pp
+If one of the functions that doesn't take a
+.Li krb5_salt
+as it argument
+.Fn krb5_get_pw_salt
+is used to get the salt value.
+.Pp
+.Fn krb5_get_pw_salt
+get the default password salt for a principal, use
+.Fn krb5_free_salt
+to free the salt when done.
+.Pp
+.Fn krb5_free_salt
+frees the content of
+.Fa salt .
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_data 3 ,
+.Xr krb5_keyblock 3 ,
+.Xr kerberos 8
diff --git a/third_party/heimdal/lib/krb5/krb5_timeofday.3 b/third_party/heimdal/lib/krb5/krb5_timeofday.3
new file mode 100644
index 0000000..f491c6b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_timeofday.3
@@ -0,0 +1,118 @@
+.\" $Id$
+.\"
+.\" Copyright (c) 2001, 2003, 2006 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd September 16, 2006
+.Dt KRB5_TIMEOFDAY 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_timeofday ,
+.Nm krb5_set_real_time ,
+.Nm krb5_us_timeofday ,
+.Nm krb5_format_time ,
+.Nm krb5_string_to_deltat
+.Nd Kerberos 5 time handling functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li krb5_timestamp ;
+.Pp
+.Li krb5_deltat ;
+.Ft krb5_error_code
+.Fo krb5_set_real_time
+.Fa "krb5_context context"
+.Fa "krb5_timestamp sec"
+.Fa "int32_t usec"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_timeofday
+.Fa "krb5_context context"
+.Fa "krb5_timestamp *timeret"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_us_timeofday
+.Fa "krb5_context context"
+.Fa "krb5_timestamp *sec"
+.Fa "int32_t *usec"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_format_time
+.Fa "krb5_context context"
+.Fa "time_t t"
+.Fa "char *s"
+.Fa "size_t len"
+.Fa "krb5_boolean include_time"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_string_to_deltat
+.Fa "const char *string"
+.Fa "krb5_deltat *deltat"
+.Fc
+.Sh DESCRIPTION
+.Nm krb5_set_real_time
+sets the absolute time that the caller knows the KDC has.
+With this the Kerberos library can calculate the relative
+difference between the KDC time and the local system time and store it
+in the
+.Fa context .
+With this information the Kerberos library can adjust all time stamps
+in Kerberos packages.
+.Pp
+.Fn krb5_timeofday
+returns the current time, but adjusted with the time difference
+between the local host and the KDC.
+.Fn krb5_us_timeofday
+also returns microseconds.
+.Pp
+.Nm krb5_format_time
+formats the time
+.Fa t
+into the string
+.Fa s
+of length
+.Fa len .
+If
+.Fa include_time
+is set, the time is set include_time.
+.Pp
+.Nm krb5_string_to_deltat
+parses delta time
+.Fa string
+into
+.Fa deltat .
+.Sh SEE ALSO
+.Xr gettimeofday 2 ,
+.Xr krb5 3
diff --git a/third_party/heimdal/lib/krb5/krb5_verify_init_creds.3 b/third_party/heimdal/lib/krb5/krb5_verify_init_creds.3
new file mode 100644
index 0000000..0fe9582
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_verify_init_creds.3
@@ -0,0 +1,103 @@
+.\" Copyright (c) 2003 - 2006 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_VERIFY_INIT_CREDS 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_verify_init_creds_opt_init ,
+.Nm krb5_verify_init_creds_opt_set_ap_req_nofail ,
+.Nm krb5_verify_init_creds
+.Nd "verifies a credential cache is correct by using a local keytab"
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Pp
+.Li "struct krb5_verify_init_creds_opt;"
+.Ft void
+.Fo krb5_verify_init_creds_opt_init
+.Fa "krb5_verify_init_creds_opt *options"
+.Fc
+.Ft void
+.Fo krb5_verify_init_creds_opt_set_ap_req_nofail
+.Fa "krb5_verify_init_creds_opt *options"
+.Fa "int ap_req_nofail"
+.Fc
+.Ft krb5_error_code
+.Fo krb5_verify_init_creds
+.Fa "krb5_context context"
+.Fa "krb5_creds *creds"
+.Fa "krb5_principal ap_req_server"
+.Fa "krb5_ccache *ccache"
+.Fa "krb5_verify_init_creds_opt *options"
+.Fc
+.Sh DESCRIPTION
+The
+.Nm krb5_verify_init_creds
+function verifies the initial tickets with the local keytab to make
+sure the response of the KDC was spoof-ed.
+.Pp
+.Nm krb5_verify_init_creds
+will use principal
+.Fa ap_req_server
+from the local keytab, if
+.Dv NULL
+is passed in, the code will guess the local hostname and use that to
+form host/hostname/GUESSED-REALM-FOR-HOSTNAME.
+.Fa creds
+is the credential that
+.Nm krb5_verify_init_creds
+should verify.
+If
+.Fa ccache
+is given
+.Fn krb5_verify_init_creds
+stores all credentials it fetched from the KDC there, otherwise it
+will use a memory credential cache that is destroyed when done.
+.Pp
+.Fn krb5_verify_init_creds_opt_init
+cleans the the structure, must be used before trying to pass it in to
+.Fn krb5_verify_init_creds .
+.Pp
+.Fn krb5_verify_init_creds_opt_set_ap_req_nofail
+controls controls the behavior if
+.Fa ap_req_server
+doesn't exists in the local keytab or in the KDC's database, if it's
+true, the error will be ignored. Note that this use is possible
+insecure.
+.Sh SEE ALSO
+.Xr krb5 3 ,
+.Xr krb5_get_init_creds 3 ,
+.Xr krb5_verify_user 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb5_verify_user.3 b/third_party/heimdal/lib/krb5/krb5_verify_user.3
new file mode 100644
index 0000000..a32986d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb5_verify_user.3
@@ -0,0 +1,241 @@
+.\" Copyright (c) 2001 - 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$
+.\"
+.Dd May 1, 2006
+.Dt KRB5_VERIFY_USER 3
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5_verify_user ,
+.Nm krb5_verify_user_lrealm ,
+.Nm krb5_verify_user_opt ,
+.Nm krb5_verify_opt_init ,
+.Nm krb5_verify_opt_alloc ,
+.Nm krb5_verify_opt_free ,
+.Nm krb5_verify_opt_set_ccache ,
+.Nm krb5_verify_opt_set_flags ,
+.Nm krb5_verify_opt_set_service ,
+.Nm krb5_verify_opt_set_secure ,
+.Nm krb5_verify_opt_set_keytab
+.Nd Heimdal password verifying functions
+.Sh LIBRARY
+Kerberos 5 Library (libkrb5, -lkrb5)
+.Sh SYNOPSIS
+.In krb5.h
+.Ft krb5_error_code
+.Fn "krb5_verify_user" "krb5_context context" " krb5_principal principal" "krb5_ccache ccache" "const char *password" "krb5_boolean secure" "const char *service"
+.Ft krb5_error_code
+.Fn "krb5_verify_user_lrealm" "krb5_context context" "krb5_principal principal" "krb5_ccache ccache" "const char *password" "krb5_boolean secure" "const char *service"
+.Ft void
+.Fn krb5_verify_opt_init "krb5_verify_opt *opt"
+.Ft void
+.Fn krb5_verify_opt_alloc "krb5_verify_opt **opt"
+.Ft void
+.Fn krb5_verify_opt_free "krb5_verify_opt *opt"
+.Ft void
+.Fn krb5_verify_opt_set_ccache "krb5_verify_opt *opt" "krb5_ccache ccache"
+.Ft void
+.Fn krb5_verify_opt_set_keytab "krb5_verify_opt *opt" "krb5_keytab keytab"
+.Ft void
+.Fn krb5_verify_opt_set_secure "krb5_verify_opt *opt" "krb5_boolean secure"
+.Ft void
+.Fn krb5_verify_opt_set_service "krb5_verify_opt *opt" "const char *service"
+.Ft void
+.Fn krb5_verify_opt_set_flags "krb5_verify_opt *opt" "unsigned int flags"
+.Ft krb5_error_code
+.Fo krb5_verify_user_opt
+.Fa "krb5_context context"
+.Fa "krb5_principal principal"
+.Fa "const char *password"
+.Fa "krb5_verify_opt *opt"
+.Fc
+.Sh DESCRIPTION
+The
+.Nm krb5_verify_user
+function verifies the password supplied by a user.
+The principal whose password will be verified is specified in
+.Fa principal .
+New tickets will be obtained as a side-effect and stored in
+.Fa ccache
+(if
+.Dv NULL ,
+the default ccache is used).
+.Fn krb5_verify_user
+will call
+.Fn krb5_cc_initialize
+on the given
+.Fa ccache ,
+so
+.Fa ccache
+must only initialized with
+.Fn krb5_cc_resolve
+or
+.Fn krb5_cc_gen_new .
+If the password is not supplied in
+.Fa password
+(and is given as
+.Dv NULL )
+the user will be prompted for it.
+If
+.Fa secure
+the ticket will be verified against the locally stored service key
+.Fa service
+(by default
+.Ql host
+if given as
+.Dv NULL
+).
+.Pp
+The
+.Fn krb5_verify_user_lrealm
+function does the same, except that it ignores the realm in
+.Fa principal
+and tries all the local realms (see
+.Xr krb5.conf 5 ) .
+After a successful return, the principal is set to the authenticated
+realm. If the call fails, the principal will not be meaningful, and
+should only be freed with
+.Xr krb5_free_principal 3 .
+.Pp
+.Fn krb5_verify_opt_alloc
+and
+.Fn krb5_verify_opt_free
+allocates and frees a
+.Li krb5_verify_opt .
+You should use the the alloc and free function instead of allocation
+the structure yourself, this is because in a future release the
+structure wont be exported.
+.Pp
+.Fn krb5_verify_opt_init
+resets all opt to default values.
+.Pp
+None of the krb5_verify_opt_set function makes a copy of the data
+structure that they are called with. It's up the caller to free them
+after the
+.Fn krb5_verify_user_opt
+is called.
+.Pp
+.Fn krb5_verify_opt_set_ccache
+sets the
+.Fa ccache
+that user of
+.Fa opt
+will use. If not set, the default credential cache will be used.
+.Pp
+.Fn krb5_verify_opt_set_keytab
+sets the
+.Fa keytab
+that user of
+.Fa opt
+will use. If not set, the default keytab will be used.
+.Pp
+.Fn krb5_verify_opt_set_secure
+if
+.Fa secure
+if true, the password verification will require that the ticket will
+be verified against the locally stored service key. If not set,
+default value is true.
+.Pp
+.Fn krb5_verify_opt_set_service
+sets the
+.Fa service
+principal that user of
+.Fa opt
+will use. If not set, the
+.Ql host
+service will be used.
+.Pp
+.Fn krb5_verify_opt_set_flags
+sets
+.Fa flags
+that user of
+.Fa opt
+will use.
+If the flag
+.Dv KRB5_VERIFY_LREALMS
+is used, the
+.Fa principal
+will be modified like
+.Fn krb5_verify_user_lrealm
+modifies it.
+.Pp
+.Fn krb5_verify_user_opt
+function verifies the
+.Fa password
+supplied by a user.
+The principal whose password will be verified is specified in
+.Fa principal .
+Options the to the verification process is pass in in
+.Fa opt .
+.Sh EXAMPLES
+Here is a example program that verifies a password. it uses the
+.Ql host/`hostname`
+service principal in
+.Pa krb5.keytab .
+.Bd -literal
+#include <krb5.h>
+
+int
+main(int argc, char **argv)
+{
+ char *user;
+ krb5_error_code error;
+ krb5_principal princ;
+ krb5_context context;
+
+ if (argc != 2)
+ errx(1, "usage: verify_passwd <principal-name>");
+
+ user = argv[1];
+
+ if (krb5_init_context(&context) < 0)
+ errx(1, "krb5_init_context");
+
+ if ((error = krb5_parse_name(context, user, &princ)) != 0)
+ krb5_err(context, 1, error, "krb5_parse_name");
+
+ error = krb5_verify_user(context, princ, NULL, NULL, TRUE, NULL);
+ if (error)
+ krb5_err(context, 1, error, "krb5_verify_user");
+
+ return 0;
+}
+.Ed
+.Sh SEE ALSO
+.Xr krb5_cc_gen_new 3 ,
+.Xr krb5_cc_initialize 3 ,
+.Xr krb5_cc_resolve 3 ,
+.Xr krb5_err 3 ,
+.Xr krb5_free_principal 3 ,
+.Xr krb5_init_context 3 ,
+.Xr krb5_kt_default 3 ,
+.Xr krb5.conf 5
diff --git a/third_party/heimdal/lib/krb5/krb_err.et b/third_party/heimdal/lib/krb5/krb_err.et
new file mode 100644
index 0000000..f7dbb6c
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krb_err.et
@@ -0,0 +1,63 @@
+#
+# Error messages for the krb4 library
+#
+# This might look like a com_err file, but is not
+#
+id "$Id: krb_err.et,v 1.7 1998/03/29 14:19:52 bg Exp $"
+
+error_table krb
+
+prefix KRB4ET
+ec KSUCCESS, "Kerberos 4 successful"
+ec KDC_NAME_EXP, "Kerberos 4 principal expired"
+ec KDC_SERVICE_EXP, "Kerberos 4 service expired"
+ec KDC_AUTH_EXP, "Kerberos 4 auth expired"
+ec KDC_PKT_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_P_MKEY_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_S_MKEY_VER, "Incorrect Kerberos 4 master key version"
+ec KDC_BYTE_ORDER, "Kerberos 4 byte order unknown"
+ec KDC_PR_UNKNOWN, "Kerberos 4 principal unknown"
+ec KDC_PR_N_UNIQUE, "Kerberos 4 principal not unique"
+ec KDC_NULL_KEY, "Kerberos 4 principal has null key"
+index 20
+ec KDC_GEN_ERR, "Generic error from KDC (Kerberos 4)"
+ec GC_TKFIL, "Can't read Kerberos 4 ticket file"
+ec GC_NOTKT, "Can't find Kerberos 4 ticket or TGT"
+index 26
+ec MK_AP_TGTEXP, "Kerberos 4 TGT Expired"
+index 31
+ec RD_AP_UNDEC, "Kerberos 4: Can't decode authenticator"
+ec RD_AP_EXP, "Kerberos 4 ticket expired"
+ec RD_AP_NYV, "Kerberos 4 ticket not yet valid"
+ec RD_AP_REPEAT, "Kerberos 4: Repeated request"
+ec RD_AP_NOT_US, "The Kerberos 4 ticket isn't for us"
+ec RD_AP_INCON, "Kerberos 4 request inconsistent"
+ec RD_AP_TIME, "Kerberos 4: delta_t too big"
+ec RD_AP_BADD, "Kerberos 4: incorrect net address"
+ec RD_AP_VERSION, "Kerberos protocol not version 4"
+ec RD_AP_MSG_TYPE, "Kerberos 4: invalid msg type"
+ec RD_AP_MODIFIED, "Kerberos 4: message stream modified"
+ec RD_AP_ORDER, "Kerberos 4: message out of order"
+ec RD_AP_UNAUTHOR, "Kerberos 4: unauthorized request"
+index 51
+ec GT_PW_NULL, "Kerberos 4: current PW is null"
+ec GT_PW_BADPW, "Kerberos 4: Incorrect current password"
+ec GT_PW_PROT, "Kerberos 4 protocol error"
+ec GT_PW_KDCERR, "Error returned by KDC (Kerberos 4)"
+ec GT_PW_NULLTKT, "Null Kerberos 4 ticket returned by KDC"
+ec SKDC_RETRY, "Kerberos 4: Retry count exceeded"
+ec SKDC_CANT, "Kerberos 4: Can't send request"
+index 61
+ec INTK_W_NOTALL, "Kerberos 4: not all tickets returned"
+ec INTK_BADPW, "Kerberos 4: incorrect password"
+ec INTK_PROT, "Kerberos 4: Protocol Error"
+index 70
+ec INTK_ERR, "Other error in Kerberos 4"
+ec AD_NOTGT, "Don't have Kerberos 4 ticket-granting ticket"
+index 76
+ec NO_TKT_FIL, "No Kerberos 4 ticket file found"
+ec TKT_FIL_ACC, "Couldn't access Kerberos 4 ticket file"
+ec TKT_FIL_LCK, "Couldn't lock Kerberos 4 ticket file"
+ec TKT_FIL_FMT, "Bad Kerberos 4 ticket file format"
+ec TKT_FIL_INI, "Kerberos 4: tf_init not called first"
+ec KNAME_FMT, "Bad Kerberos 4 name format"
diff --git a/third_party/heimdal/lib/krb5/krbhst-test.c b/third_party/heimdal/lib/krb5/krbhst-test.c
new file mode 100644
index 0000000..cd388ec
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krbhst-test.c
@@ -0,0 +1,109 @@
+/*
+ * 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"
+
+#include <err.h>
+#include <getarg.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "[realms ...]");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ int i, j;
+ krb5_context context;
+ int types[] = {KRB5_KRBHST_KDC, KRB5_KRBHST_ADMIN, KRB5_KRBHST_CHANGEPW,
+ KRB5_KRBHST_KRB524};
+ const char *type_str[] = {"kdc", "admin", "changepw", "krb524"};
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ krb5_err(NULL, 1, ret, "Failed to initialize context");
+ for(i = 0; i < argc; i++) {
+ krb5_krbhst_handle handle;
+ char host[MAXHOSTNAMELEN];
+
+ for (j = 0; j < sizeof(types)/sizeof(*types); ++j) {
+ printf ("%s for %s:\n", type_str[j], argv[i]);
+
+ ret = krb5_krbhst_init(context, argv[i], types[j], &handle);
+ if (ret)
+ krb5_err(context, 1, ret, "Could not init krbhst iterator");
+ while ((ret = krb5_krbhst_next_as_string(context, handle, host,
+ sizeof(host))) == 0)
+ printf("\thost: %s\n", host);
+ krb5_krbhst_reset(context, handle);
+ printf("\n");
+ if (ret)
+ krb5_err(context, 1, ret, "Could not iterate all krbhst");
+ }
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/krbhst.c b/third_party/heimdal/lib/krb5/krbhst.c
new file mode 100644
index 0000000..59a8e77
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krbhst.c
@@ -0,0 +1,1282 @@
+/*
+ * Copyright (c) 2001 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 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 <resolve.h>
+#include "locate_plugin.h"
+
+static int
+string_to_proto(const char *string)
+{
+ if(strcasecmp(string, "udp") == 0)
+ return KRB5_KRBHST_UDP;
+ else if(strcasecmp(string, "tcp") == 0)
+ return KRB5_KRBHST_TCP;
+ else if(strcasecmp(string, "http") == 0)
+ return KRB5_KRBHST_HTTP;
+ return -1;
+}
+
+static int
+is_invalid_tld_srv_target(const char *target)
+{
+ return (strncmp("your-dns-needs-immediate-attention.",
+ target, 35) == 0
+ && strchr(&target[35], '.') == NULL);
+}
+
+/*
+ * set `res' and `count' to the result of looking up SRV RR in DNS for
+ * `proto', `proto', `realm' using `dns_type'.
+ * if `port' != 0, force that port number
+ */
+
+static krb5_error_code
+srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
+ const char *realm, const char *dns_type, const char *sitename,
+ const char *proto, const char *service, int port)
+{
+ char domain[1024];
+ struct rk_dns_reply *r;
+ struct rk_resource_record *rr;
+ int num_srv;
+ int proto_num;
+ int def_port;
+
+ *res = NULL;
+ *count = 0;
+
+ proto_num = string_to_proto(proto);
+ if(proto_num < 0) {
+ krb5_set_error_message(context, EINVAL,
+ N_("unknown protocol `%s' to lookup", ""),
+ proto);
+ return EINVAL;
+ }
+
+ if(proto_num == KRB5_KRBHST_HTTP)
+ def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
+ else if(port == 0)
+ def_port = ntohs(krb5_getportbyname (context, service, proto, 88));
+ else
+ def_port = port;
+
+ if (sitename)
+ snprintf(domain, sizeof(domain), "_%s._%s.%s._sites.%s.",
+ service, proto, sitename, realm);
+ else
+ snprintf(domain, sizeof(domain), "_%s._%s.%s.", service, proto, realm);
+
+ r = rk_dns_lookup(domain, dns_type);
+ if(r == NULL) {
+ _krb5_debug(context, 0,
+ "DNS lookup failed domain: %s", domain);
+ return KRB5_KDC_UNREACH;
+ }
+
+ for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+ if(rr->type == rk_ns_t_srv) {
+ if (num_srv >= INT_MAX) {
+ rk_dns_free_data(r);
+ return KRB5_KDC_UNREACH;
+ }
+ if (num_srv >= SIZE_MAX / sizeof(**res)) {
+ rk_dns_free_data(r);
+ return KRB5_KDC_UNREACH;
+ }
+ num_srv++;
+ }
+
+ if (num_srv == 0) {
+ _krb5_debug(context, 0,
+ "DNS SRV RR lookup domain nodata: %s", domain);
+ rk_dns_free_data(r);
+ return KRB5_KDC_UNREACH;
+ }
+
+ *res = malloc(num_srv * sizeof(**res));
+ if(*res == NULL) {
+ rk_dns_free_data(r);
+ return krb5_enomem(context);
+ }
+
+ rk_dns_srv_order(r);
+
+ for(num_srv = 0, rr = r->head; rr; rr = rr->next)
+ if(rr->type == rk_ns_t_srv) {
+ krb5_krbhst_info *hi = NULL;
+ size_t len;
+ int invalid_tld = 1;
+
+ /* Test for top-level domain controlled interruptions */
+ if (!is_invalid_tld_srv_target(rr->u.srv->target)) {
+ invalid_tld = 0;
+ len = strlen(rr->u.srv->target);
+ hi = calloc(1, sizeof(*hi) + len);
+ }
+ if(hi == NULL) {
+ rk_dns_free_data(r);
+ while(--num_srv >= 0)
+ free((*res)[num_srv]);
+ free(*res);
+ *res = NULL;
+ if (invalid_tld) {
+ krb5_warnx(context,
+ "Domain lookup failed: "
+ "Realm %s needs immediate attention "
+ "see https://icann.org/namecollision",
+ realm);
+ return KRB5_KDC_UNREACH;
+ }
+ return krb5_enomem(context);
+ }
+ (*res)[num_srv++] = hi;
+
+ hi->proto = proto_num;
+
+ hi->def_port = def_port;
+ if (port != 0)
+ hi->port = port;
+ else
+ hi->port = rr->u.srv->port;
+
+ strlcpy(hi->hostname, rr->u.srv->target, len + 1);
+ }
+
+ *count = num_srv;
+
+ rk_dns_free_data(r);
+ return 0;
+}
+
+
+struct krb5_krbhst_data {
+ const char *config_param;
+ const char *srv_label;
+ char *realm;
+ unsigned int flags;
+ int def_port;
+ int port; /* hardwired port number if != 0 */
+#define KD_CONFIG 0x0001
+#define KD_SRV_UDP 0x0002
+#define KD_SRV_TCP 0x0004
+#define KD_SITE_SRV_UDP 0x0008
+#define KD_SITE_SRV_TCP 0x0010
+#define KD_SRV_HTTP 0x0020
+#define KD_SRV_KKDCP 0x0040
+#define KD_FALLBACK 0x0080
+#define KD_CONFIG_EXISTS 0x0100
+#define KD_LARGE_MSG 0x0200
+#define KD_PLUGIN 0x0400
+#define KD_HOSTNAMES 0x0800
+ krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
+ krb5_krbhst_info**);
+
+ char *hostname;
+ char *sitename;
+ unsigned int fallback_count;
+
+ struct krb5_krbhst_info *hosts, **index, **end;
+};
+
+static krb5_boolean
+krbhst_empty(const struct krb5_krbhst_data *kd)
+{
+ return kd->index == &kd->hosts;
+}
+
+/*
+ * Return the default protocol for the `kd' (either TCP or UDP)
+ */
+
+static int
+krbhst_get_default_proto(struct krb5_krbhst_data *kd)
+{
+ if (kd->flags & KD_LARGE_MSG)
+ return KRB5_KRBHST_TCP;
+ return KRB5_KRBHST_UDP;
+}
+
+static int
+krbhst_get_default_port(struct krb5_krbhst_data *kd)
+{
+ return kd->def_port;
+}
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
+_krb5_krbhst_get_realm(krb5_krbhst_handle handle)
+{
+ return handle->realm;
+}
+
+/*
+ * parse `spec' into a krb5_krbhst_info, defaulting the port to `def_port'
+ * and forcing it to `port' if port != 0
+ */
+
+static struct krb5_krbhst_info*
+parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *spec, int def_port, int port)
+{
+ const char *p = spec, *q;
+ struct krb5_krbhst_info *hi;
+
+ hi = calloc(1, sizeof(*hi) + strlen(spec));
+ if(hi == NULL)
+ return NULL;
+
+ hi->proto = krbhst_get_default_proto(kd);
+
+ if(strncmp(p, "http://", 7) == 0){
+ hi->proto = KRB5_KRBHST_HTTP;
+ p += 7;
+ } else if(strncmp(p, "http/", 5) == 0) {
+ hi->proto = KRB5_KRBHST_HTTP;
+ p += 5;
+ def_port = ntohs(krb5_getportbyname (context, "http", "tcp", 80));
+ }else if(strncmp(p, "tcp/", 4) == 0){
+ hi->proto = KRB5_KRBHST_TCP;
+ p += 4;
+ } else if(strncmp(p, "udp/", 4) == 0) {
+ hi->proto = KRB5_KRBHST_UDP;
+ p += 4;
+ }
+
+ if (p[0] == '[' && (q = strchr(p, ']')) != NULL) {
+ /* if address looks like [foo:bar] or [foo:bar]: its a ipv6
+ adress, strip of [] */
+ memcpy(hi->hostname, &p[1], q - p - 1);
+ hi->hostname[q - p - 1] = '\0';
+ p = q + 1;
+ /* get trailing : */
+ if (p[0] == ':')
+ p++;
+ } else if(strsep_copy(&p, ":", hi->hostname, strlen(spec) + 1) < 0) {
+ /* copy everything before : */
+ free(hi);
+ return NULL;
+ }
+ /* get rid of trailing /, and convert to lower case */
+ hi->hostname[strcspn(hi->hostname, "/")] = '\0';
+ strlwr(hi->hostname);
+
+ hi->port = hi->def_port = def_port;
+ if(p != NULL && p[0]) {
+ char *end;
+ hi->port = strtol(p, &end, 0);
+ if(end == p) {
+ free(hi);
+ return NULL;
+ }
+ }
+ if (port)
+ hi->port = port;
+ return hi;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_free_krbhst_info(krb5_krbhst_info *hi)
+{
+ if (hi->ai != NULL)
+ freeaddrinfo(hi->ai);
+ free(hi);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_krbhost_info_move(krb5_context context,
+ krb5_krbhst_info *from,
+ krb5_krbhst_info **to)
+{
+ size_t hostnamelen = strlen(from->hostname);
+ /* trailing NUL is included in structure */
+ *to = calloc(1, sizeof(**to) + hostnamelen);
+ if (*to == NULL)
+ return krb5_enomem(context);
+
+ (*to)->proto = from->proto;
+ (*to)->port = from->port;
+ (*to)->def_port = from->def_port;
+ (*to)->ai = from->ai;
+ from->ai = NULL;
+ (*to)->next = NULL;
+ memcpy((*to)->hostname, from->hostname, hostnamelen + 1);
+ return 0;
+}
+
+
+static void
+append_host_hostinfo(struct krb5_krbhst_data *kd, struct krb5_krbhst_info *host)
+{
+ struct krb5_krbhst_info *h;
+
+ for(h = kd->hosts; h; h = h->next)
+ if(h->proto == host->proto &&
+ h->port == host->port &&
+ strcmp(h->hostname, host->hostname) == 0) {
+ _krb5_free_krbhst_info(host);
+ return;
+ }
+ /*
+ * We should always initialize kd->end in common_init(), but static
+ * analyzers may not see that we do, and the compiler might conclude
+ * there's UB here.
+ */
+ if (kd->end)
+ *kd->end = host;
+ kd->end = &host->next;
+}
+
+static krb5_error_code
+append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *host, int def_port, int port)
+{
+ struct krb5_krbhst_info *hi;
+
+ hi = parse_hostspec(context, kd, host, def_port, port);
+ if(hi == NULL)
+ return krb5_enomem(context);
+
+ append_host_hostinfo(kd, hi);
+ return 0;
+}
+
+/*
+ * return a readable representation of `host' in `hostname, hostlen'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_format_string(krb5_context context, const krb5_krbhst_info *host,
+ char *hostname, size_t hostlen)
+{
+ const char *proto = "";
+ if(host->proto == KRB5_KRBHST_TCP)
+ proto = "tcp/";
+ else if(host->proto == KRB5_KRBHST_HTTP)
+ proto = "http://";
+ if (host->port != host->def_port)
+ snprintf(hostname, hostlen, "%s%s:%d", proto, host->hostname, (int)host->port);
+ else
+ snprintf(hostname, hostlen, "%s%s", proto, host->hostname);
+ return 0;
+}
+
+/*
+ * create a getaddrinfo `hints' based on `proto'
+ */
+
+static void
+make_hints(struct addrinfo *hints, int proto)
+{
+ memset(hints, 0, sizeof(*hints));
+ hints->ai_family = AF_UNSPEC;
+ switch(proto) {
+ case KRB5_KRBHST_UDP :
+ hints->ai_socktype = SOCK_DGRAM;
+ break;
+ case KRB5_KRBHST_HTTP :
+ case KRB5_KRBHST_TCP :
+ hints->ai_socktype = SOCK_STREAM;
+ break;
+ }
+}
+
+/**
+ * Return an `struct addrinfo *' for a KDC host.
+ *
+ * Returns an the struct addrinfo in in that corresponds to the
+ * information in `host'. free:ing is handled by krb5_krbhst_free, so
+ * the returned ai must not be released.
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
+ struct addrinfo **ai)
+{
+ int ret = 0;
+
+ if (host->ai == NULL) {
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+
+ snprintf (portstr, sizeof(portstr), "%d", host->port);
+ make_hints(&hints, host->proto);
+
+ ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
+ if (ret) {
+ ret = krb5_eai_to_heim_errno(ret, errno);
+ goto out;
+ }
+ }
+ out:
+ *ai = host->ai;
+ return ret;
+}
+
+static krb5_boolean
+get_next(struct krb5_krbhst_data *kd, krb5_krbhst_info **host)
+{
+ struct krb5_krbhst_info *hi = kd ? *kd->index : NULL;
+ if(hi != NULL) {
+ *host = hi;
+ kd->index = &(*kd->index)->next;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *sitename, const char *proto, const char *service)
+{
+ krb5_error_code ret;
+ krb5_krbhst_info **res;
+ int count, i;
+
+ if (krb5_realm_is_lkdc(kd->realm))
+ return;
+
+ ret = srv_find_realm(context, &res, &count, kd->realm, "SRV",
+ sitename, proto, service, kd->port);
+ _krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d",
+ kd->realm, proto, service, ret);
+ if (ret)
+ return;
+ for(i = 0; i < count; i++)
+ append_host_hostinfo(kd, res[i]);
+ free(res);
+}
+
+/*
+ * read the configuration for `conf_string', defaulting to kd->def_port and
+ * forcing it to `kd->port' if kd->port != 0
+ */
+
+static void
+config_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *conf_string)
+{
+ int i;
+ char **hostlist;
+ hostlist = krb5_config_get_strings(context, NULL,
+ "realms", kd->realm, conf_string, NULL);
+
+ _krb5_debug(context, 2, "configuration file for realm %s%s found",
+ kd->realm, hostlist ? "" : " not");
+
+ if(hostlist == NULL)
+ return;
+ kd->flags |= KD_CONFIG_EXISTS;
+ for(i = 0; hostlist && hostlist[i] != NULL; i++)
+ append_host_string(context, kd, hostlist[i], kd->def_port, kd->port);
+
+ krb5_config_free_strings(hostlist);
+}
+
+/*
+ * as a fallback, look for `serv_string.kd->realm' (typically
+ * kerberos.REALM, kerberos-1.REALM, ...
+ * `port' is the default port for the service, and `proto' the
+ * protocol
+ */
+
+static krb5_error_code
+fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
+ const char *serv_string, int port, int proto)
+{
+ char *host = NULL;
+ int ret;
+ struct addrinfo *ai;
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+
+ ret = krb5_config_get_bool_default(context, NULL, KRB5_FALLBACK_DEFAULT,
+ "libdefaults", "use_fallback", NULL);
+ if (!ret) {
+ kd->flags |= KD_FALLBACK;
+ return 0;
+ }
+
+ _krb5_debug(context, 2, "fallback lookup %d for realm %s (service %s)",
+ kd->fallback_count, kd->realm, serv_string);
+
+ /*
+ * Don't try forever in case the DNS server keep returning us
+ * entries (like wildcard entries or the .nu TLD)
+ *
+ * Also don't try LKDC realms since fallback wont work on them at all.
+ */
+ if(kd->fallback_count >= 5 || krb5_realm_is_lkdc(kd->realm)) {
+ kd->flags |= KD_FALLBACK;
+ return 0;
+ }
+
+ if(kd->fallback_count == 0)
+ ret = asprintf(&host, "%s.%s.", serv_string, kd->realm);
+ else
+ ret = asprintf(&host, "%s-%d.%s.",
+ serv_string, kd->fallback_count, kd->realm);
+
+ if (ret < 0 || host == NULL)
+ return krb5_enomem(context);
+
+ make_hints(&hints, proto);
+ snprintf(portstr, sizeof(portstr), "%d", port);
+ ret = getaddrinfo(host, portstr, &hints, &ai);
+ if (ret) {
+ /* no more hosts, so we're done here */
+ free(host);
+ kd->flags |= KD_FALLBACK;
+ } else {
+ struct krb5_krbhst_info *hi;
+ size_t hostlen;
+
+ /* Check for ICANN gTLD Name Collision address (127.0.53.53) */
+ if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+ if (sin->sin_addr.s_addr == htonl(0x7f003535)) {
+ krb5_warnx(context,
+ "Fallback lookup failed: "
+ "Realm %s needs immediate attention "
+ "see https://icann.org/namecollision",
+ kd->realm);
+ free(host);
+ freeaddrinfo(ai);
+ return KRB5_KDC_UNREACH;
+ }
+ }
+
+ hostlen = strlen(host);
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if(hi == NULL) {
+ free(host);
+ freeaddrinfo(ai);
+ return krb5_enomem(context);
+ }
+
+ hi->proto = proto;
+ hi->port = hi->def_port = port;
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ free(host);
+ append_host_hostinfo(kd, hi);
+ kd->fallback_count++;
+ }
+ return 0;
+}
+
+/*
+ * Fetch hosts from plugin
+ */
+
+static krb5_error_code
+add_plugin_host(struct krb5_krbhst_data *kd,
+ const char *host,
+ const char *port,
+ int portnum,
+ int proto)
+{
+ struct krb5_krbhst_info *hi;
+ struct addrinfo hints, *ai;
+ size_t hostlen;
+ int ret;
+
+ make_hints(&hints, proto);
+ ret = getaddrinfo(host, port, &hints, &ai);
+ if (ret)
+ return 0;
+
+ hostlen = strlen(host);
+
+ hi = calloc(1, sizeof(*hi) + hostlen);
+ if (hi == NULL) {
+ freeaddrinfo(ai);
+ return ENOMEM;
+ }
+
+ hi->proto = proto;
+ hi->port = hi->def_port = portnum;
+ hi->ai = ai;
+ memmove(hi->hostname, host, hostlen);
+ hi->hostname[hostlen] = '\0';
+ append_host_hostinfo(kd, hi);
+
+ return 0;
+}
+
+static krb5_error_code
+add_locate(void *ctx, int type, struct sockaddr *addr)
+{
+ struct krb5_krbhst_data *kd = ctx;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ socklen_t socklen;
+ krb5_error_code ret;
+ int proto, portnum;
+
+ socklen = socket_sockaddr_size(addr);
+ portnum = socket_get_port(addr);
+
+ ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ if (ret != 0)
+ return 0;
+
+ if (kd->port)
+ snprintf(port, sizeof(port), "%d", kd->port);
+ else if (atoi(port) == 0)
+ snprintf(port, sizeof(port), "%d", krbhst_get_default_port(kd));
+
+ proto = krbhst_get_default_proto(kd);
+
+ ret = add_plugin_host(kd, host, port, portnum, proto);
+ if (ret)
+ return ret;
+
+ /*
+ * This is really kind of broken and should be solved a different
+ * way, some sites block UDP, and we don't, in the general case,
+ * fall back to TCP, that should also be done. But since that
+ * should require us to invert the whole "find kdc" stack, let put
+ * this in for now.
+ */
+
+ if (proto == KRB5_KRBHST_UDP) {
+ ret = add_plugin_host(kd, host, port, portnum, KRB5_KRBHST_TCP);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+struct plctx {
+ enum locate_service_type type;
+ struct krb5_krbhst_data *kd;
+ unsigned long flags;
+};
+
+static KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+plcallback(krb5_context context,
+ const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_service_locate_ftable *locate = plug;
+ struct plctx *plctx = userctx;
+
+ if (locate->minor_version >= KRB5_PLUGIN_LOCATE_VERSION_2)
+ return locate->lookup(plugctx, plctx->flags, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd);
+
+ if (plctx->flags & KRB5_PLF_ALLOW_HOMEDIR)
+ return locate->old_lookup(plugctx, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd);
+
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+static const char *const locate_plugin_deps[] = { "krb5", NULL };
+
+static const struct heim_plugin_data
+locate_plugin_data = {
+ "krb5",
+ KRB5_PLUGIN_LOCATE,
+ KRB5_PLUGIN_LOCATE_VERSION_0,
+ locate_plugin_deps,
+ krb5_get_instance
+};
+
+static void
+plugin_get_hosts(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ enum locate_service_type type)
+{
+ struct plctx ctx = { type, kd, 0 };
+
+ if (_krb5_homedir_access(context))
+ ctx.flags |= KRB5_PLF_ALLOW_HOMEDIR;
+
+ _krb5_plugin_run_f(context, &locate_plugin_data,
+ 0, &ctx, plcallback);
+}
+
+/*
+ *
+ */
+
+static void
+hostnames_get_hosts(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ const char *type)
+{
+ kd->flags |= KD_HOSTNAMES;
+ if (kd->hostname)
+ append_host_string(context, kd, kd->hostname, kd->def_port, kd->port);
+}
+
+
+/*
+ *
+ */
+
+static krb5_error_code
+kdc_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_HOSTNAMES) == 0) {
+ hostnames_get_hosts(context, kd, "kdc");
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kdc);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, kd->config_param);
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS) {
+ _krb5_debug(context, 1,
+ "Configuration exists for realm %s, wont go to DNS",
+ kd->realm);
+ return KRB5_KDC_UNREACH;
+ }
+
+ if(context->srv_lookup) {
+ if(kd->sitename && (kd->flags & KD_SITE_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, kd->sitename, "tcp", "kerberos");
+ kd->flags |= KD_SITE_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_SRV_UDP) == 0 && (kd->flags & KD_LARGE_MSG) == 0) {
+ srv_get_hosts(context, kd, NULL, "udp", kd->srv_label);
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, NULL, "tcp", kd->srv_label);
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ if((kd->flags & KD_SRV_HTTP) == 0) {
+ srv_get_hosts(context, kd, NULL, "http", kd->srv_label);
+ kd->flags |= KD_SRV_HTTP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ while((kd->flags & KD_FALLBACK) == 0) {
+ ret = fallback_get_hosts(context, kd, "kerberos",
+ kd->def_port,
+ krbhst_get_default_proto(kd));
+ if(ret)
+ return ret;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ _krb5_debug(context, 0, "No KDC entries found for %s", kd->realm);
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+admin_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kadmin);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, kd->config_param);
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS) {
+ _krb5_debug(context, 1,
+ "Configuration exists for realm %s, wont go to DNS",
+ kd->realm);
+ return KRB5_KDC_UNREACH;
+ }
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, NULL, "tcp", kd->srv_label);
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ if (krbhst_empty(kd)
+ && (kd->flags & KD_FALLBACK) == 0) {
+ ret = fallback_get_hosts(context, kd, "kerberos",
+ kd->def_port,
+ krbhst_get_default_proto(kd));
+ if(ret)
+ return ret;
+ kd->flags |= KD_FALLBACK;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ _krb5_debug(context, 0, "No admin entries found for realm %s", kd->realm);
+
+ return KRB5_KDC_UNREACH; /* XXX */
+}
+
+static krb5_error_code
+kpasswd_get_next(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ krb5_krbhst_info **host)
+{
+ krb5_error_code ret;
+
+ if ((kd->flags & KD_PLUGIN) == 0) {
+ plugin_get_hosts(context, kd, locate_service_kpasswd);
+ kd->flags |= KD_PLUGIN;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if((kd->flags & KD_CONFIG) == 0) {
+ config_get_hosts(context, kd, kd->config_param);
+ kd->flags |= KD_CONFIG;
+ if(get_next(kd, host))
+ return 0;
+ }
+
+ if (kd->flags & KD_CONFIG_EXISTS) {
+ _krb5_debug(context, 1,
+ "Configuration exists for realm %s, wont go to DNS",
+ kd->realm);
+ return KRB5_KDC_UNREACH;
+ }
+
+ if(context->srv_lookup) {
+ if((kd->flags & KD_SRV_UDP) == 0) {
+ srv_get_hosts(context, kd, NULL, "udp", kd->srv_label);
+ kd->flags |= KD_SRV_UDP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ if((kd->flags & KD_SRV_TCP) == 0) {
+ srv_get_hosts(context, kd, NULL, "tcp", kd->srv_label);
+ kd->flags |= KD_SRV_TCP;
+ if(get_next(kd, host))
+ return 0;
+ }
+ }
+
+ /* no matches -> try admin */
+
+ if (krbhst_empty(kd)) {
+ kd->flags = 0;
+ kd->port = kd->def_port;
+ kd->get_next = admin_get_next;
+ ret = (*kd->get_next)(context, kd, host);
+ if (ret == 0)
+ (*host)->proto = krbhst_get_default_proto(kd);
+ return ret;
+ }
+
+ _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
+
+ return KRB5_KDC_UNREACH;
+}
+
+static void KRB5_CALLCONV
+krbhost_dealloc(void *ptr)
+{
+ struct krb5_krbhst_data *handle = (struct krb5_krbhst_data *)ptr;
+ krb5_krbhst_info *h, *next;
+
+ for (h = handle->hosts; h != NULL; h = next) {
+ next = h->next;
+ _krb5_free_krbhst_info(h);
+ }
+ if (handle->hostname)
+ free(handle->hostname);
+ if (handle->sitename)
+ free(handle->sitename);
+
+ free(handle->realm);
+}
+
+static struct krb5_krbhst_data*
+common_init(krb5_context context,
+ const char *config_param,
+ const char *srv_label,
+ const char *service,
+ const char *realm,
+ int flags)
+{
+ struct krb5_krbhst_data *kd;
+
+ if ((kd = heim_alloc(sizeof(*kd), "krbhst-context", krbhost_dealloc)) == NULL)
+ return NULL;
+
+ if((kd->realm = strdup(realm)) == NULL) {
+ heim_release(kd);
+ return NULL;
+ }
+
+ kd->config_param = config_param;
+ kd->srv_label = srv_label;
+
+ _krb5_debug(context, 2, "Trying to find service %s for realm %s flags %x",
+ service, realm, flags);
+
+ /* For 'realms' without a . do not even think of going to DNS */
+ if (!strchr(realm, '.'))
+ kd->flags |= KD_CONFIG_EXISTS;
+
+ if (flags & KRB5_KRBHST_FLAGS_LARGE_MSG)
+ kd->flags |= KD_LARGE_MSG;
+ kd->end = kd->index = &kd->hosts;
+ return kd;
+}
+
+/*
+ * initialize `handle' to look for hosts of type `type' in realm `realm'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_init(krb5_context context,
+ const char *realm,
+ unsigned int type,
+ krb5_krbhst_handle *handle)
+{
+ return krb5_krbhst_init_flags(context, realm, type, 0, handle);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_init_flags(krb5_context context,
+ const char *realm,
+ unsigned int type,
+ int flags,
+ krb5_krbhst_handle *handle)
+{
+ struct krb5_krbhst_data *kd;
+ krb5_error_code (*next)(krb5_context, struct krb5_krbhst_data *,
+ krb5_krbhst_info **);
+ int def_port;
+ const char *config_param;
+ const char *srv_label;
+ const char *service;
+
+ *handle = NULL;
+
+ switch(type) {
+ case KRB5_KRBHST_KDC:
+ next = kdc_get_next;
+ def_port = ntohs(krb5_getportbyname(context, "kerberos", "udp", 88));
+ config_param = "kdc";
+ srv_label = "kerberos";
+ service = "kdc";
+ break;
+ case KRB5_KRBHST_ADMIN:
+ next = admin_get_next;
+ def_port = ntohs(krb5_getportbyname(context, "kerberos-adm",
+ "tcp", 749));
+ config_param = "admin_server";
+ srv_label = "kerberos-adm";
+ service = "admin";
+ break;
+ case KRB5_KRBHST_READONLY_ADMIN:
+ next = admin_get_next;
+ def_port = ntohs(krb5_getportbyname(context, "kerberos-adm",
+ "tcp", 749));
+ config_param = "readonly_admin_server";
+ srv_label = "kerberos-adm-readonly";
+ service = "admin";
+ break;
+ case KRB5_KRBHST_CHANGEPW:
+ next = kpasswd_get_next;
+ def_port = ntohs(krb5_getportbyname(context, "kpasswd", "udp",
+ KPASSWD_PORT));
+ config_param = "kpasswd_server";
+ srv_label = "kpasswd";
+ service = "change_password";
+ break;
+ case KRB5_KRBHST_TKTBRIDGEAP:
+ next = kdc_get_next;
+ def_port = ntohs(krb5_getportbyname(context, "kerberos", "tcp", 88));
+ config_param = "tktbridgeap";
+ srv_label = "kerberos-tkt-bridge";
+ service = "kdc";
+ break;
+ default:
+ krb5_set_error_message(context, ENOTTY,
+ N_("unknown krbhst type (%u)", ""), type);
+ return ENOTTY;
+ }
+ if((kd = common_init(context, config_param, srv_label, service, realm,
+ flags)) == NULL)
+ return ENOMEM;
+ kd->get_next = next;
+ kd->def_port = def_port;
+ *handle = kd;
+ return 0;
+}
+
+/*
+ * return the next host information from `handle' in `host'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_next(krb5_context context,
+ krb5_krbhst_handle handle,
+ krb5_krbhst_info **host)
+{
+ if(get_next(handle, host))
+ return 0;
+
+ return (*handle->get_next)(context, handle, host);
+}
+
+/*
+ * return the next host information from `handle' as a host name
+ * in `hostname' (or length `hostlen)
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_next_as_string(krb5_context context,
+ krb5_krbhst_handle handle,
+ char *hostname,
+ size_t hostlen)
+{
+ krb5_error_code ret;
+ krb5_krbhst_info *host;
+ ret = krb5_krbhst_next(context, handle, &host);
+ if(ret)
+ return ret;
+ return krb5_krbhst_format_string(context, host, hostname, hostlen);
+}
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_set_hostname(krb5_context context,
+ krb5_krbhst_handle handle,
+ const char *hostname)
+{
+ if (handle->hostname)
+ free(handle->hostname);
+ handle->hostname = strdup(hostname);
+ if (handle->hostname == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_krbhst_set_sitename(krb5_context context,
+ krb5_krbhst_handle handle,
+ const char *sitename)
+{
+ if (handle->sitename)
+ free(handle->sitename);
+ handle->sitename = strdup(sitename);
+ if (handle->sitename == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
+{
+ handle->index = &handle->hosts;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
+{
+ heim_release(handle);
+}
+
+#ifndef HEIMDAL_SMALLER
+
+/* backwards compatibility ahead */
+
+static krb5_error_code
+gethostlist(krb5_context context, const char *realm,
+ unsigned int type, char ***hostlist)
+{
+ krb5_error_code ret;
+ int nhost = 0;
+ krb5_krbhst_handle handle;
+ char host[MAXHOSTNAMELEN];
+ krb5_krbhst_info *hostinfo;
+
+ ret = krb5_krbhst_init(context, realm, type, &handle);
+ if (ret)
+ return ret;
+
+ while (krb5_krbhst_next(context, handle, &hostinfo) == 0)
+ nhost++;
+ if (nhost == 0) {
+ krb5_set_error_message(context, KRB5_KDC_UNREACH,
+ N_("No KDC found for realm %s", ""), realm);
+ krb5_krbhst_free(context, handle);
+ return KRB5_KDC_UNREACH;
+ }
+ *hostlist = calloc(nhost + 1, sizeof(**hostlist));
+ if (*hostlist == NULL) {
+ krb5_krbhst_free(context, handle);
+ return krb5_enomem(context);
+ }
+
+ krb5_krbhst_reset(context, handle);
+ nhost = 0;
+ while (krb5_krbhst_next_as_string(context, handle,
+ host, sizeof(host)) == 0) {
+ if (((*hostlist)[nhost++] = strdup(host)) == NULL) {
+ krb5_free_krbhst(context, *hostlist);
+ krb5_krbhst_free(context, handle);
+ return krb5_enomem(context);
+ }
+ }
+ (*hostlist)[nhost] = NULL;
+ krb5_krbhst_free(context, handle);
+ return 0;
+}
+
+/*
+ * Return a malloced list of kadmin-hosts for `realm' in `hostlist'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_krb_admin_hst(krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_ADMIN, hostlist);
+}
+
+/*
+ * Return a malloced list of writable kadmin-hosts for `realm' in `hostlist'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_krb_readonly_admin_hst(krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_READONLY_ADMIN, hostlist);
+}
+
+/*
+ * return an malloced list of changepw-hosts for `realm' in `hostlist'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_krb_changepw_hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_CHANGEPW, hostlist);
+}
+
+/*
+ * return an malloced list of 524-hosts for `realm' in `hostlist'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_krb524hst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
+}
+
+/*
+ * return an malloced list of KDC's for `realm' in `hostlist'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_krbhst (krb5_context context,
+ const krb5_realm *realm,
+ char ***hostlist)
+{
+ return gethostlist(context, *realm, KRB5_KRBHST_KDC, hostlist);
+}
+
+/*
+ * free all the memory allocated in `hostlist'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_free_krbhst (krb5_context context,
+ char **hostlist)
+{
+ char **p;
+
+ for (p = hostlist; *p; ++p)
+ free (*p);
+ free (hostlist);
+ return 0;
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/third_party/heimdal/lib/krb5/krcache.c b/third_party/heimdal/lib/krb5/krcache.c
new file mode 100644
index 0000000..9e99221
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/krcache.c
@@ -0,0 +1,2075 @@
+/*
+ * Copyright (c) 2006 The Regents of the University of Michigan.
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2018, AuriStor, Inc.
+ *
+ * Permission is granted to use, copy, create derivative works
+ * and redistribute this software and such derivative works
+ * for any purpose, so long as the name of The University of
+ * Michigan is not used in any advertising or publicity
+ * pertaining to the use of distribution of this software
+ * without specific, written prior authorization. If the
+ * above copyright notice or any other identification of the
+ * University of Michigan is included in any copy of any
+ * portion of this software, then the disclaimer below must
+ * also be included.
+ *
+ * THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION
+ * FROM THE UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY
+ * PURPOSE, AND WITHOUT WARRANTY BY THE UNIVERSITY OF
+ * MICHIGAN OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING
+ * WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ * REGENTS OF THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE
+ * FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR
+ * CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OF THE SOFTWARE, EVEN
+ * IF IT HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGES.
+ */
+/*
+ * Copyright 1990,1991,1992,1993,1994,2000,2004 Massachusetts Institute of
+ * Technology. All Rights Reserved.
+ *
+ * Original stdio support copyright 1995 by Cygnus Support.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+/*
+ * This file implements a collection-enabled credential cache type where the
+ * credentials are stored in the Linux keyring facility.
+ *
+ * A residual of this type can have three forms:
+ * anchor:collection:subsidiary
+ * anchor:collection
+ * collection
+ *
+ * The anchor name is "process", "thread", or "legacy" and determines where we
+ * search for keyring collections. In the third form, the anchor name is
+ * presumed to be "legacy". The anchor keyring for legacy caches is the
+ * session keyring.
+ *
+ * If the subsidiary name is present, the residual identifies a single cache
+ * within a collection. Otherwise, the residual identifies the collection
+ * itself. When a residual identifying a collection is resolved, the
+ * collection's primary key is looked up (or initialized, using the collection
+ * name as the subsidiary name), and the resulting cache's name will use the
+ * first name form and will identify the primary cache.
+ *
+ * Keyring collections are named "_krb_<collection>" and are linked from the
+ * anchor keyring. The keys within a keyring collection are links to cache
+ * keyrings, plus a link to one user key named "krb_ccache:primary" which
+ * contains a serialized representation of the collection version (currently 1)
+ * and the primary name of the collection.
+ *
+ * Cache keyrings contain one user key per credential which contains a
+ * serialized representation of the credential. There is also one user key
+ * named "__krb5_princ__" which contains a serialized representation of the
+ * cache's default principal.
+ *
+ * If the anchor name is "legacy", then the initial primary cache (the one
+ * named with the collection name) is also linked to the session keyring, and
+ * we look for a cache in that location when initializing the collection. This
+ * extra link allows that cache to be visible to old versions of the KEYRING
+ * cache type, and allows us to see caches created by that code.
+ */
+
+#include "krb5_locl.h"
+
+#ifdef HAVE_KEYUTILS_H
+
+#include <keyutils.h>
+
+/*
+ * We try to use the big_key key type for credentials except in legacy caches.
+ * We fall back to the user key type if the kernel does not support big_key.
+ * If the library doesn't support keyctl_get_persistent(), we don't even try
+ * big_key since the two features were added at the same time.
+ */
+#ifdef HAVE_KEYCTL_GET_PERSISTENT
+#define KRCC_CRED_KEY_TYPE "big_key"
+#else
+#define KRCC_CRED_KEY_TYPE "user"
+#endif
+
+/*
+ * We use the "user" key type for collection primary names, for cache principal
+ * names, and for credentials in legacy caches.
+ */
+#define KRCC_KEY_TYPE_USER "user"
+
+/*
+ * We create ccaches as separate keyrings
+ */
+#define KRCC_KEY_TYPE_KEYRING "keyring"
+
+/*
+ * Special name of the key within a ccache keyring
+ * holding principal information
+ */
+#define KRCC_SPEC_PRINC_KEYNAME "__krb5_princ__"
+
+/*
+ * Special name for the key to communicate the name(s)
+ * of credentials caches to be used for requests.
+ * This should currently contain a single name, but
+ * in the future may contain a list that may be
+ * intelligently chosen from.
+ */
+#define KRCC_SPEC_CCACHE_SET_KEYNAME "__krb5_cc_set__"
+
+/*
+ * This name identifies the key containing the name of the current primary
+ * cache within a collection.
+ */
+#define KRCC_COLLECTION_PRIMARY "krb_ccache:primary"
+
+/*
+ * If the library context does not specify a keyring collection, unique ccaches
+ * will be created within this collection.
+ */
+#define KRCC_DEFAULT_UNIQUE_COLLECTION "session:__krb5_unique__"
+
+/*
+ * Collection keyring names begin with this prefix. We use a prefix so that a
+ * cache keyring with the collection name itself can be linked directly into
+ * the anchor, for legacy session keyring compatibility.
+ */
+#define KRCC_CCCOL_PREFIX "_krb_"
+
+/*
+ * For the "persistent" anchor type, we look up or create this fixed keyring
+ * name within the per-UID persistent keyring.
+ */
+#define KRCC_PERSISTENT_KEYRING_NAME "_krb"
+
+/*
+ * Name of the key holding time offsets for the individual cache
+ */
+#define KRCC_TIME_OFFSETS "__krb5_time_offsets__"
+
+/*
+ * Keyring name prefix and length of random name part
+ */
+#define KRCC_NAME_PREFIX "krb_ccache_"
+#define KRCC_NAME_RAND_CHARS 8
+
+#define KRCC_COLLECTION_VERSION 1
+
+#define KRCC_PERSISTENT_ANCHOR "persistent"
+#define KRCC_PROCESS_ANCHOR "process"
+#define KRCC_THREAD_ANCHOR "thread"
+#define KRCC_SESSION_ANCHOR "session"
+#define KRCC_USER_ANCHOR "user"
+#define KRCC_LEGACY_ANCHOR "legacy"
+
+#if SIZEOF_KEY_SERIAL_T != 4
+/* lockless implementation assumes 32-bit key serials */
+#error only 32-bit key serial numbers supported by this version of keyring ccache
+#endif
+
+typedef heim_base_atomic(key_serial_t) atomic_key_serial_t;
+
+typedef union _krb5_krcache_and_princ_id {
+ heim_base_atomic(uint64_t) krcu_cache_and_princ_id;
+ struct {
+ atomic_key_serial_t cache_id; /* keyring ID representing ccache */
+ atomic_key_serial_t princ_id; /* key ID holding principal info */
+ } krcu_id;
+ #define krcu_cache_id krcu_id.cache_id
+ #define krcu_princ_id krcu_id.princ_id
+} krb5_krcache_and_princ_id;
+
+/*
+ * This represents a credentials cache "file" where cache_id is the keyring
+ * serial number for this credentials cache "file". Each key in the keyring
+ * contains a separate key.
+ *
+ * Thread-safe as long as caches are not destroyed whilst other threads are
+ * using them.
+ */
+typedef struct _krb5_krcache {
+ char *krc_name; /* Name for this credentials cache */
+ char *krc_collection;
+ char *krc_subsidiary;
+ heim_base_atomic(krb5_timestamp) krc_changetime; /* update time, does not decrease (mutable) */
+ krb5_krcache_and_princ_id krc_id; /* cache and principal IDs (mutable) */
+ #define krc_cache_and_principal_id krc_id.krcu_cache_and_princ_id
+ #define krc_cache_id krc_id.krcu_id.cache_id
+ #define krc_princ_id krc_id.krcu_id.princ_id
+ key_serial_t krc_coll_id; /* collection containing this cache keyring */
+ krb5_boolean krc_is_legacy; /* */
+} krb5_krcache;
+
+#define KRCACHE(X) ((krb5_krcache *)(X)->data.data)
+
+static krb5_error_code KRB5_CALLCONV
+krcc_get_first(krb5_context, krb5_ccache id, krb5_cc_cursor *cursor);
+
+static krb5_error_code KRB5_CALLCONV
+krcc_get_next(krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds);
+
+static krb5_error_code KRB5_CALLCONV
+krcc_end_get(krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor);
+
+static krb5_error_code KRB5_CALLCONV
+krcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor);
+
+static krb5_error_code
+clear_cache_keyring(krb5_context context, atomic_key_serial_t *pcache_id);
+
+static krb5_error_code
+alloc_cache(krb5_context context,
+ key_serial_t collection_id,
+ key_serial_t cache_id,
+ const char *anchor_name,
+ const char *collection_name,
+ const char *subsidiary_name,
+ krb5_krcache **data);
+
+static krb5_error_code
+save_principal(krb5_context context,
+ key_serial_t cache_id,
+ krb5_const_principal princ,
+ atomic_key_serial_t *pprinc_id);
+
+static krb5_error_code
+save_time_offsets(krb5_context context,
+ key_serial_t cache_id,
+ int32_t sec_offset,
+ int32_t usec_offset);
+
+static void
+update_change_time(krb5_context context,
+ krb5_timestamp now,
+ krb5_krcache *d);
+
+/*
+ * GET_PERSISTENT(uid) acquires the persistent keyring for uid, or falls back
+ * to the user keyring if uid matches the current effective uid.
+ */
+
+static key_serial_t
+get_persistent_fallback(uid_t uid)
+{
+ return (uid == geteuid()) ? KEY_SPEC_USER_KEYRING : -1;
+}
+
+#ifdef HAVE_KEYCTL_GET_PERSISTENT
+#define GET_PERSISTENT get_persistent_real
+static key_serial_t
+get_persistent_real(uid_t uid)
+{
+ key_serial_t key;
+
+ key = keyctl_get_persistent(uid, KEY_SPEC_PROCESS_KEYRING);
+
+ return (key == -1 && errno == ENOTSUP) ? get_persistent_fallback(uid) : key;
+}
+#else
+#define GET_PERSISTENT get_persistent_fallback
+#endif
+
+/*
+ * If a process has no explicitly set session keyring, KEY_SPEC_SESSION_KEYRING
+ * will resolve to the user session keyring for ID lookup and reading, but in
+ * some kernel versions, writing to that special keyring will instead create a
+ * new empty session keyring for the process. We do not want that; the keys we
+ * create would be invisible to other processes. We can work around that
+ * behavior by explicitly writing to the user session keyring when it matches
+ * the session keyring. This function returns the keyring we should write to
+ * for the session anchor.
+ */
+static key_serial_t
+session_write_anchor(void)
+{
+ key_serial_t s, u;
+
+ s = keyctl_get_keyring_ID(KEY_SPEC_SESSION_KEYRING, 0);
+ u = keyctl_get_keyring_ID(KEY_SPEC_USER_SESSION_KEYRING, 0);
+
+ return (s == u) ? KEY_SPEC_USER_SESSION_KEYRING : KEY_SPEC_SESSION_KEYRING;
+}
+
+/*
+ * Find or create a keyring within parent with the given name. If possess is
+ * nonzero, also make sure the key is linked from possess. This is necessary
+ * to ensure that we have possession rights on the key when the parent is the
+ * user or persistent keyring.
+ */
+static krb5_error_code
+find_or_create_keyring(key_serial_t parent,
+ key_serial_t possess,
+ const char *name,
+ atomic_key_serial_t *pkey)
+{
+ key_serial_t key;
+
+ key = keyctl_search(parent, KRCC_KEY_TYPE_KEYRING, name, possess);
+ if (key == -1) {
+ if (possess != 0) {
+ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, possess);
+ if (key == -1 || keyctl_link(key, parent) == -1)
+ return errno;
+ } else {
+ key = add_key(KRCC_KEY_TYPE_KEYRING, name, NULL, 0, parent);
+ if (key == -1)
+ return errno;
+ }
+ }
+
+ heim_base_atomic_store(pkey, key);
+
+ return 0;
+}
+
+/*
+ * Parse a residual name into an anchor name, a collection name, and possibly a
+ * subsidiary name.
+ */
+static krb5_error_code
+parse_residual(krb5_context context,
+ const char *residual,
+ char **panchor_name,
+ char **pcollection_name,
+ char **psubsidiary_name)
+{
+ char *anchor_name = NULL;
+ char *collection_name = NULL;
+ char *subsidiary_name = NULL;
+ const char *sep;
+
+ *panchor_name = NULL;
+ *pcollection_name = NULL;
+ *psubsidiary_name = NULL;
+
+ if (residual == NULL)
+ residual = "";
+
+ /* Parse out the anchor name. Use the legacy anchor if not present. */
+ sep = strchr(residual, ':');
+ if (sep == NULL) {
+ anchor_name = strdup(KRCC_LEGACY_ANCHOR);
+ if (anchor_name == NULL)
+ goto nomem;
+ } else {
+ anchor_name = strndup(residual, sep - residual);
+ if (anchor_name == NULL)
+ goto nomem;
+ residual = sep + 1;
+ }
+
+ /* Parse out the collection and subsidiary name. */
+ sep = strchr(residual, ':');
+ if (sep == NULL) {
+ collection_name = strdup(residual);
+ if (collection_name == NULL)
+ goto nomem;
+ } else {
+ collection_name = strndup(residual, sep - residual);
+ if (collection_name == NULL)
+ goto nomem;
+
+ subsidiary_name = strdup(sep + 1);
+ if (subsidiary_name == NULL)
+ goto nomem;
+ }
+
+ *panchor_name = anchor_name;
+ *pcollection_name = collection_name;
+ *psubsidiary_name = subsidiary_name;
+
+ return 0;
+
+nomem:
+ free(anchor_name);
+ free(collection_name);
+ free(subsidiary_name);
+
+ return krb5_enomem(context);
+}
+
+/*
+ * Return TRUE if residual identifies a subsidiary cache which should be linked
+ * into the anchor so it can be visible to old code. This is the case if the
+ * residual has the legacy anchor and the subsidiary name matches the
+ * collection name.
+ */
+static krb5_boolean
+is_legacy_cache_name_p(const char *residual)
+{
+ const char *sep, *aname, *cname, *sname;
+ size_t alen, clen, legacy_len = sizeof(KRCC_LEGACY_ANCHOR) - 1;
+
+ /* Get pointers to the anchor, collection, and subsidiary names. */
+ aname = residual;
+ sep = strchr(residual, ':');
+ if (sep == NULL)
+ return FALSE;
+
+ alen = sep - aname;
+ cname = sep + 1;
+ sep = strchr(cname, ':');
+ if (sep == NULL)
+ return FALSE;
+
+ clen = sep - cname;
+ sname = sep + 1;
+
+ return alen == legacy_len && clen == strlen(sname) &&
+ strncmp(aname, KRCC_LEGACY_ANCHOR, alen) == 0 &&
+ strncmp(cname, sname, clen) == 0;
+}
+
+/*
+ * If the default cache name for context is a KEYRING cache, parse its residual
+ * string. Otherwise set all outputs to NULL.
+ */
+static krb5_error_code
+get_default(krb5_context context,
+ char **panchor_name,
+ char **pcollection_name,
+ char **psubsidiary_name)
+{
+ const char *defname;
+
+ *panchor_name = *pcollection_name = *psubsidiary_name = NULL;
+
+ defname = krb5_cc_default_name(context);
+ if (defname == NULL || strncmp(defname, "KEYRING:", 8) != 0)
+ return 0;
+
+ return parse_residual(context, defname + 8,
+ panchor_name, pcollection_name, psubsidiary_name);
+}
+
+/* Create a residual identifying a subsidiary cache. */
+static krb5_error_code
+make_subsidiary_residual(krb5_context context,
+ const char *anchor_name,
+ const char *collection_name,
+ const char *subsidiary_name,
+ char **presidual)
+{
+ if (asprintf(presidual, "%s:%s:%s", anchor_name, collection_name,
+ subsidiary_name ? subsidiary_name : "tkt") < 0) {
+ *presidual = NULL;
+ return krb5_enomem(context);
+ }
+
+ return 0;
+}
+
+/*
+ * Retrieve or create a keyring for collection_name within the anchor, and set
+ * *collection_id to its serial number.
+ */
+static krb5_error_code
+get_collection(krb5_context context,
+ const char *anchor_name,
+ const char *collection_name,
+ atomic_key_serial_t *pcollection_id)
+{
+ krb5_error_code ret;
+ key_serial_t persistent_id, anchor_id, possess_id = 0;
+ char *ckname, *cnend;
+ uid_t uidnum;
+
+ heim_base_atomic_init(pcollection_id, 0);
+
+ if (!anchor_name || !collection_name)
+ return KRB5_KCC_INVALID_ANCHOR;
+
+ if (strcmp(anchor_name, KRCC_PERSISTENT_ANCHOR) == 0) {
+ /*
+ * The collection name is a uid (or empty for the current effective
+ * uid), and we look up a fixed keyring name within the persistent
+ * keyring for that uid. We link it to the process keyring to ensure
+ * that we have possession rights on the collection key.
+ */
+ if (*collection_name != '\0') {
+ errno = 0;
+ uidnum = (uid_t)strtol(collection_name, &cnend, 10);
+ if (errno || *cnend != '\0')
+ return KRB5_KCC_INVALID_UID;
+ } else {
+ uidnum = geteuid();
+ }
+
+ persistent_id = GET_PERSISTENT(uidnum);
+ if (persistent_id == -1)
+ return KRB5_KCC_INVALID_UID;
+
+ return find_or_create_keyring(persistent_id, KEY_SPEC_PROCESS_KEYRING,
+ KRCC_PERSISTENT_KEYRING_NAME,
+ pcollection_id);
+ }
+
+ if (strcmp(anchor_name, KRCC_PROCESS_ANCHOR) == 0) {
+ anchor_id = KEY_SPEC_PROCESS_KEYRING;
+ } else if (strcmp(anchor_name, KRCC_THREAD_ANCHOR) == 0) {
+ anchor_id = KEY_SPEC_THREAD_KEYRING;
+ } else if (strcmp(anchor_name, KRCC_SESSION_ANCHOR) == 0) {
+ anchor_id = session_write_anchor();
+ } else if (strcmp(anchor_name, KRCC_USER_ANCHOR) == 0) {
+ /*
+ * The user keyring does not confer possession, so we need to link the
+ * collection to the process keyring to maintain possession rights.
+ */
+ anchor_id = KEY_SPEC_USER_KEYRING;
+ possess_id = KEY_SPEC_PROCESS_KEYRING;
+ } else if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
+ anchor_id = session_write_anchor();
+ } else {
+ return KRB5_KCC_INVALID_ANCHOR;
+ }
+
+ /* Look up the collection keyring name within the anchor keyring. */
+ if (asprintf(&ckname, "%s%s", KRCC_CCCOL_PREFIX, collection_name) == -1)
+ return krb5_enomem(context);
+
+ ret = find_or_create_keyring(anchor_id, possess_id, ckname,
+ pcollection_id);
+ free(ckname);
+
+ return ret;
+}
+
+/* Store subsidiary_name into the primary index key for collection_id. */
+static krb5_error_code
+set_primary_name(krb5_context context,
+ key_serial_t collection_id,
+ const char *subsidiary_name)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ krb5_data payload;
+ key_serial_t key;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ ret = krb5_store_int32(sp, KRCC_COLLECTION_VERSION);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_store_string(sp, subsidiary_name);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_storage_to_data(sp, &payload);
+ if (ret)
+ goto cleanup;
+
+ key = add_key(KRCC_KEY_TYPE_USER, KRCC_COLLECTION_PRIMARY,
+ payload.data, payload.length, collection_id);
+ ret = (key == -1) ? errno : 0;
+ krb5_data_free(&payload);
+
+cleanup:
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+static krb5_error_code
+parse_index(krb5_context context,
+ int32_t *version,
+ char **primary,
+ const unsigned char *payload,
+ size_t psize)
+{
+ krb5_error_code ret;
+ krb5_data payload_data;
+ krb5_storage *sp;
+
+ payload_data.length = psize;
+ payload_data.data = rk_UNCONST(payload);
+
+ sp = krb5_storage_from_data(&payload_data);
+ if (sp == NULL)
+ return KRB5_CC_NOMEM;
+
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ ret = krb5_ret_int32(sp, version);
+ if (ret == 0)
+ ret = krb5_ret_string(sp, primary);
+
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+/*
+ * Get or initialize the primary name within collection_id and set
+ * *subsidiary to its value. If initializing a legacy collection, look
+ * for a legacy cache and add it to the collection.
+ */
+static krb5_error_code
+get_primary_name(krb5_context context,
+ const char *anchor_name,
+ const char *collection_name,
+ key_serial_t collection_id,
+ char **psubsidiary)
+{
+ krb5_error_code ret;
+ key_serial_t primary_id, legacy;
+ void *payload = NULL;
+ int payloadlen;
+ int32_t version;
+ char *subsidiary_name = NULL;
+
+ *psubsidiary = NULL;
+
+ primary_id = keyctl_search(collection_id, KRCC_KEY_TYPE_USER,
+ KRCC_COLLECTION_PRIMARY, 0);
+ if (primary_id == -1) {
+ /*
+ * Initialize the primary key using the collection name. We can't name
+ * a key with the empty string, so map that to an arbitrary string.
+ */
+ subsidiary_name = strdup((*collection_name == '\0') ? "tkt" :
+ collection_name);
+ if (subsidiary_name == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+
+ ret = set_primary_name(context, collection_id, subsidiary_name);
+ if (ret)
+ goto cleanup;
+
+ if (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0) {
+ /*
+ * Look for a cache created by old code. If we find one, add it to
+ * the collection.
+ */
+ legacy = keyctl_search(KEY_SPEC_SESSION_KEYRING,
+ KRCC_KEY_TYPE_KEYRING, subsidiary_name, 0);
+ if (legacy != -1 && keyctl_link(legacy, collection_id) == -1) {
+ ret = errno;
+ goto cleanup;
+ }
+ }
+ } else {
+ /* Read, parse, and free the primary key's payload. */
+ payloadlen = keyctl_read_alloc(primary_id, &payload);
+ if (payloadlen == -1) {
+ ret = errno;
+ goto cleanup;
+ }
+ ret = parse_index(context, &version, &subsidiary_name, payload,
+ payloadlen);
+ if (ret)
+ goto cleanup;
+
+ if (version != KRCC_COLLECTION_VERSION) {
+ ret = KRB5_KCC_UNKNOWN_VERSION;
+ goto cleanup;
+ }
+ }
+
+ *psubsidiary = subsidiary_name;
+ subsidiary_name = NULL;
+
+cleanup:
+ free(payload);
+ free(subsidiary_name);
+
+ return ret;
+}
+
+/*
+ * Note: MIT keyring code uses krb5int_random_string() as if the second argument
+ * is a character count rather than a size. The function below takes a character
+ * count to match the usage in this file correctly.
+ */
+static krb5_error_code
+generate_random_string(krb5_context context, char *s, size_t slen)
+{
+ static char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ char *p;
+ size_t i;
+
+ p = malloc(slen);
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ krb5_generate_random_block(p, slen);
+
+ for (i = 0; i < slen; i++)
+ s[i] = chars[p[i] % (sizeof(chars) - 1)];
+
+ s[i] = '\0';
+ free(p);
+
+ return 0;
+}
+
+/*
+ * Create a keyring with a unique random name within collection_id. Set
+ * *subsidiary to its name and *cache_id to its key serial number.
+ */
+static krb5_error_code
+add_unique_keyring(krb5_context context,
+ key_serial_t collection_id,
+ char **psubsidiary,
+ key_serial_t *pcache_id)
+{
+ key_serial_t key;
+ krb5_error_code ret;
+ char uniquename[sizeof(KRCC_NAME_PREFIX) + KRCC_NAME_RAND_CHARS];
+ int prefixlen = sizeof(KRCC_NAME_PREFIX) - 1;
+ int tries;
+
+ *psubsidiary = NULL;
+ *pcache_id = 0;
+
+ memcpy(uniquename, KRCC_NAME_PREFIX, sizeof(KRCC_NAME_PREFIX));
+
+ for (key = -1, tries = 0; tries < 5; tries++) {
+ ret = generate_random_string(context, uniquename + prefixlen,
+ KRCC_NAME_RAND_CHARS);
+ if (ret)
+ return ret;
+
+ key = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING, uniquename, 0);
+ if (key == -1) {
+ /* Name does not already exist. Create it to reserve the name. */
+ key = add_key(KRCC_KEY_TYPE_KEYRING, uniquename, NULL, 0, collection_id);
+ if (key == -1)
+ return errno;
+ break;
+ }
+ }
+
+ *psubsidiary = strdup(uniquename);
+ if (*psubsidiary == NULL)
+ return krb5_enomem(context);
+
+ *pcache_id = key;
+
+ return 0;
+}
+
+static krb5_error_code
+add_cred_key(const char *name,
+ const void *payload,
+ size_t plen,
+ key_serial_t cache_id,
+ krb5_boolean legacy_type,
+ key_serial_t *pkey)
+{
+ key_serial_t key;
+
+ *pkey = -1;
+
+ if (!legacy_type) {
+ /* Try the preferred cred key type; fall back if no kernel support. */
+ key = add_key(KRCC_CRED_KEY_TYPE, name, payload, plen, cache_id);
+ if (key != -1) {
+ *pkey = key;
+ return 0;
+ } else if (errno != EINVAL && errno != ENODEV)
+ return errno;
+ }
+
+ /* Use the user key type. */
+ key = add_key(KRCC_KEY_TYPE_USER, name, payload, plen, cache_id);
+ if (key == -1)
+ return errno;
+
+ *pkey = key;
+
+ return 0;
+}
+
+static void
+update_keyring_expiration(krb5_context context,
+ krb5_ccache id,
+ key_serial_t cache_id,
+ krb5_timestamp now)
+{
+ krb5_cc_cursor cursor;
+ krb5_creds creds;
+ krb5_timestamp endtime = 0;
+ unsigned int timeout;
+
+ /*
+ * We have no way to know what is the actual timeout set on the keyring.
+ * We also cannot keep track of it in a local variable as another process
+ * can always modify the keyring independently, so just always enumerate
+ * all start TGT keys and find out the highest endtime time.
+ */
+ if (krcc_get_first(context, id, &cursor) != 0)
+ return;
+
+ for (;;) {
+ if (krcc_get_next(context, id, &cursor, &creds) != 0)
+ break;
+ if (creds.times.endtime > endtime)
+ endtime = creds.times.endtime;
+ krb5_free_cred_contents(context, &creds);
+ }
+ (void) krcc_end_get(context, id, &cursor);
+
+ if (endtime == 0) /* No creds with end times */
+ return;
+
+ /*
+ * Setting the timeout to zero would reset the timeout, so we set it to one
+ * second instead if creds are already expired.
+ */
+ timeout = endtime > now ? endtime - now : 1;
+ (void) keyctl_set_timeout(cache_id, timeout);
+}
+
+/*
+ * Create or overwrite the cache keyring, and set the default principal.
+ */
+static krb5_error_code
+initialize_internal(krb5_context context,
+ krb5_ccache id,
+ krb5_const_principal princ)
+{
+ krb5_krcache *data = KRCACHE(id);
+ krb5_error_code ret;
+ const char *cache_name, *p;
+ krb5_krcache_and_princ_id ids;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ memset(&ids, 0, sizeof(ids));
+ ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
+
+ ret = clear_cache_keyring(context, &ids.krcu_cache_id);
+ if (ret)
+ return ret;
+
+ if (ids.krcu_cache_id == 0) {
+ /*
+ * The key didn't exist at resolve time, or was destroyed after resolving.
+ * Check again and create the key if it still isn't there.
+ */
+ p = strrchr(data->krc_name, ':');
+ cache_name = (p != NULL) ? p + 1 : data->krc_name;
+ ret = find_or_create_keyring(data->krc_coll_id, 0, cache_name, &ids.krcu_cache_id);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * If this is the legacy cache in a legacy session collection, link it
+ * directly to the session keyring so that old code can see it.
+ */
+ if (is_legacy_cache_name_p(data->krc_name))
+ (void) keyctl_link(ids.krcu_cache_id, session_write_anchor());
+
+ if (princ != NULL) {
+ ret = save_principal(context, ids.krcu_cache_id, princ, &ids.krcu_princ_id);
+ if (ret)
+ return ret;
+ } else
+ ids.krcu_princ_id = 0;
+
+ /*
+ * Save time offset if it is valid and this is not a legacy cache. Legacy
+ * applications would fail to parse the new key in the cache keyring.
+ */
+ if (context->kdc_sec_offset && !is_legacy_cache_name_p(data->krc_name)) {
+ ret = save_time_offsets(context,
+ ids.krcu_cache_id,
+ context->kdc_sec_offset,
+ context->kdc_usec_offset);
+ if (ret)
+ return ret;
+ }
+
+ /* update cache and principal IDs atomically */
+ heim_base_atomic_store(&data->krc_cache_and_principal_id, ids.krcu_cache_and_princ_id);
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ)
+{
+ krb5_krcache *data = KRCACHE(id);
+ krb5_error_code ret;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ if (princ == NULL)
+ return KRB5_CC_BADNAME;
+
+ ret = initialize_internal(context, id, princ);
+ if (ret == 0)
+ update_change_time(context, 0, data);
+
+ return ret;
+}
+
+/* Release the ccache handle. */
+static krb5_error_code KRB5_CALLCONV
+krcc_close(krb5_context context, krb5_ccache id)
+{
+ krb5_krcache *data = KRCACHE(id);
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ free(data->krc_subsidiary);
+ free(data->krc_collection);
+ free(data->krc_name);
+ krb5_data_free(&id->data);
+
+ return 0;
+}
+
+/*
+ * Clear out a ccache keyring, unlinking all keys within it.
+ */
+static krb5_error_code
+clear_cache_keyring(krb5_context context,
+ atomic_key_serial_t *pcache_id)
+{
+ int res;
+ key_serial_t cache_id = heim_base_atomic_load(pcache_id);
+
+ _krb5_debug(context, 10, "clear_cache_keyring: cache_id %d\n", cache_id);
+
+ if (cache_id != 0) {
+ res = keyctl_clear(cache_id);
+ if (res == -1 && (errno == EACCES || errno == ENOKEY)) {
+ /*
+ * Possibly the keyring was destroyed between krcc_resolve() and now;
+ * if we really don't have permission, we will fail later.
+ */
+ res = 0;
+ heim_base_atomic_store(pcache_id, 0);
+ }
+ if (res == -1)
+ return errno;
+ }
+
+ return 0;
+}
+
+/* Destroy the cache keyring */
+static krb5_error_code KRB5_CALLCONV
+krcc_destroy(krb5_context context, krb5_ccache id)
+{
+ krb5_error_code ret = 0;
+ krb5_krcache *data = KRCACHE(id);
+ int res;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ /* no atomics, destroy is not thread-safe */
+ (void) clear_cache_keyring(context, &data->krc_cache_id);
+
+ if (data->krc_cache_id != 0) {
+ res = keyctl_unlink(data->krc_cache_id, data->krc_coll_id);
+ if (res < 0) {
+ ret = errno;
+ _krb5_debug(context, 10, "unlinking key %d from ring %d: %s",
+ data->krc_cache_id, data->krc_coll_id, error_message(errno));
+ }
+ /* If this is a legacy cache, unlink it from the session anchor. */
+ if (is_legacy_cache_name_p(data->krc_name))
+ (void) keyctl_unlink(data->krc_cache_id, session_write_anchor());
+ }
+
+ heim_base_atomic_store(&data->krc_princ_id, 0);
+
+ /* krcc_close is called by libkrb5, do not double-free */
+ return ret;
+}
+
+/* Create a cache handle for a cache ID. */
+static krb5_error_code
+make_cache(krb5_context context,
+ key_serial_t collection_id,
+ key_serial_t cache_id,
+ const char *anchor_name,
+ const char *collection_name,
+ const char *subsidiary_name,
+ krb5_ccache *cache)
+{
+ krb5_error_code ret;
+ krb5_krcache *data;
+ key_serial_t princ_id = 0;
+
+ /* Determine the key containing principal information, if present. */
+ princ_id = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME, 0);
+ if (princ_id == -1)
+ princ_id = 0;
+
+ ret = alloc_cache(context, collection_id, cache_id,
+ anchor_name, collection_name, subsidiary_name, &data);
+ if (ret)
+ return ret;
+
+ if (*cache == NULL) {
+ ret = _krb5_cc_allocate(context, &krb5_krcc_ops, cache);
+ if (ret) {
+ free(data->krc_name);
+ free(data);
+ return ret;
+ }
+ }
+
+ data->krc_princ_id = princ_id;
+
+ (*cache)->data.data = data;
+ (*cache)->data.length = sizeof(*data);
+
+ return 0;
+}
+
+/* Create a keyring ccache handle for the given residual string. */
+static krb5_error_code KRB5_CALLCONV
+krcc_resolve_2(krb5_context context,
+ krb5_ccache *id,
+ const char *residual,
+ const char *sub)
+{
+ krb5_error_code ret;
+ atomic_key_serial_t collection_id;
+ key_serial_t cache_id;
+ char *anchor_name = NULL, *collection_name = NULL, *subsidiary_name = NULL;
+
+ ret = parse_residual(context, residual, &anchor_name, &collection_name,
+ &subsidiary_name);
+ if (ret)
+ goto cleanup;
+ if (sub) {
+ free(subsidiary_name);
+ if ((subsidiary_name = strdup(sub)) == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ }
+
+ ret = get_collection(context, anchor_name, collection_name, &collection_id);
+ if (ret)
+ goto cleanup;
+
+ if (subsidiary_name == NULL) {
+ /* Retrieve or initialize the primary name for the collection. */
+ ret = get_primary_name(context, anchor_name, collection_name,
+ collection_id, &subsidiary_name);
+ if (ret)
+ goto cleanup;
+ }
+
+ /* Look up the cache keyring ID, if the cache is already initialized. */
+ cache_id = keyctl_search(collection_id, KRCC_KEY_TYPE_KEYRING,
+ subsidiary_name, 0);
+ if (cache_id < 0)
+ cache_id = 0;
+
+ ret = make_cache(context, collection_id, cache_id, anchor_name,
+ collection_name, subsidiary_name, id);
+ if (ret)
+ goto cleanup;
+
+cleanup:
+ free(anchor_name);
+ free(collection_name);
+ free(subsidiary_name);
+
+ return ret;
+}
+
+struct krcc_cursor {
+ size_t numkeys;
+ size_t currkey;
+ key_serial_t princ_id;
+ key_serial_t offsets_id;
+ key_serial_t *keys;
+};
+
+/* Prepare for a sequential iteration over the cache keyring. */
+static krb5_error_code
+krcc_get_first(krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ struct krcc_cursor *krcursor;
+ krb5_krcache *data = KRCACHE(id);
+ key_serial_t cache_id;
+ void *keys;
+ long size;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ cache_id = heim_base_atomic_load(&data->krc_cache_id);
+ if (cache_id == 0)
+ return KRB5_FCC_NOFILE;
+
+ size = keyctl_read_alloc(cache_id, &keys);
+ if (size == -1) {
+ _krb5_debug(context, 10, "Error getting from keyring: %s\n",
+ strerror(errno));
+ return KRB5_CC_IO;
+ }
+
+ krcursor = calloc(1, sizeof(*krcursor));
+ if (krcursor == NULL) {
+ free(keys);
+ return KRB5_CC_NOMEM;
+ }
+
+ krcursor->princ_id = heim_base_atomic_load(&data->krc_princ_id);
+ krcursor->offsets_id = keyctl_search(cache_id, KRCC_KEY_TYPE_USER,
+ KRCC_TIME_OFFSETS, 0);
+ krcursor->numkeys = size / sizeof(key_serial_t);
+ krcursor->keys = keys;
+
+ *cursor = krcursor;
+
+ return 0;
+}
+
+static krb5_error_code
+keyctl_read_krb5_data(key_serial_t keyid, krb5_data *payload)
+{
+ krb5_data_zero(payload);
+
+ payload->length = keyctl_read_alloc(keyid, &payload->data);
+
+ return (payload->length == -1) ? KRB5_FCC_NOFILE : 0;
+}
+
+/* Get the next credential from the cache keyring. */
+static krb5_error_code KRB5_CALLCONV
+krcc_get_next(krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ struct krcc_cursor *krcursor;
+ krb5_error_code ret;
+ krb5_data payload;
+ krb5_storage *sp;
+
+ memset(creds, 0, sizeof(krb5_creds));
+
+ krcursor = *cursor;
+ if (krcursor == NULL)
+ return KRB5_CC_END;
+
+ if (krcursor->currkey >= krcursor->numkeys)
+ return KRB5_CC_END;
+
+ /*
+ * If we're pointing at the entry with the principal, or at the key
+ * with the time offsets, skip it.
+ */
+ while (krcursor->keys[krcursor->currkey] == krcursor->princ_id ||
+ krcursor->keys[krcursor->currkey] == krcursor->offsets_id) {
+ krcursor->currkey++;
+ if (krcursor->currkey >= krcursor->numkeys)
+ return KRB5_CC_END;
+ }
+
+ ret = keyctl_read_krb5_data(krcursor->keys[krcursor->currkey], &payload);
+ if (ret) {
+ _krb5_debug(context, 10, "Error reading key %d: %s\n",
+ krcursor->keys[krcursor->currkey],
+ strerror(errno));
+ return ret;
+ }
+ krcursor->currkey++;
+
+ sp = krb5_storage_from_data(&payload);
+ if (sp == NULL) {
+ ret = KRB5_CC_IO;
+ } else {
+ ret = krb5_ret_creds(sp, creds);
+ krb5_storage_free(sp);
+ }
+
+ krb5_data_free(&payload);
+
+ return ret;
+}
+
+/* Release an iteration cursor. */
+static krb5_error_code KRB5_CALLCONV
+krcc_end_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor)
+{
+ struct krcc_cursor *krcursor = *cursor;
+
+ if (krcursor != NULL) {
+ free(krcursor->keys);
+ free(krcursor);
+ }
+
+ *cursor = NULL;
+
+ return 0;
+}
+
+/* Create keyring data for a credential cache. */
+static krb5_error_code
+alloc_cache(krb5_context context,
+ key_serial_t collection_id,
+ key_serial_t cache_id,
+ const char *anchor_name,
+ const char *collection_name,
+ const char *subsidiary_name,
+ krb5_krcache **pdata)
+{
+ krb5_error_code ret;
+ krb5_krcache *data;
+
+ *pdata = NULL;
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL)
+ return KRB5_CC_NOMEM;
+
+ ret = make_subsidiary_residual(context, anchor_name, collection_name,
+ subsidiary_name, &data->krc_name);
+ if (ret ||
+ (data->krc_collection = strdup(collection_name)) == NULL ||
+ (data->krc_subsidiary = strdup(subsidiary_name ? subsidiary_name : "tkt")) == NULL) {
+ if (data) {
+ free(data->krc_collection);
+ free(data->krc_name);
+ }
+ free(data);
+ if (ret == 0)
+ ret = krb5_enomem(context);
+ return ret;
+ }
+
+ heim_base_atomic_init(&data->krc_princ_id, 0);
+ heim_base_atomic_init(&data->krc_cache_id, cache_id);
+ data->krc_coll_id = collection_id;
+ data->krc_changetime = 0;
+ data->krc_is_legacy = (strcmp(anchor_name, KRCC_LEGACY_ANCHOR) == 0);
+
+ update_change_time(context, 0, data);
+
+ *pdata = data;
+
+ return 0;
+}
+
+/* Create a new keyring cache with a unique name. */
+static krb5_error_code KRB5_CALLCONV
+krcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ char *anchor_name, *collection_name, *subsidiary_name;
+ char *new_subsidiary_name = NULL, *new_residual = NULL;
+ krb5_krcache *data;
+ atomic_key_serial_t collection_id;
+ key_serial_t cache_id = 0;
+
+ /* Determine the collection in which we will create the cache.*/
+ ret = get_default(context, &anchor_name, &collection_name,
+ &subsidiary_name);
+ if (ret)
+ return ret;
+
+ if (anchor_name == NULL) {
+ ret = parse_residual(context, KRCC_DEFAULT_UNIQUE_COLLECTION, &anchor_name,
+ &collection_name, &subsidiary_name);
+ if (ret)
+ return ret;
+ }
+ if (subsidiary_name != NULL) {
+ krb5_set_error_message(context, KRB5_DCC_CANNOT_CREATE,
+ N_("Can't create new subsidiary cache because default cache "
+ "is already a subsidiary", ""));
+ ret = KRB5_DCC_CANNOT_CREATE;
+ goto cleanup;
+ }
+
+ /* Make a unique keyring within the chosen collection. */
+ ret = get_collection(context, anchor_name, collection_name, &collection_id);
+ if (ret)
+ goto cleanup;
+
+ ret = add_unique_keyring(context, collection_id, &new_subsidiary_name, &cache_id);
+ if (ret)
+ goto cleanup;
+
+ ret = alloc_cache(context, collection_id, cache_id,
+ anchor_name, collection_name, new_subsidiary_name,
+ &data);
+ if (ret)
+ goto cleanup;
+
+ (*id)->data.data = data;
+ (*id)->data.length = sizeof(*data);
+
+cleanup:
+ free(anchor_name);
+ free(collection_name);
+ free(subsidiary_name);
+ free(new_subsidiary_name);
+ free(new_residual);
+
+ return ret;
+}
+
+/* Return an alias to the residual string of the cache. */
+static krb5_error_code KRB5_CALLCONV
+krcc_get_name_2(krb5_context context,
+ krb5_ccache id,
+ const char **name,
+ const char **collection_name,
+ const char **subsidiary_name)
+{
+ krb5_krcache *data = KRCACHE(id);
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ if (name)
+ *name = data->krc_name;
+ if (collection_name)
+ *collection_name = data->krc_collection;
+ if (subsidiary_name)
+ *subsidiary_name = data->krc_subsidiary;
+ return 0;
+}
+
+/* Retrieve a copy of the default principal, if the cache is initialized. */
+static krb5_error_code KRB5_CALLCONV
+krcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *princ)
+{
+ krb5_krcache *data = KRCACHE(id);
+ krb5_error_code ret;
+ krb5_storage *sp = NULL;
+ krb5_data payload;
+ krb5_krcache_and_princ_id ids;
+
+ krb5_data_zero(&payload);
+ *princ = NULL;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ memset(&ids, 0, sizeof(ids));
+ ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
+ if (ids.krcu_cache_id == 0 || ids.krcu_princ_id == 0) {
+ ret = KRB5_FCC_NOFILE;
+ krb5_set_error_message(context, ret,
+ N_("Credentials cache keyring '%s' not found", ""),
+ data->krc_name);
+ goto cleanup;
+ }
+
+ ret = keyctl_read_krb5_data(ids.krcu_princ_id, &payload);
+ if (ret) {
+ _krb5_debug(context, 10, "Reading principal key %d: %s\n",
+ ids.krcu_princ_id, strerror(errno));
+ goto cleanup;
+ }
+
+ sp = krb5_storage_from_data(&payload);
+ if (sp == NULL) {
+ ret = KRB5_CC_IO;
+ goto cleanup;
+ }
+
+ ret = krb5_ret_principal(sp, princ);
+ if (ret)
+ goto cleanup;
+
+cleanup:
+ krb5_storage_free(sp);
+ krb5_data_free(&payload);
+
+ return ret;
+}
+
+/* Remove a cred from the cache keyring */
+static krb5_error_code KRB5_CALLCONV
+krcc_remove_cred(krb5_context context, krb5_ccache id,
+ krb5_flags which, krb5_creds *mcred)
+{
+ krb5_krcache *data = KRCACHE(id);
+ krb5_error_code ret, ret2;
+ krb5_cc_cursor cursor;
+ krb5_creds found_cred;
+ krb5_krcache_and_princ_id ids;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ ret = krcc_get_first(context, id, &cursor);
+ if (ret)
+ return ret;
+
+ memset(&ids, 0, sizeof(ids));
+ ids.krcu_cache_and_princ_id = heim_base_atomic_load(&data->krc_cache_and_principal_id);
+
+ while ((ret = krcc_get_next(context, id, &cursor, &found_cred)) == 0) {
+ struct krcc_cursor *krcursor = cursor;
+
+ if (!krb5_compare_creds(context, which, mcred, &found_cred)) {
+ krb5_free_cred_contents(context, &found_cred);
+ continue;
+ }
+
+ _krb5_debug(context, 10, "Removing cred %d from cache_id %d, princ_id %d\n",
+ krcursor->keys[krcursor->currkey - 1],
+ ids.krcu_cache_id, ids.krcu_princ_id);
+
+ keyctl_invalidate(krcursor->keys[krcursor->currkey - 1]);
+ krcursor->keys[krcursor->currkey - 1] = 0;
+ krb5_free_cred_contents(context, &found_cred);
+ }
+
+ ret2 = krcc_end_get(context, id, &cursor);
+ if (ret == KRB5_CC_END)
+ ret = ret2;
+
+ return ret;
+}
+
+/* Set flags on the cache. (We don't care about any flags.) */
+static krb5_error_code KRB5_CALLCONV
+krcc_set_flags(krb5_context context, krb5_ccache id, krb5_flags flags)
+{
+ return 0;
+}
+
+static int KRB5_CALLCONV
+krcc_get_version(krb5_context context, krb5_ccache id)
+{
+ return 0;
+}
+
+/* Store a credential in the cache keyring. */
+static krb5_error_code KRB5_CALLCONV
+krcc_store(krb5_context context, krb5_ccache id, krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_krcache *data = KRCACHE(id);
+ krb5_storage *sp = NULL;
+ char *keyname = NULL;
+ key_serial_t cred_key, cache_id;
+ krb5_timestamp now;
+ krb5_data payload;
+
+ krb5_data_zero(&payload);
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ cache_id = heim_base_atomic_load(&data->krc_cache_id);
+ if (cache_id == 0)
+ return KRB5_FCC_NOFILE;
+
+ ret = krb5_unparse_name(context, creds->server, &keyname);
+ if (ret)
+ goto cleanup;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
+ ret = KRB5_CC_NOMEM;
+ goto cleanup;
+ }
+
+ ret = krb5_store_creds(sp, creds);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_storage_to_data(sp, &payload);
+ if (ret)
+ goto cleanup;
+
+ _krb5_debug(context, 10, "krcc_store: adding new key '%s' to keyring %d\n",
+ keyname, cache_id);
+ ret = add_cred_key(keyname, payload.data, payload.length, cache_id,
+ data->krc_is_legacy, &cred_key);
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_timeofday(context, &now);
+ if (ret)
+ goto cleanup;
+
+ update_change_time(context, now, data);
+
+ /* Set timeout on credential key */
+ if (creds->times.endtime > now)
+ (void) keyctl_set_timeout(cred_key, creds->times.endtime - now);
+
+ /* Set timeout on credential cache keyring */
+ update_keyring_expiration(context, id, cache_id, now);
+
+cleanup:
+ krb5_data_free(&payload);
+ krb5_storage_free(sp);
+ krb5_xfree(keyname);
+
+ return ret;
+}
+
+/*
+ * Get the cache's last modification time. (This is currently broken; it
+ * returns only the last change made using this handle.)
+ */
+static krb5_error_code KRB5_CALLCONV
+krcc_lastchange(krb5_context context,
+ krb5_ccache id,
+ krb5_timestamp *change_time)
+{
+ krb5_krcache *data = KRCACHE(id);
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ *change_time = heim_base_atomic_load(&data->krc_changetime);
+
+ return 0;
+}
+
+static krb5_error_code
+save_principal(krb5_context context,
+ key_serial_t cache_id,
+ krb5_const_principal princ,
+ atomic_key_serial_t *pprinc_id)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ key_serial_t newkey;
+ krb5_data payload;
+
+ krb5_data_zero(&payload);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ ret = krb5_store_principal(sp, princ);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ ret = krb5_storage_to_data(sp, &payload);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ krb5_storage_free(sp);
+ {
+ krb5_error_code tmp;
+ char *princname = NULL;
+
+ tmp = krb5_unparse_name(context, princ, &princname);
+ _krb5_debug(context, 10, "save_principal: adding new key '%s' "
+ "to keyring %d for principal '%s'\n",
+ KRCC_SPEC_PRINC_KEYNAME, cache_id,
+ tmp ? "<unknown>" : princname);
+ if (tmp == 0)
+ krb5_xfree(princname);
+ }
+
+ /* Add new key into keyring */
+ newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_SPEC_PRINC_KEYNAME,
+ payload.data, payload.length, cache_id);
+ if (newkey == -1) {
+ ret = errno;
+ _krb5_debug(context, 10, "Error adding principal key: %s\n", strerror(ret));
+ } else {
+ ret = 0;
+ heim_base_atomic_store(pprinc_id, newkey);
+ }
+
+ krb5_data_free(&payload);
+
+ return ret;
+}
+
+/* Add a key to the cache keyring containing the given time offsets. */
+static krb5_error_code
+save_time_offsets(krb5_context context,
+ key_serial_t cache_id,
+ int32_t sec_offset,
+ int32_t usec_offset)
+{
+ krb5_error_code ret;
+ key_serial_t newkey;
+ krb5_storage *sp;
+ krb5_data payload;
+
+ krb5_data_zero(&payload);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM, N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ ret = krb5_store_int32(sp, sec_offset);
+ if (ret == 0)
+ ret = krb5_store_int32(sp, usec_offset);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ ret = krb5_storage_to_data(sp, &payload);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ krb5_storage_free(sp);
+
+ newkey = add_key(KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, payload.data,
+ payload.length, cache_id);
+ ret = newkey == -1 ? errno : 0;
+
+ krb5_data_free(&payload);
+
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat offset)
+{
+ krb5_krcache *data = KRCACHE(id);
+ key_serial_t cache_id;
+ krb5_error_code ret;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ cache_id = heim_base_atomic_load(&data->krc_cache_id);
+
+ ret = save_time_offsets(context, cache_id, (int32_t)offset, 0);
+ if (ret == 0)
+ update_change_time(context, 0, data);
+
+ return ret;
+}
+
+/* Retrieve and parse the key in the cache keyring containing time offsets. */
+static krb5_error_code KRB5_CALLCONV
+krcc_get_kdc_offset(krb5_context context,
+ krb5_ccache id,
+ krb5_deltat *offset)
+{
+ krb5_krcache *data = KRCACHE(id);
+ krb5_error_code ret = 0;
+ key_serial_t key, cache_id;
+ krb5_storage *sp = NULL;
+ krb5_data payload;
+ int32_t sec_offset = 0;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ krb5_data_zero(&payload);
+
+ cache_id = heim_base_atomic_load(&data->krc_cache_id);
+ if (cache_id == 0) {
+ ret = KRB5_FCC_NOFILE;
+ goto cleanup;
+ }
+
+ key = keyctl_search(cache_id, KRCC_KEY_TYPE_USER, KRCC_TIME_OFFSETS, 0);
+ if (key == -1) {
+ ret = ENOENT;
+ goto cleanup;
+ }
+
+ ret = keyctl_read_krb5_data(key, &payload);
+ if (ret) {
+ _krb5_debug(context, 10, "Reading time offsets key %d: %s\n",
+ key, strerror(errno));
+ goto cleanup;
+ }
+
+ sp = krb5_storage_from_data(&payload);
+ if (sp == NULL) {
+ ret = krb5_enomem(context);;
+ goto cleanup;
+ }
+
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+
+ ret = krb5_ret_int32(sp, &sec_offset);
+ /*
+ * We can't output nor use the usec_offset here, so we don't bother to read
+ * it, though we do write it.
+ */
+
+cleanup:
+ *offset = sec_offset;
+ krb5_storage_free(sp);
+ krb5_data_free(&payload);
+ return ret;
+}
+
+struct krcc_iter {
+ atomic_key_serial_t collection_id;
+ char *anchor_name;
+ char *collection_name;
+ char *subsidiary_name;
+ char *primary_name;
+ krb5_boolean first;
+ long num_keys;
+ long next_key;
+ key_serial_t *keys;
+};
+
+static krb5_error_code KRB5_CALLCONV
+krcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct krcc_iter *iter;
+ krb5_error_code ret;
+ void *keys;
+ long size;
+
+ *cursor = NULL;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL) {
+ ret = krb5_enomem(context);
+ goto error;
+ }
+ iter->first = TRUE;
+
+ ret = get_default(context, &iter->anchor_name, &iter->collection_name,
+ &iter->subsidiary_name);
+ if (ret)
+ goto error;
+
+ /* If there is no default collection, return an empty cursor. */
+ if (iter->anchor_name == NULL) {
+ *cursor = iter;
+ return 0;
+ }
+
+ ret = get_collection(context, iter->anchor_name, iter->collection_name,
+ &iter->collection_id);
+ if (ret)
+ goto error;
+
+ if (iter->subsidiary_name == NULL) {
+ ret = get_primary_name(context, iter->anchor_name,
+ iter->collection_name, iter->collection_id,
+ &iter->primary_name);
+ if (ret)
+ goto error;
+
+ size = keyctl_read_alloc(iter->collection_id, &keys);
+ if (size == -1) {
+ ret = errno;
+ goto error;
+ }
+ iter->keys = keys;
+ iter->num_keys = size / sizeof(key_serial_t);
+ }
+
+ *cursor = iter;
+
+ return 0;
+
+error:
+ krcc_end_cache_get(context, iter);
+
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krcc_get_cache_next(krb5_context context,
+ krb5_cc_cursor cursor,
+ krb5_ccache *cache)
+{
+ krb5_error_code ret;
+ struct krcc_iter *iter = cursor;
+ key_serial_t key, cache_id = 0;
+ const char *first_name, *keytype, *sep, *subsidiary_name;
+ size_t keytypelen;
+ char *description = NULL;
+
+ *cache = NULL;
+
+ /* No keyring available */
+ if (iter->collection_id == 0)
+ return KRB5_CC_END;
+
+ if (iter->first) {
+ /*
+ * Look for the primary cache for a collection cursor, or the
+ * subsidiary cache for a subsidiary cursor.
+ */
+ iter->first = FALSE;
+ first_name = (iter->primary_name != NULL) ? iter->primary_name :
+ iter->subsidiary_name;
+ cache_id = keyctl_search(iter->collection_id, KRCC_KEY_TYPE_KEYRING,
+ first_name, 0);
+ if (cache_id != -1) {
+ return make_cache(context, iter->collection_id, cache_id,
+ iter->anchor_name, iter->collection_name,
+ first_name, cache);
+ }
+ }
+
+ /* A subsidiary cursor yields at most the first cache. */
+ if (iter->subsidiary_name != NULL)
+ return KRB5_CC_END;
+
+ keytype = KRCC_KEY_TYPE_KEYRING ";";
+ keytypelen = strlen(keytype);
+
+ for (ret = KRB5_CC_END; iter->next_key < iter->num_keys; iter->next_key++) {
+ free(description);
+ description = NULL;
+
+ /*
+ * Get the key description, which should have the form:
+ * typename;UID;GID;permissions;description
+ */
+ key = iter->keys[iter->next_key];
+ if (keyctl_describe_alloc(key, &description) < 0)
+ continue;
+ sep = strrchr(description, ';');
+ if (sep == NULL)
+ continue;
+ subsidiary_name = sep + 1;
+
+ /* Skip this key if it isn't a keyring. */
+ if (strncmp(description, keytype, keytypelen) != 0)
+ continue;
+
+ /* Don't repeat the primary cache. */
+ if (iter->primary_name &&
+ strcmp(subsidiary_name, iter->primary_name) == 0)
+ continue;
+
+ /* We found a valid key */
+ iter->next_key++;
+ ret = make_cache(context, iter->collection_id, key, iter->anchor_name,
+ iter->collection_name, subsidiary_name, cache);
+ break;
+ }
+
+ free(description);
+
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct krcc_iter *iter = cursor;
+
+ if (iter != NULL) {
+ free(iter->anchor_name);
+ free(iter->collection_name);
+ free(iter->subsidiary_name);
+ free(iter->primary_name);
+ free(iter->keys);
+
+ memset(iter, 0, sizeof(*iter));
+ free(iter);
+ }
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krcc_set_default(krb5_context context, krb5_ccache id)
+{
+ krb5_krcache *data = KRCACHE(id);
+ krb5_error_code ret;
+ char *anchor_name, *collection_name, *subsidiary_name;
+ atomic_key_serial_t collection_id;
+
+ if (data == NULL)
+ return krb5_einval(context, 2);
+
+ ret = parse_residual(context, data->krc_name,
+ &anchor_name, &collection_name, &subsidiary_name);
+ if (ret)
+ goto cleanup;
+
+ ret = get_collection(context, anchor_name, collection_name, &collection_id);
+ if (ret)
+ goto cleanup;
+
+ ret = set_primary_name(context, collection_id, subsidiary_name);
+ if (ret)
+ goto cleanup;
+
+cleanup:
+ free(anchor_name);
+ free(collection_name);
+ free(subsidiary_name);
+
+ return ret;
+}
+
+/*
+ * Utility routine: called by krcc_* functions to keep
+ * result of krcc_last_change_time up to date.
+ */
+static void
+update_change_time(krb5_context context, krb5_timestamp now, krb5_krcache *data)
+{
+ krb5_timestamp old;
+
+ if (now == 0)
+ krb5_timeofday(context, &now);
+
+ old = heim_base_exchange_time_t(&data->krc_changetime, now);
+ if (old > now) /* don't go backwards */
+ heim_base_atomic_store(&data->krc_changetime, old + 1);
+}
+
+static int
+move_key_to_new_keyring(key_serial_t parent, key_serial_t key,
+ char *desc, int desc_len, void *data)
+{
+ key_serial_t cache_id = *(key_serial_t *)data;
+
+ if (parent) {
+ if (keyctl_link(key, cache_id) == -1 ||
+ keyctl_unlink(key, parent) == -1)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Move contents of one ccache to another; destroys from cache */
+static krb5_error_code KRB5_CALLCONV
+krcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_krcache *krfrom = KRCACHE(from);
+ krb5_krcache *krto = KRCACHE(to);
+ krb5_error_code ret;
+ krb5_timestamp now;
+ key_serial_t to_cache_id;
+
+ if (krfrom == NULL || krto == NULL)
+ return krb5_einval(context, 2);
+
+ ret = initialize_internal(context, to, NULL);
+ if (ret)
+ return ret;
+
+ krb5_timeofday(context, &now);
+ to_cache_id = heim_base_atomic_load(&krto->krc_cache_id);
+
+ if (krfrom->krc_cache_id != 0) {
+ ret = recursive_key_scan(krfrom->krc_cache_id,
+ move_key_to_new_keyring, &to_cache_id);
+ if (ret)
+ return KRB5_CC_IO;
+
+ if (keyctl_unlink(krfrom->krc_cache_id, krfrom->krc_coll_id) == -1)
+ return errno;
+
+ heim_base_exchange_32(&krto->krc_princ_id, krfrom->krc_princ_id);
+ }
+
+ update_change_time(context, now, krto);
+ krb5_cc_destroy(context, from);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+krcc_get_default_name(krb5_context context, char **str)
+{
+ *str = strdup("KEYRING:");
+ if (*str == NULL)
+ return krb5_enomem(context);
+
+ return 0;
+}
+
+/*
+ * ccache implementation storing credentials in the Linux keyring facility
+ * The default is to put them at the session keyring level.
+ * If "KEYRING:process:" or "KEYRING:thread:" is specified, then they will
+ * be stored at the process or thread level respectively.
+ */
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_krcc_ops = {
+ KRB5_CC_OPS_VERSION_5,
+ "KEYRING",
+ NULL,
+ NULL,
+ krcc_gen_new,
+ krcc_initialize,
+ krcc_destroy,
+ krcc_close,
+ krcc_store,
+ NULL, /* retrieve */
+ krcc_get_principal,
+ krcc_get_first,
+ krcc_get_next,
+ krcc_end_get,
+ krcc_remove_cred,
+ krcc_set_flags,
+ krcc_get_version,
+ krcc_get_cache_first,
+ krcc_get_cache_next,
+ krcc_end_cache_get,
+ krcc_move,
+ krcc_get_default_name,
+ krcc_set_default,
+ krcc_lastchange,
+ krcc_set_kdc_offset,
+ krcc_get_kdc_offset,
+ krcc_get_name_2,
+ krcc_resolve_2
+};
+
+#endif /* HAVE_KEYUTILS_H */
diff --git a/third_party/heimdal/lib/krb5/kuserok.c b/third_party/heimdal/lib/krb5/kuserok.c
new file mode 100644
index 0000000..7a7de45
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/kuserok.c
@@ -0,0 +1,753 @@
+/*
+ * 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 "kuserok_plugin.h"
+#include <dirent.h>
+
+#ifndef SYSTEM_K5LOGIN_DIR
+/*
+ * System k5login location. File namess in this directory are expected
+ * to be usernames and to contain a list of principals allowed to login
+ * as the user named the same as the file.
+ */
+#define SYSTEM_K5LOGIN_DIR SYSCONFDIR "/k5login.d"
+#endif
+
+/* Plugin framework bits */
+
+struct plctx {
+ const char *rule;
+ const char *k5login_dir;
+ const char *luser;
+ krb5_const_principal principal;
+ unsigned int flags;
+ krb5_boolean result;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+plcallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_kuserok_ftable *locate = plug;
+ struct plctx *plctx = userctx;
+
+ return locate->kuserok(plugctx, context, plctx->rule, plctx->flags,
+ plctx->k5login_dir, plctx->luser, plctx->principal,
+ &plctx->result);
+}
+
+static krb5_error_code plugin_reg_ret;
+static const krb5plugin_kuserok_ftable kuserok_simple_plug;
+static const krb5plugin_kuserok_ftable kuserok_sys_k5login_plug;
+static const krb5plugin_kuserok_ftable kuserok_user_k5login_plug;
+static const krb5plugin_kuserok_ftable kuserok_deny_plug;
+
+static void
+reg_def_plugins_once(void *ctx)
+{
+ krb5_error_code ret;
+ krb5_context context = ctx;
+
+ plugin_reg_ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK,
+ &kuserok_simple_plug);
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK, &kuserok_sys_k5login_plug);
+ if (!plugin_reg_ret)
+ plugin_reg_ret = ret;
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK, &kuserok_user_k5login_plug);
+ if (!plugin_reg_ret)
+ plugin_reg_ret = ret;
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK, &kuserok_deny_plug);
+ if (!plugin_reg_ret)
+ plugin_reg_ret = ret;
+}
+
+/**
+ * This function is designed to be portable for Win32 and POSIX. The
+ * design does lead to multiple getpwnam_r() calls, but this is probably
+ * not a big deal.
+ *
+ * Inputs:
+ *
+ * @param context A krb5_context
+ * @param filename Name of item to introspection
+ * @param is_system_location TRUE if the dir/file are system locations or
+ * FALSE if they are user home directory locations
+ * @param dir Directory (optional)
+ * @param dirlstat A pointer to struct stat for the directory (optional)
+ * @param file File (optional)
+ * @param owner Name of user that is expected to own the file
+ */
+
+static krb5_error_code
+check_owner_dir(krb5_context context,
+ const char *filename,
+ krb5_boolean is_system_location,
+ DIR *dir,
+ struct stat *dirlstat,
+ const char *owner)
+{
+#ifdef _WIN32
+ /*
+ * XXX Implement this!
+ *
+ * The thing to do is to call _get_osfhandle() on fileno(file) and
+ * dirfd(dir) to get HANDLEs to the same, then call
+ * GetSecurityInfo() on those HANDLEs to get the security descriptor
+ * (SD), then check the owner and DACL. Checking the DACL sounds
+ * like a lot of work (what, derive a mode from the ACL the way
+ * NFSv4 servers do?). Checking the owner means doing an LSARPC
+ * lookup at least (to get the user's SID).
+ */
+ if (is_system_location || owner == NULL)
+ return 0;
+ krb5_set_error_message(context, EACCES,
+ "User k5login files not supported on Windows");
+ return EACCES;
+#else
+ struct passwd pw, *pwd = NULL;
+ char pwbuf[2048];
+ struct stat st;
+
+ heim_assert(owner != NULL, "no directory owner ?");
+
+ if (getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
+ krb5_set_error_message(context, errno,
+ "User unknown %s (getpwnam_r())", owner);
+ return EACCES;
+ }
+ if (pwd == NULL) {
+ krb5_set_error_message(context, EACCES, "no user %s", owner);
+ return EACCES;
+ }
+
+ if (fstat(dirfd(dir), &st) == -1) {
+ krb5_set_error_message(context, EACCES,
+ "fstat(%s) of k5login.d failed",
+ filename);
+ return EACCES;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ krb5_set_error_message(context, ENOTDIR, "%s not a directory",
+ filename);
+ return ENOTDIR;
+ }
+ if (st.st_dev != dirlstat->st_dev || st.st_ino != dirlstat->st_ino) {
+ krb5_set_error_message(context, EACCES,
+ "%s was renamed during kuserok "
+ "operation", filename);
+ return EACCES;
+ }
+ if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+ krb5_set_error_message(context, EACCES,
+ "%s has world and/or group write "
+ "permissions", filename);
+ return EACCES;
+ }
+ if (pwd->pw_uid != st.st_uid && st.st_uid != 0) {
+ krb5_set_error_message(context, EACCES,
+ "%s not owned by the user (%s) or root",
+ filename, owner);
+ return EACCES;
+ }
+
+ return 0;
+#endif
+}
+
+static krb5_error_code
+check_owner_file(krb5_context context,
+ const char *filename,
+ FILE *file, const char *owner)
+{
+#ifdef _WIN32
+ /*
+ * XXX Implement this!
+ *
+ * The thing to do is to call _get_osfhandle() on fileno(file) and
+ * dirfd(dir) to get HANDLEs to the same, then call
+ * GetSecurityInfo() on those HANDLEs to get the security descriptor
+ * (SD), then check the owner and DACL. Checking the DACL sounds
+ * like a lot of work (what, derive a mode from the ACL the way
+ * NFSv4 servers do?). Checking the owner means doing an LSARPC
+ * lookup at least (to get the user's SID).
+ */
+ if (owner == NULL)
+ return 0;
+
+ krb5_set_error_message(context, EACCES,
+ "User k5login files not supported on Windows");
+ return EACCES;
+#else
+ struct passwd pw, *pwd = NULL;
+ char pwbuf[2048];
+ struct stat st;
+
+ if (owner == NULL)
+ return 0;
+
+ if (getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
+ krb5_set_error_message(context, errno,
+ "User unknown %s (getpwnam_r())", owner);
+ return EACCES;
+ }
+ if (pwd == NULL) {
+ krb5_set_error_message(context, EACCES, "no user %s", owner);
+ return EACCES;
+ }
+
+ if (fstat(fileno(file), &st) == -1) {
+ krb5_set_error_message(context, EACCES, "fstat(%s) of k5login failed",
+ filename);
+ return EACCES;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ krb5_set_error_message(context, EISDIR, "k5login: %s is a directory",
+ filename);
+ return EISDIR;
+ }
+ if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+ krb5_set_error_message(context, EISDIR,
+ "k5login %s has world and/or group write "
+ "permissions", filename);
+ return EACCES;
+ }
+ if (pwd->pw_uid != st.st_uid && st.st_uid != 0) {
+ krb5_set_error_message(context, EACCES,
+ "k5login %s not owned by the user or root",
+ filename);
+ return EACCES;
+ }
+
+ return 0;
+#endif
+}
+
+
+/* see if principal is mentioned in the filename access file, return
+ TRUE (in result) if so, FALSE otherwise */
+
+static krb5_error_code
+check_one_file(krb5_context context,
+ const char *filename,
+ const char *owner,
+ krb5_boolean is_system_location,
+ krb5_const_principal principal,
+ krb5_boolean *result)
+{
+ FILE *f;
+ char buf[BUFSIZ];
+ krb5_error_code ret;
+
+ *result = FALSE;
+
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return errno;
+ rk_cloexec_file(f);
+
+ ret = check_owner_file(context, filename, f, owner);
+ if (ret)
+ goto out;
+
+ while (fgets(buf, sizeof(buf), f) != NULL) {
+ krb5_principal tmp;
+ char *newline = buf + strcspn(buf, "\n");
+
+ if (*newline != '\n') {
+ int c;
+ c = fgetc(f);
+ if (c != EOF) {
+ while (c != EOF && c != '\n')
+ c = fgetc(f);
+ /* line was too long, so ignore it */
+ continue;
+ }
+ }
+ *newline = '\0';
+ ret = krb5_parse_name(context, buf, &tmp);
+ if (ret)
+ continue;
+ *result = krb5_principal_compare(context, principal, tmp);
+ krb5_free_principal(context, tmp);
+ if (*result) {
+ fclose (f);
+ return 0;
+ }
+ }
+
+out:
+ fclose(f);
+ return 0;
+}
+
+static krb5_error_code
+check_directory(krb5_context context,
+ const char *dirname,
+ const char *owner,
+ krb5_boolean is_system_location,
+ krb5_const_principal principal,
+ krb5_boolean *result)
+{
+ DIR *d;
+ struct dirent *dent;
+ char filename[MAXPATHLEN];
+ size_t len;
+ krb5_error_code ret = 0;
+ struct stat st;
+
+ *result = FALSE;
+
+ if (lstat(dirname, &st) < 0)
+ return errno;
+
+ if (!S_ISDIR(st.st_mode)) {
+ krb5_set_error_message(context, ENOTDIR, "k5login.d not a directory");
+ return ENOTDIR;
+ }
+
+ if ((d = opendir(dirname)) == NULL) {
+ krb5_set_error_message(context, ENOTDIR, "Could not open k5login.d");
+ return errno;
+ }
+
+ ret = check_owner_dir(context, dirname, is_system_location, d, &st, owner);
+ if (ret)
+ goto out;
+
+ while ((dent = readdir(d)) != NULL) {
+ /*
+ * XXX: Should we also skip files whose names start with "."?
+ * Vim ".filename.swp" files are also good candidates to skip.
+ * Once we ignore "#*" and "*~", it is not clear what other
+ * heuristics to apply.
+ */
+ if (strcmp(dent->d_name, ".") == 0 ||
+ strcmp(dent->d_name, "..") == 0 ||
+ dent->d_name[0] == '#' || /* emacs autosave */
+ dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
+ continue;
+ len = snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
+ /* Skip too-long filenames that got truncated by snprintf() */
+ if (len < sizeof(filename)) {
+ ret = check_one_file(context, filename, owner, is_system_location,
+ principal, result);
+ if (ret == 0 && *result == TRUE)
+ break;
+ }
+ ret = 0; /* don't propagate errors upstream */
+ }
+
+out:
+ closedir(d);
+ return ret;
+}
+
+static krb5_error_code
+check_an2ln(krb5_context context,
+ krb5_const_principal principal,
+ const char *luser,
+ krb5_boolean *result)
+{
+ krb5_error_code ret;
+ char *lname;
+
+#if 0
+ /* XXX Should we make this an option? */
+ /* multi-component principals can never match */
+ if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
+ *result = FALSE;
+ return 0;
+ }
+#endif
+
+ lname = malloc(strlen(luser) + 1);
+ if (lname == NULL)
+ return krb5_enomem(context);
+ ret = krb5_aname_to_localname(context, principal, strlen(luser)+1, lname);
+ if (ret)
+ goto out;
+ if (strcmp(lname, luser) == 0)
+ *result = TRUE;
+ else
+ *result = FALSE;
+
+out:
+ free(lname);
+ return 0;
+
+}
+
+/**
+ * This function takes the name of a local user and checks if
+ * principal is allowed to log in as that user.
+ *
+ * The user may have a ~/.k5login file listing principals that are
+ * allowed to login as that user. If that file does not exist, all
+ * principals with a only one component that is identical to the
+ * username, and a realm considered local, are allowed access.
+ *
+ * The .k5login file must contain one principal per line, be owned by
+ * user and not be writable by group or other (but must be readable by
+ * anyone).
+ *
+ * Note that if the file exists, no implicit access rights are given
+ * to user@@LOCALREALM.
+ *
+ * Optionally, a set of files may be put in ~/.k5login.d (a
+ * directory), in which case they will all be checked in the same
+ * manner as .k5login. The files may be called anything, but files
+ * starting with a hash (#) , or ending with a tilde (~) are
+ * ignored. Subdirectories are not traversed. Note that this directory
+ * may not be checked by other Kerberos implementations.
+ *
+ * If no configuration file exists, match user against local domains,
+ * ie luser@@LOCAL-REALMS-IN-CONFIGURATION-FILES.
+ *
+ * @param context Kerberos 5 context.
+ * @param principal principal to check if allowed to login
+ * @param luser local user id
+ *
+ * @return returns TRUE if access should be granted, FALSE otherwise.
+ *
+ * @ingroup krb5_support
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_kuserok(krb5_context context,
+ krb5_principal principal,
+ const char *luser)
+{
+ return _krb5_kuserok(context, principal, luser, TRUE);
+}
+
+
+static const char *const kuserok_plugin_deps[] = { "krb5", NULL };
+
+static const struct heim_plugin_data
+kuserok_plugin_data = {
+ "krb5",
+ KRB5_PLUGIN_KUSEROK,
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuserok_plugin_deps,
+ krb5_get_instance
+};
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_kuserok(krb5_context context,
+ krb5_principal principal,
+ const char *luser,
+ krb5_boolean an2ln_ok)
+{
+ static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT;
+ krb5_error_code ret;
+ struct plctx ctx;
+ char **rules;
+
+ /*
+ * XXX we should have a struct with a krb5_context field and a
+ * krb5_error_code fied and pass the address of that as the ctx
+ * argument of heim_base_once_f(). For now we use a static to
+ * communicate failures. Actually, we ignore failures anyways,
+ * since we can't return them.
+ */
+ heim_base_once_f(&reg_def_plugins, context, reg_def_plugins_once);
+
+ ctx.flags = 0;
+ ctx.luser = luser;
+ ctx.principal = principal;
+ ctx.result = FALSE;
+
+ ctx.k5login_dir = krb5_config_get_string(context, NULL, "libdefaults",
+ "k5login_directory", NULL);
+
+ if (an2ln_ok)
+ ctx.flags |= KUSEROK_ANAME_TO_LNAME_OK;
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE, "libdefaults",
+ "k5login_authoritative", NULL))
+ ctx.flags |= KUSEROK_K5LOGIN_IS_AUTHORITATIVE;
+
+ if ((ctx.flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE) && plugin_reg_ret)
+ return plugin_reg_ret; /* fail safe */
+
+ rules = krb5_config_get_strings(context, NULL, "libdefaults",
+ "kuserok", NULL);
+ if (rules == NULL) {
+ /* Default: check ~/.k5login */
+ ctx.rule = "USER-K5LOGIN";
+
+ ret = plcallback(context, &kuserok_user_k5login_plug, NULL, &ctx);
+ if (ret == 0)
+ goto out;
+
+ ctx.rule = "SIMPLE";
+ ret = plcallback(context, &kuserok_simple_plug, NULL, &ctx);
+ if (ret == 0)
+ goto out;
+
+ ctx.result = FALSE;
+ } else {
+ size_t n;
+
+ for (n = 0; rules[n]; n++) {
+ ctx.rule = rules[n];
+
+ ret = _krb5_plugin_run_f(context, &kuserok_plugin_data,
+ 0, &ctx, plcallback);
+ if (ret != KRB5_PLUGIN_NO_HANDLE)
+ goto out;
+ }
+ }
+
+out:
+ krb5_config_free_strings(rules);
+
+ return ctx.result;
+}
+
+/*
+ * Simple kuserok: check that the lname for the aname matches luser.
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_simple_plug_f(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)
+{
+ krb5_error_code ret;
+
+ if (strcmp(rule, "SIMPLE") != 0 || (flags & KUSEROK_ANAME_TO_LNAME_OK) == 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = check_an2ln(context, principal, luser, result);
+ if (ret == 0 && *result == FALSE)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ return 0;
+}
+
+/*
+ * Check k5login files in a system location, rather than in home
+ * directories.
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_sys_k5login_plug_f(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)
+{
+ char filename[MAXPATHLEN];
+ size_t len;
+ const char *profile_dir = NULL;
+ krb5_error_code ret;
+
+ *result = FALSE;
+
+ if (strcmp(rule, "SYSTEM-K5LOGIN") != 0 &&
+ strncmp(rule, "SYSTEM-K5LOGIN:", strlen("SYSTEM-K5LOGIN:")) != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ profile_dir = strchr(rule, ':');
+ if (profile_dir == NULL)
+ profile_dir = k5login_dir ? k5login_dir : SYSTEM_K5LOGIN_DIR;
+ else
+ profile_dir++;
+
+ len = snprintf(filename, sizeof(filename), "%s/%s", profile_dir, luser);
+ if (len < sizeof(filename)) {
+ ret = check_one_file(context, filename, NULL, TRUE, principal, result);
+
+ if (ret == 0 &&
+ ((flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE) || *result == TRUE))
+ return 0;
+ }
+
+ *result = FALSE;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/*
+ * Check ~luser/.k5login and/or ~/luser/.k5login.d
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_user_k5login_plug_f(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)
+{
+#ifdef _WIN32
+ return KRB5_PLUGIN_NO_HANDLE;
+#else
+ char *path;
+ char *path_exp;
+ const char *profile_dir = NULL;
+ krb5_error_code ret;
+ krb5_boolean found_file = FALSE;
+ struct passwd pw, *pwd = NULL;
+ char pwbuf[2048];
+
+ if (strcmp(rule, "USER-K5LOGIN") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ profile_dir = k5login_dir;
+ if (profile_dir == NULL) {
+ /* Don't deadlock with gssd or anything of the sort */
+ if (!_krb5_homedir_access(context))
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ if (getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
+ krb5_set_error_message(context, errno, "User unknown (getpwnam_r())");
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ if (pwd == NULL) {
+ krb5_set_error_message(context, errno, "User unknown (getpwnam())");
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ profile_dir = pwd->pw_dir;
+ }
+
+#define KLOGIN "/.k5login"
+
+ if (asprintf(&path, "%s/.k5login.d", profile_dir) == -1)
+ return krb5_enomem(context);
+
+ ret = _krb5_expand_path_tokensv(context, path, 1, &path_exp,
+ "luser", luser, NULL);
+ free(path);
+ if (ret)
+ return ret;
+ path = path_exp;
+
+ /* check user's ~/.k5login */
+ path[strlen(path) - strlen(".d")] = '\0';
+ ret = check_one_file(context, path, luser, FALSE, principal, result);
+
+ /*
+ * A match in ~/.k5login is sufficient. A non-match, falls through to the
+ * .k5login.d code below.
+ */
+ if (ret == 0 && *result == TRUE) {
+ free(path);
+ return 0;
+ }
+ if (ret != ENOENT)
+ found_file = TRUE;
+
+ /*
+ * A match in ~/.k5login.d/somefile is sufficient. A non-match, falls
+ * through to the code below that handles negative results.
+ *
+ * XXX: put back the .d; clever|hackish? you decide
+ */
+ path[strlen(path)] = '.';
+ ret = check_directory(context, path, luser, FALSE, principal, result);
+ free(path);
+ if (ret == 0 && *result == TRUE)
+ return 0;
+ if (ret != ENOENT && ret != ENOTDIR)
+ found_file = TRUE;
+
+ /*
+ * When either ~/.k5login or ~/.k5login.d/ exists, but neither matches
+ * and we're authoritative, we're done. Otherwise, give other plugins
+ * a chance.
+ */
+ *result = FALSE;
+ if (found_file && (flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE))
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+#endif
+}
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_deny_plug_f(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, "DENY") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ *result = FALSE;
+ return 0;
+}
+
+static krb5_error_code KRB5_LIB_CALL
+kuser_ok_null_plugin_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ return 0;
+}
+
+static void KRB5_LIB_CALL
+kuser_ok_null_plugin_fini(void *ctx)
+{
+ return;
+}
+
+static const krb5plugin_kuserok_ftable kuserok_simple_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_simple_plug_f,
+};
+
+static const krb5plugin_kuserok_ftable kuserok_sys_k5login_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_sys_k5login_plug_f,
+};
+
+static const krb5plugin_kuserok_ftable kuserok_user_k5login_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_user_k5login_plug_f,
+};
+
+static const krb5plugin_kuserok_ftable kuserok_deny_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_deny_plug_f,
+};
+
diff --git a/third_party/heimdal/lib/krb5/kuserok_plugin.h b/third_party/heimdal/lib/krb5/kuserok_plugin.h
new file mode 100644
index 0000000..7c3f3b4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/kuserok_plugin.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef HEIMDAL_KRB5_KUSEROK_PLUGIN_H
+#define HEIMDAL_KRB5_KUSEROK_PLUGIN_H 1
+
+#include <heimbase-svc.h>
+
+#define KRB5_PLUGIN_KUSEROK "krb5_plugin_kuserok"
+#define KRB5_PLUGIN_KUSEROK_VERSION_0 0
+
+/** @struct krb5plugin_kuserok_ftable_desc
+ *
+ * @brief Description of the krb5_kuserok(3) plugin facility.
+ *
+ * The krb5_kuserok(3) function is pluggable. The plugin is named
+ * KRB5_PLUGIN_KUSEROK ("krb5_plugin_kuserok"), with a single minor
+ * version, KRB5_PLUGIN_KUSEROK_VERSION_0 (0).
+ *
+ * The plugin for krb5_kuserok(3) consists of a data symbol referencing
+ * a structure of type krb5plugin_kuserok_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 kuserok Plugin kuserok function
+ *
+ * The kuserok field is the plugin entry point that performs the
+ * traditional kuserok operation however the plugin desires. It is
+ * invoked in no particular order relative to other kuserok plugins, but
+ * it has a 'rule' argument that indicates which plugin is intended to
+ * act on the rule. The plugin kuserok function must return
+ * KRB5_PLUGIN_NO_HANDLE if the rule is not applicable to it.
+ *
+ * The plugin kuserok 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 kuserok rule being evaluated (from krb5.conf(5))
+ * -# flags
+ * -# k5login_dir, configured location of k5login per-user files if any
+ * -# luser, name of the local user account to which principal is attempting to access.
+ * -# principal, the krb5_principal trying to access the luser account
+ * -# result, a krb5_boolean pointer where the plugin will output its result
+ *
+ * @ingroup krb5_support
+ */
+typedef struct krb5plugin_kuserok_ftable_desc {
+ HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context);
+ krb5_error_code (KRB5_LIB_CALL *kuserok)(void *, krb5_context, const char *,
+ unsigned int, const char *, const char *,
+ krb5_const_principal,
+ krb5_boolean *);
+} krb5plugin_kuserok_ftable;
+
+#define KUSEROK_ANAME_TO_LNAME_OK 1
+#define KUSEROK_K5LOGIN_IS_AUTHORITATIVE 2
+
+#endif /* HEIMDAL_KRB5_KUSEROK_PLUGIN_H */
diff --git a/third_party/heimdal/lib/krb5/kx509.c b/third_party/heimdal/lib/krb5/kx509.c
new file mode 100644
index 0000000..3bacdf1
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/kx509.c
@@ -0,0 +1,1323 @@
+/*
+ * Copyright (c) 2019 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 <kx509_asn1.h>
+#include <kx509_err.h>
+#include "../hx509/hx_locl.h" /* XXX find a better way */
+#include "hx509-private.h"
+
+/*
+ * This file implements a client for the kx509 protocol -- a Kerberized online
+ * CA that can issue a Certificate to a client that authenticates using
+ * Kerberos.
+ *
+ * The kx509 protocol is the inverse of PKINIT. Whereas PKINIT allows users
+ * with PKIX credentials to acquire Kerberos credentials, the kx509 protocol
+ * allows users with Kerberos credentials to acquire PKIX credentials.
+ *
+ * I.e., kx509 is a bridge, just like PKINIT.
+ *
+ * The kx509 protocol is very simple, and very limited.
+ *
+ * A request consists of a DER-encoded Kx509Request message prefixed with four
+ * bytes identifying the protocol (see `version_2_0' below).
+ *
+ * A Kx509Request message contains an AP-REQ, a public key, and an HMAC of the
+ * public key made with the session key of the AP-REQ's ticket.
+ *
+ * The service principal can be either kca_service/hostname.fqdn or
+ * krbtgt/REALM (a Heimdal innovation).
+ *
+ * If a request is missing a public key, then the request is a probe intended
+ * to discover whether the service is enabled, thus helping the client avoid
+ * a possibly-slow private key generation operation.
+ *
+ * The response is a DER-encoded Kx509Response also prefixed with
+ * `version_2_0', and contains: an optional error code and error text, an
+ * optional certificate (for the success case), and an optional HMAC of those
+ * fields that is present when the service was able to verify the AP-REQ.
+ *
+ * Limitations:
+ *
+ * - no proof of possession for the public key
+ * - only RSA keys are supported
+ * - no way to express options (e.g., what KUs, EKUs, or SANs are desired)
+ * - no sub-session key usage
+ * - no reflection protection other than the HMAC's forgery protection and the
+ * fact that the client could tell that a reflected attack isn't success
+ *
+ * Future directions:
+ *
+ * - Since the public key field of the request is an OCTET STRING, we could
+ * send a CSR, or even an expired certificate (possibly self-signed,
+ * possibly one issued earlier) that can serve as a template.
+ *
+ * This solves the first three limitations, as it allows the client to
+ * demonstrate proof of possession, allows arbitrary public key types, and
+ * allows the client to express desires about the to-be-issued certificate.
+ *
+ * - Use the AP-REQ's Authenticator's sub-session key for the HMAC, and derive
+ * per-direction sub-sub-keys.
+ *
+ * - We might design a new protocol that better fits the RFC4120 KDC message
+ * framework.
+ */
+
+static const unsigned char version_2_0[4] = {0 , 0, 2, 0};
+
+struct krb5_kx509_req_ctx_data {
+ krb5_auth_context ac;
+ krb5_data given_csr;
+ hx509_request csr;
+ Kx509CSRPlus csr_plus;
+ char *realm; /* Realm to which to send request */
+ krb5_keyblock *hmac_key; /* For HMAC validation */
+ hx509_private_key *keys;
+ hx509_private_key priv_key;
+ unsigned int expect_chain;
+};
+
+/**
+ * Create a kx509 request context.
+ *
+ * @param context The Kerberos library context
+ * @param out Where to place the kx509 request context
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_init(krb5_context context, krb5_kx509_req_ctx *out)
+{
+ krb5_kx509_req_ctx ctx;
+ krb5_error_code ret;
+ hx509_name name = NULL;
+
+ ALLOC(ctx, 1);
+ if (ctx == NULL)
+ return krb5_enomem(context);
+ ctx->given_csr.data = NULL;
+ ctx->priv_key = NULL;
+ ctx->hmac_key = NULL;
+ ctx->realm = NULL;
+ ctx->keys = NULL;
+ ctx->csr = NULL;
+ ret = hx509_request_init(context->hx509ctx, &ctx->csr);
+ if (ret == 0)
+ ret = hx509_parse_name(context->hx509ctx, "", &name);
+ if (ret == 0)
+ ret = hx509_request_set_name(context->hx509ctx, ctx->csr, name);
+ if (ret == 0)
+ ret = krb5_auth_con_init(context, &ctx->ac);
+ if (name)
+ hx509_name_free(&name);
+ if (ret == 0)
+ *out = ctx;
+ else
+ krb5_kx509_ctx_free(context, &ctx);
+ return ret;
+}
+
+/**
+ * Free a kx509 request context.
+ *
+ * @param context The Kerberos library context
+ * @param ctxp Pointer to krb5 request context to free
+ *
+ * @return A krb5 error code.
+ */
+void
+krb5_kx509_ctx_free(krb5_context context, krb5_kx509_req_ctx *ctxp)
+{
+ krb5_kx509_req_ctx ctx = *ctxp;
+
+ *ctxp = NULL;
+ if (ctx == NULL)
+ return;
+ krb5_free_keyblock(context, ctx->hmac_key);
+ krb5_auth_con_free(context, ctx->ac);
+ free_Kx509CSRPlus(&ctx->csr_plus);
+ free(ctx->realm);
+ hx509_request_free(&ctx->csr);
+ krb5_data_free(&ctx->given_csr);
+ hx509_private_key_free(&ctx->priv_key);
+ _hx509_certs_keys_free(context->hx509ctx, ctx->keys);
+ free(ctx);
+}
+
+/**
+ * Set a realm to send kx509 request to, if different from the client's.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param realm Realm name
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_set_realm(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *realm)
+{
+ return ((kx509_ctx->realm = strdup(realm)) == NULL) ?
+ krb5_enomem(context) : 0;
+}
+
+/**
+ * Sets a CSR for a kx509 request.
+ *
+ * Normally kx509 will generate a CSR (and even a private key for it)
+ * automatically. If a CSR is given then kx509 will use it instead of
+ * generating one.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param csr_der A DER-encoded PKCS#10 CSR
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_set_csr_der(krb5_context context,
+ krb5_kx509_req_ctx ctx,
+ krb5_data *csr_der)
+{
+ krb5_data_free(&ctx->given_csr);
+ return krb5_data_copy(&ctx->given_csr, csr_der->data, csr_der->length);
+}
+
+/**
+ * Adds an EKU as an additional desired Certificate Extension or in the CSR if
+ * the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param oids A string representation of an OID
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_eku(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *oids)
+{
+ krb5_error_code ret;
+ heim_oid oid;
+
+ ret = der_parse_heim_oid(oids, NULL, &oid);
+ if (ret == 0)
+ hx509_request_add_eku(context->hx509ctx, kx509_ctx->csr, &oid);
+ der_free_oid(&oid);
+ return ret;
+}
+
+/**
+ * Adds a dNSName SAN (domainname, hostname) as an additional desired
+ * Certificate Extension or in the CSR if the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param dname A string containing a DNS domainname
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_san_dns_name(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *dname)
+{
+ return hx509_request_add_dns_name(context->hx509ctx, kx509_ctx->csr,
+ dname);
+}
+
+/**
+ * Adds an xmppAddr SAN (jabber address) as an additional desired Certificate
+ * Extension or in the CSR if the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param jid A string containing a Jabber address
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_san_xmpp(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *jid)
+{
+ return hx509_request_add_xmpp_name(context->hx509ctx, kx509_ctx->csr, jid);
+}
+
+/**
+ * Adds an rfc822Name SAN (e-mail address) as an additional desired Certificate
+ * Extension or in the CSR if the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param email A string containing an e-mail address
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_san_rfc822Name(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *email)
+{
+ return hx509_request_add_email(context->hx509ctx, kx509_ctx->csr, email);
+}
+
+/**
+ * Adds an pkinit SAN (Kerberos principal name) as an additional desired
+ * Certificate Extension or in the CSR if the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param pname A string containing a representation of a Kerberos principal
+ * name
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_san_pkinit(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *pname)
+{
+ return hx509_request_add_pkinit(context->hx509ctx, kx509_ctx->csr, pname);
+}
+
+/**
+ * Adds a Microsoft-style UPN (user principal name) as an additional desired
+ * Certificate Extension or in the CSR if the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param upn A string containing a representation of a UPN
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_san_ms_upn(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *upn)
+{
+ return hx509_request_add_ms_upn_name(context->hx509ctx, kx509_ctx->csr,
+ upn);
+}
+
+/**
+ * Adds an registeredID SAN (OID) as an additional desired Certificate
+ * Extension or in the CSR if the caller does not set a CSR.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param oids A string representation of an OID
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_add_san_registeredID(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *oids)
+{
+ krb5_error_code ret;
+ heim_oid oid;
+
+ ret = der_parse_heim_oid(oids, NULL, &oid);
+ if (ret == 0)
+ hx509_request_add_registered(context->hx509ctx, kx509_ctx->csr, &oid);
+ der_free_oid(&oid);
+ return ret;
+}
+
+static krb5_error_code
+load_priv_key(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *fn)
+{
+ hx509_private_key *keys = NULL;
+ hx509_certs certs = NULL;
+ krb5_error_code ret;
+
+ ret = hx509_certs_init(context->hx509ctx, fn, 0, NULL, &certs);
+ if (ret == ENOENT)
+ return 0;
+ if (ret == 0)
+ ret = _hx509_certs_keys_get(context->hx509ctx, certs, &keys);
+ if (ret == 0 && keys[0] == NULL)
+ ret = ENOENT;
+ if (ret == 0)
+ kx509_ctx->priv_key = _hx509_private_key_ref(keys[0]);
+ if (ret) {
+ char *emsg = hx509_get_error_string(context->hx509ctx, ret);
+
+ krb5_set_error_message(context, ret, "Could not load private key "
+ "from %s for kx509: %s", fn, emsg);
+ hx509_free_error_string(emsg);
+ }
+ hx509_certs_free(&certs);
+ return ret;
+}
+
+/**
+ * Set a private key.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param store The name of a PKIX credential store
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_set_key(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *store)
+{
+ SubjectPublicKeyInfo key;
+ krb5_error_code ret;
+
+ memset(&key, 0, sizeof(key));
+ hx509_private_key_free(&kx509_ctx->priv_key);
+ _hx509_certs_keys_free(context->hx509ctx, kx509_ctx->keys);
+ kx509_ctx->keys = NULL;
+ ret = load_priv_key(context, kx509_ctx, store);
+ if (ret == 0)
+ ret = hx509_private_key2SPKI(context->hx509ctx, kx509_ctx->priv_key,
+ &key);
+ if (ret == 0)
+ ret = hx509_request_set_SubjectPublicKeyInfo(context->hx509ctx,
+ kx509_ctx->csr, &key);
+ free_SubjectPublicKeyInfo(&key);
+ return ret;
+}
+
+static krb5_error_code
+gen_priv_key(krb5_context context,
+ const char *gen_type,
+ unsigned long gen_bits,
+ hx509_private_key *key)
+{
+ struct hx509_generate_private_context *key_gen_ctx = NULL;
+ krb5_error_code ret;
+
+ _krb5_debug(context, 1, "kx509: gen priv key");
+ if (strcmp(gen_type, "rsa") != 0) {
+ krb5_set_error_message(context, ENOTSUP, "Key type %s is not "
+ "supported for kx509; only \"rsa\" is "
+ "supported for kx509 at this time",
+ gen_type);
+ return ENOTSUP;
+ }
+
+ ret = _hx509_generate_private_key_init(context->hx509ctx,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ &key_gen_ctx);
+ if (ret == 0)
+ ret = _hx509_generate_private_key_bits(context->hx509ctx, key_gen_ctx, gen_bits);
+
+ if (ret == 0)
+ ret = _hx509_generate_private_key(context->hx509ctx, key_gen_ctx, key);
+ _hx509_generate_private_key_free(&key_gen_ctx);
+ if (ret) {
+ char *emsg = hx509_get_error_string(context->hx509ctx, ret);
+
+ krb5_set_error_message(context, ret,
+ "Could not generate a private key: %s", emsg);
+ hx509_free_error_string(emsg);
+ }
+ return ret;
+}
+
+/**
+ * Generate a private key.
+ *
+ * @param context The Kerberos library context
+ * @param ctx The kx509 request context
+ * @param gen_type The type of key (default: rsa)
+ * @param gen_bits The size of the key (for non-ECC, really, for RSA)
+ *
+ * @return A krb5 error code.
+ */
+krb5_error_code
+krb5_kx509_ctx_gen_key(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ const char *gen_type,
+ int gen_bits)
+{
+ SubjectPublicKeyInfo key;
+ krb5_error_code ret;
+
+ memset(&key, 0, sizeof(key));
+
+ if (gen_type == NULL) {
+ gen_type = krb5_config_get_string_default(context, NULL, "rsa",
+ "libdefaults",
+ "kx509_gen_key_type", NULL);
+ }
+ if (gen_bits == 0) {
+ /*
+ * The key size is really only for non-ECC, of which we'll only support
+ * RSA. For ECC key sizes will either be implied by the `key_type' or
+ * will have to be a magic value that allows us to pick from some small
+ * set of curves (e.g., 255 == Curve25519).
+ */
+ gen_bits = krb5_config_get_int_default(context, NULL, 2048,
+ "libdefaults",
+ "kx509_gen_rsa_key_size", NULL);
+ }
+ hx509_private_key_free(&kx509_ctx->priv_key);
+ _hx509_certs_keys_free(context->hx509ctx, kx509_ctx->keys);
+ kx509_ctx->keys = NULL;
+
+ ret = gen_priv_key(context, gen_type, gen_bits, &kx509_ctx->priv_key);
+ if (ret == 0)
+ ret = hx509_private_key2SPKI(context->hx509ctx, kx509_ctx->priv_key,
+ &key);
+ if (ret == 0)
+ ret = hx509_request_set_SubjectPublicKeyInfo(context->hx509ctx,
+ kx509_ctx->csr, &key);
+ free_SubjectPublicKeyInfo(&key);
+ return ret;
+}
+
+/* Set a cc config entry indicating that the kx509 service is not available */
+static void
+store_kx509_disabled(krb5_context context, const char *realm, krb5_ccache cc)
+{
+ krb5_data data;
+
+ if (!cc)
+ return;
+
+ data.data = (void *)(uintptr_t)realm;
+ data.length = strlen(realm);
+ krb5_cc_set_config(context, cc, NULL, "kx509_service_realm", &data);
+ data.data = "disabled";
+ data.length = strlen(data.data);
+ krb5_cc_set_config(context, cc, NULL, "kx509_service_status", &data);
+}
+
+static int KRB5_CALLCONV
+certs_export_func(hx509_context context, void *d, hx509_cert c)
+{
+ heim_octet_string os;
+ Certificates *cs = d;
+ Certificate c2;
+ int ret;
+
+ ret = hx509_cert_binary(context, c, &os);
+ if (ret)
+ return ret;
+ ret = decode_Certificate(os.data, os.length, &c2, NULL);
+ der_free_octet_string(&os);
+ if (ret)
+ return ret;
+ ret = add_Certificates(cs, &c2);
+ free_Certificate(&c2);
+ return ret;
+}
+
+static krb5_error_code
+certs_export(hx509_context context, hx509_certs certs, heim_octet_string *out)
+{
+ Certificates cs;
+ size_t len;
+ int ret;
+
+ cs.len = 0;
+ cs.val = 0;
+ ret = hx509_certs_iter_f(context, certs, certs_export_func, &cs);
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(Certificates, out->data, out->length, &cs, &len, ret);
+ free_Certificates(&cs);
+ return ret;
+}
+
+/* Store the private key and certificate where requested */
+static krb5_error_code
+store(krb5_context context,
+ const char *hx509_store,
+ const char *realm,
+ krb5_ccache cc,
+ hx509_private_key key,
+ hx509_cert cert,
+ hx509_certs chain)
+{
+ heim_octet_string hdata;
+ krb5_error_code ret = 0;
+ krb5_data data;
+
+ krb5_clear_error_message(context);
+
+ if (cc) {
+ /* Record the realm we used */
+ data.data = (void *)(uintptr_t)realm;
+ data.length = strlen(realm);
+ krb5_cc_set_config(context, cc, NULL, "kx509_service_realm", &data);
+
+ /* Serialize and store the certificate in the ccache */
+ ret = hx509_cert_binary(context->hx509ctx, cert, &hdata);
+ if (ret == 0)
+ ret = krb5_cc_set_config(context, cc, NULL, "kx509cert", &hdata);
+ der_free_octet_string(&hdata);
+
+ if (ret == 0 && key) {
+ /*
+ * Serialized and store the key in the ccache. Use PKCS#8 so that we
+ * store the algorithm OID too, which is needed in order to be able to
+ * read the private key back.
+ */
+ if (ret == 0)
+ ret = _hx509_private_key_export(context->hx509ctx, key,
+ HX509_KEY_FORMAT_PKCS8, &hdata);
+ if (ret == 0)
+ ret = krb5_cc_set_config(context, cc, NULL, "kx509key", &hdata);
+ der_free_octet_string(&hdata);
+ if (ret)
+ krb5_set_error_message(context, ret, "Could not store kx509 "
+ "private key and certificate in ccache %s",
+ krb5_cc_get_name(context, cc));
+ }
+
+ if (ret == 0 && chain) {
+ ret = certs_export(context->hx509ctx, chain, &hdata);
+ if (ret == 0)
+ ret = krb5_cc_set_config(context, cc, NULL, "kx509cert-chain",
+ &hdata);
+ der_free_octet_string(&hdata);
+ }
+ }
+
+ /* Store the private key and cert in an hx509 store */
+ if (hx509_store != NULL) {
+ hx509_certs certs;
+
+ if (key)
+ _hx509_cert_assign_key(cert, key); /* store both in the same store */
+
+ ret = hx509_certs_init(context->hx509ctx, hx509_store,
+ HX509_CERTS_CREATE, NULL, &certs);
+ if (ret == 0)
+ ret = hx509_certs_add(context->hx509ctx, certs, cert);
+ if (ret == 0 && chain != NULL)
+ ret = hx509_certs_merge(context->hx509ctx, certs, chain);
+ if (ret == 0)
+ ret = hx509_certs_store(context->hx509ctx, certs, 0, NULL);
+ hx509_certs_free(&certs);
+ if (ret)
+ krb5_prepend_error_message(context, ret, "Could not store kx509 "
+ "private key and certificate in key "
+ "store %s", hx509_store);
+ }
+
+ /* Store the name of the hx509 store in the ccache too */
+ if (cc && hx509_store) {
+ data.data = (void *)(uintptr_t)hx509_store;
+ data.length = strlen(hx509_store);
+ (void) krb5_cc_set_config(context, cc, NULL, "kx509store", &data);
+ }
+ return ret;
+}
+
+/* Make a Kx509CSRPlus or a raw SPKI */
+static krb5_error_code
+mk_kx509_req_body(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ krb5_data *out)
+{
+ krb5_error_code ret;
+ size_t len;
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE,
+ "realms", kx509_ctx->realm,
+ "kx509_req_use_raw_spki", NULL)) {
+ SubjectPublicKeyInfo spki;
+
+ /* Interop with old kx509 servers, send a raw SPKI, not a CSR */
+ out->data = NULL;
+ out->length = 0;
+ memset(&spki, 0, sizeof(spki));
+ ret = hx509_private_key2SPKI(context->hx509ctx,
+ kx509_ctx->priv_key, &spki);
+ if (ret == 0) {
+ out->length = spki.subjectPublicKey.length >> 3;
+ out->data = spki.subjectPublicKey.data;
+ }
+ kx509_ctx->expect_chain = 0;
+ return ret;
+ }
+
+ /*
+ * New kx509 servers use a CSR for proof of possession, and send back a
+ * chain of certificates, with the issued certificate first.
+ */
+ kx509_ctx->expect_chain = 1;
+
+ if (kx509_ctx->given_csr.length) {
+ krb5_data exts_der;
+
+ exts_der.data = NULL;
+ exts_der.length = 0;
+
+ /* Use the given CSR */
+ ret = der_copy_octet_string(&kx509_ctx->given_csr,
+ &kx509_ctx->csr_plus.csr);
+
+ /*
+ * Extract the desired Certificate Extensions from our internal
+ * as-yet-unsigned CSR, then decode them into place in the
+ * Kx509CSRPlus.
+ */
+ if (ret == 0)
+ ret = hx509_request_get_exts(context->hx509ctx,
+ kx509_ctx->csr,
+ &exts_der);
+ if (ret == 0 && exts_der.data && exts_der.length &&
+ (kx509_ctx->csr_plus.exts =
+ calloc(1, sizeof (kx509_ctx->csr_plus.exts[0]))) == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0 && exts_der.data && exts_der.length)
+ ret = decode_Extensions(exts_der.data, exts_der.length,
+ kx509_ctx->csr_plus.exts, NULL);
+ krb5_data_free(&exts_der);
+ } else {
+ /*
+ * Sign and use our internal CSR, which will carry all our desired
+ * Certificate Extensions as an extReq CSR Attribute.
+ */
+ ret = hx509_request_to_pkcs10(context->hx509ctx,
+ kx509_ctx->csr,
+ kx509_ctx->priv_key,
+ &kx509_ctx->csr_plus.csr);
+ }
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(Kx509CSRPlus, out->data, out->length,
+ &kx509_ctx->csr_plus, &len, ret);
+ return ret;
+}
+
+static krb5_error_code
+get_start_realm(krb5_context context,
+ krb5_ccache cc,
+ krb5_const_principal princ,
+ char **out)
+{
+ krb5_error_code ret;
+ krb5_data d;
+
+ ret = krb5_cc_get_config(context, cc, NULL, "start_realm", &d);
+ if (ret == 0) {
+ *out = strndup(d.data, d.length);
+ krb5_data_free(&d);
+ } else if (princ) {
+ *out = strdup(krb5_principal_get_realm(context, princ));
+ } else {
+ krb5_principal ccprinc = NULL;
+
+ ret = krb5_cc_get_principal(context, cc, &ccprinc);
+ if (ret)
+ return ret;
+ *out = strdup(krb5_principal_get_realm(context, ccprinc));
+ krb5_free_principal(context, ccprinc);
+ }
+ return (*out) ? 0 : krb5_enomem(context);
+}
+
+/*
+ * Make a request, which is a DER-encoded Kx509Request with version_2_0
+ * prefixed to it.
+ *
+ * If no private key is given, then a probe request will be made.
+ */
+static krb5_error_code
+mk_kx509_req(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ krb5_ccache incc,
+ hx509_private_key private_key,
+ krb5_data *req)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ SubjectPublicKeyInfo spki;
+ struct Kx509Request kx509_req;
+ krb5_data pre_req;
+ krb5_error_code ret = 0;
+ krb5_creds this_cred;
+ krb5_creds *cred = NULL;
+ HMAC_CTX ctx;
+ const char *hostname;
+ char *start_realm = NULL;
+ size_t len = 0;
+
+ krb5_data_zero(&pre_req);
+ memset(&spki, 0, sizeof(spki));
+ memset(&this_cred, 0, sizeof(this_cred));
+ memset(&kx509_req, 0, sizeof(kx509_req));
+ kx509_req.pk_hash.data = digest;
+ kx509_req.pk_hash.length = SHA_DIGEST_LENGTH;
+
+ if (private_key || kx509_ctx->given_csr.data) {
+ /* Encode the CSR or public key for use in the request */
+ ret = mk_kx509_req_body(context, kx509_ctx, &kx509_req.pk_key);
+ } else {
+ /* Probe */
+ kx509_req.pk_key.data = NULL;
+ kx509_req.pk_key.length = 0;
+ }
+
+ if (ret == 0)
+ ret = krb5_cc_get_principal(context, incc, &this_cred.client);
+ if (ret == 0)
+ ret = get_start_realm(context, incc, this_cred.client, &start_realm);
+ if (ret == 0 && kx509_ctx->realm == NULL)
+ ret = krb5_kx509_ctx_set_realm(context, kx509_ctx, start_realm);
+ if (ret == 0) {
+ /*
+ * The kx509 protocol as deployed uses kca_service/kdc_hostname, but
+ * this is inconvenient in libkrb5: we want to be able to use the
+ * send_to_kdc machinery, and since the Heimdal KDC is also the kx509
+ * service, we want not to have to specify kx509 hosts separately from
+ * KDCs.
+ *
+ * We'd much rather use krbtgt/CLIENT_REALM@REQUESTED_REALM. What
+ * we do is assume all KDCs for `realm' support the kx509 service and
+ * then sendto the KDCs for that realm while using a hostbased service
+ * if still desired.
+ *
+ * Note that upstairs we try to get the start_realm cc config, so if
+ * realm wasn't given to krb5_kx509_ext(), then it should be set to
+ * that already unless there's no start_realm cc config, in which case
+ * we'll use the ccache's default client principal's realm.
+ */
+ hostname = krb5_config_get_string(context, NULL, "realms",
+ kx509_ctx->realm, "kx509_hostname",
+ NULL);
+ if (hostname == NULL)
+ hostname = krb5_config_get_string(context, NULL, "libdefaults",
+ "kx509_hostname", NULL);
+ if (hostname) {
+ ret = krb5_sname_to_principal(context, hostname, "kca_service",
+ KRB5_NT_SRV_HST, &this_cred.server);
+ if (ret == 0)
+ ret = krb5_principal_set_realm(context, this_cred.server,
+ kx509_ctx->realm);
+ } else {
+ ret = krb5_make_principal(context, &this_cred.server,
+ start_realm,
+ KRB5_TGS_NAME,
+ kx509_ctx->realm,
+ NULL);
+ }
+ }
+
+ /* Make the AP-REQ and extract the HMAC key */
+ if (ret == 0)
+ ret = krb5_get_credentials(context, 0, incc, &this_cred, &cred);
+ if (ret == 0)
+ ret = krb5_mk_req_extended(context, &kx509_ctx->ac, AP_OPTS_USE_SUBKEY,
+ NULL, cred, &kx509_req.authenticator);
+ krb5_free_keyblock(context, kx509_ctx->hmac_key);
+ kx509_ctx->hmac_key = NULL;
+ if (ret == 0)
+ ret = krb5_auth_con_getkey(context, kx509_ctx->ac,
+ &kx509_ctx->hmac_key);
+
+ if (ret)
+ goto out;
+
+ /* Add the the key and HMAC to the message */
+ HMAC_CTX_init(&ctx);
+ if (HMAC_Init_ex(&ctx, kx509_ctx->hmac_key->keyvalue.data,
+ kx509_ctx->hmac_key->keyvalue.length,
+ EVP_sha1(), NULL) == 0) {
+ HMAC_CTX_cleanup(&ctx);
+ ret = krb5_enomem(context);
+ } else {
+ HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
+ if (private_key || kx509_ctx->given_csr.data) {
+ HMAC_Update(&ctx, kx509_req.pk_key.data, kx509_req.pk_key.length);
+ } else {
+ /* Probe */
+ HMAC_Update(&ctx, kx509_req.authenticator.data, kx509_req.authenticator.length);
+ }
+ HMAC_Final(&ctx, kx509_req.pk_hash.data, 0);
+ HMAC_CTX_cleanup(&ctx);
+ }
+
+ /* Encode the message, prefix `version_2_0', output the result */
+ if (ret == 0)
+ ASN1_MALLOC_ENCODE(Kx509Request, pre_req.data, pre_req.length, &kx509_req, &len, ret);
+ if (ret == 0)
+ ret = krb5_data_alloc(req, pre_req.length + sizeof(version_2_0));
+ if (ret == 0) {
+ memcpy(req->data, version_2_0, sizeof(version_2_0));
+ memcpy(((unsigned char *)req->data) + sizeof(version_2_0),
+ pre_req.data, pre_req.length);
+ }
+
+out:
+ free(start_realm);
+ free(pre_req.data);
+ krb5_free_creds(context, cred);
+ kx509_req.pk_hash.data = NULL;
+ kx509_req.pk_hash.length = 0;
+ free_Kx509Request(&kx509_req);
+ free_SubjectPublicKeyInfo(&spki);
+ krb5_free_cred_contents(context, &this_cred);
+ if (ret == 0 && req->length != len + sizeof(version_2_0)) {
+ krb5_data_free(req);
+ krb5_set_error_message(context, ret = ERANGE,
+ "Could not make a kx509 request");
+ }
+ return ret;
+}
+
+static krb5_error_code
+rd_chain(krb5_context context,
+ heim_octet_string *d,
+ hx509_cert *cert,
+ hx509_certs *chain,
+ heim_error_t *herr)
+{
+ krb5_error_code ret;
+ Certificates certs;
+ size_t i, len;
+
+ *cert = NULL;
+ *chain = NULL;
+
+ if ((ret = decode_Certificates(d->data, d->length, &certs, &len)))
+ return ret;
+ if (certs.len == 0) {
+ *herr = heim_error_create(EINVAL, "Server sent empty Certificate list");
+ return EINVAL;
+ }
+ *cert = hx509_cert_init(context->hx509ctx, &certs.val[0], herr);
+ if (*cert == NULL) {
+ free_Certificates(&certs);
+ return errno;
+ }
+ if (certs.len == 1)
+ _krb5_debug(context, 1, "kx509 server sent certificate but no chain");
+ else
+ _krb5_debug(context, 1, "kx509 server sent %llu certificates",
+ (unsigned long long)certs.len);
+
+ ret = hx509_certs_init(context->hx509ctx, "MEMORY:anonymous",
+ HX509_CERTS_CREATE, NULL, chain);
+ if (ret) {
+ hx509_cert_free(*cert);
+ *cert = NULL;
+ free_Certificates(&certs);
+ return ret;
+ }
+
+ for (i = 1; ret == 0 && i < certs.len; i++) {
+ hx509_cert c = hx509_cert_init(context->hx509ctx, &certs.val[i], herr);
+
+ if (c == NULL)
+ ret = errno;
+ else
+ ret = hx509_certs_add(context->hx509ctx, *chain, c);
+ hx509_cert_free(c);
+ }
+ free_Certificates(&certs);
+ if (ret) {
+ hx509_certs_free(chain);
+ hx509_cert_free(*cert);
+ *cert = NULL;
+ }
+ return ret;
+}
+
+/* Parse and validate a kx509 reply */
+static krb5_error_code
+rd_kx509_resp(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ krb5_data *rep,
+ hx509_cert *cert,
+ hx509_certs *chain)
+{
+ unsigned char digest[SHA_DIGEST_LENGTH];
+ Kx509Response r;
+ krb5_error_code code = 0;
+ krb5_error_code ret = 0;
+ heim_string_t hestr;
+ heim_error_t herr = NULL;
+ const char *estr;
+ HMAC_CTX ctx;
+ size_t hdr_len = sizeof(version_2_0);
+ size_t len;
+
+ *cert = NULL;
+ *chain = NULL;
+
+ /* Strip `version_2_0' prefix */
+ if (rep->length < hdr_len || memcmp(rep->data, version_2_0, hdr_len) != 0) {
+ krb5_set_error_message(context, ENOTSUP,
+ "KDC does not support kx509 protocol");
+ return ENOTSUP; /* XXX */
+ }
+
+ /* Decode */
+ ret = decode_Kx509Response(((unsigned char *)rep->data) + 4,
+ rep->length - 4, &r, &len);
+ if (ret == 0 && len + hdr_len != rep->length)
+ ret = EINVAL; /* XXX */
+ if (ret) {
+ krb5_set_error_message(context, ret, "kx509 response is not valid");
+ return ret;
+ }
+
+ HMAC_CTX_init(&ctx);
+ if (HMAC_Init_ex(&ctx, kx509_ctx->hmac_key->keyvalue.data,
+ kx509_ctx->hmac_key->keyvalue.length, EVP_sha1(), NULL) == 0) {
+ free_Kx509Response(&r);
+ HMAC_CTX_cleanup(&ctx);
+ return krb5_enomem(context);
+ }
+
+ HMAC_Update(&ctx, version_2_0, sizeof(version_2_0));
+
+ {
+ int32_t t = r.error_code;
+ unsigned char encint[sizeof(t) + 1];
+ size_t k;
+
+ /*
+ * RFC6717 says this about how the error-code is included in the HMAC:
+ *
+ * o DER representation of the error-code exclusive of the tag and
+ * length, if it is present.
+ *
+ * So we use der_put_integer(), which encodes from the right.
+ *
+ * RFC6717 does not constrain the error-code's range. We assume it to
+ * be a 32-bit, signed integer, for which we'll need no more than 5
+ * bytes.
+ */
+ ret = der_put_integer(&encint[sizeof(encint) - 1],
+ sizeof(encint), &t, &k);
+ if (ret == 0)
+ HMAC_Update(&ctx, &encint[sizeof(encint)] - k, k);
+
+ /* Normalize error code */
+ if (r.error_code == 0) {
+ code = 0; /* No error */
+ } else if (r.error_code < 0) {
+ code = KRB5KRB_ERR_GENERIC; /* ??? */
+ } else if (r.error_code <= KX509_ERR_SRV_OVERLOADED - ERROR_TABLE_BASE_kx59) {
+ /*
+ * RFC6717 (kx509) error code. These are actually not used on the
+ * wire in any existing implementations that we are aware of. Just
+ * in case, however, we'll map these.
+ */
+ code = KX509_ERR_CLNT_FATAL + r.error_code;
+ } else if (r.error_code < kx509_krb5_error_base) {
+ /* Unknown error codes */
+ code = KRB5KRB_ERR_GENERIC;
+ } else {
+ /*
+ * Heimdal-specific enhancement to RFC6171: Kerberos wire protocol
+ * error codes.
+ */
+ code = KRB5KDC_ERR_NONE + r.error_code - kx509_krb5_error_base;
+ if (code >= KRB5_ERR_RCSID)
+ code = KRB5KRB_ERR_GENERIC;
+ if (code == KRB5KDC_ERR_NONE)
+ code = 0;
+ }
+ }
+ if (r.certificate)
+ HMAC_Update(&ctx, r.certificate->data, r.certificate->length);
+ if (r.e_text)
+ HMAC_Update(&ctx, *r.e_text, strlen(*r.e_text));
+ HMAC_Final(&ctx, &digest, 0);
+ HMAC_CTX_cleanup(&ctx);
+
+ if (r.hash == NULL) {
+ /*
+ * No HMAC -> unauthenticated [error] response.
+ *
+ * Do not output any certificate.
+ */
+ free_Kx509Response(&r);
+ return code;
+ }
+
+ /*
+ * WARNING: We do not validate that `r.certificate' is a DER-encoded
+ * Certificate, not here, and we don't use a different HMAC key
+ * for the response than for the request.
+ *
+ * If ever we start sending a Certificate as the Kx509Request
+ * pk-key field, then we'll have a reflection attack. As the
+ * Certificate we'd send in that case will be expired, the
+ * reflection attack would be just a DoS.
+ */
+ if (r.hash->length != sizeof(digest) ||
+ ct_memcmp(r.hash->data, digest, sizeof(digest)) != 0) {
+ krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
+ "kx509 response MAC mismatch");
+ free_Kx509Response(&r);
+ return KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ }
+
+ if (r.certificate == NULL) {
+ /* Authenticated response, either an error or probe success */
+ free_Kx509Response(&r);
+ if (code != KRB5KDC_ERR_POLICY && kx509_ctx->priv_key == NULL)
+ return 0; /* Probe success */
+ return code ? code : KRB5KDC_ERR_POLICY; /* Not a probe -> must fail */
+ }
+
+ /* Import the certificate payload */
+ if (kx509_ctx->expect_chain) {
+ ret = rd_chain(context, r.certificate, cert, chain, &herr);
+ } else {
+ *cert = hx509_cert_init_data(context->hx509ctx, r.certificate->data,
+ r.certificate->length, &herr);
+ if (!*cert)
+ ret = errno;
+ }
+ free_Kx509Response(&r);
+ if (*cert) {
+ heim_release(herr);
+ return 0;
+ }
+
+ hestr = herr ? heim_error_copy_string(herr) : NULL;
+ estr = hestr ? heim_string_get_utf8(hestr) : "(no error message)";
+ krb5_set_error_message(context, ret, "Could not parse certificate "
+ "produced by kx509 KDC: %s (%ld)",
+ estr,
+ herr ? (long)heim_error_get_code(herr) : 0L);
+
+ heim_release(hestr);
+ heim_release(herr);
+ return HEIM_PKINIT_CERTIFICATE_INVALID; /* XXX */
+}
+
+/*
+ * Make a request, send it, get the response, parse it, and store the
+ * private key and certificate.
+ */
+static krb5_error_code
+kx509_core(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ krb5_ccache incc,
+ const char *hx509_store,
+ krb5_ccache outcc)
+{
+ krb5_error_code ret;
+ hx509_certs chain = NULL;
+ hx509_cert cert = NULL;
+ krb5_data req, resp;
+
+ krb5_data_zero(&req);
+ krb5_data_zero(&resp);
+
+ /* Make the kx509 request */
+ ret = mk_kx509_req(context, kx509_ctx, incc, kx509_ctx->priv_key, &req);
+
+ /* Send the kx509 request and get the response */
+ if (ret == 0)
+ ret = krb5_sendto_context(context, NULL, &req,
+ kx509_ctx->realm, &resp);
+ if (ret == 0)
+ ret = rd_kx509_resp(context, kx509_ctx, &resp, &cert, &chain);
+
+ /* Store the key and cert! */
+ if (ret == 0 && cert && (kx509_ctx->priv_key || kx509_ctx->given_csr.data))
+ ret = store(context, hx509_store, kx509_ctx->realm, outcc,
+ kx509_ctx->priv_key, cert, chain);
+ else if (ret == KRB5KDC_ERR_POLICY)
+ /* Probe failed -> record that the realm does not support kx509 */
+ store_kx509_disabled(context, kx509_ctx->realm, outcc);
+
+ hx509_certs_free(&chain);
+ hx509_cert_free(cert);
+ krb5_data_free(&resp);
+ krb5_data_free(&req);
+ return ret;
+}
+
+/**
+ * Use the kx509 v2 protocol to get a certificate for the client principal.
+ *
+ * Given a private key this function will get a certificate. If no private key
+ * is given, one will be generated.
+ *
+ * The private key and certificate will be stored in the given PKIX credential
+ * store (e.g, "PEM-FILE:/path/to/file.pem") and/or given output ccache. When
+ * stored in a ccache, the DER-encoded Certificate will be stored as the data
+ * payload of a "cc config" named "kx509cert", while the key will be stored as
+ * a DER-encoded PKCS#8 PrivateKeyInfo in a cc config named "kx509key".
+ *
+ * @param context The Kerberos library context
+ * @param kx509_ctx A kx509 request context
+ * @param incc A credential cache (if NULL use default ccache)
+ * @param hx509_store An PKIX credential store into which to store the private
+ * key and certificate (e.g, "PEM-FILE:/path/to/file.pem")
+ * @param outcc A ccache into which to store the private key and certificate
+ * (mandatory)
+ *
+ * @return A krb5 error code.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_kx509_ext(krb5_context context,
+ krb5_kx509_req_ctx kx509_ctx,
+ krb5_ccache incc,
+ const char *hx509_store,
+ krb5_ccache outcc)
+{
+ krb5_ccache def_cc = NULL;
+ krb5_error_code ret;
+
+ if (incc == NULL) {
+ if ((ret = krb5_cc_default(context, &def_cc)))
+ return ret;
+ incc = def_cc;
+ }
+
+ if (kx509_ctx->realm == NULL &&
+ (ret = get_start_realm(context, incc, NULL, &kx509_ctx->realm))) {
+ if (def_cc)
+ krb5_cc_close(context, def_cc);
+ return ret;
+ }
+
+ if (kx509_ctx->priv_key || kx509_ctx->given_csr.data) {
+ /* If given a private key, use it */
+ ret = kx509_core(context, kx509_ctx, incc, hx509_store, outcc);
+ if (def_cc)
+ krb5_cc_close(context, def_cc);
+ return ret;
+ }
+
+ /*
+ * No private key given, so we generate one.
+ *
+ * However, before taking the hit for generating a keypair we probe to see
+ * if we're likely to succeeed.
+ */
+
+ /* Probe == call kx509_core() w/o a private key */
+ ret = kx509_core(context, kx509_ctx, incc, NULL, outcc);
+ if (ret == 0 && kx509_ctx->given_csr.data == NULL)
+ ret = krb5_kx509_ctx_gen_key(context, kx509_ctx, NULL, 0);
+ if (ret == 0)
+ ret = kx509_core(context, kx509_ctx, incc, hx509_store, outcc);
+
+ if (def_cc)
+ krb5_cc_close(context, def_cc);
+ return ret;
+}
+
+/**
+ * Generates a public key and uses the kx509 v2 protocol to get a certificate
+ * for that key and the client principal's subject name.
+ *
+ * The private key and certificate will be stored in the given ccache, and also
+ * in a corresponding PKIX credential store if one is configured via
+ * [libdefaults] kx509_store.
+ *
+ * XXX NOTE: Dicey feature here... Review carefully!
+ *
+ * @param context The Kerberos library context
+ * @param cc A credential cache
+ * @param realm A realm from which to get the certificate (uses the client
+ * principal's realm if NULL)
+ *
+ * @return A krb5 error code.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_kx509(krb5_context context, krb5_ccache cc, const char *realm)
+{
+ krb5_kx509_req_ctx kx509_ctx;
+ krb5_error_code ret;
+ const char *defcc;
+ char *ccache_full_name = NULL;
+ char *store_exp = NULL;
+
+ ret = krb5_kx509_ctx_init(context, &kx509_ctx);
+ if (ret)
+ return ret;
+ if (realm)
+ ret = krb5_kx509_ctx_set_realm(context, kx509_ctx, realm);
+
+ /*
+ * The idea is that IF we are asked to do kx509 w/ creds from a default
+ * ccache THEN we should store the kx509 certificate (if we get one) and
+ * private key in the default hx509 store for kx509.
+ *
+ * Ideally we could have HTTP user-agents and/or TLS libraries look for
+ * client certificates and private keys in that default hx509 store.
+ *
+ * Of course, those user-agents / libraries should be configured to use
+ * those credentials with specific hostnames/domainnames, not the entire
+ * Internet, as the latter leaks the user's identity to the world.
+ *
+ * So we check if the full name for `cc' is the same as that of the default
+ * ccache name, and if so we get the [libdefaults] kx509_store string and
+ * expand it, then use it.
+ */
+ if (ret == 0 &&
+ (defcc = krb5_cc_configured_default_name(context)) &&
+ krb5_cc_get_full_name(context, cc, &ccache_full_name) == 0 &&
+ strcmp(defcc, ccache_full_name) == 0) {
+
+ /* Find an hx509 store */
+ const char *store = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "kx509_store", NULL);
+ if (store)
+ ret = _krb5_expand_path_tokens(context, store, 1, &store_exp);
+
+ /*
+ * If there's a private key in the store already, we'll use it, else
+ * we'll let krb5_kx509_ext() generate one, so we ignore this return
+ * value:
+ */
+ (void) krb5_kx509_ctx_set_key(context, kx509_ctx, store);
+ }
+
+ /*
+ * If we did settle on a default hx509 store, we'll use it for reading the
+ * private key from (if it exists) as well as for storing the certificate
+ * (and private key) into, which may save us some key generation cycles.
+ */
+ if (ret == 0)
+ ret = krb5_kx509_ext(context, kx509_ctx, cc, store_exp, cc);
+ krb5_kx509_ctx_free(context, &kx509_ctx);
+ free(ccache_full_name);
+ free(store_exp);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/kx509_err.et b/third_party/heimdal/lib/krb5/kx509_err.et
new file mode 100644
index 0000000..0be3907
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/kx509_err.et
@@ -0,0 +1,39 @@
+#
+# Error messages for the krb5 library
+#
+# This might look like a com_err file, but is not
+#
+
+# RFC6171 says:
+#
+# +------------+-----------------------------+------------------------+
+# | error-code | Condition | Example |
+# +------------+-----------------------------+------------------------+
+# | 1 | Permanent problem with | Incompatible version |
+# | | client request | |
+# | 2 | Solvable problem with | Expired Kerberos |
+# | | client request | credentials |
+# | 3 | Temporary problem with | Packet loss |
+# | | client request | |
+# | 4 | Permanent problem with the | Internal |
+# | | server | misconfiguration |
+# | 5 | Temporary problem with the | Server overloaded |
+# | | server | |
+# +------------+-----------------------------+------------------------+
+#
+# Error 3 makes no sense on the wire, and in the library it only makes sense as
+# a timeout, so we'll name it KX509_ERR_TIMEOUT.
+
+# Error table names must be no more than four characters...
+error_table kx59
+
+prefix KX509_ERR
+
+error_code NONE, "Kx509 success"
+error_code CLNT_FATAL, "Kx509 request error, possibly unsupported version"
+error_code CLNT_SOLVABLE, "Kx509 request error such as expired credentials"
+error_code TIMEOUT, "Kx509 request timed out"
+error_code SRV_FATAL, "Permanent server problem"
+error_code SRV_OVERLOADED, "Kx509 server is overloaded"
+
+end
diff --git a/third_party/heimdal/lib/krb5/libkrb5-exports.def.in b/third_party/heimdal/lib/krb5/libkrb5-exports.def.in
new file mode 100644
index 0000000..6dacb7f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/libkrb5-exports.def.in
@@ -0,0 +1,901 @@
+EXPORTS
+ krb524_convert_creds_kdc
+ krb524_convert_creds_kdc_ccache
+ krb5_abort
+ krb5_abortx
+ krb5_acl_match_file
+ krb5_acl_match_string
+ krb5_add_et_list
+ krb5_add_extra_addresses
+ krb5_add_ignore_addresses
+ krb5_addlog_dest
+ krb5_addlog_func
+ krb5_addr2sockaddr
+ krb5_address_compare
+ krb5_address_order
+ krb5_address_prefixlen_boundary
+ krb5_address_search
+ krb5_allow_weak_crypto
+ krb5_aname_to_localname
+ krb5_anyaddr
+ krb5_appdefault_boolean
+ krb5_appdefault_string
+ krb5_appdefault_time
+ krb5_append_addresses
+ krb5_auth_con_addflags
+ krb5_auth_con_add_AuthorizationData
+ krb5_auth_con_add_AuthorizationDataIfRelevant
+ krb5_auth_con_free
+ krb5_auth_con_genaddrs
+ krb5_auth_con_generatelocalsubkey
+ krb5_auth_con_getaddrs
+ krb5_auth_con_getauthenticator
+ krb5_auth_con_getcksumtype
+ krb5_auth_con_getflags
+ krb5_auth_con_getkey
+ krb5_auth_con_getkeytype
+ krb5_auth_con_getlocalseqnumber
+ krb5_auth_con_getlocalsubkey
+ krb5_auth_con_getrcache
+ krb5_auth_con_getremoteseqnumber
+ krb5_auth_con_getremotesubkey
+ krb5_auth_con_init
+ krb5_auth_con_removeflags
+ krb5_auth_con_setaddrs
+ krb5_auth_con_setaddrs_from_fd
+ krb5_auth_con_setcksumtype
+ krb5_auth_con_setflags
+ krb5_auth_con_setkey
+ krb5_auth_con_setkeytype
+ krb5_auth_con_setlocalseqnumber
+ krb5_auth_con_setlocalsubkey
+ krb5_auth_con_setrcache
+ krb5_auth_con_setremoteseqnumber
+ krb5_auth_con_setremotesubkey
+ krb5_auth_con_setuserkey
+ krb5_auth_getremoteseqnumber
+ krb5_build_ap_req
+ krb5_build_principal
+ krb5_build_principal_ext
+ krb5_build_principal_va
+ krb5_build_principal_va_ext
+ krb5_c_block_size
+ krb5_c_checksum_length
+ krb5_c_decrypt
+ krb5_c_encrypt
+ krb5_c_encrypt_length
+ krb5_c_enctype_compare
+ krb5_c_get_checksum
+ krb5_c_is_coll_proof_cksum
+ krb5_c_is_keyed_cksum
+ krb5_c_keylengths
+ krb5_c_make_checksum
+ krb5_c_make_random_key
+ krb5_c_prf
+ krb5_c_prf_length
+ krb5_c_random_make_octets ;!
+ krb5_c_set_checksum
+ krb5_c_valid_cksumtype
+ krb5_c_valid_enctype
+ krb5_c_verify_checksum
+ krb5_cc_cache_end_seq_get
+ krb5_cc_cache_get_first
+ krb5_cc_cache_match
+ krb5_cc_cache_next
+ krb5_cc_clear_mcred
+ krb5_cc_close
+ krb5_cc_configured_default_name
+ krb5_cc_copy_cache
+ krb5_cc_copy_creds ;!
+ krb5_cc_copy_match_f
+ krb5_cc_default
+ krb5_cc_default_sub
+ krb5_cc_default_for
+ krb5_cc_default_name
+ krb5_cc_destroy
+ krb5_cc_end_seq_get
+ krb5_cc_gen_new
+ krb5_cc_get_config
+ krb5_cc_get_friendly_name
+ krb5_cc_get_full_name
+ krb5_cc_get_kdc_offset
+ krb5_cc_get_lifetime
+ krb5_cc_get_name
+ krb5_cc_get_ops
+ krb5_cc_get_prefix_ops
+ krb5_cc_get_principal
+ krb5_cc_get_type
+ krb5_cc_get_version
+ krb5_cc_initialize
+ krb5_cc_last_change_time
+ krb5_cc_move
+ krb5_cc_new_unique
+ krb5_cc_next_cred
+;! krb5_cc_next_cred_match
+ krb5_cc_register
+ krb5_cc_remove_cred
+ krb5_cc_resolve
+ krb5_cc_resolve_sub
+ krb5_cc_resolve_for
+ krb5_cc_retrieve_cred
+ krb5_cc_set_config
+ krb5_cc_set_default_name
+ krb5_cc_set_flags
+ krb5_cc_set_kdc_offset
+ krb5_cc_start_seq_get
+ krb5_cc_store_cred
+ krb5_cc_support_switch
+ krb5_cc_switch
+ krb5_cc_set_friendly_name
+ krb5_change_password
+ krb5_check_transited
+ krb5_check_transited_realms
+ krb5_checksum_disable
+ krb5_checksum_free
+ krb5_checksum_is_collision_proof
+ krb5_checksum_is_keyed
+ krb5_checksumsize
+ krb5_cksumtype_to_enctype
+ krb5_cksumtype_valid
+ krb5_clear_error_string
+ krb5_clear_error_message
+ krb5_closelog
+ krb5_compare_creds
+ krb5_config_file_free
+ krb5_config_free_strings
+; _krb5_config_get
+ krb5_config_get_bool
+ krb5_config_get_bool_default
+ krb5_config_get_int
+ krb5_config_get_int_default
+ krb5_config_get_list
+; _krb5_config_get_next
+ krb5_config_get_string
+ krb5_config_get_string_default
+ krb5_config_get_strings
+ krb5_config_get_time
+ krb5_config_get_time_default
+ krb5_config_parse_file
+ krb5_config_parse_file_multi
+ krb5_config_parse_string_multi
+; _krb5_config_vget
+ krb5_config_vget_bool
+ krb5_config_vget_bool_default
+ krb5_config_vget_int
+ krb5_config_vget_int_default
+ krb5_config_vget_list
+; _krb5_config_vget_next
+ krb5_config_vget_string
+ krb5_config_vget_string_default
+ krb5_config_vget_strings
+ krb5_config_vget_time
+ krb5_config_vget_time_default
+ krb5_copy_address
+ krb5_copy_addresses
+ krb5_copy_checksum
+ krb5_copy_context
+ krb5_copy_creds
+ krb5_copy_creds_contents
+ krb5_copy_data
+ krb5_copy_host_realm
+ krb5_copy_keyblock
+ krb5_copy_keyblock_contents
+ krb5_copy_principal
+ krb5_copy_ticket
+ krb5_create_checksum
+ krb5_create_checksum_iov
+ krb5_crypto_destroy
+ krb5_crypto_fx_cf2
+ krb5_crypto_get_checksum_type
+ krb5_crypto_getblocksize
+ krb5_crypto_getconfoundersize
+ krb5_crypto_getenctype
+ krb5_crypto_getpadsize
+ krb5_crypto_init
+ krb5_crypto_overhead
+ krb5_crypto_prf
+ krb5_crypto_prfplus
+ krb5_crypto_prf_length
+ krb5_crypto_length
+ krb5_crypto_length_iov
+ krb5_decrypt_iov_ivec
+ krb5_encrypt_iov_ivec
+ krb5_data_alloc
+ krb5_data_cmp
+ krb5_data_copy
+ krb5_data_ct_cmp
+ krb5_data_free
+ krb5_data_realloc
+ krb5_data_zero
+ krb5_debug
+ krb5_decode_Authenticator
+ krb5_decode_ETYPE_INFO2
+ krb5_decode_ETYPE_INFO
+ krb5_decode_EncAPRepPart
+ krb5_decode_EncASRepPart
+ krb5_decode_EncKrbCredPart
+ krb5_decode_EncTGSRepPart
+ krb5_decode_EncTicketPart
+ krb5_decode_ap_req
+ krb5_decrypt
+ krb5_decrypt_EncryptedData
+ krb5_decrypt_ivec
+ krb5_decrypt_ticket
+ krb5_derive_key
+ krb5_digest_alloc
+ krb5_digest_free
+ krb5_digest_get_client_binding
+ krb5_digest_get_identifier
+ krb5_digest_get_opaque
+ krb5_digest_get_rsp
+ krb5_digest_get_server_nonce
+ krb5_digest_get_session_key
+ krb5_digest_get_tickets
+ krb5_digest_init_request
+ krb5_digest_probe
+ krb5_digest_rep_get_status
+ krb5_digest_request
+ krb5_digest_set_authentication_user
+ krb5_digest_set_authid
+ krb5_digest_set_client_nonce
+ krb5_digest_set_digest
+ krb5_digest_set_hostname
+ krb5_digest_set_identifier
+ krb5_digest_set_method
+ krb5_digest_set_nonceCount
+ krb5_digest_set_opaque
+ krb5_digest_set_qop
+ krb5_digest_set_realm
+ krb5_digest_set_responseData
+ krb5_digest_set_server_cb
+ krb5_digest_set_server_nonce
+ krb5_digest_set_type
+ krb5_digest_set_uri
+ krb5_digest_set_username
+ krb5_domain_x500_decode
+ krb5_domain_x500_encode
+ krb5_eai_to_heim_errno
+ krb5_encode_Authenticator
+ krb5_encode_ETYPE_INFO2
+ krb5_encode_ETYPE_INFO
+ krb5_encode_EncAPRepPart
+ krb5_encode_EncASRepPart
+ krb5_encode_EncKrbCredPart
+ krb5_encode_EncTGSRepPart
+ krb5_encode_EncTicketPart
+ krb5_encrypt
+ krb5_encrypt_EncryptedData
+ krb5_encrypt_ivec
+ krb5_enctype_enable
+ krb5_enctype_disable
+ krb5_enctype_keybits
+ krb5_enctype_keysize
+ krb5_enctype_to_keytype
+ krb5_enctype_to_string
+ krb5_enctype_valid
+ krb5_enctypes_compatible_keys
+ krb5_enomem
+ krb5_err
+ krb5_error_from_rd_error
+ krb5_errx
+ krb5_expand_hostname
+ krb5_expand_hostname_realms
+ krb5_find_padata
+ krb5_format_time
+ krb5_free_address
+ krb5_free_addresses
+ krb5_free_ap_rep_enc_part
+ krb5_free_authenticator
+ krb5_free_checksum
+ krb5_free_checksum_contents
+ krb5_free_config_files
+ krb5_free_context
+ krb5_free_cred_contents
+ krb5_free_creds
+ krb5_free_creds_contents
+ krb5_free_data
+ krb5_free_data_contents
+ krb5_free_default_realm
+ krb5_free_error
+ krb5_free_error_contents
+ krb5_free_error_string
+ krb5_free_error_message
+ krb5_free_host_realm
+ krb5_free_kdc_rep
+ krb5_free_keyblock
+ krb5_free_keyblock_contents
+ krb5_free_krbhst
+ krb5_free_principal
+ krb5_free_salt
+ krb5_free_ticket
+ krb5_free_unparsed_name
+ krb5_fwd_tgt_creds
+ krb5_generate_random_block
+ krb5_generate_random_keyblock
+ krb5_generate_seq_number
+ krb5_generate_subkey
+ krb5_generate_subkey_extended
+ krb5_get_all_client_addrs
+ krb5_get_all_server_addrs
+ krb5_get_cred_from_kdc
+ krb5_get_cred_from_kdc_opt
+ krb5_get_credentials
+ krb5_get_credentials_with_flags
+ krb5_get_creds
+ krb5_get_creds_opt_add_options
+ krb5_get_creds_opt_alloc
+ krb5_get_creds_opt_free
+ krb5_get_creds_opt_set_enctype
+ krb5_get_creds_opt_set_impersonate
+ krb5_get_creds_opt_set_options
+ krb5_get_creds_opt_set_ticket
+ krb5_get_default_config_files
+ krb5_get_default_in_tkt_etypes
+ krb5_get_default_principal
+ krb5_get_default_realm
+ krb5_get_default_realms
+ krb5_get_dns_canonicalize_hostname
+ krb5_get_err_text
+ krb5_get_error_message
+ krb5_get_error_string
+ krb5_get_extra_addresses
+ krb5_get_fcache_version
+ krb5_get_forwarded_creds
+ krb5_get_host_realm
+ krb5_get_ignore_addresses
+ krb5_get_in_cred
+; krb5_cccol_last_change_time
+ krb5_get_in_tkt
+ krb5_get_in_tkt_with_keytab
+ krb5_get_in_tkt_with_password
+ krb5_get_in_tkt_with_skey
+;! krb5_get_init_creds
+ krb5_get_init_creds_keyblock
+ krb5_get_init_creds_keytab
+ krb5_get_init_creds_opt_alloc
+ krb5_get_init_creds_opt_free
+ krb5_get_init_creds_opt_get_error
+ krb5_get_init_creds_opt_init
+ krb5_get_init_creds_opt_set_address_list
+ krb5_get_init_creds_opt_set_addressless
+ krb5_get_init_creds_opt_set_anonymous
+ krb5_get_init_creds_opt_set_change_password_prompt
+ krb5_get_init_creds_opt_set_canonicalize
+ krb5_get_init_creds_opt_set_default_flags
+ krb5_get_init_creds_opt_set_etype_list
+ krb5_get_init_creds_opt_set_forwardable
+ krb5_get_init_creds_opt_set_pa_password
+ krb5_get_init_creds_opt_set_pac_request
+ krb5_get_init_creds_opt_set_pkinit
+ krb5_get_init_creds_opt_set_preauth_list
+ krb5_get_init_creds_opt_set_process_last_req
+ krb5_get_init_creds_opt_set_proxiable
+ krb5_get_init_creds_opt_set_renew_life
+ krb5_get_init_creds_opt_set_salt
+ krb5_get_init_creds_opt_set_tkt_life
+ krb5_get_init_creds_opt_set_win2k
+ krb5_get_init_creds_opt_set_fast_ccache
+ krb5_get_init_creds_opt_set_fast_ccache_name
+ krb5_get_init_creds_opt_set_fast_flags
+ krb5_get_init_creds_password
+ krb5_get_instance
+ krb5_get_kdc_cred
+ krb5_get_kdc_sec_offset
+ krb5_get_krb524hst
+ krb5_get_krb_admin_hst
+ krb5_get_krb_changepw_hst
+ krb5_get_krb_readonly_admin_hst
+ krb5_get_krbhst
+ krb5_get_max_time_skew
+ krb5_get_pw_salt
+ krb5_get_renewed_creds
+ krb5_get_server_rcache
+ krb5_get_use_admin_kdc
+ krb5_get_validated_creds ;!
+ krb5_get_warn_dest
+ krb5_get_wrapped_length
+ krb5_getportbyname
+ krb5_h_addr2addr
+ krb5_h_addr2sockaddr
+ krb5_h_errno_to_heim_errno
+ krb5_have_error_string
+ krb5_have_debug
+ krb5_hmac
+ krb5_init_context
+ krb5_init_ets
+ krb5_initlog
+ krb5_is_config_principal
+ krb5_is_enctype_old
+ krb5_is_enctype_weak
+ krb5_is_thread_safe
+#ifdef HAVE_KCM
+ krb5_kcm_call
+ krb5_kcm_storage_request
+#endif
+ krb5_kerberos_enctypes
+ krb5_keyblock_get_enctype
+ krb5_keyblock_init
+ krb5_keyblock_key_proc
+ krb5_keyblock_zero
+ krb5_keytab_key_proc
+ krb5_keytype_to_enctypes
+ krb5_keytype_to_enctypes_default
+ krb5_keytype_to_string
+ krb5_krbhst_format_string
+ krb5_krbhst_free
+ krb5_krbhst_get_addrinfo
+ krb5_krbhst_init
+ krb5_krbhst_init_flags
+ krb5_krbhst_next
+ krb5_krbhst_next_as_string
+ krb5_krbhst_reset
+ krb5_kt_add_entry
+ krb5_kt_close
+ krb5_kt_compare
+ krb5_kt_copy_entry_contents
+ krb5_kt_default
+ krb5_kt_default_modify_name
+ krb5_kt_default_name
+ krb5_kt_destroy
+ krb5_kt_end_seq_get
+ krb5_kt_free_entry
+ krb5_kt_get_entry
+ krb5_kt_get_full_name
+ krb5_kt_get_name
+ krb5_kt_get_type
+ krb5_kt_have_content
+ krb5_kt_next_entry
+ krb5_kt_read_service_key
+ krb5_kt_register
+ krb5_kt_remove_entry
+ krb5_kt_resolve
+ krb5_kt_start_seq_get
+ krb5_kuserok
+ krb5_kx509
+ krb5_kx509_ctx_add_eku
+ krb5_kx509_ctx_add_san_dns_name
+ krb5_kx509_ctx_add_san_ms_upn
+ krb5_kx509_ctx_add_san_pkinit
+ krb5_kx509_ctx_add_san_registeredID
+ krb5_kx509_ctx_add_san_rfc822Name
+ krb5_kx509_ctx_add_san_xmpp
+ krb5_kx509_ctx_free
+ krb5_kx509_ctx_init
+ krb5_kx509_ctx_set_csr_der
+ krb5_kx509_ctx_set_key
+ krb5_kx509_ctx_set_realm
+ krb5_kx509_ext
+ krb5_log
+ krb5_log_msg
+ krb5_make_addrport
+ krb5_make_principal
+ krb5_max_sockaddr_size
+ krb5_mk_1cred
+ krb5_mk_error
+ krb5_mk_error_ext
+ krb5_mk_ncred
+ krb5_mk_priv
+ krb5_mk_rep
+ krb5_mk_req
+ krb5_mk_req_exact
+ krb5_mk_req_extended
+ krb5_mk_safe
+ krb5_net_read
+ krb5_net_write
+ krb5_net_write_block
+ krb5_ntlm_alloc
+ krb5_ntlm_free
+ krb5_ntlm_init_get_challenge
+ krb5_ntlm_init_get_flags
+ krb5_ntlm_init_get_opaque
+ krb5_ntlm_init_get_targetinfo
+ krb5_ntlm_init_get_targetname
+ krb5_ntlm_init_request
+ krb5_ntlm_rep_get_sessionkey
+ krb5_ntlm_rep_get_status
+ krb5_ntlm_req_set_flags
+ krb5_ntlm_req_set_lm
+ krb5_ntlm_req_set_ntlm
+ krb5_ntlm_req_set_opaque
+ krb5_ntlm_req_set_session
+ krb5_ntlm_req_set_targetname
+ krb5_ntlm_req_set_username
+ krb5_ntlm_request
+ krb5_openlog
+ krb5_pac_add_buffer
+ krb5_pac_free
+ krb5_pac_get_buffer
+ _krb5_pac_get_buffer_by_name
+ krb5_pac_get_kdc_checksum_info
+ krb5_pac_get_types
+ krb5_pac_init
+ krb5_pac_is_trusted
+ krb5_pac_parse
+ krb5_pac_set_trusted
+ krb5_pac_verify
+ krb5_padata_add
+ _krb5_parse_address_no_lookup
+ krb5_parse_address
+ krb5_parse_name
+ krb5_parse_name_flags
+ krb5_parse_nametype
+ krb5_passwd_result_to_string
+ krb5_password_key_proc
+ krb5_get_permitted_enctypes
+ krb5_plugin_register
+ krb5_prepend_config_files
+ krb5_prepend_config_files_default
+ krb5_prepend_error_message
+ krb5_princ_realm
+ krb5_princ_set_realm
+ krb5_principal_compare
+ krb5_principal_compare_any_realm
+ krb5_principal_get_comp_string
+ krb5_principal_get_num_comp
+ krb5_principal_get_realm
+ krb5_principal_get_type
+ krb5_principal_is_anonymous
+ krb5_principal_is_federated
+ krb5_principal_is_krbtgt
+ krb5_principal_is_root_krbtgt
+ krb5_principal_match
+ krb5_principal_set_comp_string
+ krb5_principal_set_realm
+ krb5_principal_set_type
+ krb5_principalname_is_krbtgt
+ krb5_print_address
+ krb5_program_setup
+ krb5_prompter_posix
+ krb5_random_to_key
+ krb5_rc_close
+ krb5_rc_default
+ krb5_rc_default_name
+ krb5_rc_default_type
+ krb5_rc_destroy
+ krb5_rc_expunge
+ krb5_rc_get_lifespan
+ krb5_rc_get_name
+ krb5_rc_get_type
+ krb5_rc_initialize
+ krb5_rc_recover
+ krb5_rc_resolve
+ krb5_rc_resolve_full
+ krb5_rc_resolve_type
+ krb5_rc_store
+ krb5_rd_cred2
+ krb5_rd_cred
+ krb5_rd_error
+ krb5_rd_priv
+ krb5_rd_rep
+ krb5_rd_req
+ krb5_rd_req_ctx
+ krb5_rd_req_in_ctx_alloc
+ krb5_rd_req_in_ctx_free
+ krb5_rd_req_in_set_keyblock
+ krb5_rd_req_in_set_keytab
+ krb5_rd_req_in_set_pac_check
+ krb5_rd_req_out_ctx_free
+ krb5_rd_req_out_get_ap_req_options
+ krb5_rd_req_out_get_keyblock
+ krb5_rd_req_out_get_ticket
+ krb5_rd_req_with_keyblock
+ krb5_rd_safe
+ krb5_read_message
+ krb5_read_priv_message
+ krb5_read_safe_message
+ krb5_realm_compare
+ krb5_recvauth
+ krb5_recvauth_match_version
+ krb5_ret_address
+ krb5_ret_addrs
+ krb5_ret_authdata
+ krb5_ret_creds
+ krb5_ret_creds_tag
+ krb5_ret_data
+ krb5_ret_int16
+ krb5_ret_int32
+ krb5_ret_int64
+ krb5_ret_int8
+ krb5_ret_keyblock
+ krb5_ret_principal
+ krb5_ret_string
+ krb5_ret_stringnl
+ krb5_ret_stringz
+ krb5_ret_times
+ krb5_ret_uint16
+ krb5_ret_uint32
+ krb5_ret_uint64
+ krb5_ret_uint8
+ krb5_salttype_to_string
+ krb5_sendauth
+ krb5_sendto
+ krb5_sendto_context
+ krb5_sendto_ctx_add_flags
+ krb5_sendto_ctx_alloc
+ krb5_sendto_ctx_free
+ krb5_sendto_ctx_get_flags
+ krb5_sendto_ctx_set_func
+ krb5_sendto_ctx_set_type
+ krb5_sendto_kdc
+ krb5_sendto_kdc_flags
+ krb5_sendto_set_hostname
+ krb5_sendto_set_sitename
+ krb5_set_config
+ krb5_set_config_files
+ krb5_set_debug_dest
+ krb5_set_default_in_tkt_etypes
+ krb5_set_default_realm
+ krb5_set_dns_canonicalize_hostname
+ krb5_set_error_message
+ krb5_set_error_string
+ krb5_set_extra_addresses
+ krb5_set_fcache_version
+ krb5_set_home_dir_access
+ krb5_set_ignore_addresses
+ krb5_set_kdc_sec_offset
+ krb5_set_log_dest
+ krb5_set_max_time_skew
+ krb5_set_password
+ krb5_set_password_using_ccache
+ krb5_set_real_time
+ krb5_set_send_to_kdc_func
+ krb5_set_use_admin_kdc
+ krb5_set_warn_dest
+ krb5_sname_to_principal
+ krb5_sock_to_principal
+ krb5_sockaddr2address
+ krb5_sockaddr2port
+ krb5_sockaddr_uninteresting
+ krb5_std_usage
+ krb5_storage_clear_flags
+ krb5_storage_emem
+ krb5_storage_free
+ krb5_storage_from_data
+ krb5_storage_from_fd
+ krb5_storage_from_mem
+ krb5_storage_from_readonly_mem
+ krb5_storage_from_socket
+ krb5_storage_fsync
+ krb5_storage_get_byteorder
+ krb5_storage_get_eof_code
+ krb5_storage_is_flags
+ krb5_storage_read
+ krb5_storage_stdio_from_fd
+ krb5_storage_seek
+ krb5_storage_set_byteorder
+ krb5_storage_set_eof_code
+ krb5_storage_set_flags
+ krb5_storage_set_max_alloc
+ krb5_storage_to_data
+ krb5_storage_truncate
+ krb5_storage_write
+ krb5_store_address
+ krb5_store_addrs
+ krb5_store_authdata
+ krb5_store_bytes
+ krb5_store_creds
+ krb5_store_creds_tag
+ krb5_store_data
+ krb5_store_datalen
+ krb5_store_int16
+ krb5_store_int32
+ krb5_store_int64
+ krb5_store_int8
+ krb5_store_keyblock
+ krb5_store_principal
+ krb5_store_string
+ krb5_store_stringnl
+ krb5_store_stringz
+ krb5_store_times
+ krb5_store_uint16
+ krb5_store_uint32
+ krb5_store_uint64
+ krb5_store_uint8
+ krb5_string_to_deltat
+ krb5_string_to_enctype
+ krb5_string_to_key
+ krb5_string_to_key_data
+ krb5_string_to_key_data_salt
+ krb5_string_to_key_data_salt_opaque
+ krb5_string_to_key_derived
+ krb5_string_to_key_salt
+ krb5_string_to_key_salt_opaque
+ krb5_string_to_keysalts2
+ krb5_string_to_keytype
+ krb5_string_to_salttype
+ krb5_ticket_get_authorization_data_type
+ krb5_ticket_get_client
+ krb5_ticket_get_endtime
+ krb5_ticket_get_times
+ krb5_ticket_get_server
+ krb5_timeofday
+ krb5_unparse_name
+ krb5_unparse_name_fixed
+ krb5_unparse_name_fixed_flags
+ krb5_unparse_name_fixed_short
+ krb5_unparse_name_flags
+ krb5_unparse_name_short
+ krb5_us_timeofday
+ krb5_vabort
+ krb5_vabortx
+ krb5_verify_ap_req2
+ krb5_verify_ap_req
+ krb5_verify_authenticator_checksum
+ krb5_verify_checksum
+ krb5_verify_checksum_iov
+ krb5_verify_init_creds
+ krb5_verify_init_creds_opt_init
+ krb5_verify_init_creds_opt_set_ap_req_nofail
+ krb5_verify_opt_alloc
+ krb5_verify_opt_free
+ krb5_verify_opt_init
+ krb5_verify_opt_set_ccache
+ krb5_verify_opt_set_flags
+ krb5_verify_opt_set_keytab
+ krb5_verify_opt_set_secure
+ krb5_verify_opt_set_service
+ krb5_verify_user
+ krb5_verify_user_lrealm
+ krb5_verify_user_opt
+ krb5_verr
+ krb5_verrx
+ krb5_vlog
+ krb5_vlog_msg
+ krb5_vprepend_error_message
+ krb5_vset_error_message
+ krb5_vset_error_string
+ krb5_vwarn
+ krb5_vwarnx
+ krb5_warn
+ krb5_warnx
+ krb5_write_message
+ krb5_write_priv_message
+ krb5_write_safe_message
+ krb5_xfree
+ krb5_cccol_last_change_time
+ krb5_cccol_cursor_new
+ krb5_cccol_cursor_next
+ krb5_cccol_cursor_free
+ krb5_cccol_get_default_ccname
+
+ ; com_err error tables
+ initialize_krb5_error_table_r
+ initialize_krb5_error_table
+ initialize_krb_error_table_r
+ initialize_krb_error_table
+ initialize_heim_error_table_r
+ initialize_heim_error_table
+ initialize_k524_error_table_r
+ initialize_k524_error_table
+ initialize_k5e1_error_table_r
+ initialize_k5e1_error_table
+
+ ; variables
+ krb5_mcc_ops DATA
+ krb5_acc_ops DATA
+ krb5_fcc_ops DATA
+#ifdef HAVE_SCC
+ krb5_scc_ops DATA
+#endif
+#ifdef HAVE_KCM
+ krb5_kcm_ops DATA
+#endif
+#ifdef HAVE_KEYUTILS
+ krb5_krcc_ops DATA
+#endif
+ krb5_wrfkt_ops DATA
+ krb5_mkt_ops DATA
+ krb5_akf_ops DATA
+ krb5_any_ops DATA
+ heimdal_version DATA
+ heimdal_long_version DATA
+ krb5_config_file DATA
+ krb5_defkeyname DATA
+ krb5_cc_type_api DATA
+ krb5_cc_type_file DATA
+ krb5_cc_type_memory DATA
+ krb5_cc_type_kcm DATA
+ krb5_cc_type_keyring DATA
+ krb5_cc_type_scc DATA
+
+ ; Shared with GSSAPI krb5
+ _krb5_crc_init_table
+ _krb5_crc_update
+ _krb5_get_krbtgt
+ _krb5_build_authenticator
+ _krb5_kt_client_default_name
+ _krb5_have_debug
+ _krb5_SP800_108_HMAC_KDF
+ _krb5_get_ad
+
+ ; Shared with GSSAPI preauth wrapper
+ _krb5_init_creds_set_gss_mechanism
+ _krb5_init_creds_get_gss_mechanism
+ _krb5_init_creds_set_gss_cred
+ _krb5_init_creds_get_gss_cred
+ _krb5_init_creds_init_gss
+
+ ; Private init_creds API
+ _krb5_init_creds_get_cred_starttime
+ _krb5_init_creds_get_cred_endtime
+ _krb5_init_creds_get_cred_client
+
+ ; Shared with libkadm5
+ _krb5_load_plugins
+ _krb5_unload_plugins
+
+ ; Shared with libkdc
+ _krb5_AES_SHA1_string_to_default_iterator
+ _krb5_AES_SHA2_string_to_default_iterator
+ _krb5_dh_group_ok
+ _krb5_get_host_realm_int
+ _krb5_get_int
+ _krb5_get_int64
+ _krb5_pac_sign
+ _krb5_pac_get_attributes_info
+ _krb5_pac_get_canon_principal
+ _krb5_kdc_pac_sign_ticket
+ _krb5_kdc_pac_ticket_parse
+ _kdc_tkt_insert_pac
+ _kdc_tkt_add_if_relevant_ad
+ _krb5_parse_moduli
+ _krb5_pk_kdf
+ _krb5_pk_load_id
+ _krb5_pk_mk_ContentInfo
+ _krb5_pk_octetstring2key
+ _krb5_plugin_run_f
+ _krb5_enctype_requires_random_salt
+ _krb5_principal2principalname
+ _krb5_principalname2krb5_principal
+ _krb5_kdcrep2krb5_principal
+ _krb5_ticket2krb5_principal
+ _krb5_put_int
+ _krb5_s4u2self_to_checksumdata
+ _krb5_HMAC_MD5_checksum
+ _krb5_crypto_set_flags
+ _krb5_expand_path_tokens ;!
+ _krb5_make_pa_enc_challenge
+ _krb5_validate_pa_enc_challenge
+
+ ; kinit helper
+ krb5_get_init_creds_opt_set_pkinit_user_certs
+ krb5_pk_enterprise_cert
+
+ krb5_auth_con_getsendsubkey
+ krb5_init_creds_free
+ krb5_init_creds_get
+ krb5_init_creds_get_as_reply_key
+ krb5_init_creds_get_creds
+ krb5_init_creds_get_error
+ krb5_init_creds_init
+ krb5_init_creds_set_fast_anon_pkinit
+ _krb5_init_creds_set_fast_anon_pkinit_optimistic
+ krb5_init_creds_set_fast_ccache
+ krb5_init_creds_set_keytab
+ krb5_init_creds_set_kdc_hostname
+ krb5_init_creds_set_password
+ krb5_init_creds_set_service
+ krb5_init_creds_set_sitename
+ krb5_init_creds_step
+ krb5_init_creds_store
+ krb5_init_creds_store_config
+ krb5_init_creds_warn_user
+ krb5_process_last_request
+
+ ; testing
+;! _krb5_aes_cts_encrypt
+ krb5_time_abs;
+ _krb5_n_fold
+ _krb5_expand_default_cc_name
+
+ ; FAST
+ _krb5_fast_cf2
+ _krb5_fast_armor_key
+ _krb5_fast_explicit_armor_key
+
+ ; Recent additions
+ krb5_cc_type_dcc;
+ krb5_dcc_ops;
+ _krb5_expand_path_tokensv;
+ _krb5_find_capath;
+ _krb5_free_capath;
diff --git a/third_party/heimdal/lib/krb5/locate_plugin.h b/third_party/heimdal/lib/krb5/locate_plugin.h
new file mode 100644
index 0000000..7fcb5ec
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/locate_plugin.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 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.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_LOCATE_PLUGIN_H
+#define HEIMDAL_KRB5_LOCATE_PLUGIN_H 1
+
+#include <heimbase-svc.h>
+
+#define KRB5_PLUGIN_LOCATE "service_locator"
+#define KRB5_PLUGIN_LOCATE_VERSION 1
+#define KRB5_PLUGIN_LOCATE_VERSION_0 0
+#define KRB5_PLUGIN_LOCATE_VERSION_1 1
+#define KRB5_PLUGIN_LOCATE_VERSION_2 2
+
+enum locate_service_type {
+ locate_service_kdc = 1,
+ locate_service_master_kdc,
+ locate_service_kadmin,
+ locate_service_krb524,
+ locate_service_kpasswd
+};
+
+typedef krb5_error_code
+(KRB5_CALLCONV *krb5plugin_service_locate_lookup)
+ (void *, unsigned long, enum locate_service_type,
+ const char *, int, int,
+ int (*)(void *,int,struct sockaddr *),
+ void *);
+
+#define KRB5_PLF_ALLOW_HOMEDIR 1
+
+typedef krb5_error_code
+(KRB5_CALLCONV *krb5plugin_service_locate_lookup_old)
+ (void *, enum locate_service_type,
+ const char *, int, int,
+ int (*)(void *,int,struct sockaddr *),
+ void *);
+
+
+typedef struct krb5plugin_service_locate_ftable {
+ HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context);
+ krb5plugin_service_locate_lookup_old old_lookup;
+ krb5plugin_service_locate_lookup lookup; /* version 2 */
+} krb5plugin_service_locate_ftable;
+
+#endif /* HEIMDAL_KRB5_LOCATE_PLUGIN_H */
+
diff --git a/third_party/heimdal/lib/krb5/log.c b/third_party/heimdal/lib/krb5/log.c
new file mode 100644
index 0000000..306431a
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/log.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 1997-2006 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 <assert.h>
+#include <vis.h>
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_initlog(krb5_context context,
+ const char *program,
+ krb5_log_facility **fac)
+{
+ return heim_initlog(context->hcontext, program, fac);
+}
+
+struct krb5_addlog_func_wrapper {
+ krb5_context context;
+ krb5_log_log_func_t log_func;
+ krb5_log_close_func_t close_func;
+ void *data;
+};
+
+static void HEIM_CALLCONV
+krb5_addlog_func_wrapper_log(heim_context hcontext,
+ const char *prefix,
+ const char *msg,
+ void *data)
+{
+ struct krb5_addlog_func_wrapper *w = data;
+
+ w->log_func(w->context,
+ prefix,
+ msg,
+ w->data);
+}
+
+static void HEIM_CALLCONV
+krb5_addlog_func_wrapper_close(void *data)
+{
+ struct krb5_addlog_func_wrapper *w = data;
+
+ w->close_func(w->data);
+ free(w);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_addlog_func(krb5_context context,
+ krb5_log_facility *fac,
+ int min,
+ int max,
+ krb5_log_log_func_t log_func,
+ krb5_log_close_func_t close_func,
+ void *data)
+{
+ struct krb5_addlog_func_wrapper *w = NULL;
+
+ w = calloc(1, sizeof(*w));
+ if (w == NULL)
+ return krb5_enomem(context);
+
+ w->context = context;
+ w->log_func = log_func;
+ w->close_func = close_func;
+ w->data = data;
+
+ return heim_addlog_func(context->hcontext, fac, min, max,
+ krb5_addlog_func_wrapper_log,
+ krb5_addlog_func_wrapper_close,
+ w);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
+{
+ return heim_addlog_dest(context->hcontext, f, orig);
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_openlog(krb5_context context,
+ const char *program,
+ krb5_log_facility **fac)
+{
+ krb5_error_code ret;
+ char **p;
+
+ p = krb5_config_get_strings(context, NULL, "logging", program, NULL);
+ if (p == NULL)
+ p = krb5_config_get_strings(context, NULL, "logging", "default", NULL);
+ ret = heim_openlog(context->hcontext, program, (const char **)p, fac);
+ krb5_config_free_strings(p);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_closelog(krb5_context context,
+ krb5_log_facility *fac)
+{
+ heim_closelog(context->hcontext, fac);
+ return 0;
+}
+
+#undef __attribute__
+#define __attribute__(X)
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_vlog_msg(krb5_context context,
+ krb5_log_facility *fac,
+ char **reply,
+ int level,
+ const char *fmt,
+ va_list ap)
+ __attribute__ ((__format__ (__printf__, 5, 0)))
+{
+ return heim_vlog_msg(context->hcontext, fac, reply, level, fmt, ap);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_vlog(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ const char *fmt,
+ va_list ap)
+ __attribute__ ((__format__ (__printf__, 4, 0)))
+{
+ return heim_vlog_msg(context->hcontext, fac, NULL, level, fmt, ap);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_log_msg(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ char **reply,
+ const char *fmt,
+ ...)
+ __attribute__ ((__format__ (__printf__, 5, 6)))
+{
+ va_list ap;
+ krb5_error_code ret;
+
+ va_start(ap, fmt);
+ ret = heim_vlog_msg(context->hcontext, fac, reply, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_log(krb5_context context,
+ krb5_log_facility *fac,
+ int level,
+ const char *fmt,
+ ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)))
+{
+ va_list ap;
+ krb5_error_code ret;
+
+ va_start(ap, fmt);
+ ret = heim_vlog(context->hcontext, fac, level, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+void KRB5_LIB_FUNCTION
+_krb5_debug(krb5_context context,
+ int level,
+ const char *fmt,
+ ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (context && context->hcontext)
+ heim_vdebug(context->hcontext, level, fmt, ap);
+ va_end(ap);
+}
+
+void KRB5_LIB_FUNCTION
+krb5_debug(krb5_context context,
+ int level,
+ const char *fmt,
+ ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (context && context->hcontext)
+ heim_vdebug(context->hcontext, level, fmt, ap);
+ va_end(ap);
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_have_debug(krb5_context context, int level)
+{
+ if (context == NULL || context->hcontext == NULL)
+ return 0;
+ return heim_have_debug(context->hcontext, level);
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_have_debug(krb5_context context, int level)
+{
+ return _krb5_have_debug(context, level);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_debug_dest(krb5_context context, const char *program,
+ const char *log_spec)
+{
+ return heim_add_debug_dest(context->hcontext, program, log_spec);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_log_dest(krb5_context context, krb5_log_facility *fac)
+{
+ return heim_set_log_dest(context->hcontext, fac);
+}
diff --git a/third_party/heimdal/lib/krb5/mcache.c b/third_party/heimdal/lib/krb5/mcache.c
new file mode 100644
index 0000000..fdd5674
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mcache.c
@@ -0,0 +1,623 @@
+/*
+ * 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"
+
+typedef struct krb5_mcache {
+ char *name;
+ unsigned int refcnt;
+ unsigned int anonymous:1;
+ unsigned int dead:1;
+ krb5_principal primary_principal;
+ struct link {
+ krb5_creds cred;
+ struct link *next;
+ } *creds;
+ struct krb5_mcache *next;
+ time_t mtime;
+ krb5_deltat kdc_offset;
+ HEIMDAL_MUTEX mutex;
+} krb5_mcache;
+
+static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
+static struct krb5_mcache *mcc_head;
+
+#define MCACHE(X) ((krb5_mcache *)(X)->data.data)
+
+#define MISDEAD(X) ((X)->dead)
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_name_2(krb5_context context,
+ krb5_ccache id,
+ const char **name,
+ const char **col,
+ const char **sub)
+{
+ if (name)
+ *name = MCACHE(id)->name;
+ if (col)
+ *col = NULL;
+ if (sub)
+ *sub = MCACHE(id)->name;
+ return 0;
+}
+
+static krb5_error_code
+mcc_alloc(krb5_context context, const char *name, krb5_mcache **out)
+{
+ krb5_mcache *m, *m_c;
+ size_t counter = 0;
+ int ret = 0;
+ unsigned create_anonymous = 0;
+
+ *out = NULL;
+ ALLOC(m, 1);
+ if(m == NULL)
+ return krb5_enomem(context);
+
+again:
+ if (counter > 3) {
+ free(m->name);
+ free(m);
+ return EAGAIN; /* XXX */
+ }
+ if(name == NULL) {
+ ret = asprintf(&m->name, "u%p-%llu", m, (unsigned long long)counter);
+ } else if (strcmp(name, "anonymous") == 0) {
+ ret = asprintf(&m->name, "anonymous-%p-%llu", m, (unsigned long long)counter);
+ create_anonymous = 1;
+ } else {
+ m->name = strdup(name);
+ }
+ if(ret < 0 || m->name == NULL) {
+ free(m);
+ return krb5_enomem(context);
+ }
+
+ /* check for dups first */
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for (m_c = mcc_head; m_c != NULL; m_c = m_c->next)
+ if (strcmp(m->name, m_c->name) == 0)
+ break;
+ if (m_c) {
+ if (name && !create_anonymous) {
+ /* We raced with another thread to create this cache */
+ free(m->name);
+ free(m);
+ m = m_c;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ m->refcnt++;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ } else {
+ /* How likely are we to conflict on new_unique anyways?? */
+ counter++;
+ free(m->name);
+ m->name = NULL;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ goto again;
+ }
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ *out = m;
+ return 0;
+ }
+
+ m->anonymous = create_anonymous;
+ m->dead = 0;
+ m->refcnt = 1;
+ m->primary_principal = NULL;
+ m->creds = NULL;
+ m->mtime = time(NULL);
+ m->kdc_offset = 0;
+ m->next = mcc_head;
+ HEIMDAL_MUTEX_init(&(m->mutex));
+ mcc_head = m;
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ *out = m;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_resolve_2(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
+{
+ krb5_error_code ret;
+ krb5_mcache *m;
+
+ if ((ret = mcc_alloc(context, sub && *sub ? sub : res, &m)))
+ return ret;
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+
+static krb5_error_code KRB5_CALLCONV
+mcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ krb5_mcache *m;
+
+ if ((ret = mcc_alloc(context, NULL, &m)))
+ return ret;
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+static void KRB5_CALLCONV
+mcc_destroy_internal(krb5_context context,
+ krb5_mcache *m)
+{
+ struct link *l;
+
+ if (m->primary_principal != NULL) {
+ krb5_free_principal (context, m->primary_principal);
+ m->primary_principal = NULL;
+ }
+ m->dead = 1;
+
+ l = m->creds;
+ while (l != NULL) {
+ struct link *old;
+
+ krb5_free_cred_contents (context, &l->cred);
+ old = l;
+ l = l->next;
+ free (old);
+ }
+
+ m->creds = NULL;
+ return;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret = 0;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ heim_assert(m->refcnt != 0, "resurection released mcache");
+ /*
+ * It's important to destroy any existing
+ * creds here, that matches the baheviour
+ * of all other backends and also the
+ * MEMORY: backend in MIT.
+ */
+ mcc_destroy_internal(context, m);
+ m->dead = 0;
+ m->kdc_offset = 0;
+ m->mtime = time(NULL);
+ ret = krb5_copy_principal (context,
+ primary_principal,
+ &m->primary_principal);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ret;
+}
+
+static int
+mcc_close_internal(krb5_mcache *m)
+{
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ heim_assert(m->refcnt != 0, "closed dead cache mcache");
+ if (--m->refcnt != 0) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+ }
+ if (MISDEAD(m)) {
+ free(m->name);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 1;
+ }
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_close(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_mcache *m = MCACHE(id);
+
+ if (mcc_close_internal(MCACHE(id))) {
+ HEIMDAL_MUTEX_destroy(&(m->mutex));
+ krb5_data_free(&id->data);
+ }
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_mcache **n, *m = MCACHE(id);
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (m->refcnt == 0)
+ {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ krb5_abortx(context, "mcc_destroy: refcnt already 0");
+ }
+
+ if (!MISDEAD(m)) {
+ /* if this is an active mcache, remove it from the linked
+ list, and free all data */
+ for(n = &mcc_head; n && *n; n = &(*n)->next) {
+ if(m == *n) {
+ *n = m->next;
+ break;
+ }
+ }
+ mcc_destroy_internal(context, m);
+ }
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret;
+ struct link *l;
+
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m))
+ {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ENOENT;
+ }
+
+ l = malloc (sizeof(*l));
+ if (l == NULL)
+ return krb5_enomem(context);
+ l->next = m->creds;
+ m->creds = l;
+ memset (&l->cred, 0, sizeof(l->cred));
+ ret = krb5_copy_creds_contents (context, creds, &l->cred);
+ if (ret) {
+ m->creds = l->next;
+ free (l);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ret;
+ }
+ m->mtime = time(NULL);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret = 0;
+
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m) || m->primary_principal == NULL) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ENOENT;
+ }
+ ret = krb5_copy_principal (context,
+ m->primary_principal,
+ principal);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_mcache *m = MCACHE(id);
+
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m)) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ENOENT;
+ }
+ *cursor = m->creds;
+
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_mcache *m = MCACHE(id);
+ struct link *l;
+
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m)) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ENOENT;
+ }
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+
+ l = *cursor;
+ if (l != NULL) {
+ *cursor = l->next;
+ return krb5_copy_creds_contents (context,
+ &l->cred,
+ creds);
+ } else
+ return KRB5_CC_END;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *mcreds)
+{
+ krb5_mcache *m = MCACHE(id);
+ struct link **q, *p;
+
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+
+ for(q = &m->creds, p = *q; p; p = *q) {
+ if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
+ *q = p->next;
+ krb5_free_cred_contents(context, &p->cred);
+ free(p);
+ m->mtime = time(NULL);
+ } else
+ q = &p->next;
+ }
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0; /* XXX */
+}
+
+struct mcache_iter {
+ krb5_mcache *cache;
+};
+
+static krb5_mcache *
+mcc_get_cache_find_next_internal(krb5_mcache *next)
+{
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+ for (; next != NULL && next->anonymous; next = next->next) {
+ /* noop: iterate over all anonymous entries */
+ }
+ if (next != NULL) {
+ HEIMDAL_MUTEX_lock(&(next->mutex));
+ next->refcnt++;
+ HEIMDAL_MUTEX_unlock(&(next->mutex));
+ next = next->next;
+ }
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ return next;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct mcache_iter *iter;
+
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL)
+ return krb5_enomem(context);
+
+ iter->cache = mcc_get_cache_find_next_internal(mcc_head);
+
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct mcache_iter *iter = cursor;
+ krb5_error_code ret;
+ krb5_mcache *m;
+
+ if (iter->cache == NULL)
+ return KRB5_CC_END;
+
+ m = iter->cache;
+ iter->cache = mcc_get_cache_find_next_internal(m);
+
+ ret = _krb5_cc_allocate(context, &krb5_mcc_ops, id);
+ if (ret)
+ return ret;
+
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct mcache_iter *iter = cursor;
+
+ if (iter->cache)
+ mcc_close_internal(iter->cache);
+ iter->cache = NULL;
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_mcache *mfrom = MCACHE(from), *mto = MCACHE(to);
+ struct link *creds;
+ krb5_principal principal;
+ krb5_mcache **n;
+
+ HEIMDAL_MUTEX_lock(&mcc_mutex);
+
+ /* drop the from cache from the linked list to avoid lookups */
+ for(n = &mcc_head; n && *n; n = &(*n)->next) {
+ if(mfrom == *n) {
+ *n = mfrom->next;
+ break;
+ }
+ }
+
+ HEIMDAL_MUTEX_lock(&(mfrom->mutex));
+ HEIMDAL_MUTEX_lock(&(mto->mutex));
+ /* swap creds */
+ creds = mto->creds;
+ mto->creds = mfrom->creds;
+ mfrom->creds = creds;
+ /* swap principal */
+ principal = mto->primary_principal;
+ mto->primary_principal = mfrom->primary_principal;
+ mfrom->primary_principal = principal;
+
+ mto->mtime = mfrom->mtime = time(NULL);
+
+ HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
+ HEIMDAL_MUTEX_unlock(&(mto->mutex));
+ HEIMDAL_MUTEX_unlock(&mcc_mutex);
+
+ krb5_cc_destroy(context, from);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_default_name(krb5_context context, char **str)
+{
+ *str = strdup("MEMORY:");
+ if (*str == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+ krb5_mcache *m = MCACHE(id);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ *mtime = m->mtime;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
+{
+ krb5_mcache *m = MCACHE(id);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ m->kdc_offset = kdc_offset;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
+{
+ krb5_mcache *m = MCACHE(id);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ *kdc_offset = m->kdc_offset;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return 0;
+}
+
+
+/**
+ * Variable containing the MEMORY based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops = {
+ KRB5_CC_OPS_VERSION_5,
+ "MEMORY",
+ NULL,
+ NULL,
+ mcc_gen_new,
+ mcc_initialize,
+ mcc_destroy,
+ mcc_close,
+ mcc_store_cred,
+ NULL, /* mcc_retrieve */
+ mcc_get_principal,
+ mcc_get_first,
+ mcc_get_next,
+ mcc_end_get,
+ mcc_remove_cred,
+ mcc_set_flags,
+ NULL,
+ mcc_get_cache_first,
+ mcc_get_cache_next,
+ mcc_end_cache_get,
+ mcc_move,
+ mcc_default_name,
+ NULL,
+ mcc_lastchange,
+ mcc_set_kdc_offset,
+ mcc_get_kdc_offset,
+ mcc_get_name_2,
+ mcc_resolve_2
+};
diff --git a/third_party/heimdal/lib/krb5/misc.c b/third_party/heimdal/lib/krb5/misc.c
new file mode 100644
index 0000000..baf165c
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/misc.c
@@ -0,0 +1,111 @@
+/*
+ * 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"
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_s4u2self_to_checksumdata(krb5_context context,
+ const PA_S4U2Self *self,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t ssize;
+ krb5_storage *sp;
+ size_t size;
+ size_t i;
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return krb5_enomem(context);
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+ ret = krb5_store_int32(sp, self->name.name_type);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ for (i = 0; i < self->name.name_string.len; i++) {
+ size = strlen(self->name.name_string.val[i]);
+ ssize = krb5_storage_write(sp, self->name.name_string.val[i], size);
+ if (ssize != (krb5_ssize_t)size)
+ return krb5_enomem(context);
+ }
+ size = strlen(self->realm);
+ ssize = krb5_storage_write(sp, self->realm, size);
+ if (ssize != (krb5_ssize_t)size)
+ return krb5_enomem(context);
+ size = strlen(self->auth);
+ ssize = krb5_storage_write(sp, self->auth, size);
+ if (ssize != (krb5_ssize_t)size)
+ return krb5_enomem(context);
+
+ ret = krb5_storage_to_data(sp, data);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_debug_backtrace(krb5_context context)
+{
+#if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) && !defined(HEIMDAL_SMALLER)
+ void *stack[128];
+ char **strs = NULL;
+ int i, frames = backtrace(stack, sizeof(stack) / sizeof(stack[0]));
+ if (frames > 0)
+ strs = backtrace_symbols(stack, frames);
+ if (strs) {
+ for (i = 0; i < frames; i++)
+ _krb5_debug(context, 10, "frame %d: %s", i, strs[i]);
+ free(strs);
+ }
+#endif
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_einval(krb5_context context, const char *func, unsigned long argn)
+{
+#ifndef HEIMDAL_SMALLER
+ krb5_set_error_message(context, EINVAL,
+ N_("programmer error: invalid argument to %s argument %lu",
+ "function:line"),
+ func, argn);
+ if (_krb5_have_debug(context, 10)) {
+ _krb5_debug(context, 10, "invalid argument to function %s argument %lu",
+ func, argn);
+ _krb5_debug_backtrace(context);
+ }
+#endif
+ return EINVAL;
+}
diff --git a/third_party/heimdal/lib/krb5/mit_glue.c b/third_party/heimdal/lib/krb5/mit_glue.c
new file mode 100644
index 0000000..44725f1
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mit_glue.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#ifndef HEIMDAL_SMALLER
+
+/*
+ * Glue for MIT API
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_make_checksum(krb5_context context,
+ krb5_cksumtype cksumtype,
+ const krb5_keyblock *key,
+ krb5_keyusage usage,
+ const krb5_data *input,
+ krb5_checksum *cksum)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_create_checksum(context, crypto, usage, cksumtype,
+ input->data, input->length, cksum);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_verify_checksum(krb5_context context, const krb5_keyblock *key,
+ krb5_keyusage usage, const krb5_data *data,
+ const krb5_checksum *cksum, krb5_boolean *valid)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ *valid = 0;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_verify_checksum(context, crypto, usage,
+ data->data, data->length, rk_UNCONST(cksum));
+ krb5_crypto_destroy(context, crypto);
+
+ if (ret == 0) {
+ *valid = 1;
+ }
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_get_checksum(krb5_context context, const krb5_checksum *cksum,
+ krb5_cksumtype *type, krb5_data **data)
+{
+ krb5_error_code ret;
+
+ if (type)
+ *type = cksum->cksumtype;
+ if (data) {
+ *data = malloc(sizeof(**data));
+ if (*data == NULL)
+ return krb5_enomem(context);
+
+ ret = der_copy_octet_string(&cksum->checksum, *data);
+ if (ret) {
+ free(*data);
+ *data = NULL;
+ return ret;
+ }
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_set_checksum(krb5_context context, krb5_checksum *cksum,
+ krb5_cksumtype type, const krb5_data *data)
+{
+ cksum->cksumtype = type;
+ return der_copy_octet_string(data, &cksum->checksum);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_checksum (krb5_context context, krb5_checksum *cksum)
+{
+ krb5_checksum_free(context, cksum);
+ free(cksum);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_checksum_contents(krb5_context context, krb5_checksum *cksum)
+{
+ krb5_checksum_free(context, cksum);
+ memset(cksum, 0, sizeof(*cksum));
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_checksum_free(krb5_context context, krb5_checksum *cksum)
+{
+ free_Checksum(cksum);
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_c_valid_enctype (krb5_enctype etype)
+{
+ return !krb5_enctype_valid(NULL, etype);
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_c_valid_cksumtype(krb5_cksumtype ctype)
+{
+ return krb5_cksumtype_valid(NULL, ctype);
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_c_is_coll_proof_cksum(krb5_cksumtype ctype)
+{
+ return krb5_checksum_is_collision_proof(NULL, ctype);
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_c_is_keyed_cksum(krb5_cksumtype ctype)
+{
+ return krb5_checksum_is_keyed(NULL, ctype);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_copy_checksum (krb5_context context,
+ const krb5_checksum *old,
+ krb5_checksum **new)
+{
+ *new = malloc(sizeof(**new));
+ if (*new == NULL)
+ return krb5_enomem(context);
+ return copy_Checksum(old, *new);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_checksum_length (krb5_context context, krb5_cksumtype cksumtype,
+ size_t *length)
+{
+ return krb5_checksumsize(context, cksumtype, length);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_block_size(krb5_context context,
+ krb5_enctype enctype,
+ size_t *blocksize)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_keyblock key;
+
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret)
+ return ret;
+ ret = krb5_crypto_getblocksize(context, crypto, blocksize);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_decrypt(krb5_context context,
+ const krb5_keyblock key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ krb5_enc_data *input,
+ krb5_data *output)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, &key, input->enctype, &crypto);
+ if (ret)
+ return ret;
+
+ if (ivec) {
+ size_t blocksize;
+
+ ret = krb5_crypto_getblocksize(context, crypto, &blocksize);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+ }
+
+ if (blocksize > ivec->length) {
+ krb5_crypto_destroy(context, crypto);
+ return KRB5_BAD_MSIZE;
+ }
+ }
+
+ ret = krb5_decrypt_ivec(context, crypto, usage,
+ input->ciphertext.data, input->ciphertext.length,
+ output,
+ ivec ? ivec->data : NULL);
+
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_encrypt(krb5_context context,
+ const krb5_keyblock *key,
+ krb5_keyusage usage,
+ const krb5_data *ivec,
+ const krb5_data *input,
+ krb5_enc_data *output)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ if (ivec) {
+ size_t blocksize;
+
+ ret = krb5_crypto_getblocksize(context, crypto, &blocksize);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ return ret;
+ }
+
+ if (blocksize > ivec->length) {
+ krb5_crypto_destroy(context, crypto);
+ return KRB5_BAD_MSIZE;
+ }
+ }
+
+ ret = krb5_encrypt_ivec(context, crypto, usage,
+ input->data, input->length,
+ &output->ciphertext,
+ ivec ? ivec->data : NULL);
+ output->kvno = 0;
+ krb5_crypto_getenctype(context, crypto, &output->enctype);
+
+ krb5_crypto_destroy(context, crypto);
+
+ return ret ;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_encrypt_length(krb5_context context,
+ krb5_enctype enctype,
+ size_t inputlen,
+ size_t *length)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_keyblock key;
+
+ ret = krb5_generate_random_keyblock(context, enctype, &key);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ krb5_free_keyblock_contents(context, &key);
+ if (ret)
+ return ret;
+
+ *length = krb5_get_wrapped_length(context, crypto, inputlen);
+ krb5_crypto_destroy(context, crypto);
+
+ return 0;
+}
+
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_enctype_compare(krb5_context context,
+ krb5_enctype e1,
+ krb5_enctype e2,
+ krb5_boolean *similar)
+ KRB5_DEPRECATED_FUNCTION("Use X instead")
+{
+ *similar = (e1 == e2);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_make_random_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_keyblock *random_key)
+{
+ return krb5_generate_random_keyblock(context, enctype, random_key);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_keylengths(krb5_context context,
+ krb5_enctype enctype,
+ size_t *ilen,
+ size_t *keylen)
+{
+ krb5_error_code ret;
+
+ ret = krb5_enctype_keybits(context, enctype, ilen);
+ if (ret)
+ return ret;
+ *ilen = (*ilen + 7) / 8;
+ return krb5_enctype_keysize(context, enctype, keylen);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_prf_length(krb5_context context,
+ krb5_enctype type,
+ size_t *length)
+{
+ return krb5_crypto_prf_length(context, type, length);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_prf(krb5_context context,
+ const krb5_keyblock *key,
+ const krb5_data *input,
+ krb5_data *output)
+{
+ krb5_crypto crypto;
+ krb5_error_code ret;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_prf(context, crypto, input, output);
+ krb5_crypto_destroy(context, crypto);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_c_random_make_octets(krb5_context context, krb5_data * data)
+{
+ krb5_generate_random_block(data->data, data->length);
+ return 0;
+}
+
+/**
+ * MIT compat glue
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_cc_copy_creds(krb5_context context,
+ const krb5_ccache from,
+ krb5_ccache to)
+{
+ return krb5_cc_copy_cache(context, from, to);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_auth_con_getsendsubkey(krb5_context context, krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return krb5_auth_con_getlocalsubkey(context, auth_context, keyblock);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_auth_con_getrecvsubkey(krb5_context context, krb5_auth_context auth_context,
+ krb5_keyblock **keyblock)
+{
+ return krb5_auth_con_getremotesubkey(context, auth_context, keyblock);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_auth_con_setsendsubkey(krb5_context context, krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ return krb5_auth_con_setlocalsubkey(context, auth_context, keyblock);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_auth_con_setrecvsubkey(krb5_context context, krb5_auth_context auth_context,
+ krb5_keyblock *keyblock)
+{
+ return krb5_auth_con_setremotesubkey(context, auth_context, keyblock);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_free_default_realm(krb5_context context, krb5_realm realm)
+{
+ return krb5_xfree(realm);
+}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/third_party/heimdal/lib/krb5/mk_cred.c b/third_party/heimdal/lib/krb5/mk_cred.c
new file mode 100644
index 0000000..41e858f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_cred.c
@@ -0,0 +1,324 @@
+/*
+ * 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"
+
+#define CHECKED_ALLOC(dst) do { \
+ if ((ALLOC(dst, 1)) == NULL) { \
+ ret = krb5_enomem(context); \
+ goto out; \
+ } \
+ } while (0)
+
+#define CHECKED_COPY(cp_func, dst, src) do { \
+ if (cp_func(src, dst)) { \
+ ret = krb5_enomem(context); \
+ goto out; \
+ } \
+ } while (0)
+#define CHECKED_COPY_PPC2KCI(cp_func, dst, src) \
+ CHECKED_COPY(cp_func, krb_cred_info->dst, &ppcreds[i]->src)
+
+#define CHECKED_ALLOC_ASSIGN(dst, src) do { \
+ if ((ALLOC(dst, 1)) == NULL) { \
+ ret = krb5_enomem(context); \
+ goto out; \
+ } else \
+ *dst = src; \
+ } while (0)
+#define CHECKED_ALLOC_ASSIGN_PPC2KCI(dst, src) \
+ CHECKED_ALLOC_ASSIGN(krb_cred_info->dst, ppcreds[i]->src)
+
+#define CHECKED_ALLOC_COPY(cp_func, dst, src) do { \
+ if ((ALLOC(dst, 1)) == NULL || cp_func(src, dst)) { \
+ ret = krb5_enomem(context); \
+ goto out; \
+ } \
+ } while (0)
+#define CHECKED_ALLOC_COPY_PPC2KCI(cp_func, dst, src) \
+ CHECKED_ALLOC_COPY(cp_func, krb_cred_info->dst, &ppcreds[i]->src)
+
+/**
+ * Make a KRB-CRED PDU with N credentials.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context The auth context with the key to encrypt the out_data.
+ * @param ppcreds A null-terminated array of credentials to forward.
+ * @param ppdata The output KRB-CRED (to be freed by caller).
+ * @param replay_data (unused).
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
+/* ARGSUSED */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_ncred(krb5_context context, krb5_auth_context auth_context,
+ krb5_creds **ppcreds, krb5_data **ppdata,
+ krb5_replay_data *replay_data)
+{
+ krb5_error_code ret;
+ krb5_data out_data;
+
+ ret = _krb5_mk_ncred(context, auth_context, ppcreds, &out_data,
+ replay_data);
+ if (ret == 0) {
+ /*
+ * MIT allocates the return structure for no good reason. We do
+ * likewise as, in this case, incompatibility is the greater evil.
+ */
+ *ppdata = calloc(1, sizeof(**ppdata));
+ if (*ppdata) {
+ **ppdata = out_data;
+ } else {
+ krb5_data_free(&out_data);
+ ret = krb5_enomem(context);
+ }
+ }
+
+ return ret;
+}
+
+/* ARGSUSED */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_mk_ncred(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_creds **ppcreds,
+ krb5_data *out_data,
+ krb5_replay_data *replay_data)
+{
+ krb5_error_code ret;
+ EncKrbCredPart enc_krb_cred_part;
+ KrbCredInfo *krb_cred_info;
+ krb5_crypto crypto;
+ KRB_CRED cred;
+ unsigned char *buf = NULL;
+ size_t ncreds, i;
+ size_t buf_size;
+ size_t len;
+
+ /*
+ * The ownership of 'buf' is re-assigned to a containing structure
+ * multiple times. We enforce an invariant, either buf is non-zero
+ * and we own it, or buf is zero and it is freed or some structure
+ * owns any storage previously allocated as 'buf'.
+ */
+#define CHOWN_BUF(x, buf) do { (x) = (buf); (buf) = 0; } while (0)
+#define DISOWN_BUF(buf) do { free(buf); (buf) = 0; } while (0)
+
+ for (ncreds = 0; ppcreds[ncreds]; ncreds++)
+ ;
+
+ memset (&cred, 0, sizeof(cred));
+ memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
+ cred.pvno = 5;
+ cred.msg_type = krb_cred;
+ ALLOC_SEQ(&cred.tickets, ncreds);
+ if (cred.tickets.val == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ALLOC_SEQ(&enc_krb_cred_part.ticket_info, ncreds);
+ if (enc_krb_cred_part.ticket_info.val == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ for (i = 0; i < ncreds; i++) {
+ ret = decode_Ticket(ppcreds[i]->ticket.data,
+ ppcreds[i]->ticket.length,
+ &cred.tickets.val[i],
+ &len);/* don't care about len */
+ if (ret)
+ goto out;
+
+ /* fill ticket_info.val[i] */
+ krb_cred_info = &enc_krb_cred_part.ticket_info.val[i];
+
+ CHECKED_COPY(copy_EncryptionKey,
+ &krb_cred_info->key, &ppcreds[i]->session);
+ CHECKED_ALLOC_COPY_PPC2KCI(copy_Realm, prealm, client->realm);
+ CHECKED_ALLOC_COPY_PPC2KCI(copy_PrincipalName, pname, client->name);
+ CHECKED_ALLOC_ASSIGN_PPC2KCI(flags, flags.b);
+ CHECKED_ALLOC_ASSIGN_PPC2KCI(authtime, times.authtime);
+ CHECKED_ALLOC_ASSIGN_PPC2KCI(starttime, times.starttime);
+ CHECKED_ALLOC_ASSIGN_PPC2KCI(endtime, times.endtime);
+ CHECKED_ALLOC_ASSIGN_PPC2KCI(renew_till, times.renew_till);
+ CHECKED_ALLOC_COPY_PPC2KCI(copy_Realm, srealm, server->realm);
+ CHECKED_ALLOC_COPY_PPC2KCI(copy_PrincipalName, sname, server->name);
+ CHECKED_ALLOC_COPY_PPC2KCI(copy_HostAddresses, caddr, addresses);
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+ int32_t usec;
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ CHECKED_ALLOC_ASSIGN(enc_krb_cred_part.timestamp, sec);
+ CHECKED_ALLOC_ASSIGN(enc_krb_cred_part.usec, usec);
+ } else {
+ enc_krb_cred_part.timestamp = NULL;
+ enc_krb_cred_part.usec = NULL;
+ /* XXX Er, shouldn't we set the seq nums?? */
+ }
+
+ /* XXX: Is this needed? */
+ if (auth_context->local_address && auth_context->local_port) {
+ ret = krb5_make_addrport(context,
+ &enc_krb_cred_part.s_address,
+ auth_context->local_address,
+ auth_context->local_port);
+ if (ret)
+ goto out;
+ }
+
+ /* XXX: Is this needed? */
+ if (auth_context->remote_address) {
+ if (auth_context->remote_port) {
+ /*
+ * XXX: Should we be checking "no-addresses" for
+ * the receiving realm?
+ */
+ ret = krb5_make_addrport(context,
+ &enc_krb_cred_part.r_address,
+ auth_context->remote_address,
+ auth_context->remote_port);
+ if (ret)
+ goto out;
+ } else {
+ /*
+ * XXX Ugly, make krb5_make_addrport() handle missing port
+ * number (i.e., port == 0), then remove this else.
+ */
+ CHECKED_ALLOC(enc_krb_cred_part.r_address);
+ ret = krb5_copy_address(context, auth_context->remote_address,
+ enc_krb_cred_part.r_address);
+ if (ret)
+ goto out;
+ }
+ }
+
+ /* encode EncKrbCredPart */
+ ASN1_MALLOC_ENCODE(EncKrbCredPart, buf, buf_size,
+ &enc_krb_cred_part, &len, ret);
+ if (ret)
+ goto out;
+
+ /**
+ * Some older of the MIT gssapi library used clear-text tickets
+ * (warped inside AP-REQ encryption), use the krb5_auth_context
+ * flag KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED to support those
+ * tickets. The session key is used otherwise to encrypt the
+ * forwarded ticket.
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
+ cred.enc_part.etype = KRB5_ENCTYPE_NULL;
+ cred.enc_part.kvno = NULL;
+ CHOWN_BUF(cred.enc_part.cipher.data, buf);
+ cred.enc_part.cipher.length = buf_size;
+ } else {
+ /*
+ * Here older versions then 0.7.2 of Heimdal used the local or
+ * remote subkey. That is wrong, the session key should be
+ * used. Heimdal 0.7.2 and newer have code to try both in the
+ * receiving end.
+ */
+
+ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
+ if (ret == 0)
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ buf,
+ len,
+ 0,
+ &cred.enc_part);
+ if (ret)
+ goto out;
+ DISOWN_BUF(buf);
+ krb5_crypto_destroy(context, crypto);
+ }
+
+ ASN1_MALLOC_ENCODE(KRB_CRED, buf, buf_size, &cred, &len, ret);
+ if (ret)
+ goto out;
+
+ CHOWN_BUF(out_data->data, buf);
+ out_data->length = len;
+ ret = 0;
+
+ out:
+ free_EncKrbCredPart(&enc_krb_cred_part);
+ free_KRB_CRED(&cred);
+ free(buf);
+ return ret;
+}
+
+/**
+ * Make a KRB-CRED PDU with 1 credential.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context The auth context with the key to encrypt the out_data.
+ * @param ppcred A credential to forward.
+ * @param ppdata The output KRB-CRED (to be freed by caller).
+ * @param replay_data (unused).
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
+/* ARGSUSED */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
+ krb5_creds *ppcred, krb5_data **ppdata,
+ krb5_replay_data *replay_data)
+{
+ krb5_creds *ppcreds[2] = { ppcred, NULL };
+
+ return krb5_mk_ncred(context, auth_context, ppcreds, ppdata, replay_data);
+}
+
+/* ARGSUSED */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_mk_1cred(krb5_context context, krb5_auth_context auth_context,
+ krb5_creds *ppcred, krb5_data *ppdata,
+ krb5_replay_data *replay_data)
+{
+ krb5_creds *ppcreds[2] = { ppcred, NULL };
+
+ return _krb5_mk_ncred(context, auth_context, ppcreds, ppdata, replay_data);
+}
diff --git a/third_party/heimdal/lib/krb5/mk_error.c b/third_party/heimdal/lib/krb5/mk_error.c
new file mode 100644
index 0000000..3791fe7
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_error.c
@@ -0,0 +1,117 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_error_ext(krb5_context context,
+ krb5_error_code error_code,
+ const char *e_text,
+ const krb5_data *e_data,
+ const krb5_principal server,
+ const PrincipalName *client_name,
+ const Realm *client_realm,
+ time_t *client_time,
+ int *client_usec,
+ krb5_data *reply)
+{
+ const char *e_text2 = NULL;
+ KRB_ERROR msg;
+ krb5_timestamp sec;
+ int32_t usec;
+ size_t len = 0;
+ krb5_error_code ret = 0;
+
+ krb5_us_timeofday (context, &sec, &usec);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.pvno = 5;
+ msg.msg_type = krb_error;
+ msg.stime = sec;
+ msg.susec = usec;
+ msg.ctime = client_time;
+ msg.cusec = client_usec;
+ /* Make sure we only send `protocol' error codes */
+ if(error_code < KRB5KDC_ERR_NONE || error_code >= KRB5_ERR_RCSID) {
+ if(e_text == NULL)
+ e_text = e_text2 = krb5_get_error_message(context, error_code);
+ error_code = KRB5KRB_ERR_GENERIC;
+ }
+ msg.error_code = error_code - KRB5KDC_ERR_NONE;
+ if (e_text)
+ msg.e_text = rk_UNCONST(&e_text);
+ if (e_data)
+ msg.e_data = rk_UNCONST(e_data);
+ if(server){
+ msg.realm = server->realm;
+ msg.sname = server->name;
+ }else{
+ static const char unspec[] = "<unspecified realm>";
+ msg.realm = rk_UNCONST(unspec);
+ }
+ msg.crealm = rk_UNCONST(client_realm);
+ msg.cname = rk_UNCONST(client_name);
+
+ ASN1_MALLOC_ENCODE(KRB_ERROR, reply->data, reply->length, &msg, &len, ret);
+ if (e_text2)
+ krb5_free_error_message(context, e_text2);
+ if (ret)
+ return ret;
+ if(reply->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_error(krb5_context context,
+ krb5_error_code error_code,
+ const char *e_text,
+ const krb5_data *e_data,
+ const krb5_principal client,
+ const krb5_principal server,
+ time_t *client_time,
+ int *client_usec,
+ krb5_data *reply)
+{
+ const PrincipalName *client_name = NULL;
+ const Realm *client_realm = NULL;
+
+ if (client) {
+ client_realm = &client->realm;
+ client_name = &client->name;
+ }
+
+ return krb5_mk_error_ext(context, error_code, e_text, e_data,
+ server, client_name, client_realm,
+ client_time, client_usec, reply);
+}
diff --git a/third_party/heimdal/lib/krb5/mk_priv.c b/third_party/heimdal/lib/krb5/mk_priv.c
new file mode 100644
index 0000000..e3b2f91
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_priv.c
@@ -0,0 +1,151 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_priv(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *userdata,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_PRIV s;
+ EncKrbPrivPart part;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len = 0;
+ krb5_crypto crypto;
+ krb5_keyblock *key;
+ krb5_replay_data rdata;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else
+ key = auth_context->keyblock;
+
+ memset(&rdata, 0, sizeof(rdata));
+
+ part.user_data = *userdata;
+
+ krb5_us_timeofday (context, &rdata.timestamp, &rdata.usec);
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ part.timestamp = &rdata.timestamp;
+ part.usec = &rdata.usec;
+ } else {
+ part.timestamp = NULL;
+ part.usec = NULL;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_TIME) {
+ outdata->timestamp = rdata.timestamp;
+ outdata->usec = rdata.usec;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ rdata.seq = auth_context->local_seqnumber;
+ part.seq_number = &rdata.seq;
+ } else
+ part.seq_number = NULL;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
+ outdata->seq = auth_context->local_seqnumber;
+
+ part.s_address = auth_context->local_address;
+ part.r_address = auth_context->remote_address;
+
+ krb5_data_zero (&s.enc_part.cipher);
+
+ ASN1_MALLOC_ENCODE(EncKrbPrivPart, buf, buf_size, &part, &len, ret);
+ if (ret)
+ goto fail;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ s.pvno = 5;
+ s.msg_type = krb_priv;
+ s.enc_part.etype = key->keytype;
+ s.enc_part.kvno = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+ ret = krb5_encrypt (context,
+ crypto,
+ KRB5_KU_KRB_PRIV,
+ buf + buf_size - len,
+ len,
+ &s.enc_part.cipher);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free(buf);
+ return ret;
+ }
+ free(buf);
+
+
+ ASN1_MALLOC_ENCODE(KRB_PRIV, buf, buf_size, &s, &len, ret);
+ if (ret)
+ goto fail;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ krb5_data_free (&s.enc_part.cipher);
+
+ ret = krb5_data_copy(outbuf, buf + buf_size - len, len);
+ if (ret) {
+ free(buf);
+ return krb5_enomem(context);
+ }
+ free (buf);
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
+ auth_context->local_seqnumber =
+ (auth_context->local_seqnumber + 1) & 0xFFFFFFFF;
+ return 0;
+
+ fail:
+ free (buf);
+ krb5_data_free (&s.enc_part.cipher);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/mk_rep.c b/third_party/heimdal/lib/krb5/mk_rep.c
new file mode 100644
index 0000000..bd24c6e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_rep.c
@@ -0,0 +1,119 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_rep(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ AP_REP ap;
+ EncAPRepPart body;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len = 0;
+ krb5_crypto crypto;
+
+ ap.pvno = 5;
+ ap.msg_type = krb_ap_rep;
+
+ memset (&body, 0, sizeof(body));
+
+ body.ctime = auth_context->authenticator->ctime;
+ body.cusec = auth_context->authenticator->cusec;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_USE_SUBKEY) {
+ if (auth_context->local_subkey == NULL) {
+ ret = krb5_auth_con_generatelocalsubkey(context,
+ auth_context,
+ auth_context->keyblock);
+ if(ret) {
+ free_EncAPRepPart(&body);
+ return ret;
+ }
+ }
+ ret = krb5_copy_keyblock(context, auth_context->local_subkey,
+ &body.subkey);
+ if (ret) {
+ free_EncAPRepPart(&body);
+ return krb5_enomem(context);
+ }
+ } else
+ body.subkey = NULL;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if(auth_context->local_seqnumber == 0)
+ krb5_generate_seq_number (context,
+ auth_context->keyblock,
+ &auth_context->local_seqnumber);
+ ALLOC(body.seq_number, 1);
+ if (body.seq_number == NULL) {
+ free_EncAPRepPart(&body);
+ return krb5_enomem(context);
+ }
+ *(body.seq_number) = auth_context->local_seqnumber;
+ } else
+ body.seq_number = NULL;
+
+ ap.enc_part.etype = auth_context->keyblock->keytype;
+ ap.enc_part.kvno = NULL;
+
+ ASN1_MALLOC_ENCODE(EncAPRepPart, buf, buf_size, &body, &len, ret);
+ free_EncAPRepPart (&body);
+ if(ret)
+ return ret;
+ if (buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ ret = krb5_crypto_init(context, auth_context->keyblock,
+ 0 /* ap.enc_part.etype */, &crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+ ret = krb5_encrypt (context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ buf + buf_size - len,
+ len,
+ &ap.enc_part.cipher);
+ krb5_crypto_destroy(context, crypto);
+ free(buf);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(AP_REP, outbuf->data, outbuf->length, &ap, &len, ret);
+ if (ret == 0 && outbuf->length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ free_AP_REP (&ap);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/mk_req.c b/third_party/heimdal/lib/krb5/mk_req.c
new file mode 100644
index 0000000..44e6c8b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_req.c
@@ -0,0 +1,114 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_req_exact(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const krb5_principal server,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ krb5_creds this_cred, *cred;
+
+ memset(&this_cred, 0, sizeof(this_cred));
+
+ ret = krb5_cc_get_principal(context, ccache, &this_cred.client);
+
+ if(ret)
+ return ret;
+
+ ret = krb5_copy_principal (context, server, &this_cred.server);
+ if (ret) {
+ krb5_free_cred_contents (context, &this_cred);
+ return ret;
+ }
+
+ this_cred.times.endtime = 0;
+ if (auth_context && *auth_context && (*auth_context)->keytype)
+ this_cred.session.keytype = (*auth_context)->keytype;
+
+ ret = krb5_get_credentials (context, 0, ccache, &this_cred, &cred);
+ krb5_free_cred_contents(context, &this_cred);
+ if (ret)
+ return ret;
+
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ cred,
+ outbuf);
+ krb5_free_creds(context, cred);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ const char *service,
+ const char *hostname,
+ krb5_data *in_data,
+ krb5_ccache ccache,
+ krb5_data *outbuf)
+{
+ krb5_error_code ret;
+ char **realms;
+ char *real_hostname;
+ krb5_principal server;
+
+ ret = krb5_expand_hostname_realms (context, hostname,
+ &real_hostname, &realms);
+ if (ret)
+ return ret;
+
+ ret = krb5_build_principal (context, &server,
+ strlen(*realms),
+ *realms,
+ service,
+ real_hostname,
+ NULL);
+ free (real_hostname);
+ krb5_free_host_realm (context, realms);
+ if (ret)
+ return ret;
+ ret = krb5_mk_req_exact (context, auth_context, ap_req_options,
+ server, in_data, ccache, outbuf);
+ krb5_free_principal (context, server);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/mk_req_ext.c b/third_party/heimdal/lib/krb5/mk_req_ext.c
new file mode 100644
index 0000000..a8a07f1
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_req_ext.c
@@ -0,0 +1,151 @@
+/*
+ * 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_mk_req_internal(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_data *outbuf,
+ krb5_key_usage checksum_usage,
+ krb5_key_usage encrypt_usage)
+{
+ krb5_error_code ret;
+ krb5_data authenticator;
+ Checksum c;
+ Checksum *c_opt;
+ krb5_auth_context ac;
+
+ if(auth_context) {
+ if(*auth_context == NULL)
+ ret = krb5_auth_con_init(context, auth_context);
+ else
+ ret = 0;
+ ac = *auth_context;
+ } else
+ ret = krb5_auth_con_init(context, &ac);
+ if(ret)
+ return ret;
+
+ if(ac->local_subkey == NULL && (ap_req_options & AP_OPTS_USE_SUBKEY)) {
+ ret = krb5_auth_con_generatelocalsubkey(context,
+ ac,
+ &in_creds->session);
+ if(ret)
+ goto out;
+ }
+
+ krb5_free_keyblock(context, ac->keyblock);
+ ret = krb5_copy_keyblock(context, &in_creds->session, &ac->keyblock);
+ if (ret)
+ goto out;
+
+ /*
+ * Use the default checksum type except for some interoperability cases
+ * with older MIT, DCE and Windows KDCs.
+ */
+ if (in_data) {
+ krb5_crypto crypto;
+ krb5_cksumtype checksum_type = CKSUMTYPE_NONE;
+
+ if (ac->keyblock->keytype == ETYPE_DES_CBC_CRC)
+ checksum_type = CKSUMTYPE_RSA_MD4;
+ else if (ac->keyblock->keytype == ETYPE_DES_CBC_MD4 ||
+ ac->keyblock->keytype == ETYPE_DES_CBC_MD5 ||
+ ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5 ||
+ ac->keyblock->keytype == ETYPE_ARCFOUR_HMAC_MD5_56)
+ checksum_type = CKSUMTYPE_RSA_MD5;
+ else
+ checksum_type = CKSUMTYPE_NONE;
+
+ ret = krb5_crypto_init(context, ac->keyblock, 0, &crypto);
+ if (ret)
+ goto out;
+
+ _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
+ ret = krb5_create_checksum(context,
+ crypto,
+ checksum_usage,
+ checksum_type,
+ in_data->data,
+ in_data->length,
+ &c);
+ krb5_crypto_destroy(context, crypto);
+ c_opt = &c;
+ } else {
+ c_opt = NULL;
+ }
+
+ if (ret)
+ goto out;
+
+ ret = _krb5_build_authenticator(context,
+ ac,
+ ac->keyblock->keytype,
+ in_creds,
+ c_opt,
+ &authenticator,
+ encrypt_usage);
+ if (c_opt)
+ free_Checksum (c_opt);
+ if (ret)
+ goto out;
+
+ ret = krb5_build_ap_req (context, ac->keyblock->keytype,
+ in_creds, ap_req_options, authenticator, outbuf);
+out:
+ if(auth_context == NULL)
+ krb5_auth_con_free(context, ac);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_req_extended(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_data *outbuf)
+{
+ return _krb5_mk_req_internal (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ in_creds,
+ outbuf,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ KRB5_KU_AP_REQ_AUTH);
+}
diff --git a/third_party/heimdal/lib/krb5/mk_safe.c b/third_party/heimdal/lib/krb5/mk_safe.c
new file mode 100644
index 0000000..b327e1c
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/mk_safe.c
@@ -0,0 +1,139 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_safe(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *userdata,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_SAFE s;
+ u_char *buf = NULL;
+ size_t buf_size;
+ size_t len = 0;
+ krb5_crypto crypto;
+ krb5_keyblock *key;
+ krb5_replay_data rdata;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else
+ key = auth_context->keyblock;
+
+ s.pvno = 5;
+ s.msg_type = krb_safe;
+
+ memset(&rdata, 0, sizeof(rdata));
+
+ s.safe_body.user_data = *userdata;
+
+ krb5_us_timeofday (context, &rdata.timestamp, &rdata.usec);
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ s.safe_body.timestamp = &rdata.timestamp;
+ s.safe_body.usec = &rdata.usec;
+ } else {
+ s.safe_body.timestamp = NULL;
+ s.safe_body.usec = NULL;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_TIME) {
+ outdata->timestamp = rdata.timestamp;
+ outdata->usec = rdata.usec;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ rdata.seq = auth_context->local_seqnumber;
+ s.safe_body.seq_number = &rdata.seq;
+ } else
+ s.safe_body.seq_number = NULL;
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_RET_SEQUENCE)
+ outdata->seq = auth_context->local_seqnumber;
+
+ s.safe_body.s_address = auth_context->local_address;
+ s.safe_body.r_address = auth_context->remote_address;
+
+ s.cksum.cksumtype = 0;
+ s.cksum.checksum.data = NULL;
+ s.cksum.checksum.length = 0;
+
+ ASN1_MALLOC_ENCODE(KRB_SAFE, buf, buf_size, &s, &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_create_checksum(context,
+ crypto,
+ KRB5_KU_KRB_SAFE_CKSUM,
+ 0,
+ buf,
+ len,
+ &s.cksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free (buf);
+ return ret;
+ }
+
+ free(buf);
+ ASN1_MALLOC_ENCODE(KRB_SAFE, buf, buf_size, &s, &len, ret);
+ free_Checksum (&s.cksum);
+ if(ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ outbuf->length = len;
+ outbuf->data = buf;
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
+ auth_context->local_seqnumber =
+ (auth_context->local_seqnumber + 1) & 0xFFFFFFFF;
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/n-fold-test.c b/third_party/heimdal/lib/krb5/n-fold-test.c
new file mode 100644
index 0000000..2938b44
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/n-fold-test.c
@@ -0,0 +1,119 @@
+/*
+ * 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 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"
+
+enum { MAXSIZE = 24 };
+
+static struct testcase {
+ const char *str;
+ unsigned n;
+ unsigned char res[MAXSIZE];
+} tests[] = {
+ {"012345", 8,
+ {0xbe, 0x07, 0x26, 0x31, 0x27, 0x6b, 0x19, 0x55}
+ },
+ {"basch", 24,
+ {0x1a, 0xab, 0x6b, 0x42, 0x96, 0x4b, 0x98, 0xb2, 0x1f, 0x8c, 0xde,
+ 0x2d, 0x24, 0x48, 0xba, 0x34, 0x55, 0xd7, 0x86, 0x2c, 0x97, 0x31,
+ 0x64, 0x3f}
+ },
+ {"eichin", 24,
+ {0x65, 0x69, 0x63, 0x68, 0x69, 0x6e, 0x4b, 0x73, 0x2b, 0x4b,
+ 0x1b, 0x43, 0xda, 0x1a, 0x5b, 0x99, 0x5a, 0x58, 0xd2, 0xc6, 0xd0,
+ 0xd2, 0xdc, 0xca}
+ },
+ {"sommerfeld", 24,
+ {0x2f, 0x7a, 0x98, 0x55, 0x7c, 0x6e, 0xe4, 0xab, 0xad, 0xf4,
+ 0xe7, 0x11, 0x92, 0xdd, 0x44, 0x2b, 0xd4, 0xff, 0x53, 0x25, 0xa5,
+ 0xde, 0xf7, 0x5c}
+ },
+ {"MASSACHVSETTS INSTITVTE OF TECHNOLOGY", 24,
+ {0xdb, 0x3b, 0x0d, 0x8f, 0x0b, 0x06, 0x1e, 0x60, 0x32, 0x82,
+ 0xb3, 0x08, 0xa5, 0x08, 0x41, 0x22, 0x9a, 0xd7, 0x98, 0xfa, 0xb9,
+ 0x54, 0x0c, 0x1b}
+ },
+ {"assar@NADA.KTH.SE", 24,
+ {0x5c, 0x06, 0xc3, 0x4d, 0x2c, 0x89, 0x05, 0xbe, 0x7a, 0x51,
+ 0x83, 0x6c, 0xd6, 0xf8, 0x1c, 0x4b, 0x7a, 0x93, 0x49, 0x16, 0x5a,
+ 0xb3, 0xfa, 0xa9}
+ },
+ {"testKRBTEST.MIT.EDUtestkey", 24,
+ {0x50, 0x2c, 0xf8, 0x29, 0x78, 0xe5, 0xfb, 0x1a, 0x29, 0x06,
+ 0xbd, 0x22, 0x28, 0x91, 0x56, 0xc0, 0x06, 0xa0, 0xdc, 0xf5, 0xb6,
+ 0xc2, 0xda, 0x6c}
+ },
+ {"password", 7,
+ {0x78, 0xa0, 0x7b, 0x6c, 0xaf, 0x85, 0xfa}
+ },
+ {"Rough Consensus, and Running Code", 8,
+ {0xbb, 0x6e, 0xd3, 0x08, 0x70, 0xb7, 0xf0, 0xe0},
+ },
+ {"password", 21,
+ {0x59, 0xe4, 0xa8, 0xca, 0x7c, 0x03, 0x85, 0xc3, 0xc3, 0x7b, 0x3f,
+ 0x6d, 0x20, 0x00, 0x24, 0x7c, 0xb6, 0xe6, 0xbd, 0x5b, 0x3e},
+ },
+ {"MASSACHVSETTS INSTITVTE OF TECHNOLOGY", 24,
+ {0xdb, 0x3b, 0x0d, 0x8f, 0x0b, 0x06, 0x1e, 0x60, 0x32, 0x82, 0xb3,
+ 0x08, 0xa5, 0x08, 0x41, 0x22, 0x9a, 0xd7, 0x98, 0xfa, 0xb9, 0x54,
+ 0x0c, 0x1b}
+ },
+ {NULL, 0, {0}}
+};
+
+int
+main(int argc, char **argv)
+{
+ unsigned char data[MAXSIZE];
+ struct testcase *t;
+ int ret = 0;
+
+ for (t = tests; t->str; ++t) {
+ int i;
+
+ ret = _krb5_n_fold (t->str, strlen(t->str), data, t->n);
+ if (ret)
+ errx(1, "out of memory");
+ if (memcmp (data, t->res, t->n) != 0) {
+ printf ("n-fold(\"%s\", %d) failed\n", t->str, t->n);
+ printf ("should be: ");
+ for (i = 0; i < t->n; ++i)
+ printf ("%02x", t->res[i]);
+ printf ("\nresult was: ");
+ for (i = 0; i < t->n; ++i)
+ printf ("%02x", data[i]);
+ printf ("\n");
+ ret = 1;
+ }
+ }
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/n-fold.c b/third_party/heimdal/lib/krb5/n-fold.c
new file mode 100644
index 0000000..ba3150d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/n-fold.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "krb5_locl.h"
+
+static void
+rr13(uint8_t *dst1, uint8_t *dst2, uint8_t *src, size_t len)
+{
+ int bytes = (len + 7) / 8;
+ int i;
+ const int bits = 13 % len;
+
+ for (i = 0; i < bytes; i++) {
+ int bb;
+ int b1, s1, b2, s2;
+ /* calculate first bit position of this byte */
+ bb = 8 * i - bits;
+ while(bb < 0)
+ bb += len;
+ /* byte offset and shift count */
+ b1 = bb / 8;
+ s1 = bb % 8;
+
+ if (bb + 8 > bytes * 8)
+ /* watch for wraparound */
+ s2 = (len + 8 - s1) % 8;
+ else
+ s2 = 8 - s1;
+ b2 = (b1 + 1) % bytes;
+ dst1[i] = (src[b1] << s1) | (src[b2] >> s2);
+ dst2[i] = dst1[i];
+ }
+
+ return;
+}
+
+/*
+ * Add `b' to `a', both being one's complement numbers.
+ * This function assumes that inputs *a, *b are aligned
+ * to 4 bytes.
+ */
+static void
+add1(uint8_t *a, uint8_t *b, size_t len)
+{
+ int i;
+ int carry = 0;
+ uint32_t x;
+ uint32_t left, right;
+
+ for (i = len - 1; (i+1) % 4; i--) {
+ x = a[i] + b[i] + carry;
+ carry = x > 0xff;
+ a[i] = x & 0xff;
+ }
+
+ for (i = len / 4 - 1; i >= 0; i--) {
+ left = ntohl(((uint32_t *)a)[i]);
+ right = ntohl(((uint32_t *)b)[i]);
+ x = left + right + carry;
+ carry = x < left || x < right;
+ ((uint32_t *)a)[i] = x;
+ }
+
+ for (i = len - 1; (i+1) % 4; i--) {
+ x = a[i] + carry;
+ carry = x > 0xff;
+ a[i] = x & 0xff;
+ }
+
+ for (i = len / 4 - 1; carry && i >= 0; i--) {
+ left = ((uint32_t *)a)[i];
+ x = left + carry;
+ carry = x < left;
+ ((uint32_t *)a)[i] = x;
+ }
+
+ for (i = len / 4 - 1; i >=0; i--)
+ ((uint32_t *)a)[i] = htonl(((uint32_t *)a)[i]);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_n_fold(const void *str, size_t len, void *key, size_t size)
+{
+ /* if len < size we need at most N * len bytes, ie < 2 * size;
+ if len > size we need at most 2 * len */
+ size_t maxlen = 2 * max(size, len);
+ size_t l = 0;
+ uint8_t *tmp;
+ uint8_t *tmpbuf;
+ uint8_t *buf1;
+ uint8_t *buf2;
+
+ tmp = malloc(maxlen + 2 * len);
+ if (tmp == NULL)
+ return ENOMEM;
+
+ buf1 = tmp + maxlen;
+ buf2 = tmp + maxlen + len;
+
+ memset(key, 0, size);
+ memcpy(buf1, str, len);
+ memcpy(tmp, buf1, len);
+ do {
+ l += len;
+ while(l >= size) {
+ add1(key, tmp, size);
+ l -= size;
+ if(l == 0)
+ break;
+ memmove(tmp, tmp + size, l);
+ }
+ rr13(tmp + l, buf2, buf1, len * 8);
+ tmpbuf = buf1;
+ buf1 = buf2;
+ buf2 = tmpbuf;
+ } while(l != 0);
+
+ memset(tmp, 0, maxlen + 2 * len);
+ free(tmp);
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/net_read.c b/third_party/heimdal/lib/krb5/net_read.c
new file mode 100644
index 0000000..70b1800
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/net_read.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 1997, 1998, 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"
+
+/**
+ * Read \a len bytes from socket \a p_fd into buffer \a buf.
+ * Block until \a len bytes are read or until an error.
+ *
+ * @return If successful, the number of bytes read: \a len.
+ * On end-of-file, 0.
+ * On error, less than 0 (if single-threaded, the error can be found
+ * in the errno global variable).
+ */
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+krb5_net_read (krb5_context context,
+ void *p_fd,
+ void *buf,
+ size_t len)
+{
+ krb5_socket_t fd = *((krb5_socket_t *)p_fd);
+ return net_read(fd, buf, len);
+}
diff --git a/third_party/heimdal/lib/krb5/net_write.c b/third_party/heimdal/lib/krb5/net_write.c
new file mode 100644
index 0000000..289b965
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/net_write.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 1997, 1998 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_ssize_t KRB5_LIB_CALL
+krb5_net_write (krb5_context context,
+ void *p_fd,
+ const void *buf,
+ size_t len)
+{
+ krb5_socket_t fd = *((krb5_socket_t *)p_fd);
+ return net_write(fd, buf, len);
+}
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+krb5_net_write_block(krb5_context context,
+ void *p_fd,
+ const void *buf,
+ size_t len,
+ time_t timeout)
+{
+ krb5_socket_t fd = *((krb5_socket_t *)p_fd);
+ int ret;
+ struct timeval tv, *tvp;
+ const char *cbuf = (const char *)buf;
+ size_t rem = len;
+ ssize_t count;
+ fd_set wfds;
+
+ do {
+ FD_ZERO(&wfds);
+ FD_SET(fd, &wfds);
+
+ if (timeout != 0) {
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ tvp = &tv;
+ } else
+ tvp = NULL;
+
+ ret = select(fd + 1, NULL, &wfds, NULL, tvp);
+ if (rk_IS_SOCKET_ERROR(ret)) {
+ if (rk_SOCK_ERRNO == EINTR)
+ continue;
+ return -1;
+ }
+
+#ifdef HAVE_WINSOCK
+ if (ret == 0) {
+ WSASetLastError( WSAETIMEDOUT );
+ return 0;
+ }
+
+ count = send (fd, cbuf, rem, 0);
+
+ if (rk_IS_SOCKET_ERROR(count)) {
+ return -1;
+ }
+
+#else
+ if (ret == 0) {
+ return 0;
+ }
+
+ if (!FD_ISSET(fd, &wfds)) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+
+ count = write (fd, cbuf, rem);
+
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ }
+
+#endif
+
+ cbuf += count;
+ rem -= count;
+
+ } while (rem > 0);
+
+ return len;
+}
diff --git a/third_party/heimdal/lib/krb5/pac.c b/third_party/heimdal/lib/krb5/pac.c
new file mode 100644
index 0000000..0437a89
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/pac.c
@@ -0,0 +1,2159 @@
+/*
+ * Copyright (c) 2006 - 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"
+
+#include <heimbasepriv.h>
+#include <wind.h>
+#include <assert.h>
+
+/*
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/3341cfa2-6ef5-42e0-b7bc-4544884bf399
+ */
+struct PAC_INFO_BUFFER {
+ uint32_t type; /* ULONG ulType in the original */
+ uint32_t buffersize; /* ULONG cbBufferSize in the original */
+ uint64_t offset; /* ULONG64 Offset in the original
+ * this being the offset from the beginning of the
+ * struct PACTYPE to the beginning of the buffer
+ * containing data of type ulType
+ */
+};
+
+/*
+ * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-pac/6655b92f-ab06-490b-845d-037e6987275f
+ */
+struct PACTYPE {
+ uint32_t numbuffers; /* named cBuffers of type ULONG in the original */
+ uint32_t version; /* Named Version of type ULONG in the original */
+ struct PAC_INFO_BUFFER buffers[1]; /* an ellipsis (...) in the original */
+};
+
+/*
+ * A PAC starts with a PACTYPE header structure that is followed by an array of
+ * numbuffers PAC_INFO_BUFFER structures, each of which points to a buffer
+ * beyond the last PAC_INFO_BUFFER structures.
+ */
+
+struct krb5_pac_data {
+ struct PACTYPE *pac;
+ krb5_data data;
+ struct PAC_INFO_BUFFER *server_checksum;
+ struct PAC_INFO_BUFFER *privsvr_checksum;
+ struct PAC_INFO_BUFFER *logon_name;
+ struct PAC_INFO_BUFFER *upn_dns_info;
+ struct PAC_INFO_BUFFER *ticket_checksum;
+ struct PAC_INFO_BUFFER *attributes_info;
+ struct PAC_INFO_BUFFER *full_checksum;
+ krb5_data ticket_sign_data;
+
+ /* PAC_UPN_DNS_INFO */
+ krb5_principal upn_princ;
+ uint32_t upn_flags;
+ krb5_principal canon_princ;
+ krb5_data sid;
+
+ /* PAC_ATTRIBUTES_INFO */
+ uint64_t pac_attributes;
+
+ krb5_boolean is_trusted;
+};
+
+#define PAC_ALIGNMENT 8
+
+#define PACTYPE_SIZE 8
+#define PAC_INFO_BUFFER_SIZE 16
+
+#define PAC_LOGON_INFO 1
+#define PAC_CREDENTIALS_INFO 2
+#define PAC_SERVER_CHECKSUM 6
+#define PAC_PRIVSVR_CHECKSUM 7
+#define PAC_LOGON_NAME 10
+#define PAC_CONSTRAINED_DELEGATION 11
+#define PAC_UPN_DNS_INFO 12
+#define PAC_TICKET_CHECKSUM 16
+#define PAC_ATTRIBUTES_INFO 17
+#define PAC_REQUESTOR_SID 18
+#define PAC_FULL_CHECKSUM 19
+
+/* Flag in PAC_UPN_DNS_INFO */
+#define PAC_EXTRA_LOGON_INFO_FLAGS_UPN_DEFAULTED 0x1
+#define PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID 0x2
+
+#define CHECK(r,f,l) \
+ do { \
+ if (((r) = f ) != 0) { \
+ krb5_clear_error_message(context); \
+ goto l; \
+ } \
+ } while(0)
+
+static const char zeros[PAC_ALIGNMENT];
+
+static void HEIM_CALLCONV
+pac_dealloc(void *ctx)
+{
+ krb5_pac pac = (krb5_pac)ctx;
+
+ krb5_data_free(&pac->data);
+ krb5_data_free(&pac->ticket_sign_data);
+
+ if (pac->upn_princ) {
+ free_Principal(pac->upn_princ);
+ free(pac->upn_princ);
+ }
+ if (pac->canon_princ) {
+ free_Principal(pac->canon_princ);
+ free(pac->canon_princ);
+ }
+ krb5_data_free(&pac->sid);
+
+ free(pac->pac);
+}
+
+static const struct heim_type_data pac_object = {
+ HEIM_TID_PAC,
+ "heim-pac",
+ NULL,
+ pac_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ * Returns the size of the PACTYPE header + the PAC_INFO_BUFFER array. This is
+ * also the end of the whole thing, and any offsets to buffers from
+ * the PAC_INFO_BUFFER[] entries have to be beyond it.
+ */
+static krb5_error_code
+pac_header_size(krb5_context context, uint32_t num_buffers, uint32_t *result)
+{
+ krb5_error_code ret;
+ uint32_t header_size;
+
+ /* Guard against integer overflow */
+ if (num_buffers > UINT32_MAX / PAC_INFO_BUFFER_SIZE) {
+ ret = EOVERFLOW;
+ krb5_set_error_message(context, ret, "PAC has too many buffers");
+ return ret;
+ }
+ header_size = PAC_INFO_BUFFER_SIZE * num_buffers;
+
+ /* Guard against integer overflow */
+ if (header_size > UINT32_MAX - PACTYPE_SIZE) {
+ ret = EOVERFLOW;
+ krb5_set_error_message(context, ret, "PAC has too many buffers");
+ return ret;
+ }
+ header_size += PACTYPE_SIZE;
+
+ *result = header_size;
+
+ return 0;
+}
+
+/* Output `size' + `addend' + padding for alignment if it doesn't overflow */
+static krb5_error_code
+pac_aligned_size(krb5_context context,
+ uint32_t size,
+ uint32_t addend,
+ uint32_t *aligned_size)
+{
+ krb5_error_code ret;
+
+ if (size > UINT32_MAX - addend ||
+ (size + addend) > UINT32_MAX - (PAC_ALIGNMENT - 1)) {
+ ret = EOVERFLOW;
+ krb5_set_error_message(context, ret, "integer overrun");
+ return ret;
+ }
+ size += addend;
+ size += PAC_ALIGNMENT - 1;
+ size &= ~(PAC_ALIGNMENT - 1);
+ *aligned_size = size;
+ return 0;
+}
+
+/*
+ * HMAC-MD5 checksum over any key (needed for the PAC routines)
+ */
+
+static krb5_error_code
+HMAC_MD5_any_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *result)
+{
+ struct _krb5_key_data local_key;
+ struct krb5_crypto_iov iov;
+ krb5_error_code ret;
+
+ memset(&local_key, 0, sizeof(local_key));
+
+ ret = krb5_copy_keyblock(context, key, &local_key.key);
+ if (ret)
+ return ret;
+
+ ret = krb5_data_alloc (&result->checksum, 16);
+ if (ret) {
+ krb5_free_keyblock(context, local_key.key);
+ return ret;
+ }
+
+ result->cksumtype = CKSUMTYPE_HMAC_MD5;
+ iov.data.data = (void *)data;
+ iov.data.length = len;
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+
+ ret = _krb5_HMAC_MD5_checksum(context, NULL, &local_key, usage, &iov, 1,
+ result);
+ if (ret)
+ krb5_data_free(&result->checksum);
+
+ krb5_free_keyblock(context, local_key.key);
+ return ret;
+}
+
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_parse(krb5_context context, const void *ptr, size_t len,
+ krb5_pac *pac)
+{
+ krb5_error_code ret = 0;
+ krb5_pac p;
+ krb5_storage *sp = NULL;
+ uint32_t i, num_buffers, version, header_size = 0;
+ uint32_t prev_start = 0;
+ uint32_t prev_end = 0;
+
+ *pac = NULL;
+ p = _heim_alloc_object(&pac_object, sizeof(*p));
+ if (p)
+ sp = krb5_storage_from_readonly_mem(ptr, len);
+ if (sp == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0) {
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+ ret = krb5_ret_uint32(sp, &num_buffers);
+ }
+ if (ret == 0)
+ ret = krb5_ret_uint32(sp, &version);
+ if (ret == 0 && num_buffers < 1)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has too few buffers", ""));
+ if (ret == 0 && num_buffers > 1000)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has too many buffers", ""));
+ if (ret == 0 && version != 0)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has wrong version %d", ""),
+ (int)version);
+ if (ret == 0)
+ ret = pac_header_size(context, num_buffers, &header_size);
+ if (ret == 0 && header_size > len)
+ krb5_set_error_message(context, ret = EOVERFLOW,
+ N_("PAC encoding invalid, would overflow buffers", ""));
+ if (ret == 0)
+ p->pac = calloc(1, header_size);
+ if (ret == 0 && p->pac == NULL)
+ ret = krb5_enomem(context);
+
+ if (ret == 0) {
+ p->pac->numbuffers = num_buffers;
+ p->pac->version = version;
+ }
+
+ for (i = 0; ret == 0 && i < p->pac->numbuffers; i++) {
+ ret = krb5_ret_uint32(sp, &p->pac->buffers[i].type);
+ if (ret == 0)
+ ret = krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize);
+ if (ret == 0)
+ ret = krb5_ret_uint64(sp, &p->pac->buffers[i].offset);
+ if (ret)
+ break;
+
+ /* Consistency checks (we don't check for wasted space) */
+ if (p->pac->buffers[i].offset & (PAC_ALIGNMENT - 1)) {
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC out of alignment", ""));
+ break;
+ }
+ if (p->pac->buffers[i].offset > len ||
+ p->pac->buffers[i].buffersize > len ||
+ len - p->pac->buffers[i].offset < p->pac->buffers[i].buffersize) {
+ krb5_set_error_message(context, ret = EOVERFLOW,
+ N_("PAC buffer overflow", ""));
+ break;
+ }
+ if (p->pac->buffers[i].offset < header_size) {
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC offset inside header: %lu %lu", ""),
+ (unsigned long)p->pac->buffers[i].offset,
+ (unsigned long)header_size);
+ break;
+ }
+
+ /*
+ * We'd like to check for non-overlapping of buffers, but the buffers
+ * need not be in the same order as the PAC_INFO_BUFFER[] entries
+ * pointing to them! To fully check for overlap we'd have to have an
+ * O(N^2) loop after we parse all the PAC_INFO_BUFFER[].
+ *
+ * But we can check that each buffer does not overlap the previous
+ * buffer.
+ */
+ if (prev_start) {
+ if (p->pac->buffers[i].offset >= prev_start &&
+ p->pac->buffers[i].offset < prev_end) {
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC overlap", ""));
+ break;
+ }
+ if (p->pac->buffers[i].offset < prev_start &&
+ p->pac->buffers[i].offset +
+ p->pac->buffers[i].buffersize > prev_start) {
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC overlap", ""));
+ break;
+ }
+ }
+ prev_start = p->pac->buffers[i].offset;
+ prev_end = p->pac->buffers[i].offset + p->pac->buffers[i].buffersize;
+
+ /* Let's save pointers to buffers we'll need later */
+ switch (p->pac->buffers[i].type) {
+ case PAC_SERVER_CHECKSUM:
+ if (p->server_checksum)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple server checksums", ""));
+ else
+ p->server_checksum = &p->pac->buffers[i];
+ break;
+ case PAC_PRIVSVR_CHECKSUM:
+ if (p->privsvr_checksum)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple KDC checksums", ""));
+ else
+ p->privsvr_checksum = &p->pac->buffers[i];
+ break;
+ case PAC_LOGON_NAME:
+ if (p->logon_name)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple logon names", ""));
+ else
+ p->logon_name = &p->pac->buffers[i];
+ break;
+ case PAC_UPN_DNS_INFO:
+ if (p->upn_dns_info)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple UPN DNS info buffers", ""));
+ else
+ p->upn_dns_info = &p->pac->buffers[i];
+ break;
+ case PAC_TICKET_CHECKSUM:
+ if (p->ticket_checksum)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple ticket checksums", ""));
+ else
+ p->ticket_checksum = &p->pac->buffers[i];
+ break;
+ case PAC_ATTRIBUTES_INFO:
+ if (p->attributes_info)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple attributes info buffers", ""));
+ else
+ p->attributes_info = &p->pac->buffers[i];
+ break;
+ case PAC_FULL_CHECKSUM:
+ if (p->full_checksum)
+ krb5_set_error_message(context, ret = EINVAL,
+ N_("PAC has multiple full checksums", ""));
+ else
+ p->full_checksum = &p->pac->buffers[i];
+ break;
+ default: break;
+ }
+ }
+
+ if (ret == 0)
+ ret = krb5_data_copy(&p->data, ptr, len);
+ if (ret == 0) {
+ *pac = p;
+ p = NULL;
+ }
+ if (sp)
+ krb5_storage_free(sp);
+ krb5_pac_free(context, p);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_init(krb5_context context, krb5_pac *pac)
+{
+ krb5_error_code ret;
+ krb5_pac p;
+
+ p = _heim_alloc_object(&pac_object, sizeof(*p));
+ if (p == NULL) {
+ return krb5_enomem(context);
+ }
+
+ p->pac = calloc(1, sizeof(*p->pac));
+ if (p->pac == NULL) {
+ krb5_pac_free(context, p);
+ return krb5_enomem(context);
+ }
+
+ ret = krb5_data_alloc(&p->data, PACTYPE_SIZE);
+ if (ret) {
+ free (p->pac);
+ krb5_pac_free(context, p);
+ return krb5_enomem(context);
+ }
+ memset(p->data.data, 0, p->data.length);
+
+ *pac = p;
+ return 0;
+}
+
+/**
+ * Add a PAC buffer `nd' of type `type' to the pac `p'.
+ *
+ * @param context
+ * @param p
+ * @param type
+ * @param nd
+ *
+ * @return 0 on success or a Kerberos or system error.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_add_buffer(krb5_context context, krb5_pac p,
+ uint32_t type, const krb5_data *nd)
+{
+ krb5_error_code ret;
+ void *ptr;
+ size_t old_len = p->data.length;
+ uint32_t len, offset, header_size;
+ uint32_t i;
+ uint32_t num_buffers;
+
+ assert(nd->data != NULL);
+
+ num_buffers = p->pac->numbuffers;
+ ret = pac_header_size(context, num_buffers + 1, &header_size);
+ if (ret)
+ return ret;
+
+ ptr = realloc(p->pac, header_size);
+ if (ptr == NULL)
+ return krb5_enomem(context);
+
+ p->pac = ptr;
+ p->pac->buffers[num_buffers].type = 0;
+ p->pac->buffers[num_buffers].buffersize = 0;
+ p->pac->buffers[num_buffers].offset = 0;
+
+ /*
+ * Check that we can adjust all the buffer offsets in the existing
+ * PAC_INFO_BUFFERs, since changing the size of PAC_INFO_BUFFER[] means
+ * changing the offsets of buffers following that array.
+ *
+ * We don't adjust them until we can't fail.
+ */
+ for (i = 0; i < num_buffers; i++) {
+ if (p->pac->buffers[i].offset > UINT32_MAX - PAC_INFO_BUFFER_SIZE) {
+ krb5_set_error_message(context, ret = EOVERFLOW,
+ "too many / too large PAC buffers");
+ return ret;
+ }
+ }
+
+ /*
+ * The new buffer's offset must be past the end of the buffers we have
+ * (p->data), which is the sum of the header and p->data.length.
+ */
+
+ /* Set offset = p->data.length + PAC_INFO_BUFFER_SIZE + alignment */
+ ret = pac_aligned_size(context, p->data.length, PAC_INFO_BUFFER_SIZE, &offset);
+ if (ret == 0)
+ /* Set the new length = offset + nd->length + alignment */
+ ret = pac_aligned_size(context, offset, nd->length, &len);
+ if (ret) {
+ krb5_set_error_message(context, ret, "PAC buffer too large");
+ return ret;
+ }
+ ret = krb5_data_realloc(&p->data, len);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+ /* Zero out the new allocation to zero out any padding */
+ memset((char *)p->data.data + old_len, 0, len - old_len);
+
+ p->pac->buffers[num_buffers].type = type;
+ p->pac->buffers[num_buffers].buffersize = nd->length;
+ p->pac->buffers[num_buffers].offset = offset;
+
+ /* Adjust all the buffer offsets in the existing PAC_INFO_BUFFERs now */
+ for (i = 0; i < num_buffers; i++)
+ p->pac->buffers[i].offset += PAC_INFO_BUFFER_SIZE;
+
+ /*
+ * Make place for new PAC INFO BUFFER header
+ */
+ header_size -= PAC_INFO_BUFFER_SIZE;
+ memmove((unsigned char *)p->data.data + header_size + PAC_INFO_BUFFER_SIZE,
+ (unsigned char *)p->data.data + header_size ,
+ old_len - header_size);
+ /* Clear the space where we would put the new PAC_INFO_BUFFER[] element */
+ memset((unsigned char *)p->data.data + header_size, 0,
+ PAC_INFO_BUFFER_SIZE);
+
+ /*
+ * Copy in new data part
+ */
+ memcpy((unsigned char *)p->data.data + offset, nd->data, nd->length);
+ p->pac->numbuffers += 1;
+ return 0;
+}
+
+/**
+ * Get the PAC buffer of specific type from the pac.
+ *
+ * @param context Kerberos 5 context.
+ * @param p the pac structure returned by krb5_pac_parse().
+ * @param type type of buffer to get
+ * @param data return data, free with krb5_data_free().
+ *
+ * @return Returns 0 to indicate success, ENOENT to indicate that a buffer of
+ * the given type was not found, or a Kerberos or system error code.
+ *
+ * @ingroup krb5_pac
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_get_buffer(krb5_context context, krb5_const_pac p,
+ uint32_t type, krb5_data *data)
+{
+ krb5_error_code ret;
+ uint32_t i;
+
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ size_t len = p->pac->buffers[i].buffersize;
+ size_t offset = p->pac->buffers[i].offset;
+
+ if (p->pac->buffers[i].type != type)
+ continue;
+
+ if (!data)
+ return 0;
+
+ ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
+ if (ret)
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+ krb5_set_error_message(context, ENOENT, "No PAC buffer of type %lu was found",
+ (unsigned long)type);
+ return ENOENT;
+}
+
+static const struct {
+ uint32_t type;
+ krb5_data name;
+} pac_buffer_name_map[] = {
+#define PAC_MAP_ENTRY(type, name) { PAC_##type, { sizeof(name) - 1, name } }
+ PAC_MAP_ENTRY(LOGON_INFO, "logon-info" ),
+ PAC_MAP_ENTRY(CREDENTIALS_INFO, "credentials-info" ),
+ PAC_MAP_ENTRY(SERVER_CHECKSUM, "server-checksum" ),
+ PAC_MAP_ENTRY(PRIVSVR_CHECKSUM, "privsvr-checksum" ),
+ PAC_MAP_ENTRY(LOGON_NAME, "client-info" ),
+ PAC_MAP_ENTRY(CONSTRAINED_DELEGATION, "delegation-info" ),
+ PAC_MAP_ENTRY(UPN_DNS_INFO, "upn-dns-info" ),
+ PAC_MAP_ENTRY(TICKET_CHECKSUM, "ticket-checksum" ),
+ PAC_MAP_ENTRY(ATTRIBUTES_INFO, "attributes-info" ),
+ PAC_MAP_ENTRY(REQUESTOR_SID, "requestor-sid" )
+};
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pac_get_buffer_by_name(krb5_context context, krb5_const_pac p,
+ const krb5_data *name, krb5_data *data)
+{
+ size_t i;
+
+ for (i = 0;
+ i < sizeof(pac_buffer_name_map) / sizeof(pac_buffer_name_map[0]);
+ i++) {
+ if (krb5_data_cmp(name, &pac_buffer_name_map[i].name) == 0)
+ return krb5_pac_get_buffer(context, p, pac_buffer_name_map[i].type, data);
+ }
+
+ krb5_set_error_message(context, ENOENT, "No PAC buffer with name %.*s was found",
+ (int)name->length, (char *)name->data);
+ return ENOENT;
+}
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_get_types(krb5_context context,
+ krb5_const_pac p,
+ size_t *len,
+ uint32_t **types)
+{
+ size_t i;
+
+ *types = calloc(p->pac->numbuffers, sizeof(**types));
+ if (*types == NULL) {
+ *len = 0;
+ return krb5_enomem(context);
+ }
+ for (i = 0; i < p->pac->numbuffers; i++)
+ (*types)[i] = p->pac->buffers[i].type;
+ *len = p->pac->numbuffers;
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_pac_is_trusted(krb5_const_pac p)
+{
+ return p->is_trusted;
+}
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_pac_set_trusted(krb5_pac p, krb5_boolean is_trusted)
+{
+ p->is_trusted = is_trusted;
+}
+
+/*
+ *
+ */
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_pac_free(krb5_context context, krb5_pac pac)
+{
+ heim_release(pac);
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+verify_checksum(krb5_context context,
+ const struct PAC_INFO_BUFFER *sig,
+ const krb5_data *data,
+ void *ptr, size_t len,
+ const krb5_keyblock *key,
+ krb5_boolean strict_cksumtype_match)
+{
+ krb5_storage *sp = NULL;
+ uint32_t type;
+ krb5_error_code ret;
+ Checksum cksum;
+ size_t cksumsize;
+
+ memset(&cksum, 0, sizeof(cksum));
+
+ sp = krb5_storage_from_mem((char *)data->data + sig->offset,
+ sig->buffersize);
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &type), out);
+ cksum.cksumtype = type;
+
+ ret = krb5_checksumsize(context, type, &cksumsize);
+ if (ret)
+ goto out;
+
+ /* Allow for RODCIdentifier trailer, see MS-PAC 2.8 */
+ if (cksumsize > (sig->buffersize - krb5_storage_seek(sp, 0, SEEK_CUR))) {
+ ret = EINVAL;
+ goto out;
+ }
+ cksum.checksum.length = cksumsize;
+ cksum.checksum.data = malloc(cksum.checksum.length);
+ if (cksum.checksum.data == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ret = krb5_storage_read(sp, cksum.checksum.data, cksum.checksum.length);
+ if (ret != (int)cksum.checksum.length) {
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ krb5_set_error_message(context, ret, "PAC checksum missing checksum");
+ goto out;
+ }
+
+ if (!krb5_checksum_is_keyed(context, cksum.cksumtype)) {
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ krb5_set_error_message(context, ret, "Checksum type %d not keyed",
+ cksum.cksumtype);
+ goto out;
+ }
+
+ /* If the checksum is HMAC-MD5, the checksum type is not tied to
+ * the key type, instead the HMAC-MD5 checksum is applied blindly
+ * on whatever key is used for this connection, avoiding issues
+ * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See
+ * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
+ * for the same issue in MIT, and
+ * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
+ * for Microsoft's explanation */
+
+ if (cksum.cksumtype == CKSUMTYPE_HMAC_MD5 && !strict_cksumtype_match) {
+ Checksum local_checksum;
+
+ memset(&local_checksum, 0, sizeof(local_checksum));
+
+ ret = HMAC_MD5_any_checksum(context, key, ptr, len,
+ KRB5_KU_OTHER_CKSUM, &local_checksum);
+
+ if (ret != 0 || krb5_data_ct_cmp(&local_checksum.checksum, &cksum.checksum) != 0) {
+ ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
+ krb5_set_error_message(context, ret,
+ N_("PAC integrity check failed for "
+ "hmac-md5 checksum", ""));
+ }
+ krb5_data_free(&local_checksum.checksum);
+
+ } else {
+ krb5_crypto crypto = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_verify_checksum(context, crypto, KRB5_KU_OTHER_CKSUM,
+ ptr, len, &cksum);
+ krb5_crypto_destroy(context, crypto);
+ }
+ free(cksum.checksum.data);
+ krb5_storage_free(sp);
+
+ return ret;
+
+out:
+ if (cksum.checksum.data)
+ free(cksum.checksum.data);
+ if (sp)
+ krb5_storage_free(sp);
+ return ret;
+}
+
+static krb5_error_code
+create_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ uint32_t cksumtype,
+ void *data, size_t datalen,
+ void *sig, size_t siglen)
+{
+ krb5_crypto crypto = NULL;
+ krb5_error_code ret;
+ Checksum cksum;
+
+ /* If the checksum is HMAC-MD5, the checksum type is not tied to
+ * the key type, instead the HMAC-MD5 checksum is applied blindly
+ * on whatever key is used for this connection, avoiding issues
+ * with unkeyed checksums on des-cbc-md5 and des-cbc-crc. See
+ * http://comments.gmane.org/gmane.comp.encryption.kerberos.devel/8743
+ * for the same issue in MIT, and
+ * http://blogs.msdn.com/b/openspecification/archive/2010/01/01/verifying-the-server-signature-in-kerberos-privilege-account-certificate.aspx
+ * for Microsoft's explaination */
+
+ if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) {
+ ret = HMAC_MD5_any_checksum(context, key, data, datalen,
+ KRB5_KU_OTHER_CKSUM, &cksum);
+ if (ret)
+ return ret;
+ } else {
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_create_checksum(context, crypto, KRB5_KU_OTHER_CKSUM, 0,
+ data, datalen, &cksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+ }
+ if (cksum.checksum.length != siglen) {
+ krb5_set_error_message(context, EINVAL, "pac checksum wrong length");
+ free_Checksum(&cksum);
+ return EINVAL;
+ }
+
+ memcpy(sig, cksum.checksum.data, siglen);
+ free_Checksum(&cksum);
+
+ return 0;
+}
+
+static krb5_error_code
+parse_upn_dns_info(krb5_context context,
+ const struct PAC_INFO_BUFFER *upndnsinfo,
+ const krb5_data *data,
+ krb5_principal *upn_princ,
+ uint32_t *flags,
+ krb5_principal *canon_princ,
+ krb5_data *sid)
+{
+ krb5_error_code ret;
+ krb5_storage *sp = NULL;
+ uint16_t upn_length, upn_offset;
+ uint16_t dns_domain_name_length, dns_domain_name_offset;
+ uint16_t canon_princ_length, canon_princ_offset;
+ uint16_t sid_length, sid_offset;
+ char *upn = NULL;
+ char *dns_domain_name = NULL;
+ char *sam_name = NULL;
+
+ *upn_princ = NULL;
+ *flags = 0;
+ *canon_princ = NULL;
+ krb5_data_zero(sid);
+
+ sp = krb5_storage_from_readonly_mem((const char *)data->data + upndnsinfo->offset,
+ upndnsinfo->buffersize);
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint16(sp, &upn_length), out);
+ CHECK(ret, krb5_ret_uint16(sp, &upn_offset), out);
+ CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_length), out);
+ CHECK(ret, krb5_ret_uint16(sp, &dns_domain_name_offset), out);
+ CHECK(ret, krb5_ret_uint32(sp, flags), out);
+
+ if (*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) {
+ CHECK(ret, krb5_ret_uint16(sp, &canon_princ_length), out);
+ CHECK(ret, krb5_ret_uint16(sp, &canon_princ_offset), out);
+ CHECK(ret, krb5_ret_uint16(sp, &sid_length), out);
+ CHECK(ret, krb5_ret_uint16(sp, &sid_offset), out);
+ } else {
+ canon_princ_length = canon_princ_offset = 0;
+ sid_length = sid_offset = 0;
+ }
+
+ if (upn_offset) {
+ CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, upn_offset,
+ upn_length, &upn), out);
+ }
+ CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, dns_domain_name_offset,
+ dns_domain_name_length, &dns_domain_name), out);
+ if ((*flags & PAC_EXTRA_LOGON_INFO_FLAGS_HAS_SAM_NAME_AND_SID) && canon_princ_offset) {
+ CHECK(ret, _krb5_ret_utf8_from_ucs2le_at_offset(sp, canon_princ_offset,
+ canon_princ_length, &sam_name), out);
+ }
+
+ if (upn_offset) {
+ ret = krb5_parse_name_flags(context,
+ upn,
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE |
+ KRB5_PRINCIPAL_PARSE_NO_DEF_REALM,
+ upn_princ);
+ if (ret)
+ goto out;
+
+ ret = krb5_principal_set_realm(context, *upn_princ, dns_domain_name);
+ if (ret)
+ goto out;
+ }
+
+ if (canon_princ_offset) {
+ ret = krb5_parse_name_flags(context,
+ sam_name,
+ KRB5_PRINCIPAL_PARSE_NO_REALM |
+ KRB5_PRINCIPAL_PARSE_NO_DEF_REALM,
+ canon_princ);
+ if (ret)
+ goto out;
+
+ ret = krb5_principal_set_realm(context, *canon_princ, dns_domain_name);
+ if (ret)
+ goto out;
+ }
+
+ if (sid_offset)
+ CHECK(ret, _krb5_ret_data_at_offset(sp, sid_offset, sid_length, sid), out);
+
+out:
+ free(upn);
+ free(dns_domain_name);
+ free(sam_name);
+
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+#define NTTIME_EPOCH 0x019DB1DED53E8000LL
+
+static uint64_t
+unix2nttime(time_t unix_time)
+{
+ long long wt;
+ wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
+ return wt;
+}
+
+static krb5_error_code
+verify_logonname(krb5_context context,
+ const struct PAC_INFO_BUFFER *logon_name,
+ const krb5_data *data,
+ time_t authtime,
+ krb5_const_principal principal)
+{
+ krb5_error_code ret;
+ uint32_t time1, time2;
+ krb5_storage *sp = NULL;
+ uint16_t len;
+ char *s = NULL;
+ char *principal_string = NULL;
+ char *logon_string = NULL;
+
+ sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset,
+ logon_name->buffersize);
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_ret_uint32(sp, &time1), out);
+ CHECK(ret, krb5_ret_uint32(sp, &time2), out);
+
+ {
+ uint64_t t1, t2;
+ t1 = unix2nttime(authtime);
+ t2 = ((uint64_t)time2 << 32) | time1;
+ /*
+ * When neither the ticket nor the PAC set an explicit authtime,
+ * both times are zero, but relative to different time scales.
+ * So we must compare "not set" values without converting to a
+ * common time reference.
+ */
+ if (t1 != t2 && (t2 != 0 && authtime != 0)) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
+ return EINVAL;
+ }
+ }
+ CHECK(ret, krb5_ret_uint16(sp, &len), out);
+ if (len == 0) {
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "PAC logon name length missing");
+ return EINVAL;
+ }
+
+ s = malloc(len);
+ if (s == NULL) {
+ krb5_storage_free(sp);
+ return krb5_enomem(context);
+ }
+ ret = krb5_storage_read(sp, s, len);
+ if (ret != len) {
+ free(s);
+ krb5_storage_free(sp);
+ krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name");
+ return EINVAL;
+ }
+ krb5_storage_free(sp);
+ {
+ size_t ucs2len = len / 2;
+ uint16_t *ucs2;
+ size_t u8len;
+ unsigned int flags = WIND_RW_LE;
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
+ if (ucs2 == NULL) {
+ free(s);
+ return krb5_enomem(context);
+ }
+
+ ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len);
+ free(s);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
+ return ret;
+ }
+ ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string");
+ return ret;
+ }
+ u8len += 1; /* Add space for NUL */
+ logon_string = malloc(u8len);
+ if (logon_string == NULL) {
+ free(ucs2);
+ return krb5_enomem(context);
+ }
+ ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len);
+ free(ucs2);
+ if (ret) {
+ free(logon_string);
+ krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
+ return ret;
+ }
+ }
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM |
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+ &principal_string);
+ if (ret) {
+ free(logon_string);
+ return ret;
+ }
+
+ if (strcmp(logon_string, principal_string) != 0) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "PAC logon name [%s] mismatch principal name [%s]",
+ logon_string, principal_string);
+ }
+ free(logon_string);
+ free(principal_string);
+ return ret;
+out:
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+build_logon_name(krb5_context context,
+ time_t authtime,
+ krb5_const_principal principal,
+ krb5_data *logon)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+ uint64_t t;
+ char *s, *s2 = NULL;
+ size_t s2_len;
+
+ t = unix2nttime(authtime);
+
+ krb5_data_zero(logon);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ CHECK(ret, krb5_store_uint32(sp, t & 0xffffffff), out);
+ CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
+
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM |
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+ &s);
+ if (ret)
+ goto out;
+
+ {
+ size_t ucs2_len;
+ uint16_t *ucs2;
+ unsigned int flags;
+
+ ret = wind_utf8ucs2_length(s, &ucs2_len);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
+ free(s);
+ return ret;
+ }
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
+ if (ucs2 == NULL) {
+ free(s);
+ return krb5_enomem(context);
+ }
+
+ ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
+ if (ret) {
+ free(ucs2);
+ krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
+ free(s);
+ return ret;
+ } else
+ free(s);
+
+ s2_len = (ucs2_len + 1) * 2;
+ s2 = malloc(s2_len);
+ if (s2 == NULL) {
+ free(ucs2);
+ return krb5_enomem(context);
+ }
+
+ flags = WIND_RW_LE;
+ ret = wind_ucs2write(ucs2, ucs2_len,
+ &flags, s2, &s2_len);
+ free(ucs2);
+ if (ret) {
+ free(s2);
+ krb5_set_error_message(context, ret, "Failed to write to UCS-2 buffer");
+ return ret;
+ }
+
+ /*
+ * we do not want zero termination
+ */
+ s2_len = ucs2_len * 2;
+ }
+
+ CHECK(ret, krb5_store_uint16(sp, s2_len), out);
+
+ ret = krb5_storage_write(sp, s2, s2_len);
+ if (ret != (int)s2_len) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ret = krb5_storage_to_data(sp, logon);
+
+ out:
+ free(s2);
+ krb5_storage_free(sp);
+ return ret;
+}
+
+static krb5_error_code
+parse_attributes_info(krb5_context context,
+ const struct PAC_INFO_BUFFER *attributes_info,
+ const krb5_data *data,
+ uint64_t *pac_attributes)
+{
+ krb5_error_code ret;
+ krb5_storage *sp = NULL;
+ uint32_t flags_length;
+
+ *pac_attributes = 0;
+
+ sp = krb5_storage_from_readonly_mem((const char *)data->data + attributes_info->offset,
+ attributes_info->buffersize);
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ ret = krb5_ret_uint32(sp, &flags_length);
+ if (ret == 0) {
+ if (flags_length > 32)
+ ret = krb5_ret_uint64(sp, pac_attributes);
+ else {
+ uint32_t pac_attributes32 = 0;
+ ret = krb5_ret_uint32(sp, &pac_attributes32);
+ *pac_attributes = pac_attributes32;
+ }
+ }
+
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+/**
+ * Verify the PAC.
+ *
+ * @param context Kerberos 5 context.
+ * @param pac the pac structure returned by krb5_pac_parse().
+ * @param authtime The time of the ticket the PAC belongs to.
+ * @param principal the principal to verify.
+ * @param server The service key, may be given.
+ * @param privsvr The KDC key, may be given.
+
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_pac
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_verify(krb5_context context,
+ const krb5_pac pac,
+ time_t authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server,
+ const krb5_keyblock *privsvr)
+{
+ krb5_error_code ret;
+ /*
+ * If we are in the KDC, we expect back a full signature in the PAC
+ *
+ * This is set up as a separate variable to make it easier if a
+ * subsequent patch is added to make this configurable in the
+ * krb5.conf (or forced into the krb5_context via Samba)
+ */
+ krb5_boolean expect_full_sig = privsvr != NULL;
+
+ /*
+ * If we are on the KDC, then we trust we are not in a realm with
+ * buggy Windows 2008 or similar era DCs that give out HMAC-MD5
+ * signatures over AES keys. DES is also already gone.
+ */
+ krb5_boolean strict_cksumtype_match = expect_full_sig;
+
+ if (pac->server_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing server checksum");
+ return EINVAL;
+ }
+ if (pac->privsvr_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing kdc checksum");
+ return EINVAL;
+ }
+ if (pac->logon_name == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing logon name");
+ return EINVAL;
+ }
+ if (expect_full_sig && pac->full_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL, "PAC missing full checksum");
+ return EINVAL;
+ }
+
+ if (principal != NULL) {
+ ret = verify_logonname(context, pac->logon_name, &pac->data, authtime,
+ principal);
+ if (ret)
+ return ret;
+ }
+
+ if (pac->server_checksum->buffersize < 4 ||
+ pac->privsvr_checksum->buffersize < 4)
+ return EINVAL;
+
+ if (server != NULL || privsvr != NULL)
+ {
+ krb5_data *copy;
+
+ /*
+ * in the service case, clean out data option of the privsvr and
+ * server checksum before checking the checksum.
+ */
+
+ ret = krb5_copy_data(context, &pac->data, &copy);
+ if (ret)
+ return ret;
+
+ memset((char *)copy->data + pac->server_checksum->offset + 4,
+ 0,
+ pac->server_checksum->buffersize - 4);
+
+ memset((char *)copy->data + pac->privsvr_checksum->offset + 4,
+ 0,
+ pac->privsvr_checksum->buffersize - 4);
+
+ if (server != NULL) {
+ ret = verify_checksum(context,
+ pac->server_checksum,
+ &pac->data,
+ copy->data,
+ copy->length,
+ server,
+ strict_cksumtype_match);
+ if (ret) {
+ krb5_free_data(context, copy);
+ return ret;
+ }
+ }
+
+ if (privsvr != NULL && pac->full_checksum != NULL) {
+ /*
+ * in the full checksum case, also clean out the full
+ * checksum before verifying it.
+ */
+ memset((char *)copy->data + pac->full_checksum->offset + 4,
+ 0,
+ pac->full_checksum->buffersize - 4);
+
+ ret = verify_checksum(context,
+ pac->full_checksum,
+ &pac->data,
+ copy->data,
+ copy->length,
+ privsvr,
+ strict_cksumtype_match);
+ if (ret) {
+ krb5_free_data(context, copy);
+ return ret;
+ }
+ }
+
+ krb5_free_data(context, copy);
+ }
+ if (privsvr) {
+ /* The priv checksum covers the server checksum */
+ ret = verify_checksum(context,
+ pac->privsvr_checksum,
+ &pac->data,
+ (char *)pac->data.data
+ + pac->server_checksum->offset + 4,
+ pac->server_checksum->buffersize - 4,
+ privsvr,
+ strict_cksumtype_match);
+ if (ret)
+ return ret;
+
+ if (pac->ticket_sign_data.length != 0) {
+ if (pac->ticket_checksum == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ "PAC missing ticket checksum");
+ return EINVAL;
+ }
+
+ ret = verify_checksum(context, pac->ticket_checksum, &pac->data,
+ pac->ticket_sign_data.data,
+ pac->ticket_sign_data.length, privsvr,
+ strict_cksumtype_match);
+ if (ret)
+ return ret;
+ }
+ }
+
+ if (pac->upn_dns_info &&
+ pac->upn_princ == NULL && pac->canon_princ == NULL && pac->sid.data == NULL) {
+ ret = parse_upn_dns_info(context, pac->upn_dns_info, &pac->data,
+ &pac->upn_princ, &pac->upn_flags,
+ &pac->canon_princ, &pac->sid);
+ if (ret)
+ return ret;
+ }
+
+ if (pac->attributes_info) {
+ ret = parse_attributes_info(context, pac->attributes_info, &pac->data,
+ &pac->pac_attributes);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+fill_zeros(krb5_context context, krb5_storage *sp, size_t len)
+{
+ ssize_t sret;
+ size_t l;
+
+ while (len) {
+ l = len;
+ if (l > sizeof(zeros))
+ l = sizeof(zeros);
+ sret = krb5_storage_write(sp, zeros, l);
+ if (sret != l)
+ return krb5_enomem(context);
+
+ len -= sret;
+ }
+ return 0;
+}
+
+static krb5_error_code
+pac_checksum(krb5_context context,
+ const krb5_keyblock *key,
+ uint32_t *cksumtype,
+ size_t *cksumsize)
+{
+ krb5_cksumtype cktype;
+ krb5_error_code ret;
+ krb5_crypto crypto = NULL;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_get_checksum_type(context, crypto, &cktype);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ if (krb5_checksum_is_keyed(context, cktype) == FALSE) {
+ *cksumtype = CKSUMTYPE_HMAC_MD5;
+ *cksumsize = 16;
+ }
+
+ ret = krb5_checksumsize(context, cktype, cksumsize);
+ if (ret)
+ return ret;
+
+ *cksumtype = (uint32_t)cktype;
+
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pac_sign(krb5_context context,
+ krb5_pac p,
+ time_t authtime,
+ krb5_const_principal principal,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *priv_key,
+ uint16_t rodc_id,
+ krb5_const_principal upn_princ,
+ krb5_const_principal canon_princ,
+ krb5_boolean add_full_sig,
+ uint64_t *pac_attributes, /* optional */
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp = NULL, *spdata = NULL;
+ uint32_t end;
+ size_t server_size, priv_size;
+ uint32_t server_offset = 0, priv_offset = 0, ticket_offset = 0, full_offset = 0;
+ uint32_t server_cksumtype = 0, priv_cksumtype = 0;
+ uint32_t num = 0;
+ uint32_t i, sz;
+ krb5_data logon, d;
+
+ krb5_data_zero(&d);
+ krb5_data_zero(&logon);
+
+ /*
+ * Set convenience buffer pointers.
+ *
+ * This could really stand to be moved to krb5_pac_add_buffer() and/or
+ * utility function, so that when this function gets called they must
+ * already have been set.
+ */
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ if (p->server_checksum == NULL) {
+ p->server_checksum = &p->pac->buffers[i];
+ }
+ if (p->server_checksum != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple server checksums", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ if (p->privsvr_checksum == NULL) {
+ p->privsvr_checksum = &p->pac->buffers[i];
+ }
+ if (p->privsvr_checksum != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple KDC checksums", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ if (p->logon_name == NULL) {
+ p->logon_name = &p->pac->buffers[i];
+ }
+ if (p->logon_name != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple logon names", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_UPN_DNS_INFO) {
+ if (p->upn_dns_info == NULL) {
+ p->upn_dns_info = &p->pac->buffers[i];
+ }
+ if (p->upn_dns_info != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple UPN DNS info buffers", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
+ if (p->ticket_checksum == NULL) {
+ p->ticket_checksum = &p->pac->buffers[i];
+ }
+ if (p->ticket_checksum != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple ticket checksums", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_ATTRIBUTES_INFO) {
+ if (p->attributes_info == NULL) {
+ p->attributes_info = &p->pac->buffers[i];
+ }
+ if (p->attributes_info != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple attributes info buffers", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
+ if (p->full_checksum == NULL) {
+ p->full_checksum = &p->pac->buffers[i];
+ }
+ if (p->full_checksum != &p->pac->buffers[i]) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ krb5_set_error_message(context, ret,
+ N_("PAC has multiple full checksums", ""));
+ goto out;
+ }
+ }
+ }
+
+ /* Count missing-but-necessary buffers */
+ if (p->logon_name == NULL)
+ num++;
+ if (p->server_checksum == NULL)
+ num++;
+ if (p->privsvr_checksum == NULL)
+ num++;
+ if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL)
+ num++;
+ if (add_full_sig && p->full_checksum == NULL)
+ num++;
+
+ /* Allocate any missing-but-necessary buffers */
+ if (num) {
+ void *ptr;
+ uint32_t old_len, len;
+
+ if (p->pac->numbuffers > UINT32_MAX - num) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ ret = pac_header_size(context, p->pac->numbuffers, &old_len);
+ if (ret == 0)
+ ret = pac_header_size(context, p->pac->numbuffers + num, &len);
+ if (ret)
+ goto out;
+
+ ptr = realloc(p->pac, len);
+ if (ptr == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ memset((char *)ptr + old_len, 0, len - old_len);
+ p->pac = ptr;
+
+
+ if (p->logon_name == NULL) {
+ p->logon_name = &p->pac->buffers[p->pac->numbuffers++];
+ p->logon_name->type = PAC_LOGON_NAME;
+ }
+ if (p->server_checksum == NULL) {
+ p->server_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ p->server_checksum->type = PAC_SERVER_CHECKSUM;
+ }
+ if (p->privsvr_checksum == NULL) {
+ p->privsvr_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ p->privsvr_checksum->type = PAC_PRIVSVR_CHECKSUM;
+ }
+ if (p->ticket_sign_data.length != 0 && p->ticket_checksum == NULL) {
+ p->ticket_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ p->ticket_checksum->type = PAC_TICKET_CHECKSUM;
+ }
+ if (add_full_sig && p->full_checksum == NULL) {
+ p->full_checksum = &p->pac->buffers[p->pac->numbuffers++];
+ memset(p->full_checksum, 0, sizeof(*p->full_checksum));
+ p->full_checksum->type = PAC_FULL_CHECKSUM;
+ }
+ }
+
+ /* Calculate LOGON NAME */
+ ret = build_logon_name(context, authtime, principal, &logon);
+
+ /* Set lengths for checksum */
+ if (ret == 0)
+ ret = pac_checksum(context, server_key, &server_cksumtype, &server_size);
+
+ if (ret == 0)
+ ret = pac_checksum(context, priv_key, &priv_cksumtype, &priv_size);
+
+ /* Encode PAC */
+ if (ret == 0) {
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ ret = krb5_enomem(context);
+ }
+
+ if (ret == 0) {
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+ spdata = krb5_storage_emem();
+ if (spdata == NULL)
+ ret = krb5_enomem(context);
+ }
+
+ if (ret)
+ goto out;
+
+ krb5_storage_set_flags(spdata, KRB5_STORAGE_BYTEORDER_LE);
+
+ /* `sp' has the header, `spdata' has the buffers */
+ CHECK(ret, krb5_store_uint32(sp, p->pac->numbuffers), out);
+ CHECK(ret, krb5_store_uint32(sp, p->pac->version), out);
+
+ ret = pac_header_size(context, p->pac->numbuffers, &end);
+ if (ret)
+ goto out;
+
+ /*
+ * For each buffer we write its contents to `spdata' and then append the
+ * PAC_INFO_BUFFER for that buffer into the header in `sp'. The logical
+ * end of the whole thing is kept in `end', which functions as the offset
+ * to write in the buffer's PAC_INFO_BUFFER, then we update it at the
+ * bottom so that the next buffer can be written there.
+ *
+ * TODO? Maybe rewrite all of this so that:
+ *
+ * - we use krb5_pac_add_buffer() to add the buffers we produce
+ * - we use the krb5_data of the concatenated buffers that's maintained by
+ * krb5_pac_add_buffer() so we don't need `spdata' here
+ *
+ * We do way too much here, and that makes this code hard to read. Plus we
+ * throw away all the work done in krb5_pac_add_buffer(). On the other
+ * hand, krb5_pac_add_buffer() has to loop over all the buffers, so if we
+ * call krb5_pac_add_buffer() here in a loop, we'll be accidentally
+ * quadratic, but we only need to loop over adding the buffers we add,
+ * which is very few, so not quite quadratic. We should also cap the
+ * number of buffers we're willing to accept in a PAC we parse to something
+ * reasonable, like a few tens.
+ */
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ uint32_t len;
+ size_t sret;
+ void *ptr = NULL;
+
+ /* store data */
+
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ if (server_size > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len = server_size + 4;
+ if (end > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ server_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, server_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, server_size), out);
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ if (priv_size > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len = priv_size + 4;
+ if (end > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ priv_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, priv_size), out);
+ if (rodc_id != 0) {
+ if (len > UINT32_MAX - sizeof(rodc_id)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len += sizeof(rodc_id);
+ CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out);
+ }
+ } else if (p->ticket_sign_data.length != 0 &&
+ p->pac->buffers[i].type == PAC_TICKET_CHECKSUM) {
+ if (priv_size > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len = priv_size + 4;
+ if (end > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ ticket_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, priv_size), out);
+ if (rodc_id != 0) {
+ if (len > UINT32_MAX - sizeof(rodc_id)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len += sizeof(rodc_id);
+ CHECK(ret, krb5_store_uint16(spdata, rodc_id), out);
+ }
+ } else if (add_full_sig &&
+ p->pac->buffers[i].type == PAC_FULL_CHECKSUM) {
+ if (priv_size > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len = priv_size + 4;
+ if (end > UINT32_MAX - 4) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ full_offset = end + 4;
+ CHECK(ret, krb5_store_uint32(spdata, priv_cksumtype), out);
+ CHECK(ret, fill_zeros(context, spdata, priv_size), out);
+ if (rodc_id != 0) {
+ if (len > UINT32_MAX - sizeof(rodc_id)) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret, "integer overrun");
+ goto out;
+ }
+ len += sizeof(rodc_id);
+ CHECK(ret, fill_zeros(context, spdata, sizeof(rodc_id)), out);
+ }
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ len = krb5_storage_write(spdata, logon.data, logon.length);
+ if (logon.length != len) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto out;
+ }
+ } else {
+ len = p->pac->buffers[i].buffersize;
+ ptr = (char *)p->data.data + p->pac->buffers[i].offset;
+
+ sret = krb5_storage_write(spdata, ptr, len);
+ if (sret != len) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ /* XXX if not aligned, fill_zeros */
+ }
+
+ /* write header */
+ CHECK(ret, krb5_store_uint32(sp, p->pac->buffers[i].type), out);
+ CHECK(ret, krb5_store_uint32(sp, len), out);
+ CHECK(ret, krb5_store_uint64(sp, end), out); /* offset */
+
+ /* advance data endpointer and align */
+ {
+ uint32_t e;
+
+ ret = pac_aligned_size(context, end, len, &e);
+ if (ret == 0 && end + len != e)
+ ret = fill_zeros(context, spdata, e - (end + len));
+ if (ret)
+ goto out;
+ end = e;
+ }
+
+ }
+
+ /* assert (server_offset != 0 && priv_offset != 0); */
+
+ /* export PAC */
+ if (ret == 0)
+ ret = krb5_storage_to_data(spdata, &d);
+ if (ret == 0) {
+ sz = krb5_storage_write(sp, d.data, d.length);
+ if (sz != d.length) {
+ krb5_data_free(&d);
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ }
+ krb5_data_free(&d);
+
+ if (ret == 0)
+ ret = krb5_storage_to_data(sp, &d);
+
+ /* sign */
+ if (ret == 0 && p->ticket_sign_data.length)
+ ret = create_checksum(context, priv_key, priv_cksumtype,
+ p->ticket_sign_data.data,
+ p->ticket_sign_data.length,
+ (char *)d.data + ticket_offset, priv_size);
+ if (ret == 0 && add_full_sig)
+ ret = create_checksum(context, priv_key, priv_cksumtype,
+ d.data, d.length,
+ (char *)d.data + full_offset, priv_size);
+ if (ret == 0 && add_full_sig && rodc_id != 0) {
+ void *buf = (char *)d.data + full_offset + priv_size;
+ krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
+ if (rs == NULL)
+ ret = krb5_enomem(context);
+ else
+ krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
+ if (ret == 0)
+ ret = krb5_store_uint16(rs, rodc_id);
+ krb5_storage_free(rs);
+ }
+ if (ret == 0)
+ ret = create_checksum(context, server_key, server_cksumtype,
+ d.data, d.length,
+ (char *)d.data + server_offset, server_size);
+ if (ret == 0)
+ ret = create_checksum(context, priv_key, priv_cksumtype,
+ (char *)d.data + server_offset, server_size,
+ (char *)d.data + priv_offset, priv_size);
+ if (ret == 0 && rodc_id != 0) {
+ void *buf = (char *)d.data + priv_offset + priv_size;
+ krb5_storage *rs = krb5_storage_from_mem(buf, sizeof(rodc_id));
+ if (rs == NULL)
+ ret = krb5_enomem(context);
+ else
+ krb5_storage_set_flags(rs, KRB5_STORAGE_BYTEORDER_LE);
+ if (ret == 0)
+ ret = krb5_store_uint16(rs, rodc_id);
+ krb5_storage_free(rs);
+ }
+
+ if (ret)
+ goto out;
+
+ /* done */
+ *data = d;
+
+ krb5_data_free(&logon);
+ krb5_storage_free(sp);
+ krb5_storage_free(spdata);
+
+ return 0;
+out:
+ krb5_data_free(&d);
+ krb5_data_free(&logon);
+ if (sp)
+ krb5_storage_free(sp);
+ if (spdata)
+ krb5_storage_free(spdata);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pac_get_kdc_checksum_info(krb5_context context,
+ krb5_const_pac pac,
+ krb5_cksumtype *cstype,
+ uint16_t *rodc_id)
+{
+ krb5_error_code ret;
+ krb5_storage *sp = NULL;
+ const struct PAC_INFO_BUFFER *sig;
+ size_t cksumsize, prefix;
+ uint32_t type = 0;
+
+ *cstype = 0;
+ *rodc_id = 0;
+
+ sig = pac->privsvr_checksum;
+ if (sig == NULL) {
+ krb5_set_error_message(context, KRB5KDC_ERR_BADOPTION,
+ "PAC missing kdc checksum");
+ return KRB5KDC_ERR_BADOPTION;
+ }
+
+ sp = krb5_storage_from_mem((char *)pac->data.data + sig->offset,
+ sig->buffersize);
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ ret = krb5_ret_uint32(sp, &type);
+ if (ret)
+ goto out;
+
+ ret = krb5_checksumsize(context, type, &cksumsize);
+ if (ret)
+ goto out;
+
+ prefix = krb5_storage_seek(sp, 0, SEEK_CUR);
+
+ if ((sig->buffersize - prefix) >= cksumsize + 2) {
+ krb5_storage_seek(sp, cksumsize, SEEK_CUR);
+ ret = krb5_ret_uint16(sp, rodc_id);
+ if (ret)
+ goto out;
+ }
+
+ *cstype = type;
+
+out:
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pac_get_canon_principal(krb5_context context,
+ krb5_const_pac pac,
+ krb5_principal *canon_princ)
+{
+ *canon_princ = NULL;
+
+ if (pac->canon_princ == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ "PAC missing UPN DNS info buffer");
+ return ENOENT;
+ }
+
+ return krb5_copy_principal(context, pac->canon_princ, canon_princ);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pac_get_attributes_info(krb5_context context,
+ krb5_const_pac pac,
+ uint64_t *pac_attributes)
+{
+ *pac_attributes = 0;
+
+ if (pac->attributes_info == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ "PAC missing attributes info buffer");
+ return ENOENT;
+ }
+
+ *pac_attributes = pac->pac_attributes;
+
+ return 0;
+}
+
+static const unsigned char single_zero = '\0';
+static const krb5_data single_zero_pac = { 1, rk_UNCONST(&single_zero) };
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_kdc_pac_ticket_parse(krb5_context context,
+ EncTicketPart *tkt,
+ krb5_boolean *signedticket,
+ krb5_pac *ppac)
+{
+ AuthorizationData *ad = tkt->authorization_data;
+ krb5_pac pac = NULL;
+ unsigned i, j;
+ size_t len = 0;
+ krb5_error_code ret = 0;
+
+ *signedticket = FALSE;
+ *ppac = NULL;
+
+ if (ad == NULL || ad->len == 0)
+ return 0;
+
+ for (i = 0; i < ad->len; i++) {
+ AuthorizationData child;
+
+ if (ad->val[i].ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto out;
+ }
+
+ if (ad->val[i].ad_type != KRB5_AUTHDATA_IF_RELEVANT)
+ continue;
+
+ ret = decode_AuthorizationData(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to decode "
+ "AD-IF-RELEVANT with %d", ret);
+ goto out;
+ }
+
+ for (j = 0; j < child.len; j++) {
+ krb5_data adifr_data = ad->val[i].ad_data;
+ krb5_data pac_data = child.val[j].ad_data;
+ krb5_data recoded_adifr;
+
+ if (child.val[j].ad_type != KRB5_AUTHDATA_WIN2K_PAC)
+ continue;
+
+ if (pac != NULL) {
+ free_AuthorizationData(&child);
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto out;
+ }
+
+ ret = krb5_pac_parse(context,
+ pac_data.data,
+ pac_data.length,
+ &pac);
+ if (ret) {
+ free_AuthorizationData(&child);
+ goto out;
+ }
+
+ if (pac->ticket_checksum == NULL)
+ continue;
+
+ /*
+ * Encode the ticket with the PAC replaced with a single zero
+ * byte, to be used as input data to the ticket signature.
+ */
+
+ child.val[j].ad_data = single_zero_pac;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, recoded_adifr.data,
+ recoded_adifr.length, &child, &len, ret);
+ if (recoded_adifr.length != len)
+ krb5_abortx(context, "Internal error in ASN.1 encoder");
+
+ child.val[j].ad_data = pac_data;
+
+ if (ret) {
+ free_AuthorizationData(&child);
+ goto out;
+ }
+
+ ad->val[i].ad_data = recoded_adifr;
+
+ ASN1_MALLOC_ENCODE(EncTicketPart,
+ pac->ticket_sign_data.data,
+ pac->ticket_sign_data.length, tkt, &len,
+ ret);
+ if (pac->ticket_sign_data.length != len)
+ krb5_abortx(context, "Internal error in ASN.1 encoder");
+
+ ad->val[i].ad_data = adifr_data;
+ krb5_data_free(&recoded_adifr);
+
+ if (ret) {
+ free_AuthorizationData(&child);
+ goto out;
+ }
+
+ *signedticket = TRUE;
+ }
+ free_AuthorizationData(&child);
+ }
+
+out:
+ if (ret) {
+ krb5_pac_free(context, pac);
+ return ret;
+ }
+
+ *ppac = pac;
+
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_kdc_pac_sign_ticket(krb5_context context,
+ const krb5_pac pac,
+ krb5_const_principal client,
+ const krb5_keyblock *server_key,
+ const krb5_keyblock *kdc_key,
+ uint16_t rodc_id,
+ krb5_const_principal upn,
+ krb5_const_principal canon_name,
+ krb5_boolean add_ticket_sig,
+ krb5_boolean add_full_sig,
+ EncTicketPart *tkt,
+ uint64_t *pac_attributes) /* optional */
+{
+ krb5_error_code ret;
+ krb5_data tkt_data;
+ krb5_data rspac;
+
+ krb5_data_zero(&rspac);
+ krb5_data_zero(&tkt_data);
+
+ krb5_data_free(&pac->ticket_sign_data);
+
+ if (add_ticket_sig) {
+ size_t len = 0;
+
+ ret = _kdc_tkt_insert_pac(context, tkt, &single_zero_pac);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(EncTicketPart, tkt_data.data, tkt_data.length,
+ tkt, &len, ret);
+ if(tkt_data.length != len)
+ krb5_abortx(context, "Internal error in ASN.1 encoder");
+ if (ret)
+ return ret;
+
+ ret = remove_AuthorizationData(tkt->authorization_data, 0);
+ if (ret) {
+ krb5_data_free(&tkt_data);
+ return ret;
+ }
+
+ pac->ticket_sign_data = tkt_data;
+ }
+
+ ret = _krb5_pac_sign(context, pac, tkt->authtime, client, server_key,
+ kdc_key, rodc_id, upn, canon_name,
+ add_full_sig,
+ pac_attributes, &rspac);
+ if (ret == 0)
+ ret = _kdc_tkt_insert_pac(context, tkt, &rspac);
+ krb5_data_free(&rspac);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/padata.c b/third_party/heimdal/lib/krb5/padata.c
new file mode 100644
index 0000000..71c9a7f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/padata.c
@@ -0,0 +1,62 @@
+/*
+ * 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 PA_DATA * KRB5_LIB_CALL
+krb5_find_padata(PA_DATA *val, unsigned len, int type, int *idx)
+{
+ for(; *idx < (int)len; (*idx)++)
+ if(val[*idx].padata_type == (unsigned)type)
+ return val + *idx;
+ return NULL;
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_padata_add(krb5_context context, METHOD_DATA *md,
+ int type, void *buf, size_t len)
+{
+ PA_DATA *pa;
+
+ pa = realloc (md->val, (md->len + 1) * sizeof(*md->val));
+ if (pa == NULL)
+ return krb5_enomem(context);
+ md->val = pa;
+
+ pa[md->len].padata_type = type;
+ pa[md->len].padata_value.length = len;
+ pa[md->len].padata_value.data = buf;
+ md->len++;
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/parse-name-test.c b/third_party/heimdal/lib/krb5/parse-name-test.c
new file mode 100644
index 0000000..bb2fc02
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/parse-name-test.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 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 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 <err.h>
+
+enum { MAX_COMPONENTS = 3 };
+
+static struct testcase {
+ const char *input_string;
+ const char *output_string;
+ krb5_realm realm;
+ unsigned ncomponents;
+ char *comp_val[MAX_COMPONENTS];
+ int realmp;
+} tests[] = {
+ {"", "@", "", 1, {""}, FALSE},
+ {"a", "a@", "", 1, {"a"}, FALSE},
+ {"\\n", "\\n@", "", 1, {"\n"}, FALSE},
+ {"\\ ", "\\ @", "", 1, {" "}, FALSE},
+ {"\\t", "\\t@", "", 1, {"\t"}, FALSE},
+ {"\\b", "\\b@", "", 1, {"\b"}, FALSE},
+ {"\\\\", "\\\\@", "", 1, {"\\"}, FALSE},
+ {"\\/", "\\/@", "", 1, {"/"}, FALSE},
+ {"\\@", "\\@@", "", 1, {"@"}, FALSE},
+ {"@", "@", "", 1, {""}, TRUE},
+ {"a/b", "a/b@", "", 2, {"a", "b"}, FALSE},
+ {"a/", "a/@", "", 2, {"a", ""}, FALSE},
+ {"a\\//\\/", "a\\//\\/@", "", 2, {"a/", "/"}, FALSE},
+ {"/a", "/a@", "", 2, {"", "a"}, FALSE},
+ {"\\@@\\@", "\\@@\\@", "@", 1, {"@"}, TRUE},
+ {"a/b/c", "a/b/c@", "", 3, {"a", "b", "c"}, FALSE},
+ {NULL, NULL, "", 0, { NULL }, FALSE}};
+
+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);
+
+ /* to enable realm-less principal name above */
+
+ krb5_set_default_realm(context, "");
+
+ for (t = tests; t->input_string; ++t) {
+ krb5_principal princ;
+ int i, j;
+ char name_buf[1024];
+ char *s;
+
+ ret = krb5_parse_name(context, t->input_string, &princ);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name %s",
+ t->input_string);
+ if (strcmp (t->realm, princ->realm) != 0) {
+ printf ("wrong realm (\"%s\" should be \"%s\")"
+ " for \"%s\"\n",
+ princ->realm, t->realm,
+ t->input_string);
+ val = 1;
+ }
+
+ if (t->ncomponents != princ->name.name_string.len) {
+ printf ("wrong number of components (%u should be %u)"
+ " for \"%s\"\n",
+ princ->name.name_string.len, t->ncomponents,
+ t->input_string);
+ val = 1;
+ } else {
+ for (i = 0; i < t->ncomponents; ++i) {
+ if (strcmp(t->comp_val[i],
+ princ->name.name_string.val[i]) != 0) {
+ printf ("bad component %d (\"%s\" should be \"%s\")"
+ " for \"%s\"\n",
+ i,
+ princ->name.name_string.val[i],
+ t->comp_val[i],
+ t->input_string);
+ val = 1;
+ }
+ }
+ }
+ for (j = 0; j < strlen(t->output_string); ++j) {
+ ret = krb5_unparse_name_fixed(context, princ,
+ name_buf, j);
+ if (ret != ERANGE) {
+ printf ("unparse_name %s with length %d should have failed\n",
+ t->input_string, j);
+ val = 1;
+ break;
+ }
+ }
+ ret = krb5_unparse_name_fixed(context, princ,
+ name_buf, sizeof(name_buf));
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_unparse_name_fixed");
+
+ if (strcmp (t->output_string, name_buf) != 0) {
+ printf ("failed comparing the re-parsed"
+ " (\"%s\" should be \"%s\")\n",
+ name_buf, t->output_string);
+ val = 1;
+ }
+
+ ret = krb5_unparse_name(context, princ, &s);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_unparse_name");
+
+ if (strcmp (t->output_string, s) != 0) {
+ printf ("failed comparing the re-parsed"
+ " (\"%s\" should be \"%s\"\n",
+ s, t->output_string);
+ val = 1;
+ }
+ free(s);
+
+ if (!t->realmp) {
+ for (j = 0; j < strlen(t->input_string); ++j) {
+ ret = krb5_unparse_name_fixed_short(context, princ,
+ name_buf, j);
+ if (ret != ERANGE) {
+ printf ("unparse_name_short %s with length %d"
+ " should have failed\n",
+ t->input_string, j);
+ val = 1;
+ break;
+ }
+ }
+ ret = krb5_unparse_name_fixed_short(context, princ,
+ name_buf, sizeof(name_buf));
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_unparse_name_fixed");
+
+ if (strcmp (t->input_string, name_buf) != 0) {
+ printf ("failed comparing the re-parsed"
+ " (\"%s\" should be \"%s\")\n",
+ name_buf, t->input_string);
+ val = 1;
+ }
+
+ ret = krb5_unparse_name_short(context, princ, &s);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_unparse_name_short");
+
+ if (strcmp (t->input_string, s) != 0) {
+ printf ("failed comparing the re-parsed"
+ " (\"%s\" should be \"%s\"\n",
+ s, t->input_string);
+ val = 1;
+ }
+ free(s);
+ }
+ krb5_free_principal (context, princ);
+ }
+ krb5_free_context(context);
+ return val;
+}
diff --git a/third_party/heimdal/lib/krb5/pcache.c b/third_party/heimdal/lib/krb5/pcache.c
new file mode 100644
index 0000000..9804490
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/pcache.c
@@ -0,0 +1,81 @@
+/***********************************************************************
+ * 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.
+ *
+ **********************************************************************/
+
+#include "krb5_locl.h"
+#include "ccache_plugin.h"
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif
+#include <assert.h>
+
+/*
+ * cc_plugin_register_to_context is executed once per krb5_init_context().
+ * Its job is to register the plugin's krb5_cc_ops structure with the
+ * krb5_context.
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+cc_plugin_register_to_context(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ krb5_cc_ops *ccops = (krb5_cc_ops *)plugctx;
+ krb5_error_code ret;
+
+ if (ccops == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = krb5_cc_register(context, ccops, TRUE);
+ if (ret != 0)
+ *((krb5_error_code *)userctx) = ret;
+
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+static const char *const ccache_plugin_deps[] = { "krb5", NULL };
+
+static const struct heim_plugin_data
+ccache_plugin_data = {
+ "krb5",
+ KRB5_PLUGIN_CCACHE,
+ 0,
+ ccache_plugin_deps,
+ krb5_get_instance
+};
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_load_ccache_plugins(krb5_context context)
+{
+ krb5_error_code userctx = 0;
+
+ (void)_krb5_plugin_run_f(context, &ccache_plugin_data, 0,
+ &userctx, cc_plugin_register_to_context);
+
+ return userctx;
+}
diff --git a/third_party/heimdal/lib/krb5/pkinit-ec.c b/third_party/heimdal/lib/krb5/pkinit-ec.c
new file mode 100644
index 0000000..34cefd5
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/pkinit-ec.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2016 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 <config.h>
+#include <roken.h>
+
+#ifdef PKINIT
+
+/*
+ * As with the other *-ec.c files in Heimdal, this is a bit of a hack.
+ *
+ * The idea is to use OpenSSL for EC because hcrypto doesn't have the
+ * required functionality at this time. To do this we segregate
+ * EC-using code into separate source files and then we arrange for them
+ * to get the OpenSSL headers and not the conflicting hcrypto ones.
+ *
+ * Because of auto-generated *-private.h headers, we end up needing to
+ * make sure various types are defined before we include them, thus the
+ * strange header include order here.
+ */
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#include <openssl/dh.h>
+#define HEIM_NO_CRYPTO_HDRS
+#endif
+
+/*
+ * NO_HCRYPTO_POLLUTION -> don't refer to hcrypto type/function names
+ * that we don't need in this file and which would clash with OpenSSL's
+ * in ways that are difficult to address in cleaner ways.
+ *
+ * In the medium- to long-term what we should do is move all PK in
+ * Heimdal to the newer EVP interfaces for PK and then nothing outside
+ * lib/hcrypto should ever have to include OpenSSL headers, and -more
+ * specifically- the only thing that should ever have to include OpenSSL
+ * headers is the OpenSSL backend to hcrypto.
+ */
+#define NO_HCRYPTO_POLLUTION
+
+#include "krb5_locl.h"
+#include <hcrypto/des.h>
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <pkinit_asn1.h>
+#include <asn1_err.h>
+
+#include <der.h>
+
+krb5_error_code
+_krb5_build_authpack_subjectPK_EC(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ AuthPack *a)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ krb5_error_code ret;
+ ECParameters ecp;
+ unsigned char *p;
+ size_t size;
+ int xlen;
+
+ /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
+
+ ecp.element = choice_ECParameters_namedCurve;
+ ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
+ &ecp.u.namedCurve);
+ if (ret)
+ return ret;
+
+ ALLOC(a->clientPublicValue->algorithm.parameters, 1);
+ if (a->clientPublicValue->algorithm.parameters == NULL) {
+ free_ECParameters(&ecp);
+ return krb5_enomem(context);
+ }
+ ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
+ free_ECParameters(&ecp);
+ if (ret)
+ return ret;
+ if ((int)size != xlen)
+ krb5_abortx(context, "asn1 internal error");
+
+ a->clientPublicValue->algorithm.parameters->data = p;
+ a->clientPublicValue->algorithm.parameters->length = size;
+
+ /* copy in public key */
+
+ ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
+ &a->clientPublicValue->algorithm.algorithm);
+ if (ret)
+ return ret;
+
+#ifdef HAVE_OPENSSL_30
+ ctx->u.eckey = EVP_EC_gen(OSSL_EC_curve_nid2name(NID_X9_62_prime256v1));
+#else
+ ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ if (ctx->u.eckey == NULL)
+ return krb5_enomem(context);
+
+ ret = EC_KEY_generate_key(ctx->u.eckey);
+ if (ret != 1)
+ return EINVAL;
+#endif
+
+#ifdef HAVE_OPENSSL_30
+ xlen = i2d_PublicKey(ctx->u.eckey, NULL);
+#else
+ xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
+#endif
+ if (xlen <= 0)
+ return EINVAL;
+
+ p = malloc(xlen);
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ a->clientPublicValue->subjectPublicKey.data = p;
+
+#ifdef HAVE_OPENSSL_30
+ xlen = i2d_PublicKey(ctx->u.eckey, &p);
+#else
+ xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
+#endif
+ if (xlen <= 0) {
+ a->clientPublicValue->subjectPublicKey.data = NULL;
+ free(p);
+ return EINVAL;
+ }
+
+ a->clientPublicValue->subjectPublicKey.length = xlen * 8;
+
+ return 0;
+
+ /* XXX verify that this is right with RFC3279 */
+#else
+ krb5_set_error_message(context, ENOTSUP,
+ N_("PKINIT: ECDH not supported", ""));
+ return ENOTSUP;
+#endif
+}
+
+krb5_error_code
+_krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ const unsigned char *in,
+ size_t in_sz,
+ unsigned char **out,
+ int *out_sz)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+#ifdef HAVE_OPENSSL_30
+ krb5_error_code ret = 0;
+ EVP_PKEY_CTX *pctx = NULL;
+ EVP_PKEY *template = NULL;
+ EVP_PKEY *public = NULL;
+ size_t shared_len = 0;
+
+ if ((template = EVP_PKEY_new()) == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0 &&
+ EVP_PKEY_copy_parameters(template, ctx->u.eckey) != 1)
+ ret = krb5_enomem(context);
+ if (ret == 0 && (pctx = EVP_PKEY_CTX_new(ctx->u.eckey, NULL)) == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0 && EVP_PKEY_derive_init(pctx) != 1)
+ ret = krb5_enomem(context);
+ if (ret == 0 &&
+ EVP_PKEY_CTX_set_ecdh_kdf_type(pctx, EVP_PKEY_ECDH_KDF_NONE) != 1)
+ ret = krb5_enomem(context);
+ if (ret == 0 &&
+ (public = d2i_PublicKey(EVP_PKEY_EC, &template, &in, in_sz)) == NULL)
+ krb5_set_error_message(context,
+ ret = HX509_PARSING_KEY_FAILED,
+ "PKINIT: Can't parse the KDC's ECDH public key");
+ if (ret == 0 &&
+ EVP_PKEY_derive_set_peer_ex(pctx, public, 1) != 1)
+ krb5_set_error_message(context,
+ ret = KRB5KRB_ERR_GENERIC,
+ "Could not derive ECDH shared secret for PKINIT key exchange "
+ "(EVP_PKEY_derive_set_peer_ex)");
+ if (ret == 0 &&
+ (EVP_PKEY_derive(pctx, NULL, &shared_len) != 1 || shared_len == 0))
+ krb5_set_error_message(context,
+ ret = KRB5KRB_ERR_GENERIC,
+ "Could not derive ECDH shared secret for PKINIT key exchange "
+ "(EVP_PKEY_derive to get length)");
+ if (ret == 0 && shared_len > INT_MAX)
+ krb5_set_error_message(context,
+ ret = KRB5KRB_ERR_GENERIC,
+ "Could not derive ECDH shared secret for PKINIT key exchange "
+ "(shared key too large)");
+ if (ret == 0 && (*out = malloc(shared_len)) == NULL)
+ ret = krb5_enomem(context);
+ if (ret == 0 && EVP_PKEY_derive(pctx, *out, &shared_len) != 1)
+ krb5_set_error_message(context,
+ ret = KRB5KRB_ERR_GENERIC,
+ "Could not derive ECDH shared secret for PKINIT key exchange "
+ "(EVP_PKEY_derive)");
+ if (ret == 0)
+ *out_sz = shared_len;
+ EVP_PKEY_CTX_free(pctx); // move
+ EVP_PKEY_free(template);
+
+ return ret;
+#else
+ krb5_error_code ret = 0;
+ int dh_gen_keylen;
+
+ const EC_GROUP *group;
+ EC_KEY *public = NULL;
+
+ group = EC_KEY_get0_group(ctx->u.eckey);
+
+ public = EC_KEY_new();
+ if (public == NULL)
+ return krb5_enomem(context);
+ if (EC_KEY_set_group(public, group) != 1) {
+ EC_KEY_free(public);
+ return krb5_enomem(context);
+ }
+
+ if (o2i_ECPublicKey(&public, &in, in_sz) == NULL) {
+ EC_KEY_free(public);
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Can't parse ECDH public key", ""));
+ return ret;
+ }
+
+ *out_sz = (EC_GROUP_get_degree(group) + 7) / 8;
+ if (*out_sz < 0)
+ return EOVERFLOW;
+ *out = malloc(*out_sz);
+ if (*out == NULL) {
+ EC_KEY_free(public);
+ return krb5_enomem(context);
+ }
+ dh_gen_keylen = ECDH_compute_key(*out, *out_sz,
+ EC_KEY_get0_public_key(public),
+ ctx->u.eckey, NULL);
+ EC_KEY_free(public);
+ if (dh_gen_keylen <= 0) {
+ ret = KRB5KRB_ERR_GENERIC;
+ dh_gen_keylen = 0;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Can't compute ECDH public key", ""));
+ free(*out);
+ *out = NULL;
+ *out_sz = 0;
+ }
+ *out_sz = dh_gen_keylen;
+
+ return ret;
+#endif
+#else
+ krb5_set_error_message(context, ENOTSUP,
+ N_("PKINIT: ECDH not supported", ""));
+ return ENOTSUP;
+#endif
+}
+
+void
+_krb5_pk_eckey_free(void *eckey)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+#ifdef HAVE_OPENSSL_30
+ EVP_PKEY_free(eckey);
+#else
+ EC_KEY_free(eckey);
+#endif
+#endif
+}
+
+#else
+
+static char lib_krb5_pkinit_ec_c = '\0';
+
+#endif
diff --git a/third_party/heimdal/lib/krb5/pkinit.c b/third_party/heimdal/lib/krb5/pkinit.c
new file mode 100644
index 0000000..6dcdcfa
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/pkinit.c
@@ -0,0 +1,2670 @@
+/*
+ * Copyright (c) 2003 - 2016 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"
+
+struct krb5_dh_moduli {
+ char *name;
+ unsigned long bits;
+ heim_integer p;
+ heim_integer g;
+ heim_integer q;
+};
+
+#ifdef PKINIT
+
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <pkinit_asn1.h>
+#include <asn1_err.h>
+
+#include <der.h>
+
+struct krb5_pk_cert {
+ hx509_cert cert;
+};
+
+static void
+pk_copy_error(krb5_context context,
+ hx509_context hx509ctx,
+ int hxret,
+ const char *fmt,
+ ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_pk_cert_free(struct krb5_pk_cert *cert)
+{
+ if (cert->cert) {
+ hx509_cert_free(cert->cert);
+ }
+ free(cert);
+}
+
+static krb5_error_code
+BN_to_integer(krb5_context context, BIGNUM *bn, heim_integer *integer)
+{
+ integer->length = BN_num_bytes(bn);
+ integer->data = malloc(integer->length);
+ if (integer->data == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ BN_bn2bin(bn, integer->data);
+ integer->negative = BN_is_negative(bn);
+ return 0;
+}
+
+static BIGNUM *
+integer_to_BN(krb5_context context, const char *field, const heim_integer *f)
+{
+ BIGNUM *bn;
+
+ bn = BN_bin2bn((const unsigned char *)f->data, f->length, NULL);
+ if (bn == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("PKINIT: parsing BN failed %s", ""), field);
+ return NULL;
+ }
+ BN_set_negative(bn, f->negative);
+ return bn;
+}
+
+static krb5_error_code
+select_dh_group(krb5_context context, DH *dh, unsigned long min_bits,
+ struct krb5_dh_moduli **moduli)
+{
+ const struct krb5_dh_moduli *m;
+
+ if (moduli[0] == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("Did not find a DH group parameter "
+ "matching requirement of %lu bits", ""),
+ min_bits);
+ return EINVAL;
+ }
+
+ if (min_bits == 0) {
+ m = moduli[1]; /* XXX */
+ if (m == NULL)
+ m = moduli[0]; /* XXX */
+ } else {
+ int i;
+ for (i = 0; moduli[i] != NULL; i++) {
+ if (moduli[i]->bits >= min_bits)
+ break;
+ }
+ if (moduli[i] == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("Did not find a DH group parameter "
+ "matching requirement of %lu bits", ""),
+ min_bits);
+ return EINVAL;
+ }
+ m = moduli[i];
+ }
+
+ dh->p = integer_to_BN(context, "p", &m->p);
+ if (dh->p == NULL)
+ return ENOMEM;
+ dh->g = integer_to_BN(context, "g", &m->g);
+ if (dh->g == NULL)
+ return ENOMEM;
+ dh->q = integer_to_BN(context, "q", &m->q);
+ if (dh->q == NULL)
+ return ENOMEM;
+
+ return 0;
+}
+
+struct certfind {
+ const char *type;
+ const heim_oid *oid;
+};
+
+/*
+ * Try searchin the key by to use by first looking for for PK-INIT
+ * EKU, then the Microsoft smart card EKU and last, no special EKU at all.
+ */
+
+static krb5_error_code
+find_cert(krb5_context context, struct krb5_pk_identity *id,
+ hx509_query *q, hx509_cert *cert)
+{
+ struct certfind cf[4] = {
+ { "MobileMe EKU", NULL },
+ { "PKINIT EKU", NULL },
+ { "MS EKU", NULL },
+ { "any (or no)", NULL }
+ };
+ int ret = HX509_CERT_NOT_FOUND;
+ size_t i, start = 1;
+ unsigned oids[] = { 1, 2, 840, 113635, 100, 3, 2, 1 };
+ const heim_oid mobileMe = { sizeof(oids)/sizeof(oids[0]), oids };
+
+
+ if (id->flags & PKINIT_BTMM)
+ start = 0;
+
+ cf[0].oid = &mobileMe;
+ cf[1].oid = &asn1_oid_id_pkekuoid;
+ cf[2].oid = &asn1_oid_id_pkinit_ms_eku;
+ cf[3].oid = NULL;
+
+ for (i = start; i < sizeof(cf)/sizeof(cf[0]); i++) {
+ ret = hx509_query_match_eku(q, cf[i].oid);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed setting %s OID", cf[i].type);
+ return ret;
+ }
+
+ ret = hx509_certs_find(context->hx509ctx, id->certs, q, cert);
+ if (ret == 0)
+ break;
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed finding certificate with %s OID", cf[i].type);
+ }
+ return ret;
+}
+
+
+static krb5_error_code
+create_signature(krb5_context context,
+ const heim_oid *eContentType,
+ krb5_data *eContent,
+ struct krb5_pk_identity *id,
+ hx509_peer_info peer,
+ krb5_data *sd_data)
+{
+ int ret, flags = 0;
+
+ if (id->cert == NULL)
+ flags |= HX509_CMS_SIGNATURE_NO_SIGNER;
+
+ ret = hx509_cms_create_signed_1(context->hx509ctx,
+ flags,
+ eContentType,
+ eContent->data,
+ eContent->length,
+ NULL,
+ id->cert,
+ peer,
+ NULL,
+ id->certs,
+ sd_data);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Create CMS signedData");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int KRB5_LIB_CALL
+cert2epi(hx509_context context, void *ctx, hx509_cert c)
+{
+ ExternalPrincipalIdentifiers *ids = ctx;
+ ExternalPrincipalIdentifier id;
+ hx509_name subject = NULL;
+ void *p;
+ int ret;
+
+ if (ids->len > 10)
+ return 0;
+
+ memset(&id, 0, sizeof(id));
+
+ ret = hx509_cert_get_subject(c, &subject);
+ if (ret)
+ return ret;
+
+ if (hx509_name_is_null_p(subject) != 0) {
+
+ id.subjectName = calloc(1, sizeof(*id.subjectName));
+ if (id.subjectName == NULL) {
+ hx509_name_free(&subject);
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ ret = hx509_name_binary(subject, id.subjectName);
+ if (ret) {
+ hx509_name_free(&subject);
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+ }
+ hx509_name_free(&subject);
+
+
+ id.issuerAndSerialNumber = calloc(1, sizeof(*id.issuerAndSerialNumber));
+ if (id.issuerAndSerialNumber == NULL) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ {
+ IssuerAndSerialNumber iasn;
+ hx509_name issuer;
+ size_t size = 0;
+
+ memset(&iasn, 0, sizeof(iasn));
+
+ ret = hx509_cert_get_issuer(c, &issuer);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ret = hx509_name_to_Name(issuer, &iasn.issuer);
+ hx509_name_free(&issuer);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ret = hx509_cert_get_serialnumber(c, &iasn.serialNumber);
+ if (ret) {
+ free_IssuerAndSerialNumber(&iasn);
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(IssuerAndSerialNumber,
+ id.issuerAndSerialNumber->data,
+ id.issuerAndSerialNumber->length,
+ &iasn, &size, ret);
+ free_IssuerAndSerialNumber(&iasn);
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ret;
+ }
+ if (id.issuerAndSerialNumber->length != size)
+ abort();
+ }
+
+ id.subjectKeyIdentifier = NULL;
+
+ p = realloc(ids->val, sizeof(ids->val[0]) * (ids->len + 1));
+ if (p == NULL) {
+ free_ExternalPrincipalIdentifier(&id);
+ return ENOMEM;
+ }
+
+ ids->val = p;
+ ids->val[ids->len] = id;
+ ids->len++;
+
+ return 0;
+}
+
+static krb5_error_code
+build_edi(krb5_context context,
+ hx509_context hx509ctx,
+ hx509_certs certs,
+ ExternalPrincipalIdentifiers *ids)
+{
+ return hx509_certs_iter_f(hx509ctx, certs, cert2epi, ids);
+}
+
+static krb5_error_code
+build_auth_pack(krb5_context context,
+ unsigned nonce,
+ krb5_pk_init_ctx ctx,
+ const KDC_REQ_BODY *body,
+ AuthPack *a)
+{
+ size_t buf_size, len = 0;
+ krb5_error_code ret;
+ void *buf;
+ krb5_timestamp sec;
+ int32_t usec;
+ Checksum checksum;
+
+ krb5_clear_error_message(context);
+
+ memset(&checksum, 0, sizeof(checksum));
+
+ krb5_us_timeofday(context, &sec, &usec);
+ a->pkAuthenticator.ctime = sec;
+ a->pkAuthenticator.nonce = nonce;
+
+ 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");
+
+ ret = krb5_create_checksum(context,
+ NULL,
+ 0,
+ CKSUMTYPE_SHA1,
+ buf,
+ len,
+ &checksum);
+ free(buf);
+ if (ret)
+ return ret;
+
+ ALLOC(a->pkAuthenticator.paChecksum, 1);
+ if (a->pkAuthenticator.paChecksum == NULL) {
+ return krb5_enomem(context);
+ }
+
+ ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
+ checksum.checksum.data, checksum.checksum.length);
+ free_Checksum(&checksum);
+ if (ret)
+ return ret;
+
+ if (ctx->keyex == USE_DH || ctx->keyex == USE_ECDH) {
+ const char *moduli_file;
+ unsigned long dh_min_bits;
+ krb5_data dhbuf;
+ size_t size = 0;
+
+ krb5_data_zero(&dhbuf);
+
+
+
+ moduli_file = krb5_config_get_string(context, NULL,
+ "libdefaults",
+ "moduli",
+ NULL);
+
+ dh_min_bits =
+ krb5_config_get_int_default(context, NULL, 0,
+ "libdefaults",
+ "pkinit_dh_min_bits",
+ NULL);
+
+ ret = _krb5_parse_moduli(context, moduli_file, &ctx->m);
+ if (ret)
+ return ret;
+
+ ctx->u.dh = DH_new();
+ if (ctx->u.dh == NULL)
+ return krb5_enomem(context);
+
+ ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
+ if (ret)
+ return ret;
+
+ if (DH_generate_key(ctx->u.dh) != 1) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("pkinit: failed to generate DH key", ""));
+ return ENOMEM;
+ }
+
+
+ if (1 /* support_cached_dh */) {
+ ALLOC(a->clientDHNonce, 1);
+ if (a->clientDHNonce == NULL) {
+ krb5_clear_error_message(context);
+ return ENOMEM;
+ }
+ ret = krb5_data_alloc(a->clientDHNonce, 40);
+ if (a->clientDHNonce == NULL) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ ret = RAND_bytes(a->clientDHNonce->data, a->clientDHNonce->length);
+ if (ret != 1)
+ return KRB5_CRYPTO_INTERNAL;
+ ret = krb5_copy_data(context, a->clientDHNonce,
+ &ctx->clientDHNonce);
+ if (ret)
+ return ret;
+ }
+
+ ALLOC(a->clientPublicValue, 1);
+ if (a->clientPublicValue == NULL)
+ return ENOMEM;
+
+ if (ctx->keyex == USE_DH) {
+ DH *dh = ctx->u.dh;
+ DomainParameters dp;
+ heim_integer dh_pub_key;
+
+ ret = der_copy_oid(&asn1_oid_id_dhpublicnumber,
+ &a->clientPublicValue->algorithm.algorithm);
+ if (ret)
+ return ret;
+
+ memset(&dp, 0, sizeof(dp));
+
+ ret = BN_to_integer(context, dh->p, &dp.p);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ ret = BN_to_integer(context, dh->g, &dp.g);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ if (dh->q && BN_num_bits(dh->q)) {
+ /*
+ * The q parameter is required, but MSFT made it optional.
+ * It's only required in order to verify the domain parameters
+ * -- the security of the DH group --, but we validate groups
+ * against known groups rather than accepting arbitrary groups
+ * chosen by the peer, so we really don't need to have put it
+ * on the wire. Because these are Oakley groups, and the
+ * primes are Sophie Germain primes, q is p>>1 and we can
+ * compute it on the fly like MIT Kerberos does, but we'd have
+ * to implement BN_rshift1().
+ */
+ dp.q = calloc(1, sizeof(*dp.q));
+ if (dp.q == NULL) {
+ free_DomainParameters(&dp);
+ return ENOMEM;
+ }
+ ret = BN_to_integer(context, dh->q, dp.q);
+ if (ret) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+ }
+ dp.j = NULL;
+ dp.validationParms = NULL;
+
+ a->clientPublicValue->algorithm.parameters =
+ malloc(sizeof(*a->clientPublicValue->algorithm.parameters));
+ if (a->clientPublicValue->algorithm.parameters == NULL) {
+ free_DomainParameters(&dp);
+ return ret;
+ }
+
+ ASN1_MALLOC_ENCODE(DomainParameters,
+ a->clientPublicValue->algorithm.parameters->data,
+ a->clientPublicValue->algorithm.parameters->length,
+ &dp, &size, ret);
+ free_DomainParameters(&dp);
+ if (ret)
+ return ret;
+ if (size != a->clientPublicValue->algorithm.parameters->length)
+ krb5_abortx(context, "Internal ASN1 encoder error");
+
+ ret = BN_to_integer(context, dh->pub_key, &dh_pub_key);
+ if (ret)
+ return ret;
+
+ ASN1_MALLOC_ENCODE(DHPublicKey, dhbuf.data, dhbuf.length,
+ &dh_pub_key, &size, ret);
+ der_free_heim_integer(&dh_pub_key);
+ if (ret)
+ return ret;
+ if (size != dhbuf.length)
+ krb5_abortx(context, "asn1 internal error");
+ a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
+ a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
+ } else if (ctx->keyex == USE_ECDH) {
+ ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
+ if (ret)
+ return ret;
+ } else
+ krb5_abortx(context, "internal error");
+ }
+
+ {
+ a->supportedCMSTypes = calloc(1, sizeof(*a->supportedCMSTypes));
+ if (a->supportedCMSTypes == NULL)
+ return ENOMEM;
+
+ ret = hx509_crypto_available(context->hx509ctx, HX509_SELECT_ALL,
+ ctx->id->cert,
+ &a->supportedCMSTypes->val,
+ &a->supportedCMSTypes->len);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pk_mk_ContentInfo(krb5_context context,
+ const krb5_data *buf,
+ const heim_oid *oid,
+ struct ContentInfo *content_info)
+{
+ krb5_error_code ret;
+
+ ret = der_copy_oid(oid, &content_info->contentType);
+ if (ret)
+ return ret;
+ ALLOC(content_info->content, 1);
+ if (content_info->content == NULL)
+ return ENOMEM;
+ content_info->content->data = malloc(buf->length);
+ if (content_info->content->data == NULL)
+ return ENOMEM;
+ memcpy(content_info->content->data, buf->data, buf->length);
+ content_info->content->length = buf->length;
+ return 0;
+}
+
+static krb5_error_code
+pk_mk_padata(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ const KDC_REQ_BODY *req_body,
+ unsigned nonce,
+ METHOD_DATA *md)
+{
+ struct ContentInfo content_info;
+ krb5_error_code ret;
+ const heim_oid *oid = NULL;
+ size_t size = 0;
+ krb5_data buf, sd_buf;
+ int pa_type = -1;
+
+ krb5_data_zero(&buf);
+ krb5_data_zero(&sd_buf);
+ memset(&content_info, 0, sizeof(content_info));
+
+ if (ctx->type == PKINIT_WIN2K) {
+ AuthPack_Win2k ap;
+ krb5_timestamp sec;
+ int32_t usec;
+
+ memset(&ap, 0, sizeof(ap));
+
+ /* fill in PKAuthenticator */
+ ret = copy_PrincipalName(req_body->sname, &ap.pkAuthenticator.kdcName);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ ret = copy_Realm(&req_body->realm, &ap.pkAuthenticator.kdcRealm);
+ if (ret) {
+ free_AuthPack_Win2k(&ap);
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ krb5_us_timeofday(context, &sec, &usec);
+ ap.pkAuthenticator.ctime = sec;
+ ap.pkAuthenticator.cusec = usec;
+ ap.pkAuthenticator.nonce = nonce;
+
+ ASN1_MALLOC_ENCODE(AuthPack_Win2k, buf.data, buf.length,
+ &ap, &size, ret);
+ free_AuthPack_Win2k(&ap);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed encoding AuthPackWin: %d", ""),
+ (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "internal ASN1 encoder error");
+
+ oid = &asn1_oid_id_pkcs7_data;
+ } else if (ctx->type == PKINIT_27) {
+ AuthPack ap;
+
+ memset(&ap, 0, sizeof(ap));
+
+ ret = build_auth_pack(context, nonce, ctx, req_body, &ap);
+ if (ret) {
+ free_AuthPack(&ap);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(AuthPack, buf.data, buf.length, &ap, &size, ret);
+ free_AuthPack(&ap);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed encoding AuthPack: %d", ""),
+ (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "internal ASN1 encoder error");
+
+ oid = &asn1_oid_id_pkauthdata;
+ } else
+ krb5_abortx(context, "internal pkinit error");
+
+ ret = create_signature(context, oid, &buf, ctx->id,
+ ctx->peer, &sd_buf);
+ krb5_data_free(&buf);
+ if (ret)
+ goto out;
+
+ ret = hx509_cms_wrap_ContentInfo(&asn1_oid_id_pkcs7_signedData, &sd_buf, &buf);
+ krb5_data_free(&sd_buf);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("ContentInfo wrapping of signedData failed",""));
+ goto out;
+ }
+
+ if (ctx->type == PKINIT_WIN2K) {
+ PA_PK_AS_REQ_Win2k winreq;
+
+ pa_type = KRB5_PADATA_PK_AS_REQ_WIN;
+
+ memset(&winreq, 0, sizeof(winreq));
+
+ winreq.signed_auth_pack = buf;
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REQ_Win2k, buf.data, buf.length,
+ &winreq, &size, ret);
+ free_PA_PK_AS_REQ_Win2k(&winreq);
+
+ } else if (ctx->type == PKINIT_27) {
+ PA_PK_AS_REQ req;
+
+ pa_type = KRB5_PADATA_PK_AS_REQ;
+
+ memset(&req, 0, sizeof(req));
+ req.signedAuthPack = buf;
+
+ if (ctx->trustedCertifiers) {
+
+ req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
+ if (req.trustedCertifiers == NULL) {
+ ret = krb5_enomem(context);
+ free_PA_PK_AS_REQ(&req);
+ goto out;
+ }
+ ret = build_edi(context, context->hx509ctx,
+ ctx->id->anchors, req.trustedCertifiers);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("pk-init: failed to build "
+ "trustedCertifiers", ""));
+ free_PA_PK_AS_REQ(&req);
+ goto out;
+ }
+ }
+ req.kdcPkId = NULL;
+
+ ASN1_MALLOC_ENCODE(PA_PK_AS_REQ, buf.data, buf.length,
+ &req, &size, ret);
+
+ free_PA_PK_AS_REQ(&req);
+
+ } else
+ krb5_abortx(context, "internal pkinit error");
+ if (ret) {
+ krb5_set_error_message(context, ret, "PA-PK-AS-REQ %d", (int)ret);
+ goto out;
+ }
+ if (buf.length != size)
+ krb5_abortx(context, "Internal ASN1 encoder error");
+
+ ret = krb5_padata_add(context, md, pa_type, buf.data, buf.length);
+ if (ret)
+ free(buf.data);
+
+ if (ret == 0)
+ ret = krb5_padata_add(context, md, KRB5_PADATA_PK_AS_09_BINDING, NULL, 0);
+
+ out:
+ free_ContentInfo(&content_info);
+
+ return ret;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pk_mk_padata(krb5_context context,
+ void *c,
+ int ic_flags,
+ int win2k,
+ const KDC_REQ_BODY *req_body,
+ unsigned nonce,
+ METHOD_DATA *md)
+{
+ krb5_pk_init_ctx ctx = c;
+ int win2k_compat;
+
+ if (ctx->id->certs == NULL && ctx->anonymous == 0) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_PRIVATE_KEY,
+ N_("PKINIT: No user certificate given", ""));
+ return HEIM_PKINIT_NO_PRIVATE_KEY;
+ }
+
+ win2k_compat = krb5_config_get_bool_default(context, NULL,
+ win2k,
+ "realms",
+ req_body->realm,
+ "pkinit_win2k",
+ NULL);
+
+ if (win2k_compat) {
+ ctx->require_binding =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_win2k_require_binding",
+ NULL);
+ ctx->type = PKINIT_WIN2K;
+ } else
+ ctx->type = PKINIT_27;
+
+ ctx->require_eku =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_eku",
+ NULL);
+ if (ic_flags & KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK)
+ ctx->require_eku = 0;
+ if (ctx->id->flags & (PKINIT_BTMM | PKINIT_NO_KDC_ANCHOR))
+ ctx->require_eku = 0;
+
+ ctx->require_krbtgt_otherName =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_krbtgt_otherName",
+ NULL);
+ if (ic_flags & KRB5_INIT_CREDS_PKINIT_NO_KRBTGT_OTHERNAME_CHECK)
+ ctx->require_krbtgt_otherName = FALSE;
+
+ ctx->require_hostname_match =
+ krb5_config_get_bool_default(context, NULL,
+ FALSE,
+ "realms",
+ req_body->realm,
+ "pkinit_require_hostname_match",
+ NULL);
+
+ ctx->trustedCertifiers =
+ krb5_config_get_bool_default(context, NULL,
+ TRUE,
+ "realms",
+ req_body->realm,
+ "pkinit_trustedCertifiers",
+ NULL);
+
+ return pk_mk_padata(context, ctx, req_body, nonce, md);
+}
+
+static krb5_error_code
+pk_verify_sign(krb5_context context,
+ const void *data,
+ size_t length,
+ struct krb5_pk_identity *id,
+ heim_oid *contentType,
+ krb5_data *content,
+ struct krb5_pk_cert **signer)
+{
+ hx509_certs signer_certs;
+ int ret;
+ unsigned flags = 0, verify_flags = 0;
+
+ *signer = NULL;
+
+ if (id->flags & PKINIT_BTMM) {
+ flags |= HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH;
+ flags |= HX509_CMS_VS_NO_KU_CHECK;
+ flags |= HX509_CMS_VS_NO_VALIDATE;
+ }
+ if (id->flags & PKINIT_NO_KDC_ANCHOR)
+ flags |= HX509_CMS_VS_NO_VALIDATE;
+
+ ret = hx509_cms_verify_signed_ext(context->hx509ctx,
+ id->verify_ctx,
+ flags,
+ data,
+ length,
+ NULL,
+ id->certpool,
+ contentType,
+ content,
+ &signer_certs,
+ &verify_flags);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "CMS verify signed failed");
+ return ret;
+ }
+
+ heim_assert((verify_flags & HX509_CMS_VSE_VALIDATED) ||
+ (id->flags & PKINIT_NO_KDC_ANCHOR),
+ "Either PKINIT signer must be validated, or NO_KDC_ANCHOR must be set");
+
+ if ((verify_flags & HX509_CMS_VSE_VALIDATED) == 0)
+ goto out;
+
+ *signer = calloc(1, sizeof(**signer));
+ if (*signer == NULL) {
+ krb5_clear_error_message(context);
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = hx509_get_one_cert(context->hx509ctx, signer_certs, &(*signer)->cert);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to get one of the signer certs");
+ goto out;
+ }
+
+ out:
+ hx509_certs_free(&signer_certs);
+ if (ret) {
+ if (*signer) {
+ hx509_cert_free((*signer)->cert);
+ free(*signer);
+ *signer = NULL;
+ }
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+get_reply_key_win(krb5_context context,
+ const krb5_data *content,
+ unsigned nonce,
+ krb5_keyblock **key)
+{
+ ReplyKeyPack_Win2k key_pack;
+ krb5_error_code ret;
+ size_t size;
+
+ ret = decode_ReplyKeyPack_Win2k(content->data,
+ content->length,
+ &key_pack,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT decoding reply key failed", ""));
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return ret;
+ }
+
+ if ((unsigned)key_pack.nonce != nonce) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT enckey nonce is wrong", ""));
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ free_ReplyKeyPack_Win2k(&key_pack);
+ return krb5_enomem(context);
+ }
+
+ ret = copy_EncryptionKey(&key_pack.replyKey, *key);
+ free_ReplyKeyPack_Win2k(&key_pack);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT failed copying reply key", ""));
+ free(*key);
+ *key = NULL;
+ }
+
+ return ret;
+}
+
+static krb5_error_code
+get_reply_key(krb5_context context,
+ const krb5_data *content,
+ const krb5_data *req_buffer,
+ krb5_keyblock **key)
+{
+ ReplyKeyPack key_pack;
+ krb5_error_code ret;
+ size_t size;
+
+ ret = decode_ReplyKeyPack(content->data,
+ content->length,
+ &key_pack,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT decoding reply key failed", ""));
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+
+ {
+ krb5_crypto crypto;
+
+ /*
+ * XXX Verify kp.replyKey is a allowed enctype in the
+ * configuration file
+ */
+
+ ret = krb5_crypto_init(context, &key_pack.replyKey, 0, &crypto);
+ if (ret) {
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+
+ ret = krb5_verify_checksum(context, crypto, 6,
+ req_buffer->data, req_buffer->length,
+ &key_pack.asChecksum);
+ krb5_crypto_destroy(context, crypto);
+ if (ret) {
+ free_ReplyKeyPack(&key_pack);
+ return ret;
+ }
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ free_ReplyKeyPack(&key_pack);
+ return krb5_enomem(context);
+ }
+
+ ret = copy_EncryptionKey(&key_pack.replyKey, *key);
+ free_ReplyKeyPack(&key_pack);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT failed copying reply key", ""));
+ free(*key);
+ *key = NULL;
+ }
+
+ return ret;
+}
+
+
+static krb5_error_code
+pk_verify_host(krb5_context context,
+ const char *realm,
+ struct krb5_pk_init_ctx_data *ctx,
+ struct krb5_pk_cert *host)
+{
+ krb5_error_code ret = 0;
+
+ if (ctx->require_eku) {
+ ret = hx509_cert_check_eku(context->hx509ctx, host->cert,
+ &asn1_oid_id_pkkdcekuoid, 0);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("No PK-INIT KDC EKU in kdc certificate", ""));
+ return ret;
+ }
+ }
+ if (ctx->require_krbtgt_otherName) {
+ hx509_octet_string_list list;
+ size_t i;
+ int matched = 0;
+
+ ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
+ host->cert,
+ &asn1_oid_id_pkinit_san,
+ &list);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to find the PK-INIT "
+ "subjectAltName in the KDC "
+ "certificate", ""));
+
+ return ret;
+ }
+
+ /*
+ * subjectAltNames are multi-valued, and a single KDC may serve
+ * multiple realms. The SAN validation here must accept
+ * the KDC's cert if *any* of the SANs match the expected KDC.
+ * It is OK for *some* of the SANs to not match, provided at least
+ * one does.
+ */
+ for (i = 0; matched == 0 && i < list.len; i++) {
+ KRB5PrincipalName r;
+
+ ret = decode_KRB5PrincipalName(list.val[i].data,
+ list.val[i].length,
+ &r,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode the PK-INIT "
+ "subjectAltName in the "
+ "KDC certificate", ""));
+
+ break;
+ }
+
+ if (r.principalName.name_string.len == 2 &&
+ strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
+ && strcmp(r.principalName.name_string.val[1], realm) == 0
+ && strcmp(r.realm, realm) == 0)
+ matched = 1;
+
+ free_KRB5PrincipalName(&r);
+ }
+ hx509_free_octet_string_list(&list);
+
+ if (matched == 0 &&
+ (ctx->id->flags & PKINIT_NO_KDC_ANCHOR) == 0) {
+ ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
+ /* XXX: Lost in translation... */
+ krb5_set_error_message(context, ret,
+ N_("KDC has wrong realm name in "
+ "the certificate", ""));
+ }
+ }
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static krb5_error_code
+pk_rd_pa_reply_enckey(krb5_context context,
+ int type,
+ const heim_octet_string *indata,
+ const heim_oid *dataType,
+ const char *realm,
+ krb5_pk_init_ctx ctx,
+ krb5_enctype etype,
+ unsigned nonce,
+ const krb5_data *req_buffer,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ krb5_error_code ret;
+ struct krb5_pk_cert *host = NULL;
+ krb5_data content;
+ heim_octet_string unwrapped;
+ heim_oid contentType = { 0, NULL };
+ int flags = HX509_CMS_UE_DONT_REQUIRE_KU_ENCIPHERMENT;
+
+ if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_envelopedData, dataType)) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: Invalid content type", ""));
+ return EINVAL;
+ }
+
+ if (ctx->type == PKINIT_WIN2K)
+ flags |= HX509_CMS_UE_ALLOW_WEAK;
+
+ ret = hx509_cms_unenvelope(context->hx509ctx,
+ ctx->id->certs,
+ flags,
+ indata->data,
+ indata->length,
+ NULL,
+ 0,
+ &contentType,
+ &content);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to unenvelope CMS data in PK-INIT reply");
+ return ret;
+ }
+ der_free_oid(&contentType);
+
+ /* win2k uses ContentInfo */
+ if (type == PKINIT_WIN2K) {
+ heim_oid type2;
+
+ ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
+ if (ret) {
+ /* windows LH with interesting CMS packets */
+ size_t ph = 1 + der_length_len(content.length);
+ unsigned char *ptr = malloc(content.length + ph);
+ size_t l;
+
+ memcpy(ptr + ph, content.data, content.length);
+
+ ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
+ ASN1_C_UNIV, CONS, UT_Sequence, &l);
+ if (ret) {
+ free(ptr);
+ return ret;
+ }
+ free(content.data);
+ content.data = ptr;
+ content.length += ph;
+
+ ret = hx509_cms_unwrap_ContentInfo(&content, &type2, &unwrapped, NULL);
+ if (ret)
+ goto out;
+ }
+ if (der_heim_oid_cmp(&type2, &asn1_oid_id_pkcs7_signedData)) {
+ ret = EINVAL; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Invalid content type", ""));
+ der_free_oid(&type2);
+ der_free_octet_string(&unwrapped);
+ goto out;
+ }
+ der_free_oid(&type2);
+ krb5_data_free(&content);
+ ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
+ der_free_octet_string(&unwrapped);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+ }
+
+ ret = pk_verify_sign(context,
+ content.data,
+ content.length,
+ ctx->id,
+ &contentType,
+ &unwrapped,
+ &host);
+ if (ret == 0) {
+ krb5_data_free(&content);
+ ret = krb5_data_copy(&content, unwrapped.data, unwrapped.length);
+ der_free_octet_string(&unwrapped);
+ }
+ if (ret)
+ goto out;
+
+ heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
+ "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
+
+ if (host) {
+ /* make sure that it is the kdc's certificate */
+ ret = pk_verify_host(context, realm, ctx, host);
+ if (ret)
+ goto out;
+
+ ctx->kdc_verified = 1;
+ }
+
+#if 0
+ if (type == PKINIT_WIN2K) {
+ if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkcs7_data) != 0) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
+ goto out;
+ }
+ } else {
+ if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkrkeydata) != 0) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret, "PKINIT: reply key, wrong oid");
+ goto out;
+ }
+ }
+#endif
+
+ switch(type) {
+ case PKINIT_WIN2K:
+ ret = get_reply_key(context, &content, req_buffer, key);
+ if (ret != 0 && ctx->require_binding == 0)
+ ret = get_reply_key_win(context, &content, nonce, key);
+ break;
+ case PKINIT_27:
+ ret = get_reply_key(context, &content, req_buffer, key);
+ break;
+ }
+ if (ret)
+ goto out;
+
+ /* XXX compare given etype with key->etype */
+
+ out:
+ if (host)
+ _krb5_pk_cert_free(host);
+ der_free_oid(&contentType);
+ krb5_data_free(&content);
+
+ return ret;
+}
+
+/*
+ * RFC 8062 section 7:
+ *
+ * The client then decrypts the KDC contribution key and verifies that
+ * the ticket session key in the returned ticket is the combined key of
+ * the KDC contribution key and the reply key.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pk_kx_confirm(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ krb5_keyblock *reply_key,
+ krb5_keyblock *session_key,
+ PA_DATA *pa_pkinit_kx)
+{
+ krb5_error_code ret;
+ EncryptedData ed;
+ krb5_keyblock ck, sk_verify;
+ krb5_crypto ck_crypto = NULL;
+ krb5_crypto rk_crypto = NULL;
+ size_t len;
+ krb5_data data;
+ krb5_data p1 = { sizeof("PKINIT") - 1, "PKINIT" };
+ krb5_data p2 = { sizeof("KEYEXCHANGE") - 1, "KEYEXCHANGE" };
+
+ heim_assert(ctx != NULL, "PKINIT context is non-NULL");
+ heim_assert(reply_key != NULL, "reply key is non-NULL");
+ heim_assert(session_key != NULL, "session key is non-NULL");
+
+ /* PA-PKINIT-KX is optional unless anonymous */
+ if (pa_pkinit_kx == NULL)
+ return ctx->anonymous ? KRB5_KDCREP_MODIFIED : 0;
+
+ memset(&ed, 0, sizeof(ed));
+ krb5_keyblock_zero(&ck);
+ krb5_keyblock_zero(&sk_verify);
+ krb5_data_zero(&data);
+
+ ret = decode_EncryptedData(pa_pkinit_kx->padata_value.data,
+ pa_pkinit_kx->padata_value.length,
+ &ed, &len);
+ if (ret)
+ goto out;
+
+ if (len != pa_pkinit_kx->padata_value.length) {
+ ret = KRB5_KDCREP_MODIFIED;
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, reply_key, 0, &rk_crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context, rk_crypto,
+ KRB5_KU_PA_PKINIT_KX,
+ &ed, &data);
+ if (ret)
+ goto out;
+
+ ret = decode_EncryptionKey(data.data, data.length,
+ &ck, &len);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, &ck, 0, &ck_crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_fx_cf2(context, ck_crypto, rk_crypto,
+ &p1, &p2, session_key->keytype,
+ &sk_verify);
+ if (ret)
+ goto out;
+
+ if (sk_verify.keytype != session_key->keytype ||
+ krb5_data_ct_cmp(&sk_verify.keyvalue, &session_key->keyvalue) != 0) {
+ ret = KRB5_KDCREP_MODIFIED;
+ goto out;
+ }
+
+out:
+ free_EncryptedData(&ed);
+ krb5_free_keyblock_contents(context, &ck);
+ krb5_free_keyblock_contents(context, &sk_verify);
+ if (ck_crypto)
+ krb5_crypto_destroy(context, ck_crypto);
+ if (rk_crypto)
+ krb5_crypto_destroy(context, rk_crypto);
+ krb5_data_free(&data);
+
+ return ret;
+}
+
+static krb5_error_code
+pk_rd_pa_reply_dh(krb5_context context,
+ const heim_octet_string *indata,
+ const heim_oid *dataType,
+ const char *realm,
+ krb5_pk_init_ctx ctx,
+ krb5_enctype etype,
+ const DHNonce *c_n,
+ const DHNonce *k_n,
+ unsigned nonce,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ const unsigned char *p;
+ unsigned char *dh_gen_key = NULL;
+ struct krb5_pk_cert *host = NULL;
+ BIGNUM *kdc_dh_pubkey = NULL;
+ KDCDHKeyInfo kdc_dh_info;
+ heim_oid contentType = { 0, NULL };
+ krb5_data content;
+ krb5_error_code ret;
+ int dh_gen_keylen = 0;
+ size_t size;
+
+ krb5_data_zero(&content);
+ memset(&kdc_dh_info, 0, sizeof(kdc_dh_info));
+
+ if (der_heim_oid_cmp(&asn1_oid_id_pkcs7_signedData, dataType)) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: Invalid content type", ""));
+ return EINVAL;
+ }
+
+ ret = pk_verify_sign(context,
+ indata->data,
+ indata->length,
+ ctx->id,
+ &contentType,
+ &content,
+ &host);
+ if (ret)
+ goto out;
+
+ heim_assert(host || (ctx->id->flags & PKINIT_NO_KDC_ANCHOR),
+ "KDC signature must be verified unless PKINIT_NO_KDC_ANCHOR set");
+
+ if (host) {
+ /* make sure that it is the kdc's certificate */
+ ret = pk_verify_host(context, realm, ctx, host);
+ if (ret)
+ goto out;
+
+ ctx->kdc_verified = 1;
+ }
+
+ if (der_heim_oid_cmp(&contentType, &asn1_oid_id_pkdhkeydata)) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_set_error_message(context, ret,
+ N_("pkinit - dh reply contains wrong oid", ""));
+ goto out;
+ }
+
+ ret = decode_KDCDHKeyInfo(content.data,
+ content.length,
+ &kdc_dh_info,
+ &size);
+
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("pkinit - failed to decode "
+ "KDC DH Key Info", ""));
+ goto out;
+ }
+
+ if (kdc_dh_info.nonce != nonce) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: DH nonce is wrong", ""));
+ goto out;
+ }
+
+ if (kdc_dh_info.dhKeyExpiration) {
+ if (k_n == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("pkinit; got key expiration "
+ "without server nonce", ""));
+ goto out;
+ }
+ if (c_n == NULL) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("pkinit; got DH reuse but no "
+ "client nonce", ""));
+ goto out;
+ }
+ } else {
+ if (k_n) {
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("pkinit: got server nonce "
+ "without key expiration", ""));
+ goto out;
+ }
+ c_n = NULL;
+ }
+
+
+ p = kdc_dh_info.subjectPublicKey.data;
+ size = (kdc_dh_info.subjectPublicKey.length + 7) / 8;
+
+ if (ctx->keyex == USE_DH) {
+ DHPublicKey k;
+ ret = decode_DHPublicKey(p, size, &k, NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("pkinit: can't decode "
+ "without key expiration", ""));
+ goto out;
+ }
+
+ kdc_dh_pubkey = integer_to_BN(context, "DHPublicKey", &k);
+ free_DHPublicKey(&k);
+ if (kdc_dh_pubkey == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+
+ size = DH_size(ctx->u.dh);
+
+ dh_gen_key = malloc(size);
+ if (dh_gen_key == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ dh_gen_keylen = DH_compute_key(dh_gen_key, kdc_dh_pubkey, ctx->u.dh);
+ if (dh_gen_keylen == -1) {
+ ret = KRB5KRB_ERR_GENERIC;
+ dh_gen_keylen = 0;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Can't compute Diffie-Hellman key", ""));
+ goto out;
+ }
+ if (dh_gen_keylen < (int)size) {
+ size -= dh_gen_keylen;
+ memmove(dh_gen_key + size, dh_gen_key, dh_gen_keylen);
+ memset(dh_gen_key, 0, size);
+ dh_gen_keylen += size;
+ }
+
+ } else {
+ ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
+ size, &dh_gen_key,
+ &dh_gen_keylen);
+ if (ret)
+ goto out;
+ }
+
+ if (dh_gen_keylen <= 0) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: resulting DH key <= 0", ""));
+ dh_gen_keylen = 0;
+ goto out;
+ }
+
+ *key = malloc (sizeof (**key));
+ if (*key == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ ret = _krb5_pk_octetstring2key(context,
+ etype,
+ dh_gen_key, dh_gen_keylen,
+ c_n, k_n,
+ *key);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: can't create key from DH key", ""));
+ free(*key);
+ *key = NULL;
+ goto out;
+ }
+
+ out:
+ if (kdc_dh_pubkey)
+ BN_free(kdc_dh_pubkey);
+ if (dh_gen_key) {
+ memset(dh_gen_key, 0, dh_gen_keylen);
+ free(dh_gen_key);
+ }
+ if (host)
+ _krb5_pk_cert_free(host);
+ if (content.data)
+ krb5_data_free(&content);
+ der_free_oid(&contentType);
+ free_KDCDHKeyInfo(&kdc_dh_info);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pk_rd_pa_reply(krb5_context context,
+ const char *realm,
+ void *c,
+ krb5_enctype etype,
+ unsigned nonce,
+ const krb5_data *req_buffer,
+ PA_DATA *pa,
+ krb5_keyblock **key)
+{
+ krb5_pk_init_ctx ctx = c;
+ krb5_error_code ret;
+ size_t size;
+
+ /* Check for IETF PK-INIT first */
+ if (ctx->type == PKINIT_27) {
+ PA_PK_AS_REP rep;
+ heim_octet_string os, data;
+ heim_oid oid;
+
+ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: wrong padata recv", ""));
+ return EINVAL;
+ }
+
+ ret = decode_PA_PK_AS_REP(pa->padata_value.data,
+ pa->padata_value.length,
+ &rep,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode pkinit AS rep", ""));
+ return ret;
+ }
+
+ switch (rep.element) {
+ case choice_PA_PK_AS_REP_dhInfo:
+ _krb5_debug(context, 5, "krb5_get_init_creds: using pkinit dh");
+ os = rep.u.dhInfo.dhSignedData;
+ break;
+ case choice_PA_PK_AS_REP_encKeyPack:
+ _krb5_debug(context, 5, "krb5_get_init_creds: using kinit enc reply key");
+ os = rep.u.encKeyPack;
+ break;
+ default: {
+ PA_PK_AS_REP_BTMM btmm;
+ free_PA_PK_AS_REP(&rep);
+ memset(&rep, 0, sizeof(rep));
+
+ _krb5_debug(context, 5, "krb5_get_init_creds: using BTMM kinit enc reply key");
+
+ ret = decode_PA_PK_AS_REP_BTMM(pa->padata_value.data,
+ pa->padata_value.length,
+ &btmm,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: -27 reply "
+ "invalid content type", ""));
+ return EINVAL;
+ }
+
+ if (btmm.dhSignedData || btmm.encKeyPack == NULL) {
+ free_PA_PK_AS_REP_BTMM(&btmm);
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("DH mode not supported for BTMM mode", ""));
+ return ret;
+ }
+
+ /*
+ * Transform to IETF style PK-INIT reply so that free works below
+ */
+
+ rep.element = choice_PA_PK_AS_REP_encKeyPack;
+ rep.u.encKeyPack.data = btmm.encKeyPack->data;
+ rep.u.encKeyPack.length = btmm.encKeyPack->length;
+ btmm.encKeyPack->data = NULL;
+ btmm.encKeyPack->length = 0;
+ free_PA_PK_AS_REP_BTMM(&btmm);
+ os = rep.u.encKeyPack;
+ }
+ }
+
+ ret = hx509_cms_unwrap_ContentInfo(&os, &oid, &data, NULL);
+ if (ret) {
+ free_PA_PK_AS_REP(&rep);
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: failed to unwrap CI", ""));
+ return ret;
+ }
+
+ switch (rep.element) {
+ case choice_PA_PK_AS_REP_dhInfo:
+ ret = pk_rd_pa_reply_dh(context, &data, &oid, realm, ctx, etype,
+ ctx->clientDHNonce,
+ rep.u.dhInfo.serverDHNonce,
+ nonce, pa, key);
+ break;
+ case choice_PA_PK_AS_REP_encKeyPack:
+ ret = pk_rd_pa_reply_enckey(context, PKINIT_27, &data, &oid, realm,
+ ctx, etype, nonce, req_buffer, pa, key);
+ break;
+ default:
+ krb5_abortx(context, "pk-init as-rep case not possible to happen");
+ }
+ der_free_octet_string(&data);
+ der_free_oid(&oid);
+ free_PA_PK_AS_REP(&rep);
+
+ } else if (ctx->type == PKINIT_WIN2K) {
+ PA_PK_AS_REP_Win2k w2krep;
+
+ /* Check for Windows encoding of the AS-REP pa data */
+
+#if 0 /* should this be ? */
+ if (pa->padata_type != KRB5_PADATA_PK_AS_REP) {
+ krb5_set_error_message(context, EINVAL,
+ "PKINIT: wrong padata recv");
+ return EINVAL;
+ }
+#endif
+
+ memset(&w2krep, 0, sizeof(w2krep));
+
+ ret = decode_PA_PK_AS_REP_Win2k(pa->padata_value.data,
+ pa->padata_value.length,
+ &w2krep,
+ &size);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Failed decoding windows "
+ "pkinit reply %d", ""), (int)ret);
+ return ret;
+ }
+
+ krb5_clear_error_message(context);
+
+ switch (w2krep.element) {
+ case choice_PA_PK_AS_REP_Win2k_encKeyPack: {
+ heim_octet_string data;
+ heim_oid oid;
+
+ ret = hx509_cms_unwrap_ContentInfo(&w2krep.u.encKeyPack,
+ &oid, &data, NULL);
+ free_PA_PK_AS_REP_Win2k(&w2krep);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: failed to unwrap CI", ""));
+ return ret;
+ }
+
+ ret = pk_rd_pa_reply_enckey(context, PKINIT_WIN2K, &data, &oid, realm,
+ ctx, etype, nonce, req_buffer, pa, key);
+ der_free_octet_string(&data);
+ der_free_oid(&oid);
+
+ break;
+ }
+ default:
+ free_PA_PK_AS_REP_Win2k(&w2krep);
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: win2k reply invalid "
+ "content type", ""));
+ break;
+ }
+
+ } else {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: unknown reply type", ""));
+ }
+
+ return ret;
+}
+
+struct prompter {
+ krb5_context context;
+ krb5_prompter_fct prompter;
+ void *prompter_data;
+};
+
+static int
+hx_pass_prompter(void *data, const hx509_prompt *prompter)
+{
+ krb5_error_code ret;
+ krb5_prompt prompt;
+ krb5_data password_data;
+ struct prompter *p = data;
+
+ password_data.data = prompter->reply.data;
+ password_data.length = prompter->reply.length;
+
+ prompt.prompt = prompter->prompt;
+ prompt.hidden = hx509_prompt_hidden(prompter->type);
+ prompt.reply = &password_data;
+
+ switch (prompter->type) {
+ case HX509_PROMPT_TYPE_INFO:
+ prompt.type = KRB5_PROMPT_TYPE_INFO;
+ break;
+ case HX509_PROMPT_TYPE_PASSWORD:
+ case HX509_PROMPT_TYPE_QUESTION:
+ default:
+ prompt.type = KRB5_PROMPT_TYPE_PASSWORD;
+ break;
+ }
+
+ ret = (*p->prompter)(p->context, p->prompter_data, NULL, NULL, 1, &prompt);
+ if (ret) {
+ memset (prompter->reply.data, 0, prompter->reply.length);
+ return 1;
+ }
+ return 0;
+}
+
+static krb5_error_code
+_krb5_pk_set_user_id(krb5_context context,
+ krb5_principal principal,
+ krb5_pk_init_ctx ctx,
+ struct hx509_certs_data *certs)
+{
+ hx509_certs c = hx509_certs_ref(certs);
+ hx509_query *q = NULL;
+ int ret;
+
+ if (ctx->id->certs)
+ hx509_certs_free(&ctx->id->certs);
+ if (ctx->id->cert) {
+ hx509_cert_free(ctx->id->cert);
+ ctx->id->cert = NULL;
+ }
+
+ ctx->id->certs = c;
+ ctx->anonymous = 0;
+
+ ret = hx509_query_alloc(context->hx509ctx, &q);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Allocate query to find signing certificate");
+ return ret;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+
+ if (principal && strncmp("LKDC:SHA1.", krb5_principal_get_realm(context, principal), 9) == 0) {
+ ctx->id->flags |= PKINIT_BTMM;
+ }
+
+ ret = find_cert(context, ctx->id, q, &ctx->id->cert);
+ hx509_query_free(context->hx509ctx, q);
+
+ if (ret == 0 && _krb5_have_debug(context, 2)) {
+ hx509_name name;
+ char *str, *sn;
+ heim_integer i;
+
+ ret = hx509_cert_get_subject(ctx->id->cert, &name);
+ if (ret)
+ goto out;
+
+ ret = hx509_name_to_string(name, &str);
+ hx509_name_free(&name);
+ if (ret)
+ goto out;
+
+ ret = hx509_cert_get_serialnumber(ctx->id->cert, &i);
+ if (ret) {
+ free(str);
+ goto out;
+ }
+
+ ret = der_print_hex_heim_integer(&i, &sn);
+ der_free_heim_integer(&i);
+ if (ret) {
+ free(str);
+ goto out;
+ }
+
+ _krb5_debug(context, 2, "using cert: subject: %s sn: %s", str, sn);
+ free(str);
+ free(sn);
+ }
+ out:
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_pk_load_id(krb5_context context,
+ struct krb5_pk_identity **ret_id,
+ const char *user_id,
+ const char *anchor_id,
+ char * const *chain_list,
+ char * const *revoke_list,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ char *password)
+{
+ struct krb5_pk_identity *id = NULL;
+ struct prompter p;
+ krb5_error_code ret;
+
+ *ret_id = NULL;
+
+ /* load cert */
+
+ id = calloc(1, sizeof(*id));
+ if (id == NULL)
+ return krb5_enomem(context);
+
+ if (user_id) {
+ hx509_lock lock;
+
+ ret = hx509_lock_init(context->hx509ctx, &lock);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret, "Failed init lock");
+ goto out;
+ }
+
+ if (password && password[0])
+ hx509_lock_add_password(lock, password);
+
+ if (prompter) {
+ p.context = context;
+ p.prompter = prompter;
+ p.prompter_data = prompter_data;
+
+ ret = hx509_lock_set_prompter(lock, hx_pass_prompter, &p);
+ if (ret) {
+ hx509_lock_free(lock);
+ goto out;
+ }
+ }
+
+ ret = hx509_certs_init(context->hx509ctx, user_id, 0, lock, &id->certs);
+ hx509_lock_free(lock);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to init cert certs");
+ goto out;
+ }
+ } else {
+ id->certs = NULL;
+ }
+
+ ret = hx509_certs_init(context->hx509ctx, anchor_id, 0, NULL, &id->anchors);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to init anchors");
+ goto out;
+ }
+
+ ret = hx509_certs_init(context->hx509ctx, "MEMORY:pkinit-cert-chain",
+ 0, NULL, &id->certpool);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to init chain");
+ goto out;
+ }
+
+ while (chain_list && *chain_list) {
+ ret = hx509_certs_append(context->hx509ctx, id->certpool,
+ NULL, *chain_list);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to load chain %s",
+ *chain_list);
+ goto out;
+ }
+ chain_list++;
+ }
+
+ if (revoke_list) {
+ ret = hx509_revoke_init(context->hx509ctx, &id->revokectx);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to init revoke list");
+ goto out;
+ }
+
+ while (*revoke_list) {
+ ret = hx509_revoke_add_crl(context->hx509ctx,
+ id->revokectx,
+ *revoke_list);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to load revoke list");
+ goto out;
+ }
+ revoke_list++;
+ }
+ } else
+ hx509_context_set_missing_revoke(context->hx509ctx, 1);
+
+ ret = hx509_verify_init_ctx(context->hx509ctx, &id->verify_ctx);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to init verify context");
+ goto out;
+ }
+
+ hx509_verify_attach_anchors(id->verify_ctx, id->anchors);
+ hx509_verify_attach_revoke(id->verify_ctx, id->revokectx);
+
+ out:
+ if (ret) {
+ hx509_verify_destroy_ctx(id->verify_ctx);
+ hx509_certs_free(&id->certs);
+ hx509_certs_free(&id->anchors);
+ hx509_certs_free(&id->certpool);
+ hx509_revoke_free(&id->revokectx);
+ free(id);
+ } else
+ *ret_id = id;
+
+ return ret;
+}
+
+/*
+ *
+ */
+
+static void
+pk_copy_error(krb5_context context,
+ hx509_context hx509ctx,
+ int hxret,
+ const char *fmt,
+ ...)
+{
+ va_list va;
+ char *s, *f;
+ int ret;
+
+ va_start(va, fmt);
+ ret = vasprintf(&f, fmt, va);
+ va_end(va);
+ if (ret == -1 || f == NULL) {
+ krb5_clear_error_message(context);
+ return;
+ }
+
+ s = hx509_get_error_string(hx509ctx, hxret);
+ if (s == NULL) {
+ krb5_clear_error_message(context);
+ free(f);
+ return;
+ }
+ krb5_set_error_message(context, hxret, "%s: %s", f, s);
+ free(s);
+ free(f);
+}
+
+static int
+parse_integer(krb5_context context, char **p, const char *file, int lineno,
+ const char *name, heim_integer *integer)
+{
+ int ret;
+ char *p1;
+ p1 = strsep(p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("moduli file %s missing %s on line %d", ""),
+ file, name, lineno);
+ return EINVAL;
+ }
+ ret = der_parse_hex_heim_integer(p1, integer);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s failed parsing %s "
+ "on line %d", ""),
+ file, name, lineno);
+ return ret;
+ }
+
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_parse_moduli_line(krb5_context context,
+ const char *file,
+ int lineno,
+ char *p,
+ struct krb5_dh_moduli **m)
+{
+ struct krb5_dh_moduli *m1;
+ char *p1;
+ int ret;
+
+ *m = NULL;
+
+ m1 = calloc(1, sizeof(*m1));
+ if (m1 == NULL)
+ return krb5_enomem(context);
+
+ while (isspace((unsigned char)*p))
+ p++;
+ if (*p == '#') {
+ free(m1);
+ return 0;
+ }
+ ret = EINVAL;
+
+ p1 = strsep(&p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s missing name on line %d", ""),
+ file, lineno);
+ goto out;
+ }
+ m1->name = strdup(p1);
+ if (m1->name == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ p1 = strsep(&p, " \t");
+ if (p1 == NULL) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s missing bits on line %d", ""),
+ file, lineno);
+ goto out;
+ }
+
+ m1->bits = atoi(p1);
+ if (m1->bits == 0) {
+ krb5_set_error_message(context, ret,
+ N_("moduli file %s has un-parsable "
+ "bits on line %d", ""), file, lineno);
+ goto out;
+ }
+
+ ret = parse_integer(context, &p, file, lineno, "p", &m1->p);
+ if (ret)
+ goto out;
+ ret = parse_integer(context, &p, file, lineno, "g", &m1->g);
+ if (ret)
+ goto out;
+ ret = parse_integer(context, &p, file, lineno, "q", &m1->q);
+ if (ret) {
+ m1->q.negative = 0;
+ m1->q.length = 0;
+ m1->q.data = 0;
+ krb5_clear_error_message(context);
+ }
+
+ *m = m1;
+
+ return 0;
+ out:
+ free(m1->name);
+ der_free_heim_integer(&m1->p);
+ der_free_heim_integer(&m1->g);
+ der_free_heim_integer(&m1->q);
+ free(m1);
+ return ret;
+}
+
+static void
+free_moduli_element(struct krb5_dh_moduli *element)
+{
+ free(element->name);
+ der_free_heim_integer(&element->p);
+ der_free_heim_integer(&element->g);
+ der_free_heim_integer(&element->q);
+ free(element);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_free_moduli(struct krb5_dh_moduli **moduli)
+{
+ int i;
+ for (i = 0; moduli[i] != NULL; i++)
+ free_moduli_element(moduli[i]);
+ free(moduli);
+}
+
+static const char *default_moduli_RFC2412_MODP_group2 =
+ /* name */
+ "RFC2412-MODP-group2 "
+ /* bits */
+ "1024 "
+ /* p */
+ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
+ "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
+ "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
+ "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
+ "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE65381"
+ "FFFFFFFF" "FFFFFFFF "
+ /* g */
+ "02 "
+ /* q */
+ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
+ "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
+ "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
+ "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
+ "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F67329C0"
+ "FFFFFFFF" "FFFFFFFF";
+
+static const char *default_moduli_rfc3526_MODP_group14 =
+ /* name */
+ "rfc3526-MODP-group14 "
+ /* bits */
+ "2048 "
+ /* p */
+ "FFFFFFFF" "FFFFFFFF" "C90FDAA2" "2168C234" "C4C6628B" "80DC1CD1"
+ "29024E08" "8A67CC74" "020BBEA6" "3B139B22" "514A0879" "8E3404DD"
+ "EF9519B3" "CD3A431B" "302B0A6D" "F25F1437" "4FE1356D" "6D51C245"
+ "E485B576" "625E7EC6" "F44C42E9" "A637ED6B" "0BFF5CB6" "F406B7ED"
+ "EE386BFB" "5A899FA5" "AE9F2411" "7C4B1FE6" "49286651" "ECE45B3D"
+ "C2007CB8" "A163BF05" "98DA4836" "1C55D39A" "69163FA8" "FD24CF5F"
+ "83655D23" "DCA3AD96" "1C62F356" "208552BB" "9ED52907" "7096966D"
+ "670C354E" "4ABC9804" "F1746C08" "CA18217C" "32905E46" "2E36CE3B"
+ "E39E772C" "180E8603" "9B2783A2" "EC07A28F" "B5C55DF0" "6F4C52C9"
+ "DE2BCBF6" "95581718" "3995497C" "EA956AE5" "15D22618" "98FA0510"
+ "15728E5A" "8AACAA68" "FFFFFFFF" "FFFFFFFF "
+ /* g */
+ "02 "
+ /* q */
+ "7FFFFFFF" "FFFFFFFF" "E487ED51" "10B4611A" "62633145" "C06E0E68"
+ "94812704" "4533E63A" "0105DF53" "1D89CD91" "28A5043C" "C71A026E"
+ "F7CA8CD9" "E69D218D" "98158536" "F92F8A1B" "A7F09AB6" "B6A8E122"
+ "F242DABB" "312F3F63" "7A262174" "D31BF6B5" "85FFAE5B" "7A035BF6"
+ "F71C35FD" "AD44CFD2" "D74F9208" "BE258FF3" "24943328" "F6722D9E"
+ "E1003E5C" "50B1DF82" "CC6D241B" "0E2AE9CD" "348B1FD4" "7E9267AF"
+ "C1B2AE91" "EE51D6CB" "0E3179AB" "1042A95D" "CF6A9483" "B84B4B36"
+ "B3861AA7" "255E4C02" "78BA3604" "650C10BE" "19482F23" "171B671D"
+ "F1CF3B96" "0C074301" "CD93C1D1" "7603D147" "DAE2AEF8" "37A62964"
+ "EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
+ "0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_parse_moduli(krb5_context context, const char *file,
+ struct krb5_dh_moduli ***moduli)
+{
+ /* name bits P G Q */
+ krb5_error_code ret;
+ struct krb5_dh_moduli **m = NULL, **m2;
+ char buf[4096];
+ FILE *f;
+ int lineno = 0, n = 0;
+
+ *moduli = NULL;
+
+ m = calloc(1, sizeof(m[0]) * 3);
+ if (m == NULL)
+ return krb5_enomem(context);
+
+ strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
+ ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ n++;
+
+ strlcpy(buf, default_moduli_RFC2412_MODP_group2, sizeof(buf));
+ ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[1]);
+ if (ret) {
+ _krb5_free_moduli(m);
+ return ret;
+ }
+ n++;
+
+
+ if (file == NULL)
+ file = MODULI_FILE;
+
+ {
+ char *exp_file;
+
+ if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
+ f = fopen(exp_file, "r");
+ krb5_xfree(exp_file);
+ } else {
+ f = NULL;
+ }
+ }
+
+ if (f == NULL) {
+ *moduli = m;
+ return 0;
+ }
+ rk_cloexec_file(f);
+
+ while(fgets(buf, sizeof(buf), f) != NULL) {
+ struct krb5_dh_moduli *element;
+
+ buf[strcspn(buf, "\n")] = '\0';
+ lineno++;
+
+ ret = _krb5_parse_moduli_line(context, file, lineno, buf, &element);
+ if (ret)
+ break;
+ if (element == NULL)
+ continue;
+
+ m2 = realloc(m, (n + 2) * sizeof(m[0]));
+ if (m2 == NULL) {
+ free_moduli_element(element);
+ ret = krb5_enomem(context);
+ break;
+ }
+ m = m2;
+
+ m[n] = element;
+ m[n + 1] = NULL;
+ n++;
+ }
+ if (ret) {
+ _krb5_free_moduli(m);
+ m = NULL;
+ }
+
+ *moduli = m;
+
+ (void) fclose(f);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_dh_group_ok(krb5_context context, unsigned long bits,
+ heim_integer *p, heim_integer *g, heim_integer *q,
+ struct krb5_dh_moduli **moduli,
+ char **name)
+{
+ int i;
+
+ if (name)
+ *name = NULL;
+
+ for (i = 0; moduli[i] != NULL; i++) {
+ if (der_heim_integer_cmp(&moduli[i]->g, g) == 0 &&
+ der_heim_integer_cmp(&moduli[i]->p, p) == 0 &&
+ (q == NULL || moduli[i]->q.length == 0 ||
+ der_heim_integer_cmp(&moduli[i]->q, q) == 0))
+ {
+ if (bits && bits > moduli[i]->bits) {
+ krb5_set_error_message(context,
+ KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
+ N_("PKINIT: DH group parameter %s "
+ "not accepted, not enough bits "
+ "generated", ""),
+ moduli[i]->name);
+ return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+ }
+ if (name)
+ *name = strdup(moduli[i]->name);
+ return 0;
+ }
+ }
+ krb5_set_error_message(context,
+ KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED,
+ N_("PKINIT: DH group parameter not ok", ""));
+ return KRB5_KDC_ERR_DH_KEY_PARAMETERS_NOT_ACCEPTED;
+}
+#endif /* PKINIT */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
+{
+#ifdef PKINIT
+ krb5_pk_init_ctx ctx;
+
+ if (opt->opt_private == NULL || opt->opt_private->pk_init_ctx == NULL)
+ return;
+ ctx = opt->opt_private->pk_init_ctx;
+ switch (ctx->keyex) {
+ case USE_DH:
+ if (ctx->u.dh)
+ DH_free(ctx->u.dh);
+ break;
+ case USE_RSA:
+ break;
+ case USE_ECDH:
+ if (ctx->u.eckey)
+ _krb5_pk_eckey_free(ctx->u.eckey);
+ break;
+ }
+ if (ctx->id) {
+ hx509_verify_destroy_ctx(ctx->id->verify_ctx);
+ hx509_certs_free(&ctx->id->certs);
+ hx509_cert_free(ctx->id->cert);
+ hx509_certs_free(&ctx->id->anchors);
+ hx509_certs_free(&ctx->id->certpool);
+
+ if (ctx->clientDHNonce) {
+ krb5_free_data(NULL, ctx->clientDHNonce);
+ ctx->clientDHNonce = NULL;
+ }
+ if (ctx->m)
+ _krb5_free_moduli(ctx->m);
+ free(ctx->id);
+ ctx->id = NULL;
+ }
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+#endif
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_init_creds_opt_set_pkinit(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ krb5_principal principal,
+ const char *user_id,
+ const char *x509_anchors,
+ char * const * pool,
+ char * const * pki_revoke,
+ int flags,
+ krb5_prompter_fct prompter,
+ void *prompter_data,
+ char *password)
+{
+#ifdef PKINIT
+ krb5_error_code ret;
+ char **freeme1 = NULL;
+ char **freeme2 = NULL;
+ char *anchors = NULL;
+
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: on non extendable opt", ""));
+ return EINVAL;
+ }
+
+ opt->opt_private->pk_init_ctx =
+ calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
+ if (opt->opt_private->pk_init_ctx == NULL)
+ return krb5_enomem(context);
+ opt->opt_private->pk_init_ctx->require_binding = 0;
+ opt->opt_private->pk_init_ctx->require_eku = 1;
+ opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
+ opt->opt_private->pk_init_ctx->peer = NULL;
+
+ /* XXX implement krb5_appdefault_strings */
+ if (pool == NULL)
+ pool = freeme1 = krb5_config_get_strings(context, NULL, "appdefaults",
+ "pkinit_pool", NULL);
+
+ if (pki_revoke == NULL)
+ pki_revoke = freeme2 = krb5_config_get_strings(context, NULL,
+ "appdefaults",
+ "pkinit_revoke", NULL);
+
+ if (x509_anchors == NULL) {
+ krb5_appdefault_string(context, "kinit",
+ krb5_principal_get_realm(context, principal),
+ "pkinit_anchors", NULL, &anchors);
+ x509_anchors = anchors;
+ }
+
+ if (flags & KRB5_GIC_OPT_PKINIT_ANONYMOUS)
+ opt->opt_private->pk_init_ctx->anonymous = 1;
+
+ if ((flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR) == 0 &&
+ x509_anchors == NULL) {
+ krb5_set_error_message(context, HEIM_PKINIT_NO_VALID_CA,
+ N_("PKINIT: No anchor given", ""));
+ return HEIM_PKINIT_NO_VALID_CA;
+ }
+
+ ret = _krb5_pk_load_id(context,
+ &opt->opt_private->pk_init_ctx->id,
+ user_id,
+ x509_anchors,
+ pool,
+ pki_revoke,
+ prompter,
+ prompter_data,
+ password);
+ krb5_config_free_strings(freeme2);
+ krb5_config_free_strings(freeme1);
+ free(anchors);
+ if (ret) {
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+ return ret;
+ }
+ if (flags & KRB5_GIC_OPT_PKINIT_BTMM)
+ opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
+ if (principal && krb5_principal_is_lkdc(context, principal))
+ opt->opt_private->pk_init_ctx->id->flags |= PKINIT_BTMM;
+ if (flags & KRB5_GIC_OPT_PKINIT_NO_KDC_ANCHOR)
+ opt->opt_private->pk_init_ctx->id->flags |= PKINIT_NO_KDC_ANCHOR;
+
+ if (opt->opt_private->pk_init_ctx->id->certs) {
+ ret = _krb5_pk_set_user_id(context,
+ principal,
+ opt->opt_private->pk_init_ctx,
+ opt->opt_private->pk_init_ctx->id->certs);
+ if (ret) {
+ free(opt->opt_private->pk_init_ctx);
+ opt->opt_private->pk_init_ctx = NULL;
+ return ret;
+ }
+ } else
+ opt->opt_private->pk_init_ctx->id->cert = NULL;
+
+ if ((flags & KRB5_GIC_OPT_PKINIT_USE_ENCKEY) == 0) {
+ hx509_context hx509ctx = context->hx509ctx;
+ hx509_cert cert = opt->opt_private->pk_init_ctx->id->cert;
+
+ opt->opt_private->pk_init_ctx->keyex = USE_DH;
+
+ /*
+ * If its a ECDSA certs, lets select ECDSA as the keyex algorithm.
+ */
+ if (cert) {
+ AlgorithmIdentifier alg;
+
+ ret = hx509_cert_get_SPKI_AlgorithmIdentifier(hx509ctx, cert, &alg);
+ if (ret == 0) {
+ if (der_heim_oid_cmp(&alg.algorithm, &asn1_oid_id_ecPublicKey) == 0)
+ opt->opt_private->pk_init_ctx->keyex = USE_ECDH;
+ free_AlgorithmIdentifier(&alg);
+ }
+ }
+
+ } else {
+ opt->opt_private->pk_init_ctx->keyex = USE_RSA;
+
+ if (opt->opt_private->pk_init_ctx->id->certs == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("No anonymous pkinit support in RSA mode", ""));
+ return EINVAL;
+ }
+ }
+
+ return 0;
+#else
+ krb5_set_error_message(context, EINVAL,
+ N_("no support for PKINIT compiled in", ""));
+ return EINVAL;
+#endif
+}
+
+krb5_error_code KRB5_LIB_FUNCTION
+krb5_get_init_creds_opt_set_pkinit_user_certs(krb5_context context,
+ krb5_get_init_creds_opt *opt,
+ struct hx509_certs_data *certs)
+{
+#ifdef PKINIT
+ if (opt->opt_private == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: on non extendable opt", ""));
+ return EINVAL;
+ }
+ if (opt->opt_private->pk_init_ctx == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("PKINIT: on pkinit context", ""));
+ return EINVAL;
+ }
+
+ return _krb5_pk_set_user_id(context, NULL, opt->opt_private->pk_init_ctx, certs);
+#else
+ krb5_set_error_message(context, EINVAL,
+ N_("no support for PKINIT compiled in", ""));
+ return EINVAL;
+#endif
+}
+
+#ifdef PKINIT
+
+static int
+get_ms_san(hx509_context context, hx509_cert cert, char **upn)
+{
+ hx509_octet_string_list list;
+ int ret;
+
+ *upn = NULL;
+
+ ret = hx509_cert_find_subjectAltName_otherName(context,
+ cert,
+ &asn1_oid_id_pkinit_ms_san,
+ &list);
+ if (ret)
+ return 0;
+
+ if (list.len > 0 && list.val[0].length > 0)
+ ret = decode_MS_UPN_SAN(list.val[0].data, list.val[0].length,
+ upn, NULL);
+ else
+ ret = 1;
+ hx509_free_octet_string_list(&list);
+
+ return ret;
+}
+
+static int
+find_ms_san(hx509_context context, hx509_cert cert, void *ctx)
+{
+ char *upn;
+ int ret;
+
+ ret = get_ms_san(context, cert, &upn);
+ if (ret == 0)
+ free(upn);
+ return ret;
+}
+
+
+
+#endif
+
+/*
+ * Private since it need to be redesigned using krb5_get_init_creds()
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_pk_enterprise_cert(krb5_context context,
+ const char *user_id,
+ krb5_const_realm realm,
+ krb5_principal *principal,
+ struct hx509_certs_data **res)
+{
+#ifdef PKINIT
+ krb5_error_code ret;
+ hx509_certs certs, result;
+ hx509_cert cert = NULL;
+ hx509_query *q;
+ char *name;
+
+ *principal = NULL;
+ if (res)
+ *res = NULL;
+
+ if (user_id == NULL) {
+ krb5_set_error_message(context, ENOENT, "no user id");
+ return ENOENT;
+ }
+
+ ret = hx509_certs_init(context->hx509ctx, user_id, 0, NULL, &certs);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to init cert certs");
+ goto out;
+ }
+
+ ret = hx509_query_alloc(context->hx509ctx, &q);
+ if (ret) {
+ krb5_set_error_message(context, ret, "out of memory");
+ hx509_certs_free(&certs);
+ goto out;
+ }
+
+ hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY);
+ hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE);
+ hx509_query_match_eku(q, &asn1_oid_id_pkinit_ms_eku);
+ hx509_query_match_cmp_func(q, find_ms_san, NULL);
+
+ ret = hx509_certs_filter(context->hx509ctx, certs, q, &result);
+ hx509_query_free(context->hx509ctx, q);
+ hx509_certs_free(&certs);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to find PKINIT certificate");
+ return ret;
+ }
+
+ ret = hx509_get_one_cert(context->hx509ctx, result, &cert);
+ hx509_certs_free(&result);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to get one cert");
+ goto out;
+ }
+
+ ret = get_ms_san(context->hx509ctx, cert, &name);
+ if (ret) {
+ pk_copy_error(context, context->hx509ctx, ret,
+ "Failed to get MS SAN");
+ goto out;
+ }
+
+ ret = krb5_make_principal(context, principal, realm, name, NULL);
+ free(name);
+ if (ret)
+ goto out;
+
+ krb5_principal_set_type(context, *principal, KRB5_NT_ENTERPRISE_PRINCIPAL);
+
+ if (res) {
+ ret = hx509_certs_init(context->hx509ctx, "MEMORY:", 0, NULL, res);
+ if (ret)
+ goto out;
+
+ ret = hx509_certs_add(context->hx509ctx, *res, cert);
+ if (ret) {
+ hx509_certs_free(res);
+ goto out;
+ }
+ }
+
+ out:
+ hx509_cert_free(cert);
+
+ return ret;
+#else
+ krb5_set_error_message(context, EINVAL,
+ N_("no support for PKINIT compiled in", ""));
+ return EINVAL;
+#endif
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_pk_is_kdc_verified(krb5_context context,
+ krb5_get_init_creds_opt *opt)
+{
+ if (opt == NULL ||
+ opt->opt_private == NULL ||
+ opt->opt_private->pk_init_ctx == NULL)
+ return FALSE;
+
+ return opt->opt_private->pk_init_ctx->kdc_verified;
+}
diff --git a/third_party/heimdal/lib/krb5/plugin.c b/third_party/heimdal/lib/krb5/plugin.c
new file mode 100644
index 0000000..b4035d3
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/plugin.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2018 AuriStor, Inc.
+ *
+ * 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 "common_plugin.h"
+
+/*
+ * Definitions:
+ *
+ * module - a category of plugin module, identified by subsystem
+ * (typically "krb5")
+ * dso - a library for a module containing a map of plugin
+ * types to plugins (e.g. "service_locator")
+ * plugin - a set of callbacks and state that follows the
+ * common plugin module definition (version, init, fini)
+ *
+ * Obviously it would have been clearer to use the term "module" rather than
+ * "DSO" given there is an internal "DSO", but "module" was already taken...
+ *
+ * modules := { module: dsos }
+ * dsos := { path, dsohandle, plugins-by-name }
+ * plugins-by-name := { plugin-name: [plug] }
+ * plug := { ftable, ctx }
+ *
+ * Some existing plugin consumers outside libkrb5 use the "krb5" module
+ * namespace, but going forward the module should match the consumer library
+ * name (e.g. libhdb should use the "hdb" module rather than "krb5").
+ */
+
+/**
+ * Register a plugin symbol name of specific type.
+ * @param context a Keberos context
+ * @param type type of plugin symbol
+ * @param name name of plugin symbol
+ * @param symbol a pointer to the named symbol
+ * @return In case of error a non zero error com_err error is returned
+ * and the Kerberos error string is set.
+ *
+ * @ingroup krb5_support
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_plugin_register(krb5_context context,
+ enum krb5_plugin_type type,
+ const char *name,
+ const void *symbol)
+{
+ /*
+ * It's not clear that PLUGIN_TYPE_FUNC was ever used or supported. It likely
+ * would have caused _krb5_plugin_run_f() to crash as the previous implementation
+ * assumed PLUGIN_TYPE_DATA.
+ */
+ if (type != PLUGIN_TYPE_DATA) {
+ krb5_warnx(context, "krb5_plugin_register: PLUGIN_TYPE_DATA no longer supported");
+ return EINVAL;
+ }
+
+ return heim_plugin_register(context->hcontext, (heim_pcontext)context,
+ "krb5", name, symbol);
+}
+
+/**
+ * Load plugins (new system) for the given module @name (typically
+ * "krb5") from the given directory @paths.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @name Name of plugin module (typically "krb5")
+ * @paths Array of directory paths where to look
+ */
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_load_plugins(krb5_context context, const char *name, const char **paths)
+{
+ heim_load_plugins(context->hcontext, name, paths);
+}
+
+/**
+ * Unload plugins (new system)
+ */
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_unload_plugins(krb5_context context, const char *name)
+{
+ heim_unload_plugins(context->hcontext, name);
+}
+
+/**
+ * Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
+ * "kuserok"). Specifically, the @func is invoked once per-plugin with
+ * four arguments: the @context, the plugin symbol value (a pointer to a
+ * struct whose first three fields are the same as common_plugin_ftable),
+ * a context value produced by the plugin's init method, and @userctx.
+ *
+ * @func should unpack arguments for a plugin function and invoke it
+ * with arguments taken from @userctx. @func should save plugin
+ * outputs, if any, in @userctx.
+ *
+ * All loaded and registered plugins are invoked via @func until @func
+ * returns something other than KRB5_PLUGIN_NO_HANDLE. Plugins that
+ * have nothing to do for the given arguments should return
+ * KRB5_PLUGIN_NO_HANDLE.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @module Name of module (typically "krb5")
+ * @name Name of pluggable interface (e.g., "kuserok")
+ * @min_version Lowest acceptable plugin minor version number
+ * @flags Flags (none defined at this time)
+ * @userctx Callback data for the callback function @func
+ * @func A callback function, invoked once per-plugin
+ *
+ * Outputs: None, other than the return value and such outputs as are
+ * gathered by @func.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_plugin_run_f(krb5_context context,
+ const struct heim_plugin_data *caller,
+ int flags,
+ void *userctx,
+ krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *))
+{
+ int32_t (HEIM_LIB_CALL *func2)(void *, const void *, void *, void *) = (void *)func;
+ return heim_plugin_run_f(context->hcontext, (heim_pcontext)context, caller,
+ flags, KRB5_PLUGIN_NO_HANDLE, userctx, func2);
+}
+
+/**
+ * Return a cookie identifying this instance of a library.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @module Our library name or a library we depend on
+ *
+ * Outputs: The instance cookie
+ *
+ * @ingroup krb5_support
+ */
+
+#ifdef WIN32
+static uintptr_t
+djb2(uintptr_t hash, unsigned char *str)
+{
+ int c;
+
+ while (c = *str++)
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash;
+}
+#endif
+
+KRB5_LIB_FUNCTION uintptr_t KRB5_LIB_CALL
+krb5_get_instance(const char *libname)
+{
+#ifdef WIN32
+ char *version;
+ char *name;
+ uintptr_t instance;
+
+ if (win32_getLibraryVersion("heimdal", &name, &version))
+ return 0;
+ instance = djb2(5381, name);
+ instance = djb2(instance, version);
+ free(name);
+ free(version);
+ return instance;
+#else
+ static const char *instance = "libkrb5";
+
+ if (strcmp(libname, "krb5") == 0)
+ return (uintptr_t)instance;
+ return 0;
+#endif
+}
diff --git a/third_party/heimdal/lib/krb5/principal.c b/third_party/heimdal/lib/krb5/principal.c
new file mode 100644
index 0000000..33ebd19
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/principal.c
@@ -0,0 +1,2220 @@
+/*
+ * 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.
+ */
+
+/**
+ * @page krb5_principal_intro The principal handing functions.
+ *
+ * A Kerberos principal is a email address looking string that
+ * contains two parts separated by @. The second part is the kerberos
+ * realm the principal belongs to and the first is a list of 0 or
+ * more components. For example
+ * @verbatim
+lha@SU.SE
+host/hummel.it.su.se@SU.SE
+host/admin@H5L.ORG
+@endverbatim
+ *
+ * See the library functions here: @ref krb5_principal
+ */
+
+#include "krb5_locl.h"
+#ifdef HAVE_RES_SEARCH
+#define USE_RESOLVER
+#endif
+#ifdef HAVE_ARPA_NAMESER_H
+#include <arpa/nameser.h>
+#endif
+#include <fnmatch.h>
+#include "resolve.h"
+
+#define princ_num_comp(P) ((P)->name.name_string.len)
+#define princ_type(P) ((P)->name.name_type)
+#define princ_comp(P) ((P)->name.name_string.val)
+#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
+#define princ_realm(P) ((P)->realm)
+
+static krb5_error_code
+set_default_princ_type(krb5_principal p, NAME_TYPE defnt)
+{
+ if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), KRB5_TGS_NAME) == 0)
+ princ_type(p) = KRB5_NT_SRV_INST;
+ else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "host") == 0)
+ princ_type(p) = KRB5_NT_SRV_HST;
+ else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "kca_service") == 0)
+ princ_type(p) = KRB5_NT_SRV_HST;
+ else if (princ_num_comp(p) == 2 &&
+ strcmp(princ_ncomp(p, 0), KRB5_WELLKNOWN_NAME) == 0)
+ princ_type(p) = KRB5_NT_WELLKNOWN;
+ else if (princ_num_comp(p) == 1 && strchr(princ_ncomp(p, 0), '@') != NULL)
+ princ_type(p) = KRB5_NT_SMTP_NAME;
+ else
+ princ_type(p) = defnt;
+ return 0;
+}
+
+static krb5_error_code append_component(krb5_context, krb5_principal,
+ const char *, size_t);
+
+/**
+ * Frees a Kerberos principal allocated by the library with
+ * krb5_parse_name(), krb5_make_principal() or any other related
+ * principal functions.
+ *
+ * @param context A Kerberos context.
+ * @param p a principal to free.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_principal(krb5_context context,
+ krb5_principal p)
+{
+ if(p){
+ if (p->nameattrs && p->nameattrs->pac)
+ heim_release(p->nameattrs->pac);
+ free_Principal(p);
+ free(p);
+ }
+}
+
+/**
+ * Set the type of the principal
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to set the type for
+ * @param type the new type
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_principal_set_type(krb5_context context,
+ krb5_principal principal,
+ int type)
+{
+ princ_type(principal) = type;
+}
+
+/**
+ * Get the type of the principal
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to get the type for
+ *
+ * @return the type of principal
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_principal_get_type(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_type(principal);
+}
+
+/**
+ * Get the realm of the principal
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to get the realm for
+ *
+ * @return realm of the principal, don't free or use after krb5_principal is freed
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_principal_get_realm(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_realm(principal);
+}
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_principal_get_comp_string(krb5_context context,
+ krb5_const_principal principal,
+ unsigned int component)
+{
+ if(component >= princ_num_comp(principal))
+ return NULL;
+ return princ_ncomp(principal, component);
+}
+
+/**
+ * Get number of component is principal.
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ *
+ * @return number of components in string
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
+krb5_principal_get_num_comp(krb5_context context,
+ krb5_const_principal principal)
+{
+ return princ_num_comp(principal);
+}
+
+/**
+ * Parse a name into a krb5_principal structure, flags controls the behavior.
+ *
+ * @param context Kerberos 5 context
+ * @param name name to parse into a Kerberos principal
+ * @param flags flags to control the behavior
+ * @param principal returned principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_parse_name_flags(krb5_context context,
+ const char *name,
+ int flags,
+ krb5_principal *principal)
+{
+ krb5_error_code ret;
+ heim_general_string *comp;
+ heim_general_string realm = NULL;
+ int ncomp;
+
+ const char *p;
+ char *q;
+ char *s;
+ char *start;
+
+ int n;
+ char c;
+ int got_realm = 0;
+ int first_at = 1;
+ int no_realm = flags & KRB5_PRINCIPAL_PARSE_NO_REALM;
+ int require_realm = flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
+ int enterprise = flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+ int ignore_realm = flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM;
+ int no_def_realm = flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
+
+ *principal = NULL;
+
+ if (no_realm && require_realm) {
+ krb5_set_error_message(context, EINVAL,
+ N_("Can't require both realm and "
+ "no realm at the same time", ""));
+ return EINVAL;
+ }
+
+ /* count number of component,
+ * enterprise names only have one component
+ */
+ ncomp = 1;
+ if (!enterprise) {
+ for (p = name; *p; p++) {
+ if (*p=='\\') {
+ if (!p[1]) {
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("trailing \\ in principal name", ""));
+ return KRB5_PARSE_MALFORMED;
+ }
+ p++;
+ } else if (*p == '/')
+ ncomp++;
+ else if (*p == '@')
+ break;
+ }
+ }
+ comp = calloc(ncomp, sizeof(*comp));
+ if (comp == NULL)
+ return krb5_enomem(context);
+
+ n = 0;
+ p = start = q = s = strdup(name);
+ if (start == NULL) {
+ free(comp);
+ return krb5_enomem(context);
+ }
+ while (*p) {
+ c = *p++;
+ if (c == '\\') {
+ c = *p++;
+ if (c == 'n')
+ c = '\n';
+ else if (c == 't')
+ c = '\t';
+ else if (c == 'b')
+ c = '\b';
+ else if (c == '0') {
+ /*
+ * We'll ignore trailing embedded NULs in components and
+ * realms, but can't support any other embedded NULs.
+ */
+ while (*p) {
+ if ((*p == '/' || *p == '@') && !got_realm)
+ break;
+ if (*(p++) != '\\' || *(p++) != '0') {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("embedded NULs in principal "
+ "name not supported", ""));
+ goto exit;
+ }
+ }
+ continue;
+ } else if (c == '\0') {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("trailing \\ in principal name", ""));
+ goto exit;
+ }
+ } else if (enterprise && first_at) {
+ if (c == '@')
+ first_at = 0;
+ } else if ((c == '/' && !enterprise) || c == '@') {
+ if (got_realm) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("part after realm in principal name", ""));
+ goto exit;
+ } else {
+ comp[n] = malloc(q - start + 1);
+ if (comp[n] == NULL) {
+ ret = krb5_enomem(context);
+ goto exit;
+ }
+ memcpy(comp[n], start, q - start);
+ comp[n][q - start] = 0;
+ n++;
+ }
+ if (c == '@')
+ got_realm = 1;
+ start = q;
+ continue;
+ }
+ if (got_realm && (c == '/' || c == '\0')) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("part after realm in principal name", ""));
+ goto exit;
+ }
+ *q++ = c;
+ }
+ if (got_realm) {
+ if (no_realm) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("realm found in 'short' principal "
+ "expected to be without one", ""));
+ goto exit;
+ }
+ if (!ignore_realm) {
+ realm = malloc(q - start + 1);
+ if (realm == NULL) {
+ ret = krb5_enomem(context);
+ goto exit;
+ }
+ memcpy(realm, start, q - start);
+ realm[q - start] = 0;
+ }
+ } else {
+ if (require_realm) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret,
+ N_("realm NOT found in principal "
+ "expected to be with one", ""));
+ goto exit;
+ } else if (no_realm || no_def_realm) {
+ realm = NULL;
+ } else {
+ ret = krb5_get_default_realm(context, &realm);
+ if (ret)
+ goto exit;
+ }
+
+ comp[n] = malloc(q - start + 1);
+ if (comp[n] == NULL) {
+ ret = krb5_enomem(context);
+ goto exit;
+ }
+ memcpy(comp[n], start, q - start);
+ comp[n][q - start] = 0;
+ n++;
+ }
+ *principal = calloc(1, sizeof(**principal));
+ if (*principal == NULL) {
+ ret = krb5_enomem(context);
+ goto exit;
+ }
+ (*principal)->name.name_string.val = comp;
+ princ_num_comp(*principal) = n;
+ (*principal)->realm = realm;
+ if (enterprise)
+ princ_type(*principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
+ else
+ set_default_princ_type(*principal, KRB5_NT_PRINCIPAL);
+ free(s);
+ return 0;
+exit:
+ while (n>0) {
+ free(comp[--n]);
+ }
+ free(comp);
+ krb5_free_default_realm(context, realm);
+ free(s);
+ return ret;
+}
+
+/**
+ * Parse a name into a krb5_principal structure
+ *
+ * @param context Kerberos 5 context
+ * @param name name to parse into a Kerberos principal
+ * @param principal returned principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_parse_name(krb5_context context,
+ const char *name,
+ krb5_principal *principal)
+{
+ return krb5_parse_name_flags(context, name, 0, principal);
+}
+
+static const char quotable_chars[] = " \n\t\b\\/@";
+static const char replace_chars[] = " ntb\\/@";
+
+#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
+
+static size_t
+quote_string(const char *s, char *out, size_t idx, size_t len, int display)
+{
+ const char *p, *q;
+ for(p = s; *p && idx < len; p++){
+ q = strchr(quotable_chars, *p);
+ if (q && display) {
+ add_char(out, idx, len, replace_chars[q - quotable_chars]);
+ } else if (q) {
+ add_char(out, idx, len, '\\');
+ add_char(out, idx, len, replace_chars[q - quotable_chars]);
+ }else
+ add_char(out, idx, len, *p);
+ }
+ if(idx < len)
+ out[idx] = '\0';
+ return idx;
+}
+
+
+static krb5_error_code
+unparse_name_fixed(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len,
+ int flags)
+{
+ size_t idx = 0;
+ size_t i;
+ int short_form = (flags & KRB5_PRINCIPAL_UNPARSE_SHORT) != 0;
+ int no_realm = (flags & KRB5_PRINCIPAL_UNPARSE_NO_REALM) != 0;
+ int display = (flags & KRB5_PRINCIPAL_UNPARSE_DISPLAY) != 0;
+
+ if (name == NULL) {
+ krb5_set_error_message(context, EINVAL,
+ N_("Invalid name buffer, "
+ "can't unparse", ""));
+ return EINVAL;
+ }
+
+ if (len == 0) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Invalid name buffer length, "
+ "can't unparse", ""));
+ return ERANGE;
+ }
+
+ name[0] = '\0';
+
+ if (!no_realm && princ_realm(principal) == NULL) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Realm missing from principal, "
+ "can't unparse", ""));
+ return ERANGE;
+ }
+
+ for(i = 0; i < princ_num_comp(principal); i++){
+ if(i)
+ add_char(name, idx, len, '/');
+ idx = quote_string(princ_ncomp(principal, i), name, idx, len, display);
+ if(idx == len) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Out of space printing principal", ""));
+ return ERANGE;
+ }
+ }
+ /* add realm if different from default realm */
+ if(short_form && !no_realm) {
+ krb5_realm r;
+ krb5_error_code ret;
+ ret = krb5_get_default_realm(context, &r);
+ if(ret)
+ return ret;
+ if(strcmp(princ_realm(principal), r) != 0)
+ short_form = 0;
+ krb5_free_default_realm(context, r);
+ }
+ if(!short_form && !no_realm) {
+ add_char(name, idx, len, '@');
+ idx = quote_string(princ_realm(principal), name, idx, len, display);
+ if(idx == len) {
+ krb5_set_error_message(context, ERANGE,
+ N_("Out of space printing "
+ "realm of principal", ""));
+ return ERANGE;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Unparse the principal name to a fixed buffer
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param name buffer to write name to
+ * @param len length of buffer
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_unparse_name_fixed(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len, 0);
+}
+
+/**
+ * Unparse the principal name to a fixed buffer. The realm is skipped
+ * if its a default realm.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param name buffer to write name to
+ * @param len length of buffer
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_unparse_name_fixed_short(krb5_context context,
+ krb5_const_principal principal,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len,
+ KRB5_PRINCIPAL_UNPARSE_SHORT);
+}
+
+/**
+ * Unparse the principal name with unparse flags to a fixed buffer.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param flags unparse flags
+ * @param name buffer to write name to
+ * @param len length of buffer
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_unparse_name_fixed_flags(krb5_context context,
+ krb5_const_principal principal,
+ int flags,
+ char *name,
+ size_t len)
+{
+ return unparse_name_fixed(context, principal, name, len, flags);
+}
+
+static krb5_error_code
+unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **name,
+ int flags)
+{
+ size_t len = 0, plen;
+ size_t i;
+ krb5_error_code ret;
+ /* count length */
+ if (princ_realm(principal)) {
+ plen = strlen(princ_realm(principal));
+
+ if(strcspn(princ_realm(principal), quotable_chars) == plen)
+ len += plen;
+ else
+ len += 2*plen;
+ len++; /* '@' */
+ }
+ for(i = 0; i < princ_num_comp(principal); i++){
+ plen = strlen(princ_ncomp(principal, i));
+ if(strcspn(princ_ncomp(principal, i), quotable_chars) == plen)
+ len += plen;
+ else
+ len += 2*plen;
+ len++;
+ }
+ len++; /* '\0' */
+ *name = malloc(len);
+ if(*name == NULL)
+ return krb5_enomem(context);
+ ret = unparse_name_fixed(context, principal, *name, len, flags);
+ if(ret) {
+ free(*name);
+ *name = NULL;
+ }
+ return ret;
+}
+
+/**
+ * Unparse the Kerberos name into a string
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ * @param name resulting string, free with krb5_xfree()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_unparse_name(krb5_context context,
+ krb5_const_principal principal,
+ char **name)
+{
+ return unparse_name(context, principal, name, 0);
+}
+
+/**
+ * Unparse the Kerberos name into a string
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ * @param flags flag to determine the behavior
+ * @param name resulting string, free with krb5_xfree()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_unparse_name_flags(krb5_context context,
+ krb5_const_principal principal,
+ int flags,
+ char **name)
+{
+ return unparse_name(context, principal, name, flags);
+}
+
+/**
+ * Unparse the principal name to a allocated buffer. The realm is
+ * skipped if its a default realm.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param name returned buffer, free with krb5_xfree()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_unparse_name_short(krb5_context context,
+ krb5_const_principal principal,
+ char **name)
+{
+ return unparse_name(context, principal, name, KRB5_PRINCIPAL_UNPARSE_SHORT);
+}
+
+/**
+ * Set a new realm for a principal, and as a side-effect free the
+ * previous realm.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal set the realm for
+ * @param realm the new realm to set
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_principal_set_realm(krb5_context context,
+ krb5_principal principal,
+ krb5_const_realm realm)
+{
+ if (princ_realm(principal))
+ free(princ_realm(principal));
+
+ if (realm == NULL)
+ princ_realm(principal) = NULL;
+ else if ((princ_realm(principal) = strdup(realm)) == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_principal_set_comp_string(krb5_context context,
+ krb5_principal principal,
+ unsigned int k,
+ const char *component)
+{
+ char *s;
+ size_t i;
+
+ for (i = princ_num_comp(principal); i <= k; i++)
+ append_component(context, principal, "", 0);
+ s = strdup(component);
+ if (s == NULL)
+ return krb5_enomem(context);
+ free(princ_ncomp(principal, k));
+ princ_ncomp(principal, k) = s;
+ return 0;
+}
+
+#ifndef HEIMDAL_SMALLER
+/**
+ * Build a principal using vararg style building
+ *
+ * @param context A Kerberos context.
+ * @param principal returned principal
+ * @param rlen length of realm
+ * @param realm realm name
+ * @param ... a list of components ended with NULL.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_build_principal(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ va_list ap;
+ va_start(ap, realm);
+ ret = krb5_build_principal_va(context, principal, rlen, realm, ap);
+ va_end(ap);
+ return ret;
+}
+#endif
+
+/**
+ * Build a principal using vararg style building
+ *
+ * @param context A Kerberos context.
+ * @param principal returned principal
+ * @param realm realm name
+ * @param ... a list of components ended with NULL.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+/* coverity[+alloc : arg-*1] */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_make_principal(krb5_context context,
+ krb5_principal *principal,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ krb5_realm r = NULL;
+ va_list ap;
+
+ *principal = NULL;
+
+ if(realm == NULL) {
+ ret = krb5_get_default_realm(context, &r);
+ if(ret)
+ return ret;
+ realm = r;
+ }
+ va_start(ap, realm);
+ ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
+ va_end(ap);
+ if(r)
+ krb5_free_default_realm(context, r);
+ return ret;
+}
+
+static krb5_error_code
+append_component(krb5_context context, krb5_principal p,
+ const char *comp,
+ size_t comp_len)
+{
+ heim_general_string *tmp;
+ size_t len = princ_num_comp(p);
+
+ tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
+ if(tmp == NULL)
+ return krb5_enomem(context);
+ princ_comp(p) = tmp;
+ princ_ncomp(p, len) = malloc(comp_len + 1);
+ if (princ_ncomp(p, len) == NULL)
+ return krb5_enomem(context);
+ memcpy (princ_ncomp(p, len), comp, comp_len);
+ princ_ncomp(p, len)[comp_len] = '\0';
+ princ_num_comp(p)++;
+ return 0;
+}
+
+static krb5_error_code
+va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
+{
+ krb5_error_code ret = 0;
+
+ while (1){
+ const char *s;
+ int len;
+
+ if ((len = va_arg(ap, int)) == 0)
+ break;
+ s = va_arg(ap, const char*);
+ if ((ret = append_component(context, p, s, len)) != 0)
+ break;
+ }
+ return ret;
+}
+
+static krb5_error_code
+va_princ(krb5_context context, krb5_principal p, va_list ap)
+{
+ krb5_error_code ret = 0;
+
+ while (1){
+ const char *s;
+
+ if ((s = va_arg(ap, const char*)) == NULL)
+ break;
+ if ((ret = append_component(context, p, s, strlen(s))) != 0)
+ break;
+ }
+ return ret;
+}
+
+static krb5_error_code
+build_principal(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ krb5_error_code (*func)(krb5_context, krb5_principal, va_list),
+ va_list ap)
+{
+ krb5_error_code ret;
+ krb5_principal p;
+
+ *principal = NULL;
+ p = calloc(1, sizeof(*p));
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ princ_realm(p) = strdup(realm);
+ if (p->realm == NULL) {
+ free(p);
+ return krb5_enomem(context);
+ }
+
+ ret = func(context, p, ap);
+ if (ret == 0) {
+ *principal = p;
+ set_default_princ_type(p, KRB5_NT_PRINCIPAL);
+ } else
+ krb5_free_principal(context, p);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_build_principal_va(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ va_list ap)
+{
+ return build_principal(context, principal, rlen, realm, va_princ, ap);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_build_principal_va_ext(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ va_list ap)
+{
+ return build_principal(context, principal, rlen, realm, va_ext_princ, ap);
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_build_principal_ext(krb5_context context,
+ krb5_principal *principal,
+ int rlen,
+ krb5_const_realm realm,
+ ...)
+{
+ krb5_error_code ret;
+ va_list ap;
+ va_start(ap, realm);
+ ret = krb5_build_principal_va_ext(context, principal, rlen, realm, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Copy a principal
+ *
+ * @param context A Kerberos context.
+ * @param inprinc principal to copy
+ * @param outprinc copied principal, free with krb5_free_principal()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_copy_principal(krb5_context context,
+ krb5_const_principal inprinc,
+ krb5_principal *outprinc)
+{
+ krb5_principal p;
+
+ *outprinc = NULL;
+
+ p = malloc(sizeof(*p));
+ if (p == NULL)
+ return krb5_enomem(context);
+ if(copy_Principal(inprinc, p)) {
+ free(p);
+ return krb5_enomem(context);
+ }
+ if (inprinc->nameattrs && inprinc->nameattrs->pac)
+ p->nameattrs->pac = heim_retain(inprinc->nameattrs->pac);
+
+ *outprinc = p;
+ return 0;
+}
+
+/**
+ * Return TRUE iff princ1 == princ2 (without considering the realm)
+ *
+ * @param context Kerberos 5 context
+ * @param princ1 first principal to compare
+ * @param princ2 second principal to compare
+ *
+ * @return non zero if equal, 0 if not
+ *
+ * @ingroup krb5_principal
+ * @see krb5_principal_compare()
+ * @see krb5_realm_compare()
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principal_compare_any_realm(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ size_t i;
+ if(princ_num_comp(princ1) != princ_num_comp(princ2))
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ1); i++){
+ if(strcmp(princ_ncomp(princ1, i), princ_ncomp(princ2, i)) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_principal_compare_PrincipalName(krb5_context context,
+ krb5_const_principal princ1,
+ PrincipalName *princ2)
+{
+ size_t i;
+ if (princ_num_comp(princ1) != princ2->name_string.len)
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ1); i++){
+ if(strcmp(princ_ncomp(princ1, i), princ2->name_string.val[i]) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/**
+ * Compares the two principals, including realm of the principals and returns
+ * TRUE if they are the same and FALSE if not.
+ *
+ * @param context Kerberos 5 context
+ * @param princ1 first principal to compare
+ * @param princ2 second principal to compare
+ *
+ * @ingroup krb5_principal
+ * @see krb5_principal_compare_any_realm()
+ * @see krb5_realm_compare()
+ */
+
+/*
+ * return TRUE iff princ1 == princ2
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principal_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ if (!krb5_realm_compare(context, princ1, princ2))
+ return FALSE;
+ return krb5_principal_compare_any_realm(context, princ1, princ2);
+}
+
+/**
+ * return TRUE iff realm(princ1) == realm(princ2)
+ *
+ * @param context Kerberos 5 context
+ * @param princ1 first principal to compare
+ * @param princ2 second principal to compare
+ *
+ * @ingroup krb5_principal
+ * @see krb5_principal_compare_any_realm()
+ * @see krb5_principal_compare()
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_realm_compare(krb5_context context,
+ krb5_const_principal princ1,
+ krb5_const_principal princ2)
+{
+ return strcmp(princ_realm(princ1), princ_realm(princ2)) == 0;
+}
+
+/**
+ * return TRUE iff princ matches pattern
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principal_match(krb5_context context,
+ krb5_const_principal princ,
+ krb5_const_principal pattern)
+{
+ size_t i;
+ if(princ_num_comp(princ) != princ_num_comp(pattern))
+ return FALSE;
+ if(fnmatch(princ_realm(pattern), princ_realm(princ), 0) != 0)
+ return FALSE;
+ for(i = 0; i < princ_num_comp(princ); i++){
+ if(fnmatch(princ_ncomp(pattern, i), princ_ncomp(princ, i), 0) != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
+ * This is the original krb5_sname_to_principal(), renamed to be a
+ * helper of the new one.
+ */
+static krb5_error_code
+krb5_sname_to_principal_old(krb5_context context,
+ const char *realm,
+ const char *hostname,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
+{
+ krb5_error_code ret;
+ char localhost[MAXHOSTNAMELEN];
+ char **realms = NULL, *host = NULL;
+
+ if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
+ krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
+ N_("unsupported name type %d", ""),
+ (int)type);
+ return KRB5_SNAME_UNSUPP_NAMETYPE;
+ }
+ if(hostname == NULL) {
+ ret = gethostname(localhost, sizeof(localhost) - 1);
+ if (ret != 0) {
+ ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("Failed to get local hostname", ""));
+ return ret;
+ }
+ localhost[sizeof(localhost) - 1] = '\0';
+ hostname = localhost;
+ }
+ if(sname == NULL)
+ sname = "host";
+ if(type == KRB5_NT_SRV_HST) {
+ if (realm)
+ ret = krb5_expand_hostname(context, hostname, &host);
+ else
+ ret = krb5_expand_hostname_realms(context, hostname,
+ &host, &realms);
+ if (ret)
+ return ret;
+ strlwr(host);
+ hostname = host;
+ if (!realm)
+ realm = realms[0];
+ } else if (!realm) {
+ ret = krb5_get_host_realm(context, hostname, &realms);
+ if(ret)
+ return ret;
+ realm = realms[0];
+ }
+
+ ret = krb5_make_principal(context, ret_princ, realm, sname,
+ hostname, NULL);
+ if(host)
+ free(host);
+ if (realms)
+ krb5_free_host_realm(context, realms);
+ return ret;
+}
+
+static const struct {
+ const char *type;
+ int32_t value;
+} nametypes[] = {
+ { "UNKNOWN", KRB5_NT_UNKNOWN },
+ { "PRINCIPAL", KRB5_NT_PRINCIPAL },
+ { "SRV_INST", KRB5_NT_SRV_INST },
+ { "SRV_HST", KRB5_NT_SRV_HST },
+ { "SRV_XHST", KRB5_NT_SRV_XHST },
+ { "UID", KRB5_NT_UID },
+ { "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
+ { "SMTP_NAME", KRB5_NT_SMTP_NAME },
+ { "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
+ { "WELLKNOWN", KRB5_NT_WELLKNOWN },
+ { "SRV_HST_DOMAIN", KRB5_NT_SRV_HST_DOMAIN },
+ { "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
+ { "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
+ { "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
+ { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON },
+ { NULL, 0 }
+};
+
+/**
+ * Parse nametype string and return a nametype integer
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
+{
+ size_t i;
+
+ for(i = 0; nametypes[i].type; i++) {
+ if (strcasecmp(nametypes[i].type, str) == 0) {
+ *nametype = nametypes[i].value;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
+ N_("Failed to find name type %s", ""), str);
+ return KRB5_PARSE_MALFORMED;
+}
+
+/**
+ * Returns true if name is Kerberos NULL name
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_null(krb5_context context, krb5_const_principal principal)
+{
+ if (principal->name.name_type == KRB5_NT_WELLKNOWN &&
+ principal->name.name_string.len == 2 &&
+ strcmp(principal->name.name_string.val[0], "WELLKNOWN") == 0 &&
+ strcmp(principal->name.name_string.val[1], "NULL") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC";
+static const char lkdc_prefix[] = "LKDC:";
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_realm_is_lkdc(const char *realm)
+{
+
+ return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 ||
+ strncmp(realm, _krb5_wellknown_lkdc, sizeof(_krb5_wellknown_lkdc) - 1) == 0;
+}
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal)
+{
+ return krb5_realm_is_lkdc(principal->realm);
+}
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_pku2u(krb5_context context, krb5_const_principal principal)
+{
+ return strcmp(principal->realm, KRB5_PKU2U_REALM_NAME) == 0;
+}
+
+/**
+ * Check if the cname part of the principal name is a krbtgt principal
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principalname_is_krbtgt(krb5_context context, const PrincipalName *p)
+{
+ return 1 <= p->name_string.len &&
+ p->name_string.len <= 2 &&
+ strcmp(p->name_string.val[0], KRB5_TGS_NAME) == 0;
+}
+
+/**
+ * Check if the cname part of the principal is a krbtgt principal
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
+{
+ return krb5_principalname_is_krbtgt(context, &p->name);
+}
+
+/**
+ * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_gss_hostbased_service(krb5_context context,
+ krb5_const_principal principal)
+{
+ if (principal == NULL)
+ return FALSE;
+ if (principal->name.name_string.len != 2)
+ return FALSE;
+ if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * Check if the cname part of the principal is a initial or renewed krbtgt principal
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p)
+{
+ return p->name.name_string.len == 2 &&
+ strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 &&
+ strcmp(p->name.name_string.val[1], p->realm) == 0;
+}
+
+/**
+ * Returns true iff name is WELLKNOWN/ANONYMOUS
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principal_is_anonymous(krb5_context context,
+ krb5_const_principal p,
+ unsigned int flags)
+{
+ /*
+ * Heimdal versions 7.5 and below left the name-type at KRB5_NT_PRINCIPAL
+ * even with anonymous pkinit responses. To retain interoperability with
+ * legacy KDCs, the name-type is not checked by the client after requesting
+ * a fully anonymous ticket.
+ */
+ if (!(flags & KRB5_ANON_IGNORE_NAME_TYPE) &&
+ p->name.name_type != KRB5_NT_WELLKNOWN &&
+ p->name.name_type != KRB5_NT_UNKNOWN)
+ return FALSE;
+
+ if (p->name.name_string.len != 2 ||
+ strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
+ strcmp(p->name.name_string.val[1], KRB5_ANON_NAME) != 0)
+ return FALSE;
+
+ /*
+ * While unauthenticated clients SHOULD get "WELLKNOWN:ANONYMOUS" as their
+ * realm, Heimdal KDCs prior to 7.0 returned the requested realm. While
+ * such tickets might lead *servers* to unwittingly grant access to fully
+ * anonymous clients, trusting that the client was authenticated to the
+ * realm in question, doing it right is the KDC's job, the client should
+ * not refuse such a ticket.
+ *
+ * If we ever do decide to enforce WELLKNOWN:ANONYMOUS for unauthenticated
+ * clients, it is essential that calls that pass KRB5_ANON_MATCH_ANY still
+ * ignore the realm, as in that case either case matches one of the two
+ * possible conditions.
+ */
+ if (flags & KRB5_ANON_MATCH_UNAUTHENTICATED)
+ return TRUE;
+
+ /*
+ * Finally, authenticated clients that asked to be only anonymized do
+ * legitimately expect a non-anon realm.
+ */
+ return strcmp(p->realm, KRB5_ANON_REALM) != 0;
+}
+
+/**
+ * Returns true iff name is WELLKNOWN/FEDERATED
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_principal_is_federated(krb5_context context,
+ krb5_const_principal p)
+{
+ if (p->name.name_type != KRB5_NT_WELLKNOWN &&
+ p->name.name_type != KRB5_NT_UNKNOWN)
+ return FALSE;
+
+ if (p->name.name_string.len != 2 ||
+ strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
+ strcmp(p->name.name_string.val[1], KRB5_FEDERATED_NAME) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+tolower_ascii(int c)
+{
+ if (c >= 'A' && c <= 'Z')
+ return 'a' + (c - 'A');
+ return c;
+}
+
+typedef enum krb5_name_canon_rule_type {
+ KRB5_NCRT_BOGUS = 0,
+ KRB5_NCRT_AS_IS,
+ KRB5_NCRT_QUALIFY,
+ KRB5_NCRT_NSS
+} krb5_name_canon_rule_type;
+
+#ifdef UINT8_MAX
+#define MAXDOTS UINT8_MAX
+#else
+#define MAXDOTS (255U)
+#endif
+#ifdef UINT16_MAX
+#define MAXORDER UINT16_MAX
+#else
+#define MAXORDER (65535U)
+#endif
+
+struct krb5_name_canon_rule_data {
+ krb5_name_canon_rule_type type;
+ krb5_name_canon_rule_options options;
+ uint8_t mindots; /* match this many dots or more */
+ uint8_t maxdots; /* match no more than this many dots */
+ uint16_t explicit_order; /* given order */
+ uint16_t order; /* actual order */
+ char *match_domain; /* match this stem */
+ char *match_realm; /* match this realm */
+ char *domain; /* qualify with this domain */
+ char *realm; /* qualify with this realm */
+};
+
+/**
+ * Create a principal for the given service running on the given
+ * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
+ * according the configured name canonicalization rules, with
+ * canonicalization delayed in some cases. One rule involves DNS, which
+ * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
+ * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
+ *
+ * Canonicalization is immediate (not delayed) only when there is only
+ * one canonicalization rule and that rule indicates that we should do a
+ * host lookup by name (i.e., DNS).
+ *
+ * @param context A Kerberos context.
+ * @param hostname hostname to use
+ * @param sname Service name to use
+ * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
+ * @param ret_princ return principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+/* coverity[+alloc : arg-*4] */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sname_to_principal(krb5_context context,
+ const char *hostname,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
+{
+ char *realm, *remote_host;
+ krb5_error_code ret;
+ register char *cp;
+ char localname[MAXHOSTNAMELEN];
+
+ *ret_princ = NULL;
+
+ if ((type != KRB5_NT_UNKNOWN) &&
+ (type != KRB5_NT_SRV_HST))
+ return KRB5_SNAME_UNSUPP_NAMETYPE;
+
+ /* if hostname is NULL, use local hostname */
+ if (hostname == NULL) {
+ if (gethostname(localname, MAXHOSTNAMELEN))
+ return errno;
+ hostname = localname;
+ }
+
+ /* if sname is NULL, use "host" */
+ if (sname == NULL)
+ sname = "host";
+
+ remote_host = strdup(hostname);
+ if (remote_host == NULL)
+ return krb5_enomem(context);
+
+ if (type == KRB5_NT_SRV_HST) {
+ krb5_name_canon_rule rules;
+
+ /* Lower-case the hostname, because that's the convention */
+ for (cp = remote_host; *cp; cp++)
+ if (isupper((unsigned char) (*cp)))
+ *cp = tolower((unsigned char) (*cp));
+
+ /*
+ * If there is only one name canon rule and it says to
+ * canonicalize the old way, do that now, as we used to.
+ */
+ ret = _krb5_get_name_canon_rules(context, &rules);
+ if (ret) {
+ _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
+ ret);
+ free(remote_host);
+ return ret;
+ }
+ if (rules[0].type == KRB5_NCRT_NSS &&
+ rules[1].type == KRB5_NCRT_BOGUS) {
+ _krb5_debug(context, 5, "Using nss for name canon immediately");
+ ret = krb5_sname_to_principal_old(context, rules[0].realm,
+ remote_host, sname,
+ KRB5_NT_SRV_HST, ret_princ);
+ free(remote_host);
+ return ret;
+ }
+ }
+
+ /* Remove trailing dots */
+ if (remote_host[0]) {
+ for (cp = remote_host + strlen(remote_host)-1;
+ *cp == '.' && cp > remote_host;
+ cp--) {
+ *cp = '\0';
+ }
+ }
+
+ realm = ""; /* "Referral realm" */
+
+ ret = krb5_build_principal(context, ret_princ, strlen(realm),
+ realm, sname, remote_host,
+ (char *)0);
+
+ if (ret == 0 && type == KRB5_NT_SRV_HST) {
+ /*
+ * Hostname canonicalization is done elsewhere (in
+ * krb5_get_credentials() and krb5_kt_get_entry()).
+ *
+ * We overload the name type to indicate to those functions that
+ * this principal name requires canonicalization.
+ *
+ * We can't use the empty realm to denote the need to
+ * canonicalize the hostname too: it would mean that users who
+ * want to assert knowledge of a service's realm must also know
+ * the canonical hostname, but in practice they don't.
+ */
+ (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON;
+
+ _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@",
+ sname, remote_host);
+ }
+
+ free(remote_host);
+ return ret;
+}
+
+static void
+tolower_str(char *s)
+{
+ for (; *s != '\0'; s++) {
+ if (isupper((unsigned char)*s))
+ *s = tolower_ascii(*s);
+ }
+}
+
+static krb5_error_code
+rule_parse_token(krb5_context context, krb5_name_canon_rule rule,
+ const char *tok)
+{
+ long int n;
+ int needs_type = rule->type == KRB5_NCRT_BOGUS;
+
+ /*
+ * Rules consist of a sequence of tokens, some of which indicate
+ * what type of rule the rule is, and some of which set rule options
+ * or ancilliary data. Last rule type token wins.
+ */
+
+ /* Rule type tokens: */
+ if (needs_type && strcmp(tok, "as-is") == 0) {
+ rule->type = KRB5_NCRT_AS_IS;
+ } else if (needs_type && strcmp(tok, "qualify") == 0) {
+ rule->type = KRB5_NCRT_QUALIFY;
+ } else if (needs_type && strcmp(tok, "nss") == 0) {
+ rule->type = KRB5_NCRT_NSS;
+ /* Rule options: */
+ } else if (strcmp(tok, "use_fast") == 0) {
+ rule->options |= KRB5_NCRO_USE_FAST;
+ } else if (strcmp(tok, "use_dnssec") == 0) {
+ rule->options |= KRB5_NCRO_USE_DNSSEC;
+ } else if (strcmp(tok, "ccache_only") == 0) {
+ rule->options |= KRB5_NCRO_GC_ONLY;
+ } else if (strcmp(tok, "no_referrals") == 0) {
+ rule->options |= KRB5_NCRO_NO_REFERRALS;
+ } else if (strcmp(tok, "use_referrals") == 0) {
+ rule->options &= ~KRB5_NCRO_NO_REFERRALS;
+ if (rule->realm == NULL) {
+ rule->realm = strdup("");
+ if (rule->realm == NULL)
+ return krb5_enomem(context);
+ }
+ } else if (strcmp(tok, "lookup_realm") == 0) {
+ rule->options |= KRB5_NCRO_LOOKUP_REALM;
+ free(rule->realm);
+ rule->realm = NULL;
+ /* Rule ancilliary data: */
+ } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) {
+ free(rule->domain);
+ rule->domain = strdup(tok + strlen("domain="));
+ if (rule->domain == NULL)
+ return krb5_enomem(context);
+ tolower_str(rule->domain);
+ } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
+ free(rule->realm);
+ rule->realm = strdup(tok + strlen("realm="));
+ if (rule->realm == NULL)
+ return krb5_enomem(context);
+ } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) {
+ free(rule->match_domain);
+ rule->match_domain = strdup(tok + strlen("match_domain="));
+ if (rule->match_domain == NULL)
+ return krb5_enomem(context);
+ tolower_str(rule->match_domain);
+ } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) {
+ free(rule->match_realm);
+ rule->match_realm = strdup(tok + strlen("match_realm="));
+ if (rule->match_realm == NULL)
+ return krb5_enomem(context);
+ } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
+ errno = 0;
+ n = strtol(tok + strlen("mindots="), NULL, 10);
+ if (errno == 0 && n > 0 && n <= MAXDOTS)
+ rule->mindots = n;
+ } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) {
+ errno = 0;
+ n = strtol(tok + strlen("maxdots="), NULL, 10);
+ if (errno == 0 && n > 0 && n <= MAXDOTS)
+ rule->maxdots = n;
+ } else if (strncmp(tok, "order=", strlen("order=")) == 0) {
+ errno = 0;
+ n = strtol(tok + strlen("order="), NULL, 10);
+ if (errno == 0 && n > 0 && n <= MAXORDER)
+ rule->explicit_order = n;
+ } else {
+ _krb5_debug(context, 5,
+ "Unrecognized name canonicalization rule token %s", tok);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+rule_cmp(const void *a, const void *b)
+{
+ krb5_const_name_canon_rule left = a;
+ krb5_const_name_canon_rule right = b;
+
+ if (left->type == KRB5_NCRT_BOGUS &&
+ right->type == KRB5_NCRT_BOGUS)
+ return 0;
+ if (left->type == KRB5_NCRT_BOGUS)
+ return 1;
+ if (right->type == KRB5_NCRT_BOGUS)
+ return -1;
+ if (left->explicit_order < right->explicit_order)
+ return -1;
+ if (left->explicit_order > right->explicit_order)
+ return 1;
+ return left->order - right->order;
+}
+
+static krb5_error_code
+parse_name_canon_rules(krb5_context context, char **rulestrs,
+ krb5_name_canon_rule *rules)
+{
+ krb5_error_code ret;
+ char *tok;
+ char *cp;
+ char **cpp;
+ size_t n;
+ size_t i, k;
+ int do_sort = 0;
+ krb5_name_canon_rule r;
+
+ *rules = NULL;
+
+ for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++)
+ n++;
+
+ n += 2; /* Always at least one rule; two for the default case */
+
+ if ((r = calloc(n, sizeof (*r))) == NULL)
+ return krb5_enomem(context);
+
+ for (k = 0; k < n; k++) {
+ r[k].type = KRB5_NCRT_BOGUS;
+ r[k].match_domain = NULL;
+ r[k].match_realm = NULL;
+ r[k].domain = NULL;
+ r[k].realm = NULL;
+ }
+
+ for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) {
+ cp = rulestrs[i];
+ r[k].explicit_order = MAXORDER; /* mark order, see below */
+ r[k].maxdots = MAXDOTS;
+ r[k].order = k; /* default order */
+
+ /* Tokenize and parse value */
+ do {
+ tok = cp;
+ cp = strchr(cp, ':'); /* XXX use strtok_r() */
+ if (cp)
+ *cp++ = '\0'; /* delimit token */
+ ret = rule_parse_token(context, &r[k], tok);
+ if (ret == EINVAL) {
+ r[k].type = KRB5_NCRT_BOGUS;
+ break;
+ }
+ if (ret) {
+ _krb5_free_name_canon_rules(context, r);
+ return ret;
+ }
+ } while (cp && *cp);
+ if (r[k].explicit_order != MAXORDER)
+ do_sort = 1;
+
+ /* Validate parsed rule */
+ if (r[k].type == KRB5_NCRT_BOGUS ||
+ (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) ||
+ (r[k].type == KRB5_NCRT_NSS && r[k].domain)) {
+ /* Invalid rule; mark it so and clean up */
+ r[k].type = KRB5_NCRT_BOGUS;
+ free(r[k].match_domain);
+ free(r[k].match_realm);
+ free(r[k].domain);
+ free(r[k].realm);
+ r[k].realm = NULL;
+ r[k].domain = NULL;
+ r[k].match_domain = NULL;
+ r[k].match_realm = NULL;
+ _krb5_debug(context, 5,
+ "Ignoring invalid name canonicalization rule %lu",
+ (unsigned long)i);
+ continue;
+ }
+ k++; /* good rule */
+ }
+
+ if (do_sort) {
+ /*
+ * Note that we make make this a stable sort by using appareance
+ * and explicit order.
+ */
+ qsort(r, n, sizeof(r[0]), rule_cmp);
+ }
+
+ if (r[0].type == KRB5_NCRT_BOGUS) {
+ /* No rules, or no valid rules */
+ if (context->flags & KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME) {
+ r[0].type = KRB5_NCRT_NSS;
+ } else {
+ r[0].type = KRB5_NCRT_AS_IS;
+ }
+ }
+
+ *rules = r;
+ return 0; /* We don't communicate bad rule errors here */
+}
+
+/*
+ * This exists only because the hostname canonicalization behavior in Heimdal
+ * (and other implementations of Kerberos) has been to use getaddrinfo(),
+ * unsafe though it is, for ages. We can't fix it in one day.
+ */
+static void
+make_rules_safe(krb5_context context, krb5_name_canon_rule rules)
+{
+ /*
+ * If the only rule were to use the name service (getaddrinfo()) then we're
+ * bound to fail. We could try to convert that rule to an as-is rule, but
+ * when we do get a validating resolver we'd be unhappy that we did such a
+ * conversion. Better let the user get failures and make them think about
+ * their naming rules.
+ */
+ if (rules == NULL)
+ return;
+ for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) {
+ if (rules->type == KRB5_NCRT_NSS)
+ rules->options |= KRB5_NCRO_USE_DNSSEC;
+ else
+ rules->options |= KRB5_NCRO_USE_FAST;
+ }
+}
+
+/**
+ * This function returns an array of host-based service name
+ * canonicalization rules. The array of rules is organized as a list.
+ * See the definition of krb5_name_canon_rule.
+ *
+ * @param context A Kerberos context.
+ * @param rules Output location for array of rules.
+ */
+KRB5_LIB_FUNCTION krb5_error_code
+_krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules)
+{
+ krb5_error_code ret;
+ char **values = NULL;
+
+ *rules = context->name_canon_rules;
+ if (*rules != NULL)
+ return 0;
+
+ values = krb5_config_get_strings(context, NULL,
+ "libdefaults", "name_canon_rules", NULL);
+ ret = parse_name_canon_rules(context, values, rules);
+ krb5_config_free_strings(values);
+ if (ret)
+ return ret;
+ if (*rules == NULL)
+ return krb5_enomem(context);
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE,
+ "libdefaults", "safe_name_canon", NULL))
+ make_rules_safe(context, *rules);
+
+ heim_assert((*rules)[0].type != KRB5_NCRT_BOGUS,
+ "internal error in parsing principal name "
+ "canonicalization rules");
+
+ /* Memoize */
+ context->name_canon_rules = *rules;
+
+ return 0;
+}
+
+static krb5_error_code
+get_host_realm(krb5_context context, const char *hostname, char **realm)
+{
+ krb5_error_code ret;
+ char **hrealms = NULL;
+
+ *realm = NULL;
+ ret = krb5_get_host_realm(context, hostname, &hrealms);
+ if (ret)
+ return ret;
+ if (hrealms == NULL)
+ return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
+ if (hrealms[0] == NULL) {
+ krb5_free_host_realm(context, hrealms);
+ return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
+ }
+ *realm = strdup(hrealms[0]);
+ krb5_free_host_realm(context, hrealms);
+ if (*realm == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+static int
+is_domain_suffix(const char *domain, const char *suffix)
+{
+ size_t dlen = strlen(domain);
+ size_t slen = strlen(suffix);
+
+ if (dlen < slen + 2)
+ return 0;
+
+ if (strcasecmp(domain + (dlen - slen), suffix) != 0)
+ return 0;
+
+ if (domain[(dlen - slen) - 1] != '.')
+ return 0;
+ return 1;
+}
+
+/*
+ * Applies a name canonicalization rule to a principal.
+ *
+ * Returns zero and no out_princ if the rule does not match.
+ * Returns zero and an out_princ if the rule does match.
+ */
+static krb5_error_code
+apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
+ size_t rule_idx, krb5_const_principal in_princ,
+ krb5_principal *out_princ,
+ krb5_name_canon_rule_options *rule_opts)
+{
+ krb5_name_canon_rule rule = &rules[rule_idx];
+ krb5_error_code ret = 0;
+ unsigned int ndots = 0;
+ krb5_principal nss = NULL;
+ const char *sname = NULL;
+ const char *orig_hostname = NULL;
+ const char *new_hostname = NULL;
+ const char *new_realm = NULL;
+ const char *port = "";
+ const char *cp;
+ char *hostname_sans_port = NULL;
+ char *hostname_with_port = NULL;
+ char *tmp_hostname = NULL;
+ char *tmp_realm = NULL;
+
+ *out_princ = NULL; /* Signal no match */
+
+ if (rule_opts != NULL)
+ *rule_opts = rule->options;
+
+ if (rule->type == KRB5_NCRT_BOGUS)
+ return 0; /* rule doesn't apply */
+
+ sname = krb5_principal_get_comp_string(context, in_princ, 0);
+ orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1);
+
+ /*
+ * Some apps want to use the very non-standard svc/hostname:port@REALM
+ * form. We do our best to support that here :(
+ */
+ port = strchr(orig_hostname, ':');
+ if (port != NULL) {
+ hostname_sans_port = strndup(orig_hostname, port - orig_hostname);
+ if (hostname_sans_port == NULL)
+ return krb5_enomem(context);
+ orig_hostname = hostname_sans_port;
+ }
+
+ _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
+ rule->type, orig_hostname);
+
+ if (rule->mindots > 0 || rule->maxdots > 0) {
+ for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
+ ndots++;
+ }
+ if (rule->mindots > 0 && ndots < rule->mindots)
+ goto out;
+ if (ndots > rule->maxdots)
+ goto out;
+
+ if (rule->match_domain != NULL &&
+ !is_domain_suffix(orig_hostname, rule->match_domain))
+ goto out;
+
+ if (rule->match_realm != NULL &&
+ strcmp(rule->match_realm, in_princ->realm) != 0)
+ goto out;
+
+ new_realm = rule->realm;
+ switch (rule->type) {
+ case KRB5_NCRT_AS_IS:
+ break;
+
+ case KRB5_NCRT_QUALIFY:
+ heim_assert(rule->domain != NULL,
+ "missing domain for qualify name canon rule");
+ if (asprintf(&tmp_hostname, "%s.%s", orig_hostname,
+ rule->domain) == -1 || tmp_hostname == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ new_hostname = tmp_hostname;
+ break;
+
+ case KRB5_NCRT_NSS:
+ if ((rule->options & KRB5_NCRO_USE_DNSSEC)) {
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ krb5_set_error_message(context, ret,
+ "Secure hostname resolution not supported");
+ goto out;
+ }
+ _krb5_debug(context, 5, "Using name service lookups");
+ ret = krb5_sname_to_principal_old(context, rule->realm,
+ orig_hostname, sname,
+ KRB5_NT_SRV_HST,
+ &nss);
+ if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
+ (ret == KRB5_ERR_BAD_HOSTNAME ||
+ ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
+ /*
+ * Bad hostname / realm unknown -> rule inapplicable if
+ * there's more rules. If it's the last rule then we want
+ * to return all errors from krb5_sname_to_principal_old()
+ * here.
+ */
+ ret = 0;
+ goto out;
+ }
+ if (ret)
+ goto out;
+
+ new_hostname = krb5_principal_get_comp_string(context, nss, 1);
+ new_realm = krb5_principal_get_realm(context, nss);
+ break;
+
+ default:
+ /* Can't happen */
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * This rule applies.
+ *
+ * Copy in_princ and mutate the copy per the matched rule.
+ *
+ * This way we apply to principals with two or more components, such as
+ * domain-based names.
+ */
+ ret = krb5_copy_principal(context, in_princ, out_princ);
+ if (ret)
+ goto out;
+
+ if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) {
+ ret = get_host_realm(context, new_hostname, &tmp_realm);
+ if (ret)
+ goto out;
+ new_realm = tmp_realm;
+ }
+
+ /* If we stripped off a :port, add it back in */
+ if (port != NULL && new_hostname != NULL) {
+ if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 ||
+ hostname_with_port == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ new_hostname = hostname_with_port;
+ }
+
+ if (new_realm != NULL &&
+ (ret = krb5_principal_set_realm(context, *out_princ, new_realm)))
+ goto out;
+ if (new_hostname != NULL &&
+ (ret = krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname)))
+ goto out;
+ if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON)
+ princ_type(*out_princ) = KRB5_NT_SRV_HST;
+
+ /* Trace rule application */
+ {
+ krb5_error_code ret2;
+ char *unparsed;
+
+ ret2 = krb5_unparse_name(context, *out_princ, &unparsed);
+ if (ret2) {
+ _krb5_debug(context, 5,
+ N_("Couldn't unparse canonicalized princicpal (%d)",
+ ""),
+ ret);
+ } else {
+ _krb5_debug(context, 5,
+ N_("Name canon rule application yields %s", ""),
+ unparsed);
+ free(unparsed);
+ }
+ }
+
+out:
+ free(hostname_sans_port);
+ free(hostname_with_port);
+ free(tmp_hostname);
+ free(tmp_realm);
+ krb5_free_principal(context, nss);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Name canon rule application failed", ""));
+ return ret;
+}
+
+/**
+ * Free name canonicalization rules
+ */
+KRB5_LIB_FUNCTION void
+_krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules)
+{
+ size_t k;
+
+ if (rules == NULL)
+ return;
+
+ for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) {
+ free(rules[k].match_domain);
+ free(rules[k].match_realm);
+ free(rules[k].domain);
+ free(rules[k].realm);
+ }
+ free(rules);
+}
+
+struct krb5_name_canon_iterator_data {
+ krb5_name_canon_rule rules;
+ krb5_const_principal in_princ; /* given princ */
+ krb5_const_principal out_princ; /* princ to be output */
+ krb5_principal tmp_princ; /* to be freed */
+ int is_trivial; /* no canon to be done */
+ int done; /* no more rules to be applied */
+ size_t cursor; /* current/next rule */
+};
+
+/**
+ * Initialize name canonicalization iterator.
+ *
+ * @param context Kerberos context
+ * @param in_princ principal name to be canonicalized OR
+ * @param iter output iterator object
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_name_canon_iterator_start(krb5_context context,
+ krb5_const_principal in_princ,
+ krb5_name_canon_iterator *iter)
+{
+ krb5_error_code ret;
+ krb5_name_canon_iterator state;
+
+ *iter = NULL;
+
+ state = calloc(1, sizeof (*state));
+ if (state == NULL)
+ return krb5_enomem(context);
+ state->in_princ = in_princ;
+
+ if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) {
+ ret = _krb5_get_name_canon_rules(context, &state->rules);
+ if (ret)
+ goto out;
+ } else {
+ /* Name needs no canon -> trivial iterator: in_princ is canonical */
+ state->is_trivial = 1;
+ }
+
+ *iter = state;
+ return 0;
+
+out:
+ krb5_free_name_canon_iterator(context, state);
+ return krb5_enomem(context);
+}
+
+/*
+ * Helper for name canon iteration.
+ */
+static krb5_error_code
+name_canon_iterate(krb5_context context,
+ krb5_name_canon_iterator *iter,
+ krb5_name_canon_rule_options *rule_opts)
+{
+ krb5_error_code ret;
+ krb5_name_canon_iterator state = *iter;
+
+ if (rule_opts)
+ *rule_opts = 0;
+
+ if (state == NULL)
+ return 0;
+
+ if (state->done) {
+ krb5_free_name_canon_iterator(context, state);
+ *iter = NULL;
+ return 0;
+ }
+
+ if (state->is_trivial && !state->done) {
+ state->out_princ = state->in_princ;
+ state->done = 1;
+ return 0;
+ }
+
+ heim_assert(state->rules != NULL &&
+ state->rules[state->cursor].type != KRB5_NCRT_BOGUS,
+ "Internal error during name canonicalization");
+
+ do {
+ krb5_free_principal(context, state->tmp_princ);
+ ret = apply_name_canon_rule(context, state->rules, state->cursor,
+ state->in_princ, &state->tmp_princ, rule_opts);
+ if (ret) {
+ krb5_free_name_canon_iterator(context, state);
+ *iter = NULL;
+ return ret;
+ }
+ state->cursor++;
+ } while (state->tmp_princ == NULL &&
+ state->rules[state->cursor].type != KRB5_NCRT_BOGUS);
+
+ if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS)
+ state->done = 1;
+
+ state->out_princ = state->tmp_princ;
+ if (state->tmp_princ == NULL) {
+ krb5_free_name_canon_iterator(context, state);
+ *iter = NULL;
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Iteratively apply name canon rules, outputing a principal and rule
+ * options each time. Iteration completes when the @iter is NULL on
+ * return or when an error is returned. Callers must free the iterator
+ * if they abandon it mid-way.
+ *
+ * @param context Kerberos context
+ * @param iter name canon rule iterator (input/output)
+ * @param try_princ output principal name
+ * @param rule_opts output rule options
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_name_canon_iterate(krb5_context context,
+ krb5_name_canon_iterator *iter,
+ krb5_const_principal *try_princ,
+ krb5_name_canon_rule_options *rule_opts)
+{
+ krb5_error_code ret;
+
+ *try_princ = NULL;
+
+ ret = name_canon_iterate(context, iter, rule_opts);
+ if (*iter)
+ *try_princ = (*iter)->out_princ;
+ return ret;
+}
+
+/**
+ * Free a name canonicalization rule iterator.
+ */
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_name_canon_iterator(krb5_context context,
+ krb5_name_canon_iterator iter)
+{
+ if (iter == NULL)
+ return;
+ if (iter->tmp_princ)
+ krb5_free_principal(context, iter->tmp_princ);
+ free(iter);
+}
diff --git a/third_party/heimdal/lib/krb5/prog_setup.c b/third_party/heimdal/lib/krb5/prog_setup.c
new file mode 100644
index 0000000..21afbf8
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/prog_setup.c
@@ -0,0 +1,64 @@
+/*
+ * 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"
+#include <getarg.h>
+#include <err.h>
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_std_usage(int code, struct getargs *args, int num_args)
+{
+ arg_printusage(args, num_args, NULL, "");
+ exit(code);
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_program_setup(krb5_context *context, int argc, char **argv,
+ struct getargs *args, int num_args,
+ void (KRB5_LIB_CALL *usage)(int, struct getargs*, int))
+{
+ krb5_error_code ret;
+ int optidx = 0;
+
+ if(usage == NULL)
+ usage = krb5_std_usage;
+
+ setprogname(argv[0]);
+ ret = krb5_init_context(context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if(getarg(args, num_args, argc, argv, &optidx))
+ (*usage)(1, args, num_args);
+ return optidx;
+}
diff --git a/third_party/heimdal/lib/krb5/prompter_posix.c b/third_party/heimdal/lib/krb5/prompter_posix.c
new file mode 100644
index 0000000..1bf748c
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/prompter_posix.c
@@ -0,0 +1,72 @@
+/*
+ * 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_CALLCONV
+krb5_prompter_posix (krb5_context context,
+ void *data,
+ const char *name,
+ const char *banner,
+ int num_prompts,
+ krb5_prompt prompts[])
+{
+ int i;
+
+ if (name)
+ fprintf (stderr, "%s\n", name);
+ if (banner)
+ fprintf (stderr, "%s\n", banner);
+ if (name || banner)
+ fflush(stderr);
+ for (i = 0; i < num_prompts; ++i) {
+ if (prompts[i].hidden) {
+ if(UI_UTIL_read_pw_string(prompts[i].reply->data,
+ prompts[i].reply->length,
+ prompts[i].prompt,
+ 0))
+ return 1;
+ } else {
+ char *s = prompts[i].reply->data;
+
+ fputs (prompts[i].prompt, stdout);
+ fflush (stdout);
+ if(fgets(prompts[i].reply->data,
+ prompts[i].reply->length,
+ stdin) == NULL)
+ return 1;
+ s[strcspn(s, "\n")] = '\0';
+ }
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/pseudo-random-test.c b/third_party/heimdal/lib/krb5/pseudo-random-test.c
new file mode 100644
index 0000000..4acf808
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/pseudo-random-test.c
@@ -0,0 +1,112 @@
+/*
+ * 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 <err.h>
+
+enum { MAXSIZE = 48 };
+
+static struct testcase {
+ krb5_enctype enctype;
+ unsigned char constant[MAXSIZE];
+ size_t constant_len;
+ unsigned char key[MAXSIZE];
+ unsigned char res[MAXSIZE];
+} tests[] = {
+ {ETYPE_AES128_CTS_HMAC_SHA256_128, "test", 4,
+ {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C},
+ {0x9D, 0x18, 0x86, 0x16, 0xF6, 0x38, 0x52, 0xFE, 0x86, 0x91, 0x5B, 0xB8, 0x40, 0xB4, 0xA8, 0x86,
+ 0xFF, 0x3E, 0x6B, 0xB0, 0xF8, 0x19, 0xB4, 0x9B, 0x89, 0x33, 0x93, 0xD3, 0x93, 0x85, 0x42, 0x95}},
+ {ETYPE_AES256_CTS_HMAC_SHA384_192, "test", 4,
+ {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},
+ {0x98, 0x01, 0xF6, 0x9A, 0x36, 0x8C, 0x2B, 0xF6, 0x75, 0xE5, 0x95, 0x21, 0xE1, 0x77, 0xD9, 0xA0,
+ 0x7F, 0x67, 0xEF, 0xE1, 0xCF, 0xDE, 0x8D, 0x3C, 0x8D, 0x6F, 0x6A, 0x02, 0x56, 0xE3, 0xB1, 0x7D,
+ 0xB3, 0xC1, 0xB6, 0x2A, 0xD1, 0xB8, 0x55, 0x33, 0x60, 0xD1, 0x73, 0x67, 0xEB, 0x15, 0x14, 0xD2}},
+ {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_crypto crypto;
+ krb5_data constant, prf;
+
+ krb5_data_zero(&prf);
+
+ key.keytype = t->enctype;
+ krb5_enctype_keysize(context, t->enctype, &key.keyvalue.length);
+ key.keyvalue.data = t->key;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_crypto_init");
+
+ constant.data = t->constant;
+ constant.length = t->constant_len;
+
+ ret = krb5_crypto_prf(context, crypto, &constant, &prf);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_crypto_prf");
+
+ if (memcmp(prf.data, t->res, prf.length) != 0) {
+ const unsigned char *p = prf.data;
+ int i;
+
+ printf ("PRF failed (enctype %d)\n", t->enctype);
+ printf ("should be: ");
+ for (i = 0; i < prf.length; ++i)
+ printf ("%02x", t->res[i]);
+ printf ("\nresult was: ");
+ for (i = 0; i < prf.length; ++i)
+ printf ("%02x", p[i]);
+ printf ("\n");
+ val = 1;
+ }
+ krb5_data_free(&prf);
+ krb5_crypto_destroy(context, crypto);
+ }
+ krb5_free_context(context);
+
+ return val;
+}
diff --git a/third_party/heimdal/lib/krb5/rd_cred.c b/third_party/heimdal/lib/krb5/rd_cred.c
new file mode 100644
index 0000000..f8d5736
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/rd_cred.c
@@ -0,0 +1,348 @@
+/*
+ * 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"
+
+static krb5_error_code
+compare_addrs(krb5_context context,
+ krb5_address *a,
+ krb5_address *b,
+ const char *message)
+{
+ char a_str[64], b_str[64];
+ size_t len;
+
+ if(krb5_address_compare (context, a, b))
+ return 0;
+
+ krb5_print_address (a, a_str, sizeof(a_str), &len);
+ krb5_print_address (b, b_str, sizeof(b_str), &len);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_BADADDR,
+ "%s: %s != %s", message, b_str, a_str);
+ return KRB5KRB_AP_ERR_BADADDR;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_cred(krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *in_data,
+ krb5_creds ***ret_creds,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ size_t len;
+ KRB_CRED cred;
+ EncKrbCredPart enc_krb_cred_part;
+ krb5_data enc_krb_cred_part_data;
+ krb5_crypto crypto;
+ size_t i;
+
+ memset(&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
+ krb5_data_zero(&enc_krb_cred_part_data);
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)) &&
+ outdata == NULL)
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+
+ *ret_creds = NULL;
+
+ ret = decode_KRB_CRED(in_data->data, in_data->length,
+ &cred, &len);
+ if(ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+
+ if (cred.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ if (cred.msg_type != krb_cred) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ if (cred.enc_part.etype == ETYPE_NULL) {
+ /* DK: MIT GSS-API Compatibility */
+ enc_krb_cred_part_data.length = cred.enc_part.cipher.length;
+ enc_krb_cred_part_data.data = cred.enc_part.cipher.data;
+ } else {
+ /* Try both subkey and session key.
+ *
+ * RFC4120 claims we should use the session key, but Heimdal
+ * before 0.8 used the remote subkey if it was send in the
+ * auth_context.
+ */
+
+ if (auth_context->remote_subkey) {
+ ret = krb5_crypto_init(context, auth_context->remote_subkey,
+ 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ &cred.enc_part,
+ &enc_krb_cred_part_data);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+
+ /*
+ * If there was not subkey, or we failed using subkey,
+ * retry using the session key
+ */
+ if (auth_context->remote_subkey == NULL || ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
+ {
+
+ ret = krb5_crypto_init(context, auth_context->keyblock,
+ 0, &crypto);
+
+ if (ret)
+ goto out;
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_CRED,
+ &cred.enc_part,
+ &enc_krb_cred_part_data);
+
+ krb5_crypto_destroy(context, crypto);
+ }
+ if (ret)
+ goto out;
+ }
+
+ ret = decode_EncKrbCredPart(enc_krb_cred_part_data.data,
+ enc_krb_cred_part_data.length,
+ &enc_krb_cred_part,
+ &len);
+ if (enc_krb_cred_part_data.data != cred.enc_part.cipher.data)
+ krb5_data_free(&enc_krb_cred_part_data);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode "
+ "encrypte credential part", ""));
+ goto out;
+ }
+
+ /* check sender address */
+
+ if (enc_krb_cred_part.s_address
+ && auth_context->remote_address
+ && auth_context->remote_port) {
+ krb5_address *a;
+
+ ret = krb5_make_addrport (context, &a,
+ auth_context->remote_address,
+ auth_context->remote_port);
+ if (ret)
+ goto out;
+
+
+ ret = compare_addrs(context, a, enc_krb_cred_part.s_address,
+ N_("sender address is wrong "
+ "in received creds", ""));
+ krb5_free_address(context, a);
+ free(a);
+ if(ret)
+ goto out;
+ }
+
+ /* check receiver address */
+
+ if (enc_krb_cred_part.r_address
+ && auth_context->local_address) {
+ if(auth_context->local_port &&
+ enc_krb_cred_part.r_address->addr_type == KRB5_ADDRESS_ADDRPORT) {
+ krb5_address *a;
+ ret = krb5_make_addrport (context, &a,
+ auth_context->local_address,
+ auth_context->local_port);
+ if (ret)
+ goto out;
+
+ ret = compare_addrs(context, a, enc_krb_cred_part.r_address,
+ N_("receiver address is wrong "
+ "in received creds", ""));
+ krb5_free_address(context, a);
+ free(a);
+ if(ret)
+ goto out;
+ } else {
+ ret = compare_addrs(context, auth_context->local_address,
+ enc_krb_cred_part.r_address,
+ N_("receiver address is wrong "
+ "in received creds", ""));
+ if(ret)
+ goto out;
+ }
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+
+ if (enc_krb_cred_part.timestamp == NULL ||
+ enc_krb_cred_part.usec == NULL ||
+ krb5_time_abs(*enc_krb_cred_part.timestamp, sec)
+ > context->max_skew) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_SKEW;
+ goto out;
+ }
+ }
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+ /* if these fields are not present in the cred-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ if(enc_krb_cred_part.timestamp)
+ outdata->timestamp = *enc_krb_cred_part.timestamp;
+ if(enc_krb_cred_part.usec)
+ outdata->usec = *enc_krb_cred_part.usec;
+ if(enc_krb_cred_part.nonce)
+ outdata->seq = *enc_krb_cred_part.nonce;
+ }
+
+ /* Convert to NULL terminated list of creds */
+
+ *ret_creds = calloc(enc_krb_cred_part.ticket_info.len + 1,
+ sizeof(**ret_creds));
+
+ if (*ret_creds == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ for (i = 0; i < enc_krb_cred_part.ticket_info.len; ++i) {
+ KrbCredInfo *kci = &enc_krb_cred_part.ticket_info.val[i];
+ krb5_creds *creds;
+
+ creds = calloc(1, sizeof(*creds));
+ if(creds == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+ &cred.tickets.val[i], &len, ret);
+ if (ret) {
+ free(creds);
+ goto out;
+ }
+ if(creds->ticket.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ ret = copy_EncryptionKey (&kci->key, &creds->session);
+ if (ret) {
+ krb5_free_creds(context, creds);
+ goto out;
+ }
+ if (kci->prealm && kci->pname)
+ _krb5_principalname2krb5_principal (context,
+ &creds->client,
+ *kci->pname,
+ *kci->prealm);
+ if (kci->flags)
+ creds->flags.b = *kci->flags;
+ if (kci->authtime)
+ creds->times.authtime = *kci->authtime;
+ if (kci->starttime)
+ creds->times.starttime = *kci->starttime;
+ if (kci->endtime)
+ creds->times.endtime = *kci->endtime;
+ if (kci->renew_till)
+ creds->times.renew_till = *kci->renew_till;
+ if (kci->srealm && kci->sname)
+ _krb5_principalname2krb5_principal (context,
+ &creds->server,
+ *kci->sname,
+ *kci->srealm);
+ if (kci->caddr)
+ krb5_copy_addresses (context,
+ kci->caddr,
+ &creds->addresses);
+
+ (*ret_creds)[i] = creds;
+
+ }
+ (*ret_creds)[i] = NULL;
+
+ free_KRB_CRED (&cred);
+ free_EncKrbCredPart(&enc_krb_cred_part);
+
+ return 0;
+
+ out:
+ free_EncKrbCredPart(&enc_krb_cred_part);
+ free_KRB_CRED (&cred);
+ if(*ret_creds) {
+ for(i = 0; (*ret_creds)[i]; i++)
+ krb5_free_creds(context, (*ret_creds)[i]);
+ free(*ret_creds);
+ *ret_creds = NULL;
+ }
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_cred2 (krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_ccache ccache,
+ krb5_data *in_data)
+{
+ krb5_error_code ret;
+ krb5_creds **creds;
+ int i;
+
+ ret = krb5_rd_cred(context, auth_context, in_data, &creds, NULL);
+ if(ret)
+ return ret;
+
+ /* Store the creds in the ccache */
+
+ for(i = 0; creds && creds[i]; i++) {
+ krb5_cc_store_cred(context, ccache, creds[i]);
+ krb5_free_creds(context, creds[i]);
+ }
+ free(creds);
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/rd_error.c b/third_party/heimdal/lib/krb5/rd_error.c
new file mode 100644
index 0000000..d778c68
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/rd_error.c
@@ -0,0 +1,125 @@
+/*
+ * 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_rd_error(krb5_context context,
+ const krb5_data *msg,
+ KRB_ERROR *result)
+{
+
+ size_t len;
+ krb5_error_code ret;
+
+ ret = decode_KRB_ERROR(msg->data, msg->length, result, &len);
+ if(ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ result->error_code += KRB5KDC_ERR_NONE;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_error_contents (krb5_context context,
+ krb5_error *error)
+{
+ free_KRB_ERROR(error);
+ memset(error, 0, sizeof(*error));
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_error (krb5_context context,
+ krb5_error *error)
+{
+ krb5_free_error_contents (context, error);
+ free (error);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_error_from_rd_error(krb5_context context,
+ const krb5_error *error,
+ const krb5_creds *creds)
+{
+ krb5_error_code ret;
+
+ ret = error->error_code;
+ if (error->e_text != NULL) {
+ krb5_set_error_message(context, ret, "%s", *error->e_text);
+ } else {
+ char clientname[256], servername[256];
+
+ if (creds != NULL) {
+ krb5_unparse_name_fixed(context, creds->client,
+ clientname, sizeof(clientname));
+ krb5_unparse_name_fixed(context, creds->server,
+ servername, sizeof(servername));
+ }
+
+ switch (ret) {
+ case KRB5KDC_ERR_NAME_EXP :
+ krb5_set_error_message(context, ret,
+ N_("Client %s%s%s expired", ""),
+ creds ? "(" : "",
+ creds ? clientname : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_SERVICE_EXP :
+ krb5_set_error_message(context, ret,
+ N_("Server %s%s%s expired", ""),
+ creds ? "(" : "",
+ creds ? servername : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN :
+ krb5_set_error_message(context, ret,
+ N_("Client %s%s%s unknown", ""),
+ creds ? "(" : "",
+ creds ? clientname : "",
+ creds ? ")" : "");
+ break;
+ case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN :
+ krb5_set_error_message(context, ret,
+ N_("Server %s%s%s unknown", ""),
+ creds ? "(" : "",
+ creds ? servername : "",
+ creds ? ")" : "");
+ break;
+ default :
+ krb5_clear_error_message(context);
+ break;
+ }
+ }
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/rd_priv.c b/third_party/heimdal/lib/krb5/rd_priv.c
new file mode 100644
index 0000000..ab1a165
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/rd_priv.c
@@ -0,0 +1,184 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_priv(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_PRIV priv;
+ EncKrbPrivPart part;
+ size_t len;
+ krb5_data plain;
+ krb5_keyblock *key;
+ krb5_crypto crypto;
+
+ krb5_data_zero(outbuf);
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)))
+ {
+ if (outdata == NULL) {
+ krb5_clear_error_message (context);
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+ }
+ /* if these fields are not present in the priv-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ }
+
+ memset(&priv, 0, sizeof(priv));
+ ret = decode_KRB_PRIV (inbuf->data, inbuf->length, &priv, &len);
+ if (ret) {
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+ if (priv.pvno != 5) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ goto failure;
+ }
+ if (priv.msg_type != krb_priv) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ goto failure;
+ }
+
+ if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else
+ key = auth_context->keyblock;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto failure;
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_KRB_PRIV,
+ &priv.enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto failure;
+
+ ret = decode_EncKrbPrivPart (plain.data, plain.length, &part, &len);
+ krb5_data_free (&plain);
+ if (ret) {
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+
+ /* check sender address */
+
+ if (part.s_address
+ && auth_context->remote_address
+ && !krb5_address_compare (context,
+ auth_context->remote_address,
+ part.s_address)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ goto failure_part;
+ }
+
+ /* check receiver address */
+
+ if (part.r_address
+ && auth_context->local_address
+ && !krb5_address_compare (context,
+ auth_context->local_address,
+ part.r_address)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ goto failure_part;
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+ if (part.timestamp == NULL ||
+ part.usec == NULL ||
+ krb5_time_abs(*part.timestamp, sec) > context->max_skew) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_SKEW;
+ goto failure_part;
+ }
+ }
+
+ /* XXX - check replay cache */
+
+ /* check sequence number. since MIT krb5 cannot generate a sequence
+ number of zero but instead generates no sequence number, we accept that
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if ((part.seq_number == NULL
+ && auth_context->remote_seqnumber != 0)
+ || (part.seq_number != NULL
+ && *part.seq_number != auth_context->remote_seqnumber)) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_BADORDER;
+ goto failure_part;
+ }
+ auth_context->remote_seqnumber++;
+ }
+
+ ret = krb5_data_copy (outbuf, part.user_data.data, part.user_data.length);
+ if (ret)
+ goto failure_part;
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+ if(part.timestamp)
+ outdata->timestamp = *part.timestamp;
+ if(part.usec)
+ outdata->usec = *part.usec;
+ if(part.seq_number)
+ outdata->seq = *part.seq_number;
+ }
+
+ failure_part:
+ free_EncKrbPrivPart (&part);
+
+ failure:
+ free_KRB_PRIV (&priv);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/rd_rep.c b/third_party/heimdal/lib/krb5/rd_rep.c
new file mode 100644
index 0000000..9968db3
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/rd_rep.c
@@ -0,0 +1,121 @@
+/*
+ * 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_rd_rep(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_ap_rep_enc_part **repl)
+{
+ krb5_error_code ret;
+ AP_REP ap_rep;
+ size_t len;
+ krb5_data data;
+ krb5_crypto crypto;
+
+ *repl = NULL;
+ krb5_data_zero (&data);
+
+ ret = decode_AP_REP(inbuf->data, inbuf->length, &ap_rep, &len);
+ if (ret)
+ return ret;
+ if (ap_rep.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ if (ap_rep.msg_type != krb_ap_rep) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
+ if (ret)
+ goto out;
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ &ap_rep.enc_part,
+ &data);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ goto out;
+
+ *repl = malloc(sizeof(**repl));
+ if (*repl == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ret = decode_EncAPRepPart(data.data, data.length, *repl, &len);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("Failed to decode EncAPRepPart", ""));
+ goto out;
+ }
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ if ((*repl)->ctime != auth_context->authenticator->ctime ||
+ (*repl)->cusec != auth_context->authenticator->cusec)
+ {
+ ret = KRB5KRB_AP_ERR_MUT_FAIL;
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+ if ((*repl)->seq_number)
+ krb5_auth_con_setremoteseqnumber(context, auth_context,
+ *((*repl)->seq_number));
+ if ((*repl)->subkey)
+ krb5_auth_con_setremotesubkey(context, auth_context, (*repl)->subkey);
+
+ out:
+ if (ret) {
+ krb5_free_ap_rep_enc_part(context, *repl);
+ *repl = NULL;
+ }
+ krb5_data_free(&data);
+ free_AP_REP(&ap_rep);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_ap_rep_enc_part (krb5_context context,
+ krb5_ap_rep_enc_part *val)
+{
+ if (val) {
+ free_EncAPRepPart (val);
+ free (val);
+ }
+}
diff --git a/third_party/heimdal/lib/krb5/rd_req.c b/third_party/heimdal/lib/krb5/rd_req.c
new file mode 100644
index 0000000..012cfef
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/rd_req.c
@@ -0,0 +1,1107 @@
+
+/*
+ * 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"
+
+static krb5_error_code
+decrypt_tkt_enc_part (krb5_context context,
+ krb5_keyblock *key,
+ EncryptedData *enc_part,
+ EncTicketPart *decr_part)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TICKET,
+ enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ret = decode_EncTicketPart(plain.data, plain.length, decr_part, &len);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode encrypted "
+ "ticket part", ""));
+ krb5_data_free (&plain);
+ return ret;
+}
+
+static krb5_error_code
+decrypt_authenticator (krb5_context context,
+ EncryptionKey *key,
+ EncryptedData *enc_part,
+ Authenticator *authenticator,
+ krb5_key_usage usage)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage /* KRB5_KU_AP_REQ_AUTH */,
+ enc_part,
+ &plain);
+ /* for backwards compatibility, also try the old usage */
+ if (ret && usage == KRB5_KU_TGS_REQ_AUTH)
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_AP_REQ_AUTH,
+ enc_part,
+ &plain);
+ krb5_crypto_destroy(context, crypto);
+ if (ret)
+ return ret;
+
+ ret = decode_Authenticator(plain.data, plain.length,
+ authenticator, &len);
+ krb5_data_free (&plain);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_decode_ap_req(krb5_context context,
+ const krb5_data *inbuf,
+ krb5_ap_req *ap_req)
+{
+ krb5_error_code ret;
+ size_t len;
+ ret = decode_AP_REQ(inbuf->data, inbuf->length, ap_req, &len);
+ if (ret)
+ return ret;
+ if (ap_req->pvno != 5){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_BADVERSION;
+ }
+ if (ap_req->msg_type != krb_ap_req){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_MSG_TYPE;
+ }
+ if (ap_req->ticket.tkt_vno != 5){
+ free_AP_REQ(ap_req);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_BADVERSION;
+ }
+ return 0;
+}
+
+static krb5_error_code
+check_transited(krb5_context context, Ticket *ticket, EncTicketPart *enc)
+{
+ char **realms;
+ unsigned int num_realms, n;
+ krb5_error_code ret;
+
+ /*
+ * Windows 2000 and 2003 uses this inside their TGT so it's normaly
+ * not seen by others, however, samba4 joined with a Windows AD as
+ * a Domain Controller gets exposed to this.
+ */
+ if(enc->transited.tr_type == 0 && enc->transited.contents.length == 0)
+ return 0;
+
+ if(enc->transited.tr_type != domain_X500_Compress)
+ return KRB5KDC_ERR_TRTYPE_NOSUPP;
+
+ if(enc->transited.contents.length == 0)
+ return 0;
+
+ ret = krb5_domain_x500_decode(context, enc->transited.contents,
+ &realms, &num_realms,
+ enc->crealm,
+ ticket->realm);
+ if(ret)
+ return ret;
+ ret = krb5_check_transited(context, enc->crealm,
+ ticket->realm,
+ realms, num_realms, NULL);
+ for (n = 0; n < num_realms; n++)
+ free(realms[n]);
+ free(realms);
+ return ret;
+}
+
+static krb5_error_code
+find_etypelist(krb5_context context,
+ krb5_auth_context auth_context,
+ EtypeList *etypes)
+{
+ krb5_error_code ret;
+ krb5_data data;
+
+ ret = _krb5_get_ad(context, auth_context->authenticator->authorization_data, NULL, KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION, &data);
+ if (ret)
+ return 0;
+
+ ret = decode_EtypeList(data.data, data.length, etypes, NULL);
+ krb5_data_free(&data);
+ if (ret)
+ krb5_clear_error_message(context);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_decrypt_ticket(krb5_context context,
+ Ticket *ticket,
+ krb5_keyblock *key,
+ EncTicketPart *out,
+ krb5_flags flags)
+{
+ EncTicketPart t;
+ krb5_error_code ret;
+ ret = decrypt_tkt_enc_part (context, key, &ticket->enc_part, &t);
+ if (ret)
+ return ret;
+
+ {
+ krb5_timestamp now;
+ time_t start = t.authtime;
+
+ krb5_timeofday (context, &now);
+ if(t.starttime)
+ start = *t.starttime;
+ if(start - now > context->max_skew
+ || (t.flags.invalid
+ && !(flags & KRB5_VERIFY_AP_REQ_IGNORE_INVALID))) {
+ free_EncTicketPart(&t);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_TKT_NYV;
+ }
+ if(now - t.endtime > context->max_skew) {
+ free_EncTicketPart(&t);
+ krb5_clear_error_message (context);
+ return KRB5KRB_AP_ERR_TKT_EXPIRED;
+ }
+
+ if(!t.flags.transited_policy_checked) {
+ ret = check_transited(context, ticket, &t);
+ if(ret) {
+ free_EncTicketPart(&t);
+ return ret;
+ }
+ }
+ }
+
+ if(out)
+ *out = t;
+ else
+ free_EncTicketPart(&t);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_authenticator_checksum(krb5_context context,
+ krb5_auth_context ac,
+ void *data,
+ size_t len)
+{
+ krb5_error_code ret;
+ krb5_keyblock *key = NULL;
+ krb5_authenticator authenticator;
+ krb5_crypto crypto;
+
+ ret = krb5_auth_con_getauthenticator(context, ac, &authenticator);
+ if (ret)
+ return ret;
+ if (authenticator->cksum == NULL) {
+ ret = -17;
+ goto out;
+ }
+ ret = krb5_auth_con_getkey(context, ac, &key);
+ if (ret)
+ goto out;
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+
+ _krb5_crypto_set_flags(context, crypto, KRB5_CRYPTO_FLAG_ALLOW_UNKEYED_CHECKSUM);
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ data, len, authenticator->cksum);
+ krb5_crypto_destroy(context, crypto);
+out:
+ krb5_free_authenticator(context, &authenticator);
+ krb5_free_keyblock(context, key);
+ return ret;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_ap_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags flags,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ return krb5_verify_ap_req2 (context,
+ auth_context,
+ ap_req,
+ server,
+ keyblock,
+ flags,
+ ap_req_options,
+ ticket,
+ KRB5_KU_AP_REQ_AUTH);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_ap_req2(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags flags,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket,
+ krb5_key_usage usage)
+{
+ krb5_ticket *t;
+ krb5_auth_context ac;
+ krb5_error_code ret;
+ EtypeList etypes;
+ int badaddr = 0;
+
+ memset(&etypes, 0, sizeof(etypes));
+
+ if (ticket)
+ *ticket = NULL;
+
+ if (auth_context && *auth_context) {
+ ac = *auth_context;
+ } else {
+ ret = krb5_auth_con_init (context, &ac);
+ if (ret)
+ return ret;
+ }
+
+ t = calloc(1, sizeof(*t));
+ if (t == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ if (ap_req->ap_options.use_session_key && ac->keyblock){
+ ret = krb5_decrypt_ticket(context, &ap_req->ticket,
+ ac->keyblock,
+ &t->ticket,
+ flags);
+ krb5_free_keyblock(context, ac->keyblock);
+ ac->keyblock = NULL;
+ }else
+ ret = krb5_decrypt_ticket(context, &ap_req->ticket,
+ keyblock,
+ &t->ticket,
+ flags);
+
+ if(ret)
+ goto out;
+
+ ret = _krb5_principalname2krb5_principal(context,
+ &t->server,
+ ap_req->ticket.sname,
+ ap_req->ticket.realm);
+ if (ret) goto out;
+
+ ret = decrypt_authenticator (context,
+ &t->ticket.key,
+ &ap_req->authenticator,
+ ac->authenticator,
+ usage);
+ if (ret)
+ goto out;
+
+ {
+ krb5_principal p1, p2;
+ krb5_boolean res;
+
+ _krb5_principalname2krb5_principal(context,
+ &p1,
+ ac->authenticator->cname,
+ ac->authenticator->crealm);
+ _krb5_principalname2krb5_principal(context,
+ &p2,
+ t->ticket.cname,
+ t->ticket.crealm);
+ res = krb5_principal_compare (context, p1, p2);
+ krb5_free_principal (context, p1);
+ krb5_free_principal (context, p2);
+ if (!res) {
+ ret = KRB5KRB_AP_ERR_BADMATCH;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ }
+
+ /*
+ * The ticket authenticates the client, and conveys naming attributes that
+ * we want to expose in GSS using RFC6680 APIs.
+ *
+ * So we same the ticket enc-part in the client's krb5_principal object
+ * (note though that the session key will be absent in that copy of the
+ * ticket enc-part).
+ */
+ ret = _krb5_ticket2krb5_principal(context, &t->client, &t->ticket,
+ ac->authenticator->authorization_data);
+ if (ret) goto out;
+
+ t->client->nameattrs->peer_realm =
+ calloc(1, sizeof(t->client->nameattrs->peer_realm[0]));
+ if (t->client->nameattrs->peer_realm == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ret = copy_Realm(&ap_req->ticket.realm, t->client->nameattrs->peer_realm);
+ if (ret) goto out;
+
+ /* check addresses */
+
+ if (t->ticket.caddr
+ && ac->remote_address
+ && !krb5_address_search (context,
+ ac->remote_address,
+ t->ticket.caddr)) {
+ /*
+ * Hack alert. If KRB5_VERIFY_AP_REQ_IGNORE_ADDRS and the client's
+ * address didn't check out then we'll return KRB5KRB_AP_ERR_BADADDR
+ * even on success, and we'll let the caller figure it out because
+ * `*ticket != NULL' or `*auth_context != NULL'.
+ */
+ if ((flags & KRB5_VERIFY_AP_REQ_IGNORE_ADDRS)) {
+ badaddr = 1;
+ } else {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+
+ /* check timestamp in authenticator */
+ {
+ krb5_timestamp now;
+
+ krb5_timeofday (context, &now);
+
+ if (krb5_time_abs(ac->authenticator->ctime, now) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_clear_error_message (context);
+ goto out;
+ }
+ }
+
+ if (ac->authenticator->seq_number)
+ krb5_auth_con_setremoteseqnumber(context, ac,
+ *ac->authenticator->seq_number);
+
+ /* XXX - Xor sequence numbers */
+
+ if (ac->authenticator->subkey) {
+ ret = krb5_auth_con_setremotesubkey(context, ac,
+ ac->authenticator->subkey);
+ if (ret)
+ goto out;
+ }
+
+ ret = find_etypelist(context, ac, &etypes);
+ if (ret)
+ goto out;
+
+ ac->keytype = ETYPE_NULL;
+
+ if (etypes.val) {
+ size_t i;
+
+ for (i = 0; i < etypes.len; i++) {
+ if (krb5_enctype_valid(context, etypes.val[i]) == 0) {
+ ac->keytype = etypes.val[i];
+ break;
+ }
+ }
+ }
+
+ /* save key */
+ ret = krb5_copy_keyblock(context, &t->ticket.key, &ac->keyblock);
+ if (ret) goto out;
+
+ if (ap_req_options) {
+ *ap_req_options = 0;
+ if (ac->keytype != ETYPE_NULL)
+ *ap_req_options |= AP_OPTS_USE_SUBKEY;
+ if (ap_req->ap_options.use_session_key)
+ *ap_req_options |= AP_OPTS_USE_SESSION_KEY;
+ if (ap_req->ap_options.mutual_required)
+ *ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
+ }
+
+ if(ticket)
+ *ticket = t;
+ else
+ krb5_free_ticket (context, t);
+ if (auth_context) {
+ if (*auth_context == NULL)
+ *auth_context = ac;
+ } else
+ krb5_auth_con_free (context, ac);
+ free_EtypeList(&etypes);
+
+ if (badaddr) {
+ krb5_clear_error_message(context);
+ return KRB5KRB_AP_ERR_BADADDR;
+ }
+ return 0;
+ out:
+ free_EtypeList(&etypes);
+ if (t)
+ krb5_free_ticket (context, t);
+ if (auth_context == NULL || *auth_context == NULL)
+ krb5_auth_con_free (context, ac);
+ return ret;
+}
+
+/*
+ *
+ */
+
+struct krb5_rd_req_in_ctx_data {
+ krb5_keytab keytab;
+ krb5_keyblock *keyblock;
+ krb5_boolean check_pac;
+};
+
+struct krb5_rd_req_out_ctx_data {
+ krb5_keyblock *keyblock;
+ krb5_flags ap_req_options;
+ krb5_ticket *ticket;
+ krb5_principal server;
+};
+
+/**
+ * Allocate a krb5_rd_req_in_ctx as an input parameter to
+ * krb5_rd_req_ctx(). The caller should free the context with
+ * krb5_rd_req_in_ctx_free() when done with the context.
+ *
+ * @param context Keberos 5 context.
+ * @param ctx in ctx to krb5_rd_req_ctx().
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
+{
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
+ return krb5_enomem(context);
+ (*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
+ return 0;
+}
+
+/**
+ * Set the keytab that krb5_rd_req_ctx() will use.
+ *
+ * @param context Keberos 5 context.
+ * @param in in ctx to krb5_rd_req_ctx().
+ * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the
+ * pointer, so the caller must free they keytab after
+ * krb5_rd_req_in_ctx_free() is called.
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_keytab(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_keytab keytab)
+{
+ in->keytab = keytab;
+ return 0;
+}
+
+/**
+ * Set if krb5_rq_red() is going to check the Windows PAC or not
+ *
+ * @param context Keberos 5 context.
+ * @param in krb5_rd_req_in_ctx to check the option on.
+ * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_pac_check(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_boolean flag)
+{
+ in->check_pac = flag;
+ return 0;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_in_set_keyblock(krb5_context context,
+ krb5_rd_req_in_ctx in,
+ krb5_keyblock *keyblock)
+{
+ in->keyblock = keyblock; /* XXX should make copy */
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_out_get_ap_req_options(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_flags *ap_req_options)
+{
+ *ap_req_options = out->ap_req_options;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_out_get_ticket(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_ticket **ticket)
+{
+ return krb5_copy_ticket(context, out->ticket, ticket);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_out_get_keyblock(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_keyblock **keyblock)
+{
+ return krb5_copy_keyblock(context, out->keyblock, keyblock);
+}
+
+/**
+ * Get the principal that was used in the request from the
+ * client. Might not match whats in the ticket if krb5_rd_req_ctx()
+ * searched in the keytab for a matching key.
+ *
+ * @param context a Kerberos 5 context.
+ * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx().
+ * @param principal return principal, free with krb5_free_principal().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_out_get_server(krb5_context context,
+ krb5_rd_req_out_ctx out,
+ krb5_principal *principal)
+{
+ return krb5_copy_principal(context, out->server, principal);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_rd_req_in_ctx_free(krb5_context context, krb5_rd_req_in_ctx ctx)
+{
+ free(ctx);
+}
+
+/**
+ * Free the krb5_rd_req_out_ctx.
+ *
+ * @param context Keberos 5 context.
+ * @param ctx krb5_rd_req_out_ctx context to free.
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
+{
+ if (ctx->ticket)
+ krb5_free_ticket(context, ctx->ticket);
+ if (ctx->keyblock)
+ krb5_free_keyblock(context, ctx->keyblock);
+ if (ctx->server)
+ krb5_free_principal(context, ctx->server);
+ free(ctx);
+}
+
+/**
+ * Process an AP_REQ message.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context authentication context of the peer.
+ * @param inbuf the AP_REQ message, obtained for example with krb5_read_message().
+ * @param server server principal.
+ * @param keytab server keytab.
+ * @param ap_req_options set to the AP_REQ options. See the AP_OPTS_* defines.
+ * @param ticket on success, set to the authenticated client credentials.
+ * Must be deallocated with krb5_free_ticket(). If not
+ * interested, pass a NULL value.
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ krb5_rd_req_in_ctx in;
+ krb5_rd_req_out_ctx out;
+
+ ret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req_in_set_keytab(context, in, keytab);
+ if (ret) {
+ krb5_rd_req_in_ctx_free(context, in);
+ return ret;
+ }
+
+ ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (ret)
+ return ret;
+
+ if (ap_req_options)
+ *ap_req_options = out->ap_req_options;
+ if (ticket) {
+ ret = krb5_copy_ticket(context, out->ticket, ticket);
+ if (ret)
+ goto out;
+ }
+
+out:
+ krb5_rd_req_out_ctx_free(context, out);
+ return ret;
+}
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_with_keyblock(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_keyblock *keyblock,
+ krb5_flags *ap_req_options,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ krb5_rd_req_in_ctx in;
+ krb5_rd_req_out_ctx out;
+
+ ret = krb5_rd_req_in_ctx_alloc(context, &in);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req_in_set_keyblock(context, in, keyblock);
+ if (ret) {
+ krb5_rd_req_in_ctx_free(context, in);
+ return ret;
+ }
+
+ ret = krb5_rd_req_ctx(context, auth_context, inbuf, server, in, &out);
+ krb5_rd_req_in_ctx_free(context, in);
+ if (ret)
+ return ret;
+
+ if (ap_req_options)
+ *ap_req_options = out->ap_req_options;
+ if (ticket) {
+ ret = krb5_copy_ticket(context, out->ticket, ticket);
+ if (ret)
+ goto out;
+ }
+
+out:
+ krb5_rd_req_out_ctx_free(context, out);
+ return ret;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+get_key_from_keytab(krb5_context context,
+ krb5_ap_req *ap_req,
+ krb5_const_principal server,
+ krb5_keytab keytab,
+ krb5_keyblock **out_key)
+{
+ krb5_keytab_entry entry;
+ krb5_error_code ret;
+ int kvno;
+ krb5_keytab real_keytab;
+
+ if(keytab == NULL)
+ krb5_kt_default(context, &real_keytab);
+ else
+ real_keytab = keytab;
+
+ if (ap_req->ticket.enc_part.kvno)
+ kvno = *ap_req->ticket.enc_part.kvno;
+ else
+ kvno = 0;
+
+ ret = krb5_kt_get_entry (context,
+ real_keytab,
+ server,
+ kvno,
+ ap_req->ticket.enc_part.etype,
+ &entry);
+ if(ret == 0) {
+ ret = krb5_copy_keyblock(context, &entry.keyblock, out_key);
+ krb5_kt_free_entry(context, &entry);
+ }
+ if(keytab == NULL)
+ krb5_kt_close(context, real_keytab);
+
+ return ret;
+}
+
+/**
+ * The core server function that verify application authentication
+ * requests from clients.
+ *
+ * @param context Keberos 5 context.
+ * @param auth_context the authentication context, can be NULL, then
+ * default values for the authentication context will used.
+ * @param inbuf the (AP-REQ) authentication buffer
+ *
+ * @param server the server to authenticate to. If NULL the function
+ * will try to find any available credential in the keytab
+ * that will verify the reply. The function will prefer the
+ * server specified in the AP-REQ, but if
+ * there is no mach, it will try all keytab entries for a
+ * match. This has serious performance issues for large keytabs.
+ *
+ * @param inctx control the behavior of the function, if NULL, the
+ * default behavior is used.
+ * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free().
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_req_ctx(krb5_context context,
+ krb5_auth_context *auth_context,
+ const krb5_data *inbuf,
+ krb5_const_principal server,
+ krb5_rd_req_in_ctx inctx,
+ krb5_rd_req_out_ctx *outctx)
+{
+ krb5_error_code ret;
+ krb5_ap_req ap_req;
+ krb5_rd_req_out_ctx o = NULL;
+ krb5_keytab id = NULL, keytab = NULL;
+ krb5_principal service = NULL;
+
+ if (outctx)
+ *outctx = NULL;
+
+ o = calloc(1, sizeof(*o));
+ if (o == NULL)
+ return krb5_enomem(context);
+
+ if (*auth_context == NULL) {
+ ret = krb5_auth_con_init(context, auth_context);
+ if (ret)
+ goto out;
+ }
+
+ ret = krb5_decode_ap_req(context, inbuf, &ap_req);
+ if(ret)
+ goto out;
+
+ /* Save the principal that was in the request */
+ ret = _krb5_principalname2krb5_principal(context,
+ &o->server,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+ if (ret)
+ goto out;
+
+ if (ap_req.ap_options.use_session_key &&
+ (*auth_context)->keyblock == NULL) {
+ ret = KRB5KRB_AP_ERR_NOKEY;
+ krb5_set_error_message(context, ret,
+ N_("krb5_rd_req: user to user auth "
+ "without session key given", ""));
+ goto out;
+ }
+
+ if (inctx && inctx->keytab)
+ id = inctx->keytab;
+
+ if((*auth_context)->keyblock){
+ ret = krb5_copy_keyblock(context,
+ (*auth_context)->keyblock,
+ &o->keyblock);
+ if (ret)
+ goto out;
+ } else if(inctx && inctx->keyblock){
+ ret = krb5_copy_keyblock(context,
+ inctx->keyblock,
+ &o->keyblock);
+ if (ret)
+ goto out;
+ } else {
+
+ if(id == NULL) {
+ krb5_kt_default(context, &keytab);
+ id = keytab;
+ }
+ if (id == NULL)
+ goto out;
+
+ if (server == NULL) {
+ ret = _krb5_principalname2krb5_principal(context,
+ &service,
+ ap_req.ticket.sname,
+ ap_req.ticket.realm);
+ if (ret)
+ goto out;
+ server = service;
+ }
+
+ ret = get_key_from_keytab(context,
+ &ap_req,
+ server,
+ id,
+ &o->keyblock);
+ if (ret) {
+ /* If caller specified a server, fail. */
+ if (service == NULL && (context->flags & KRB5_CTX_F_RD_REQ_IGNORE) == 0)
+ goto out;
+ /* Otherwise, fall back to iterating over the keytab. This
+ * have serious performace issues for larger keytab.
+ */
+ o->keyblock = NULL;
+ }
+ }
+
+ if (o->keyblock) {
+ /*
+ * We got an exact keymatch, use that.
+ */
+
+ ret = krb5_verify_ap_req2(context,
+ auth_context,
+ &ap_req,
+ server,
+ o->keyblock,
+ 0,
+ &o->ap_req_options,
+ &o->ticket,
+ KRB5_KU_AP_REQ_AUTH);
+
+ if (ret)
+ goto out;
+
+ } else {
+ /*
+ * Interate over keytab to find a key that can decrypt the request.
+ */
+
+ krb5_keytab_entry entry;
+ krb5_kt_cursor cursor;
+ int done = 0, kvno = 0;
+
+ memset(&cursor, 0, sizeof(cursor));
+
+ if (ap_req.ticket.enc_part.kvno)
+ kvno = *ap_req.ticket.enc_part.kvno;
+
+ ret = krb5_kt_start_seq_get(context, id, &cursor);
+ if (ret)
+ goto out;
+
+ done = 0;
+ while (!done) {
+ krb5_principal p;
+
+ ret = krb5_kt_next_entry(context, id, &entry, &cursor);
+ if (ret) {
+ _krb5_kt_principal_not_found(context, ret, id, o->server,
+ ap_req.ticket.enc_part.etype,
+ kvno);
+ break;
+ }
+
+ if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) {
+ krb5_kt_free_entry (context, &entry);
+ continue;
+ }
+
+ ret = krb5_verify_ap_req2(context,
+ auth_context,
+ &ap_req,
+ server,
+ &entry.keyblock,
+ 0,
+ &o->ap_req_options,
+ &o->ticket,
+ KRB5_KU_AP_REQ_AUTH);
+ if (ret) {
+ krb5_kt_free_entry (context, &entry);
+ continue;
+ }
+
+ /*
+ * Found a match, save the keyblock for PAC processing,
+ * and update the service principal in the ticket to match
+ * whatever is in the keytab.
+ */
+
+ ret = krb5_copy_keyblock(context,
+ &entry.keyblock,
+ &o->keyblock);
+ if (ret) {
+ krb5_kt_free_entry (context, &entry);
+ break;
+ }
+
+ ret = krb5_copy_principal(context, entry.principal, &p);
+ if (ret) {
+ krb5_kt_free_entry (context, &entry);
+ break;
+ }
+ krb5_free_principal(context, o->ticket->server);
+ o->ticket->server = p;
+
+ krb5_kt_free_entry (context, &entry);
+
+ done = 1;
+ }
+ krb5_kt_end_seq_get (context, id, &cursor);
+ if (ret)
+ goto out;
+ }
+
+ if (krb5_ticket_get_authorization_data_type(context, o->ticket,
+ KRB5_AUTHDATA_KDC_ISSUED,
+ NULL) == 0)
+ o->ticket->client->nameattrs->kdc_issued_verified = 1;
+
+ /* If there is a PAC, verify its server signature */
+ if (inctx == NULL || inctx->check_pac) {
+ krb5_pac pac;
+ krb5_data data;
+
+ ret = krb5_ticket_get_authorization_data_type(context,
+ o->ticket,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &data);
+ if (ret == 0) {
+ ret = krb5_pac_parse(context, data.data, data.length, &pac);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+
+ ret = krb5_pac_verify(context,
+ pac,
+ o->ticket->ticket.authtime,
+ o->ticket->client,
+ o->keyblock,
+ NULL);
+ if (ret == 0)
+ o->ticket->client->nameattrs->pac_verified = 1;
+ if (ret == 0 && (context->flags & KRB5_CTX_F_REPORT_CANONICAL_CLIENT_NAME)) {
+ krb5_error_code ret2;
+ krb5_principal canon_name;
+
+ ret2 = _krb5_pac_get_canon_principal(context, pac, &canon_name);
+ if (ret2 == 0) {
+ free_Realm(&o->ticket->client->realm);
+ free_PrincipalName(&o->ticket->client->name);
+ ret = copy_Realm(&canon_name->realm, &o->ticket->client->realm);
+ if (ret == 0)
+ ret = copy_PrincipalName(&canon_name->name, &o->ticket->client->name);
+ krb5_free_principal(context, canon_name);
+ } else if (ret2 != ENOENT)
+ ret = ret2;
+ }
+ if (ret) {
+ krb5_pac_free(context, pac);
+ goto out;
+ }
+ o->ticket->client->nameattrs->pac = pac;
+ } else
+ ret = 0;
+ }
+out:
+
+ if (ret || outctx == NULL)
+ krb5_rd_req_out_ctx_free(context, o);
+ else
+ *outctx = o;
+
+ free_AP_REQ(&ap_req);
+
+ if (service)
+ krb5_free_principal(context, service);
+
+ if (keytab)
+ krb5_kt_close(context, keytab);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/rd_safe.c b/third_party/heimdal/lib/krb5/rd_safe.c
new file mode 100644
index 0000000..24ed636
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/rd_safe.c
@@ -0,0 +1,214 @@
+/*
+ * 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
+verify_checksum(krb5_context context,
+ krb5_auth_context auth_context,
+ KRB_SAFE *safe)
+{
+ krb5_error_code ret;
+ u_char *buf;
+ size_t buf_size;
+ size_t len = 0;
+ Checksum c;
+ krb5_crypto crypto;
+ krb5_keyblock *key;
+
+ c = safe->cksum;
+ safe->cksum.cksumtype = 0;
+ safe->cksum.checksum.data = NULL;
+ safe->cksum.checksum.length = 0;
+
+ ASN1_MALLOC_ENCODE(KRB_SAFE, buf, buf_size, safe, &len, ret);
+ if(ret)
+ return ret;
+ if(buf_size != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ if (auth_context->remote_subkey)
+ key = auth_context->remote_subkey;
+ else if (auth_context->local_subkey)
+ key = auth_context->local_subkey;
+ else
+ key = auth_context->keyblock;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+ ret = krb5_verify_checksum (context,
+ crypto,
+ KRB5_KU_KRB_SAFE_CKSUM,
+ buf + buf_size - len,
+ len,
+ &c);
+ krb5_crypto_destroy(context, crypto);
+out:
+ safe->cksum = c;
+ free (buf);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rd_safe(krb5_context context,
+ krb5_auth_context auth_context,
+ const krb5_data *inbuf,
+ krb5_data *outbuf,
+ krb5_replay_data *outdata)
+{
+ krb5_error_code ret;
+ KRB_SAFE safe;
+ size_t len;
+
+ krb5_data_zero(outbuf);
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE)))
+ {
+ if (outdata == NULL) {
+ krb5_set_error_message(context, KRB5_RC_REQUIRED,
+ N_("rd_safe: need outdata "
+ "to return data", ""));
+ return KRB5_RC_REQUIRED; /* XXX better error, MIT returns this */
+ }
+ /* if these fields are not present in the safe-part, silently
+ return zero */
+ memset(outdata, 0, sizeof(*outdata));
+ }
+
+ ret = decode_KRB_SAFE (inbuf->data, inbuf->length, &safe, &len);
+ if (ret)
+ return ret;
+ if (safe.pvno != 5) {
+ ret = KRB5KRB_AP_ERR_BADVERSION;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+ if (safe.msg_type != krb_safe) {
+ ret = KRB5KRB_AP_ERR_MSG_TYPE;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+ if (!krb5_checksum_is_keyed(context, safe.cksum.cksumtype)
+ || !krb5_checksum_is_collision_proof(context, safe.cksum.cksumtype)) {
+ ret = KRB5KRB_AP_ERR_INAPP_CKSUM;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+
+ /* check sender address */
+
+ if (safe.safe_body.s_address
+ && auth_context->remote_address
+ && !krb5_address_compare (context,
+ auth_context->remote_address,
+ safe.safe_body.s_address)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+
+ /* check receiver address */
+
+ if (safe.safe_body.r_address
+ && auth_context->local_address
+ && !krb5_address_compare (context,
+ auth_context->local_address,
+ safe.safe_body.r_address)) {
+ ret = KRB5KRB_AP_ERR_BADADDR;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+
+ /* check timestamp */
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
+ krb5_timestamp sec;
+
+ krb5_timeofday (context, &sec);
+
+ if (safe.safe_body.timestamp == NULL ||
+ safe.safe_body.usec == NULL ||
+ krb5_time_abs(*safe.safe_body.timestamp, sec) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+ }
+ /* XXX - check replay cache */
+
+ /* check sequence number. since MIT krb5 cannot generate a sequence
+ number of zero but instead generates no sequence number, we accept that
+ */
+
+ if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE) {
+ if ((safe.safe_body.seq_number == NULL
+ && auth_context->remote_seqnumber != 0)
+ || (safe.safe_body.seq_number != NULL
+ && *safe.safe_body.seq_number !=
+ auth_context->remote_seqnumber)) {
+ ret = KRB5KRB_AP_ERR_BADORDER;
+ krb5_clear_error_message (context);
+ goto failure;
+ }
+ auth_context->remote_seqnumber++;
+ }
+
+ ret = verify_checksum (context, auth_context, &safe);
+ if (ret)
+ goto failure;
+
+ outbuf->length = safe.safe_body.user_data.length;
+ outbuf->data = malloc(outbuf->length);
+ if (outbuf->data == NULL && outbuf->length != 0) {
+ ret = krb5_enomem(context);
+ krb5_data_zero(outbuf);
+ goto failure;
+ }
+ memcpy (outbuf->data, safe.safe_body.user_data.data, outbuf->length);
+
+ if ((auth_context->flags &
+ (KRB5_AUTH_CONTEXT_RET_TIME | KRB5_AUTH_CONTEXT_RET_SEQUENCE))) {
+
+ if(safe.safe_body.timestamp)
+ outdata->timestamp = *safe.safe_body.timestamp;
+ if(safe.safe_body.usec)
+ outdata->usec = *safe.safe_body.usec;
+ if(safe.safe_body.seq_number)
+ outdata->seq = *safe.safe_body.seq_number;
+ }
+
+ failure:
+ free_KRB_SAFE (&safe);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/read_message.c b/third_party/heimdal/lib/krb5/read_message.c
new file mode 100644
index 0000000..4e9bd01
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/read_message.c
@@ -0,0 +1,104 @@
+/*
+ * 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_read_message (krb5_context context,
+ krb5_pointer p_fd,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ uint32_t len;
+ uint8_t buf[4];
+
+ krb5_data_zero(data);
+
+ ret = krb5_net_read (context, p_fd, buf, 4);
+ if(ret == -1) {
+ ret = errno;
+ krb5_clear_error_message (context);
+ return ret;
+ }
+ if(ret < 4) {
+ krb5_clear_error_message(context);
+ return HEIM_ERR_EOF;
+ }
+ len = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ ret = krb5_data_alloc (data, len);
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ if (krb5_net_read (context, p_fd, data->data, len) != len) {
+ ret = errno;
+ krb5_data_free (data);
+ krb5_clear_error_message (context);
+ return ret;
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_read_priv_message(krb5_context context,
+ krb5_auth_context ac,
+ krb5_pointer p_fd,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_data packet;
+
+ ret = krb5_read_message(context, p_fd, &packet);
+ if(ret)
+ return ret;
+ ret = krb5_rd_priv (context, ac, &packet, data, NULL);
+ krb5_data_free(&packet);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_read_safe_message(krb5_context context,
+ krb5_auth_context ac,
+ krb5_pointer p_fd,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_data packet;
+
+ ret = krb5_read_message(context, p_fd, &packet);
+ if(ret)
+ return ret;
+ ret = krb5_rd_safe (context, ac, &packet, data, NULL);
+ krb5_data_free(&packet);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/recvauth.c b/third_party/heimdal/lib/krb5/recvauth.c
new file mode 100644
index 0000000..6563783
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/recvauth.c
@@ -0,0 +1,269 @@
+/*
+ * 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"
+
+/*
+ * See `sendauth.c' for the format.
+ */
+
+static krb5_boolean
+match_exact(const void *data, const char *appl_version)
+{
+ return strcmp(data, appl_version) == 0;
+}
+
+/**
+ * Perform the server side of the sendauth protocol.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context authentication context of the peer.
+ * @param p_fd socket associated to the connection.
+ * @param appl_version server-specific string.
+ * @param server server principal.
+ * @param flags if KRB5_RECVAUTH_IGNORE_VERSION is set, skip the sendauth version
+ * part of the protocol.
+ * @param keytab server keytab.
+ * @param ticket on success, set to the authenticated client credentials.
+ * Must be deallocated with krb5_free_ticket(). If not
+ * interested, pass a NULL value.
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_recvauth(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_pointer p_fd,
+ const char *appl_version,
+ krb5_principal server,
+ int32_t flags,
+ krb5_keytab keytab,
+ krb5_ticket **ticket)
+{
+ return krb5_recvauth_match_version(context, auth_context, p_fd,
+ match_exact, appl_version,
+ server, flags,
+ keytab, ticket);
+}
+
+/**
+ * Perform the server side of the sendauth protocol like krb5_recvauth(), but support
+ * a user-specified callback, \a match_appl_version, to perform the match of the application
+ * version \a match_data.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_recvauth_match_version(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_pointer p_fd,
+ krb5_boolean (*match_appl_version)(const void *,
+ const char*),
+ const void *match_data,
+ krb5_principal server,
+ int32_t flags,
+ krb5_keytab keytab,
+ krb5_ticket **ticket)
+{
+ krb5_error_code ret;
+ const char *version = KRB5_SENDAUTH_VERSION;
+ char her_version[sizeof(KRB5_SENDAUTH_VERSION)];
+ char *her_appl_version;
+ uint32_t len;
+ u_char repl;
+ krb5_data data;
+ krb5_flags ap_options;
+ ssize_t n;
+
+ /*
+ * If there are no addresses in auth_context, get them from `fd'.
+ */
+
+ if (*auth_context == NULL) {
+ ret = krb5_auth_con_init (context, auth_context);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb5_auth_con_setaddrs_from_fd (context,
+ *auth_context,
+ p_fd);
+ if (ret)
+ return ret;
+
+ /*
+ * Expect SENDAUTH protocol version.
+ */
+ if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) {
+ n = krb5_net_read (context, p_fd, &len, 4);
+ if (n < 0) {
+ ret = errno ? errno : EINVAL;
+ krb5_set_error_message(context, ret, "read: %s", strerror(ret));
+ return ret;
+ }
+ if (n == 0) {
+ krb5_set_error_message(context, KRB5_SENDAUTH_BADAUTHVERS,
+ N_("Failed to receive sendauth data", ""));
+ return KRB5_SENDAUTH_BADAUTHVERS;
+ }
+ len = ntohl(len);
+ if (len != sizeof(her_version)
+ || krb5_net_read (context, p_fd, her_version, len) != len
+ || strncmp (version, her_version, len) != 0) {
+ repl = 1;
+ krb5_net_write (context, p_fd, &repl, 1);
+ krb5_clear_error_message (context);
+ return KRB5_SENDAUTH_BADAUTHVERS;
+ }
+ }
+
+ /*
+ * Expect application protocol version.
+ */
+ n = krb5_net_read (context, p_fd, &len, 4);
+ if (n < 0) {
+ ret = errno ? errno : EINVAL;
+ krb5_set_error_message(context, ret, "read: %s", strerror(ret));
+ return ret;
+ }
+ if (n == 0) {
+ krb5_clear_error_message (context);
+ return KRB5_SENDAUTH_BADAPPLVERS;
+ }
+ len = ntohl(len);
+ if (len > 1024 * 1024) {
+ krb5_set_error_message(context, ret = KRB5_SENDAUTH_REJECTED,
+ "AP-REQ too long");
+ return ret;
+ }
+ her_appl_version = malloc (len);
+ if (her_appl_version == NULL) {
+ repl = 2;
+ krb5_net_write (context, p_fd, &repl, 1);
+ return krb5_enomem(context);
+ }
+ if (krb5_net_read (context, p_fd, her_appl_version, len) != len
+ || !(*match_appl_version)(match_data, her_appl_version)) {
+ repl = 2;
+ krb5_net_write (context, p_fd, &repl, 1);
+ krb5_set_error_message(context, KRB5_SENDAUTH_BADAPPLVERS,
+ N_("wrong sendauth application version (%s)", ""),
+ her_appl_version);
+ free (her_appl_version);
+ return KRB5_SENDAUTH_BADAPPLVERS;
+ }
+ free (her_appl_version);
+
+ /*
+ * Send OK.
+ */
+ repl = 0;
+ if (krb5_net_write (context, p_fd, &repl, 1) != 1) {
+ ret = errno ? errno : EINVAL;
+ krb5_set_error_message(context, ret, "write: %s", strerror(ret));
+ return ret;
+ }
+
+ /*
+ * Until here, the fields in the message were in cleartext and unauthenticated.
+ * From now on, Kerberos kicks in.
+ */
+
+ /*
+ * Expect AP_REQ.
+ */
+ krb5_data_zero (&data);
+ ret = krb5_read_message (context, p_fd, &data);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_req (context,
+ auth_context,
+ &data,
+ server,
+ keytab,
+ &ap_options,
+ ticket);
+ krb5_data_free (&data);
+ if (ret) {
+ krb5_data error_data;
+ krb5_error_code ret2;
+
+ ret2 = krb5_mk_error (context,
+ ret,
+ NULL,
+ NULL,
+ NULL,
+ server,
+ NULL,
+ NULL,
+ &error_data);
+ if (ret2 == 0) {
+ krb5_write_message (context, p_fd, &error_data);
+ krb5_data_free (&error_data);
+ }
+ return ret;
+ }
+
+ /*
+ * Send OK.
+ */
+ len = 0;
+ if (krb5_net_write (context, p_fd, &len, 4) != 4) {
+ ret = errno ? errno : EINVAL;
+ krb5_set_error_message(context, ret, "write: %s", strerror(ret));
+ krb5_free_ticket(context, *ticket);
+ *ticket = NULL;
+ return ret;
+ }
+
+ /*
+ * If client requires mutual authentication, send AP_REP.
+ */
+ if (ap_options & AP_OPTS_MUTUAL_REQUIRED) {
+ ret = krb5_mk_rep (context, *auth_context, &data);
+ if (ret) {
+ krb5_free_ticket(context, *ticket);
+ *ticket = NULL;
+ return ret;
+ }
+
+ ret = krb5_write_message (context, p_fd, &data);
+ if (ret) {
+ krb5_free_ticket(context, *ticket);
+ *ticket = NULL;
+ return ret;
+ }
+ krb5_data_free (&data);
+ }
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/replay.c b/third_party/heimdal/lib/krb5/replay.c
new file mode 100644
index 0000000..2fec8af
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/replay.c
@@ -0,0 +1,328 @@
+/*
+ * 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"
+#include <vis.h>
+
+struct krb5_rcache_data {
+ char *name;
+};
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_resolve(krb5_context context,
+ krb5_rcache id,
+ const char *name)
+{
+ id->name = strdup(name);
+ if(id->name == NULL) {
+ krb5_set_error_message(context, KRB5_RC_MALLOC,
+ N_("malloc: out of memory", ""));
+ return KRB5_RC_MALLOC;
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_resolve_type(krb5_context context,
+ krb5_rcache *id,
+ const char *type)
+{
+ *id = NULL;
+ if (strcmp(type, "FILE") != 0) {
+ krb5_set_error_message (context, KRB5_RC_TYPE_NOTFOUND,
+ N_("replay cache type %s not supported", ""),
+ type);
+ return KRB5_RC_TYPE_NOTFOUND;
+ }
+ *id = calloc(1, sizeof(**id));
+ if(*id == NULL) {
+ krb5_set_error_message(context, KRB5_RC_MALLOC,
+ N_("malloc: out of memory", ""));
+ return KRB5_RC_MALLOC;
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_resolve_full(krb5_context context,
+ krb5_rcache *id,
+ const char *string_name)
+{
+ krb5_error_code ret;
+
+ *id = NULL;
+
+ if (strncmp(string_name, "FILE:", 5) != 0) {
+ krb5_set_error_message(context, KRB5_RC_TYPE_NOTFOUND,
+ N_("replay cache type %s not supported", ""),
+ string_name);
+ return KRB5_RC_TYPE_NOTFOUND;
+ }
+ ret = krb5_rc_resolve_type(context, id, "FILE");
+ if(ret)
+ return ret;
+ ret = krb5_rc_resolve(context, *id, string_name + 5);
+ if (ret) {
+ krb5_rc_close(context, *id);
+ *id = NULL;
+ }
+ return ret;
+}
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_rc_default_name(krb5_context context)
+{
+ return "FILE:/var/run/default_rcache";
+}
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_rc_default_type(krb5_context context)
+{
+ return "FILE";
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_default(krb5_context context,
+ krb5_rcache *id)
+{
+ return krb5_rc_resolve_full(context, id, krb5_rc_default_name(context));
+}
+
+struct rc_entry{
+ time_t stamp;
+ unsigned char data[16];
+};
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_initialize(krb5_context context,
+ krb5_rcache id,
+ krb5_deltat auth_lifespan)
+{
+ FILE *f = fopen(id->name, "w");
+ struct rc_entry tmp;
+ int ret;
+
+ if(f == NULL) {
+ char buf[128];
+ ret = errno;
+ rk_strerror_r(ret, buf, sizeof(buf));
+ krb5_set_error_message(context, ret, "open(%s): %s", id->name, buf);
+ return ret;
+ }
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.stamp = auth_lifespan;
+ fwrite(&tmp, 1, sizeof(tmp), f);
+ fclose(f);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_recover(krb5_context context,
+ krb5_rcache id)
+{
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_destroy(krb5_context context,
+ krb5_rcache id)
+{
+ int ret;
+
+ if(remove(id->name) < 0) {
+ char buf[128];
+ ret = errno;
+ rk_strerror_r(ret, buf, sizeof(buf));
+ krb5_set_error_message(context, ret, "remove(%s): %s", id->name, buf);
+ return ret;
+ }
+ return krb5_rc_close(context, id);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_close(krb5_context context,
+ krb5_rcache id)
+{
+ free(id->name);
+ free(id);
+ return 0;
+}
+
+static void
+checksum_authenticator(Authenticator *auth, void *data)
+{
+ EVP_MD_CTX *m = EVP_MD_CTX_create();
+ unsigned i;
+
+ EVP_DigestInit_ex(m, EVP_md5(), NULL);
+
+ EVP_DigestUpdate(m, auth->crealm, strlen(auth->crealm));
+ for(i = 0; i < auth->cname.name_string.len; i++)
+ EVP_DigestUpdate(m, auth->cname.name_string.val[i],
+ strlen(auth->cname.name_string.val[i]));
+ EVP_DigestUpdate(m, &auth->ctime, sizeof(auth->ctime));
+ EVP_DigestUpdate(m, &auth->cusec, sizeof(auth->cusec));
+
+ EVP_DigestFinal_ex(m, data, NULL);
+ EVP_MD_CTX_destroy(m);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_store(krb5_context context,
+ krb5_rcache id,
+ krb5_donot_replay *rep)
+{
+ struct rc_entry ent, tmp;
+ time_t t;
+ FILE *f;
+ int ret;
+ size_t count;
+
+ ent.stamp = time(NULL);
+ checksum_authenticator(rep, ent.data);
+ f = fopen(id->name, "r");
+ if(f == NULL) {
+ char buf[128];
+ ret = errno;
+ rk_strerror_r(ret, buf, sizeof(buf));
+ krb5_set_error_message(context, ret, "open(%s): %s", id->name, buf);
+ return ret;
+ }
+ rk_cloexec_file(f);
+ count = fread(&tmp, sizeof(ent), 1, f);
+ if (count != 1) {
+ fclose(f);
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ t = ent.stamp - tmp.stamp;
+ while(fread(&tmp, sizeof(ent), 1, f)){
+ if(tmp.stamp < t)
+ continue;
+ if(memcmp(tmp.data, ent.data, sizeof(ent.data)) == 0){
+ fclose(f);
+ krb5_clear_error_message (context);
+ return KRB5_RC_REPLAY;
+ }
+ }
+ if(ferror(f)){
+ char buf[128];
+ ret = errno;
+ fclose(f);
+ rk_strerror_r(ret, buf, sizeof(buf));
+ krb5_set_error_message(context, ret, "%s: %s",
+ id->name, buf);
+ return ret;
+ }
+ fclose(f);
+ f = fopen(id->name, "a");
+ if(f == NULL) {
+ char buf[128];
+ rk_strerror_r(errno, buf, sizeof(buf));
+ krb5_set_error_message(context, KRB5_RC_IO_UNKNOWN,
+ "open(%s): %s", id->name, buf);
+ return KRB5_RC_IO_UNKNOWN;
+ }
+ fwrite(&ent, 1, sizeof(ent), f);
+ fclose(f);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_expunge(krb5_context context,
+ krb5_rcache id)
+{
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_rc_get_lifespan(krb5_context context,
+ krb5_rcache id,
+ krb5_deltat *auth_lifespan)
+{
+ FILE *f = fopen(id->name, "r");
+ int r;
+ struct rc_entry ent;
+ r = fread(&ent, sizeof(ent), 1, f);
+ fclose(f);
+ if(r){
+ *auth_lifespan = ent.stamp;
+ return 0;
+ }
+ krb5_clear_error_message (context);
+ return KRB5_RC_IO_UNKNOWN;
+}
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_rc_get_name(krb5_context context,
+ krb5_rcache id)
+{
+ return id->name;
+}
+
+KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
+krb5_rc_get_type(krb5_context context,
+ krb5_rcache id)
+{
+ return "FILE";
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_server_rcache(krb5_context context,
+ const krb5_data *piece,
+ krb5_rcache *id)
+{
+ krb5_rcache rcache;
+ krb5_error_code ret;
+
+ char *tmp = malloc(4 * piece->length + 1);
+ char *name;
+
+ if (tmp == NULL)
+ return krb5_enomem(context);
+ strvisx(tmp, piece->data, piece->length, VIS_WHITE | VIS_OCTAL);
+#ifdef HAVE_GETEUID
+ ret = asprintf(&name, "FILE:rc_%s_%u", tmp, (unsigned)geteuid());
+#else
+ ret = asprintf(&name, "FILE:rc_%s", tmp);
+#endif
+ free(tmp);
+ if (ret < 0 || name == NULL)
+ return krb5_enomem(context);
+
+ ret = krb5_rc_resolve_full(context, &rcache, name);
+ free(name);
+ if(ret)
+ return ret;
+ *id = rcache;
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/salt-aes-sha1.c b/third_party/heimdal/lib/krb5/salt-aes-sha1.c
new file mode 100644
index 0000000..2c556f1
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/salt-aes-sha1.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 "krb5_locl.h"
+
+const int _krb5_AES_SHA1_string_to_default_iterator = 4096;
+
+static krb5_error_code
+AES_SHA1_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint32_t iter;
+ struct _krb5_encryption_type *et;
+ struct _krb5_key_data kd;
+
+ if (opaque.length == 0)
+ iter = _krb5_AES_SHA1_string_to_default_iterator;
+ else if (opaque.length == 4) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 4);
+ iter = ((uint32_t)v);
+ } else
+ return KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */
+
+ et = _krb5_find_enctype(enctype);
+ if (et == NULL)
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+
+ kd.schedule = NULL;
+ ALLOC(kd.key, 1);
+ if (kd.key == NULL)
+ return krb5_enomem(context);
+ kd.key->keytype = enctype;
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if (ret) {
+ krb5_set_error_message (context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+ ret = PKCS5_PBKDF2_HMAC(password.data, password.length,
+ salt.saltvalue.data, salt.saltvalue.length,
+ iter,
+ EVP_sha1(),
+ et->keytype->size, kd.key->keyvalue.data);
+ if (ret != 1) {
+ _krb5_free_key_data(context, &kd, et);
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "Error calculating s2k");
+ return KRB5_PROG_KEYTYPE_NOSUPP;
+ }
+
+ ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos"));
+ if (ret == 0)
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ _krb5_free_key_data(context, &kd, et);
+
+ return ret;
+}
+
+struct salt_type _krb5_AES_SHA1_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ AES_SHA1_string_to_key
+ },
+ { 0, NULL, NULL }
+};
diff --git a/third_party/heimdal/lib/krb5/salt-aes-sha2.c b/third_party/heimdal/lib/krb5/salt-aes-sha2.c
new file mode 100644
index 0000000..9de1c28
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/salt-aes-sha2.c
@@ -0,0 +1,136 @@
+/*
+ * 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"
+
+const int _krb5_AES_SHA2_string_to_default_iterator = 32768;
+
+static krb5_error_code
+AES_SHA2_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint32_t iter;
+ struct _krb5_encryption_type *et = NULL;
+ struct _krb5_key_data kd;
+ krb5_data saltp;
+ size_t enctypesz;
+ const EVP_MD *md = NULL;
+
+ krb5_data_zero(&saltp);
+ kd.key = NULL;
+ kd.schedule = NULL;
+
+ if (opaque.length == 0) {
+ iter = _krb5_AES_SHA2_string_to_default_iterator;
+ } else if (opaque.length == 4) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 4);
+ iter = ((uint32_t)v);
+ } else {
+ ret = KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */
+ goto cleanup;
+ }
+
+ et = _krb5_find_enctype(enctype);
+ if (et == NULL) {
+ ret = KRB5_PROG_KEYTYPE_NOSUPP;
+ goto cleanup;
+ }
+
+ kd.schedule = NULL;
+ ALLOC(kd.key, 1);
+ if (kd.key == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ kd.key->keytype = enctype;
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if (ret) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+
+ enctypesz = strlen(et->name) + 1;
+ ret = krb5_data_alloc(&saltp, enctypesz + salt.saltvalue.length);
+ if (ret) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ memcpy(saltp.data, et->name, enctypesz);
+ if (salt.saltvalue.length)
+ memcpy((unsigned char *)saltp.data + enctypesz,
+ salt.saltvalue.data, salt.saltvalue.length);
+
+ ret = _krb5_aes_sha2_md_for_enctype(context, enctype, &md);
+ if (ret)
+ goto cleanup;
+
+ ret = PKCS5_PBKDF2_HMAC(password.data, password.length,
+ saltp.data, saltp.length,
+ iter, md,
+ et->keytype->size, kd.key->keyvalue.data);
+ if (ret != 1) {
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "Error calculating s2k");
+ ret = KRB5_PROG_KEYTYPE_NOSUPP;
+ goto cleanup;
+ }
+
+ ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos"));
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ if (ret)
+ goto cleanup;
+
+cleanup:
+ krb5_data_free(&saltp);
+ _krb5_free_key_data(context, &kd, et);
+
+ return ret;
+}
+
+struct salt_type _krb5_AES_SHA2_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ AES_SHA2_string_to_key
+ },
+ { 0, NULL, NULL }
+};
diff --git a/third_party/heimdal/lib/krb5/salt-arcfour.c b/third_party/heimdal/lib/krb5/salt-arcfour.c
new file mode 100644
index 0000000..033128e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/salt-arcfour.c
@@ -0,0 +1,107 @@
+/*
+ * 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 krb5_error_code
+ARCFOUR_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint16_t *s = NULL;
+ size_t len = 0, i;
+ EVP_MD_CTX *m;
+
+ m = EVP_MD_CTX_create();
+ if (m == NULL)
+ return krb5_enomem(context);
+
+ EVP_DigestInit_ex(m, EVP_md4(), NULL);
+
+ ret = wind_utf8ucs2_length(password.data, &len);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ N_("Password is not valid UTF-8", ""));
+ goto out;
+ }
+
+ s = malloc (len * sizeof(s[0]));
+ if (len != 0 && s == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ ret = wind_utf8ucs2(password.data, s, &len);
+ if (ret) {
+ krb5_set_error_message (context, ret,
+ N_("Password is not valid UTF-8", ""));
+ goto out;
+ }
+
+ /* LE encoding */
+ for (i = 0; i < len; i++) {
+ unsigned char p;
+ p = (s[i] & 0xff);
+ EVP_DigestUpdate (m, &p, 1);
+ p = (s[i] >> 8) & 0xff;
+ EVP_DigestUpdate (m, &p, 1);
+ }
+
+ key->keytype = enctype;
+ ret = krb5_data_alloc (&key->keyvalue, 16);
+ if (ret) {
+ krb5_enomem(context);
+ goto out;
+ }
+ EVP_DigestFinal_ex (m, key->keyvalue.data, NULL);
+
+ out:
+ EVP_MD_CTX_destroy(m);
+ if (s)
+ memset (s, 0, len);
+ free (s);
+ return ret;
+}
+
+struct salt_type _krb5_arcfour_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ ARCFOUR_string_to_key
+ },
+ { 0, NULL, NULL }
+};
diff --git a/third_party/heimdal/lib/krb5/salt-des.c b/third_party/heimdal/lib/krb5/salt-des.c
new file mode 100644
index 0000000..474ba5d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/salt-des.c
@@ -0,0 +1,223 @@
+/*
+ * 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
+
+#ifdef ENABLE_AFS_STRING_TO_KEY
+
+/* This defines the Andrew string_to_key function. It accepts a password
+ * string as input and converts it via a one-way encryption algorithm to a DES
+ * encryption key. It is compatible with the original Andrew authentication
+ * service password database.
+ */
+
+/*
+ * Short passwords, i.e 8 characters or less.
+ */
+static void
+krb5_DES_AFS3_CMU_string_to_key (krb5_data pw,
+ krb5_data cell,
+ DES_cblock *key)
+{
+ char password[8+1]; /* crypt is limited to 8 chars anyway */
+ size_t i;
+
+ for(i = 0; i < 8; i++) {
+ char c = ((i < pw.length) ? ((char*)pw.data)[i] : 0) ^
+ ((i < cell.length) ?
+ tolower(((unsigned char*)cell.data)[i]) : 0);
+ password[i] = c ? c : 'X';
+ }
+ password[8] = '\0';
+
+ memcpy(key, crypt(password, "p1") + 2, sizeof(DES_cblock));
+
+ /* parity is inserted into the LSB so left shift each byte up one
+ bit. This allows ascii characters with a zero MSB to retain as
+ much significance as possible. */
+ for (i = 0; i < sizeof(DES_cblock); i++)
+ ((unsigned char*)key)[i] <<= 1;
+ DES_set_odd_parity (key);
+}
+
+/*
+ * Long passwords, i.e 9 characters or more.
+ */
+static void
+krb5_DES_AFS3_Transarc_string_to_key (krb5_data pw,
+ krb5_data cell,
+ DES_cblock *key)
+{
+ DES_key_schedule schedule;
+ DES_cblock temp_key;
+ DES_cblock ivec;
+ char password[512];
+ size_t passlen;
+
+ memcpy(password, pw.data, min(pw.length, sizeof(password)));
+ if(pw.length < sizeof(password)) {
+ int len = min(cell.length, sizeof(password) - pw.length);
+ size_t i;
+
+ memcpy(password + pw.length, cell.data, len);
+ for (i = pw.length; i < pw.length + len; ++i)
+ password[i] = tolower((unsigned char)password[i]);
+ }
+ passlen = min(sizeof(password), pw.length + cell.length);
+ memcpy(&ivec, "kerberos", 8);
+ memcpy(&temp_key, "kerberos", 8);
+ DES_set_odd_parity (&temp_key);
+ DES_set_key_unchecked (&temp_key, &schedule);
+ DES_cbc_cksum ((void*)password, &ivec, passlen, &schedule, &ivec);
+
+ memcpy(&temp_key, &ivec, 8);
+ DES_set_odd_parity (&temp_key);
+ DES_set_key_unchecked (&temp_key, &schedule);
+ DES_cbc_cksum ((void*)password, key, passlen, &schedule, &ivec);
+ memset(&schedule, 0, sizeof(schedule));
+ memset(&temp_key, 0, sizeof(temp_key));
+ memset(&ivec, 0, sizeof(ivec));
+ memset_s(password, sizeof(password), 0, sizeof(password));
+
+ DES_set_odd_parity (key);
+}
+
+static krb5_error_code
+DES_AFS3_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ DES_cblock tmp;
+ if(password.length > 8)
+ krb5_DES_AFS3_Transarc_string_to_key(password, salt.saltvalue, &tmp);
+ else
+ krb5_DES_AFS3_CMU_string_to_key(password, salt.saltvalue, &tmp);
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
+ memset(&key, 0, sizeof(key));
+ return 0;
+}
+#endif /* ENABLE_AFS_STRING_TO_KEY */
+
+static void
+DES_string_to_key_int(unsigned char *data, size_t length, DES_cblock *key)
+{
+ DES_key_schedule schedule;
+ size_t i;
+ int reverse = 0;
+ unsigned char *p;
+
+ unsigned char swap[] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf };
+ memset(key, 0, 8);
+
+ p = (unsigned char*)key;
+ for (i = 0; i < length; i++) {
+ unsigned char tmp = data[i];
+ if (!reverse)
+ *p++ ^= (tmp << 1);
+ else
+ *--p ^= (swap[tmp & 0xf] << 4) | swap[(tmp & 0xf0) >> 4];
+ if((i % 8) == 7)
+ reverse = !reverse;
+ }
+ DES_set_odd_parity(key);
+ if(DES_is_weak_key(key))
+ (*key)[7] ^= 0xF0;
+ DES_set_key_unchecked(key, &schedule);
+ DES_cbc_cksum((void*)data, key, length, &schedule, key);
+ memset(&schedule, 0, sizeof(schedule));
+ DES_set_odd_parity(key);
+ if(DES_is_weak_key(key))
+ (*key)[7] ^= 0xF0;
+}
+
+static krb5_error_code
+krb5_DES_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ unsigned char *s;
+ size_t len;
+ DES_cblock tmp;
+
+#ifdef ENABLE_AFS_STRING_TO_KEY
+ if (opaque.length == 1) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 1);
+ if (v == 1)
+ return DES_AFS3_string_to_key(context, enctype, password,
+ salt, opaque, key);
+ }
+#endif
+
+ len = password.length + salt.saltvalue.length;
+ s = malloc(len);
+ if (len > 0 && s == NULL)
+ return krb5_enomem(context);
+ memcpy(s, password.data, password.length);
+ if (salt.saltvalue.length)
+ memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ DES_string_to_key_int(s, len, &tmp);
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, tmp, sizeof(tmp));
+ memset(&tmp, 0, sizeof(tmp));
+ memset(s, 0, len);
+ free(s);
+ return 0;
+}
+
+struct salt_type _krb5_des_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ krb5_DES_string_to_key
+ },
+#ifdef ENABLE_AFS_STRING_TO_KEY
+ {
+ KRB5_AFS3_SALT,
+ "afs3-salt",
+ DES_AFS3_string_to_key
+ },
+#endif
+ { 0, NULL, NULL }
+};
+#endif
diff --git a/third_party/heimdal/lib/krb5/salt-des3.c b/third_party/heimdal/lib/krb5/salt-des3.c
new file mode 100644
index 0000000..a9293cc
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/salt-des3.c
@@ -0,0 +1,147 @@
+/*
+ * 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 DES3_OLD_ENCTYPE
+static krb5_error_code
+DES3_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ char *str;
+ size_t len;
+ unsigned char tmp[24];
+ DES_cblock keys[3];
+ krb5_error_code ret;
+
+ len = password.length + salt.saltvalue.length;
+ str = malloc(len);
+ if (len != 0 && str == NULL)
+ return krb5_enomem(context);
+ memcpy(str, password.data, password.length);
+ memcpy(str + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ {
+ DES_cblock ivec;
+ DES_key_schedule s[3];
+ int i;
+
+ ret = _krb5_n_fold(str, len, tmp, 24);
+ if (ret) {
+ memset_s(str, len, 0, len);
+ free(str);
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ return ret;
+ }
+
+ for(i = 0; i < 3; i++){
+ memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
+ DES_set_odd_parity(keys + i);
+ if(DES_is_weak_key(keys + i))
+ _krb5_xor8(*(keys + i), (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ DES_set_key_unchecked(keys + i, &s[i]);
+ }
+ memset_s(&ivec, sizeof(ivec), 0, sizeof(ivec));
+ DES_ede3_cbc_encrypt(tmp,
+ tmp, sizeof(tmp),
+ &s[0], &s[1], &s[2], &ivec, DES_ENCRYPT);
+ memset_s(s, sizeof(s), 0, sizeof(s));
+ memset_s(&ivec, sizeof(ivec), 0, sizeof(ivec));
+ for(i = 0; i < 3; i++){
+ memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
+ DES_set_odd_parity(keys + i);
+ if(DES_is_weak_key(keys + i))
+ _krb5_xor8(*(keys + i), (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ }
+ memset_s(tmp, sizeof(tmp), 0, sizeof(tmp));
+ }
+ key->keytype = enctype;
+ krb5_data_copy(&key->keyvalue, keys, sizeof(keys));
+ memset_s(keys, sizeof(keys), 0, sizeof(keys));
+ memset_s(str, len, 0, len);
+ free(str);
+ return 0;
+}
+#endif
+
+static krb5_error_code
+DES3_string_to_key_derived(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ size_t len = password.length + salt.saltvalue.length;
+ char *s;
+
+ s = malloc(len);
+ if (len != 0 && s == NULL)
+ return krb5_enomem(context);
+ memcpy(s, password.data, password.length);
+ if (salt.saltvalue.length)
+ memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
+ ret = krb5_string_to_key_derived(context,
+ s,
+ len,
+ enctype,
+ key);
+ memset_s(s, len, 0, len);
+ free(s);
+ return ret;
+}
+
+
+#ifdef DES3_OLD_ENCTYPE
+struct salt_type _krb5_des3_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ DES3_string_to_key
+ },
+ { 0, NULL, NULL }
+};
+#endif
+
+struct salt_type _krb5_des3_salt_derived[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ DES3_string_to_key_derived
+ },
+ { 0, NULL, NULL }
+};
diff --git a/third_party/heimdal/lib/krb5/salt.c b/third_party/heimdal/lib/krb5/salt.c
new file mode 100644
index 0000000..fa926f3
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/salt.c
@@ -0,0 +1,362 @@
+/*
+ * 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"
+
+/* coverity[+alloc : arg-*3] */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_salttype_to_string (krb5_context context,
+ krb5_enctype etype,
+ krb5_salttype stype,
+ char **string)
+{
+ struct _krb5_encryption_type *e;
+ struct salt_type *st;
+
+ *string = NULL;
+ e = _krb5_find_enctype (etype);
+ if (e == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ "encryption type %d not supported",
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for (st = e->keytype->string_to_key; st && st->type; st++) {
+ if (st->type == stype) {
+ *string = strdup (st->name);
+ if (*string == NULL)
+ return krb5_enomem(context);
+ return 0;
+ }
+ }
+ krb5_set_error_message (context, HEIM_ERR_SALTTYPE_NOSUPP,
+ "salttype %d not supported", stype);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_salttype (krb5_context context,
+ krb5_enctype etype,
+ const char *string,
+ krb5_salttype *salttype)
+{
+ struct _krb5_encryption_type *e;
+ struct salt_type *st;
+
+ e = _krb5_find_enctype (etype);
+ if (e == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for (st = e->keytype->string_to_key; st && st->type; st++) {
+ if (strcasecmp (st->name, string) == 0) {
+ *salttype = st->type;
+ return 0;
+ }
+ }
+ krb5_set_error_message(context, HEIM_ERR_SALTTYPE_NOSUPP,
+ N_("salttype %s not supported", ""), string);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+/*
+ * Like MIT's krb5_string_to_keysalts(), but simpler and with a context
+ * argument.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_keysalts2(krb5_context context, const char *string,
+ size_t *nksaltp, krb5_key_salt_tuple **ksaltp)
+{
+ /* deleted: tupleseps, ksaltseps, dups */
+ krb5_key_salt_tuple *tmp = NULL;
+ krb5_error_code ret = 0;
+ char *copy, *token, *stype_str;
+ char *lasts = NULL;
+ krb5_enctype etype;
+ krb5_salttype stype;
+ size_t i;
+
+ *ksaltp = NULL;
+ *nksaltp = 0;
+ if ((copy = strdup(string)) == NULL)
+ return krb5_enomem(context);
+ for (token = strtok_r(copy, ", \t", &lasts), ret = 0;
+ token != NULL;
+ token = strtok_r(NULL, ", \t", &lasts)) {
+ if ((stype_str = strchr(token, ':')) != NULL)
+ *(stype_str++) = '\0';
+ if ((ret = krb5_string_to_enctype(context, token, &etype)))
+ continue;
+ if (stype_str == NULL)
+ stype = KRB5_PW_SALT;
+ else if ((ret = krb5_string_to_salttype(context, etype, stype_str, &stype)))
+ continue;
+ for (i = 0; i < *nksaltp; i++) {
+ if ((*ksaltp)[i].ks_enctype == etype &&
+ (*ksaltp)[i].ks_salttype == stype)
+ goto skip;
+ }
+ tmp = realloc(*ksaltp, ((*nksaltp) + 1) * sizeof(**ksaltp));
+ if (tmp == NULL) {
+ ret = krb5_enomem(context);
+ break;
+ }
+ *ksaltp = tmp;
+ (*ksaltp)[*nksaltp].ks_enctype = etype;
+ (*ksaltp)[*nksaltp].ks_salttype = stype;
+ (*nksaltp)++;
+skip:
+ (void)1;
+ }
+ free(copy);
+ if (ret == ENOMEM) {
+ free(*ksaltp);
+ *nksaltp = 0;
+ *ksaltp = NULL;
+ } else if (*nksaltp) {
+ return 0;
+ } else if (ret == 0) {
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ return ret;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_pw_salt(krb5_context context,
+ krb5_const_principal principal,
+ krb5_salt *salt)
+{
+ size_t len;
+ size_t i;
+ krb5_error_code ret;
+ char *p;
+
+ salt->salttype = KRB5_PW_SALT;
+ len = strlen(principal->realm);
+ for (i = 0; i < principal->name.name_string.len; ++i)
+ len += strlen(principal->name.name_string.val[i]);
+ ret = krb5_data_alloc (&salt->saltvalue, len);
+ if (ret)
+ return ret;
+ p = salt->saltvalue.data;
+ memcpy (p, principal->realm, strlen(principal->realm));
+ p += strlen(principal->realm);
+ for (i = 0; i < principal->name.name_string.len; ++i) {
+ memcpy (p,
+ principal->name.name_string.val[i],
+ strlen(principal->name.name_string.val[i]));
+ p += strlen(principal->name.name_string.val[i]);
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_free_salt(krb5_context context,
+ krb5_salt salt)
+{
+ krb5_data_free(&salt.saltvalue);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key_data (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_const_principal principal,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ krb5_salt salt;
+
+ ret = krb5_get_pw_salt(context, principal, &salt);
+ if(ret)
+ return ret;
+ ret = krb5_string_to_key_data_salt(context, enctype, password, salt, key);
+ krb5_free_salt(context, salt);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_const_principal principal,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data(context, enctype, pw, principal, key);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key_data_salt (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_keyblock *key)
+{
+ krb5_data opaque;
+ krb5_data_zero(&opaque);
+ return krb5_string_to_key_data_salt_opaque(context, enctype, password,
+ salt, opaque, key);
+}
+
+/*
+ * Do a string -> key for encryption type `enctype' operation on
+ * `password' (with salt `salt' and the enctype specific data string
+ * `opaque'), returning the resulting key in `key'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key_data_salt_opaque (krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ struct _krb5_encryption_type *et =_krb5_find_enctype(enctype);
+ struct salt_type *st;
+ if(et == NULL) {
+ krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ enctype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ for(st = et->keytype->string_to_key; st && st->type; st++)
+ if(st->type == salt.salttype)
+ return (*st->string_to_key)(context, enctype, password,
+ salt, opaque, key);
+ krb5_set_error_message(context, HEIM_ERR_SALTTYPE_NOSUPP,
+ N_("salt type %d not supported", ""),
+ salt.salttype);
+ return HEIM_ERR_SALTTYPE_NOSUPP;
+}
+
+/*
+ * Do a string -> key for encryption type `enctype' operation on the
+ * string `password' (with salt `salt'), returning the resulting key
+ * in `key'
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key_salt (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_salt salt,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data_salt(context, enctype, pw, salt, key);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key_salt_opaque (krb5_context context,
+ krb5_enctype enctype,
+ const char *password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_data pw;
+ pw.data = rk_UNCONST(password);
+ pw.length = strlen(password);
+ return krb5_string_to_key_data_salt_opaque(context, enctype,
+ pw, salt, opaque, key);
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_key_derived(krb5_context context,
+ const void *str,
+ size_t len,
+ krb5_enctype etype,
+ krb5_keyblock *key)
+{
+ struct _krb5_encryption_type *et = _krb5_find_enctype(etype);
+ krb5_error_code ret;
+ struct _krb5_key_data kd;
+ size_t keylen;
+ u_char *tmp;
+
+ if(et == NULL) {
+ krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
+ N_("encryption type %d not supported", ""),
+ etype);
+ return KRB5_PROG_ETYPE_NOSUPP;
+ }
+ keylen = et->keytype->bits / 8;
+
+ ALLOC(kd.key, 1);
+ if (kd.key == NULL)
+ return krb5_enomem(context);
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if(ret) {
+ free(kd.key);
+ return ret;
+ }
+ kd.key->keytype = etype;
+ tmp = malloc (keylen);
+ if(tmp == NULL) {
+ krb5_free_keyblock(context, kd.key);
+ return krb5_enomem(context);
+ }
+ ret = _krb5_n_fold(str, len, tmp, keylen);
+ if (ret) {
+ free(tmp);
+ krb5_enomem(context);
+ return ret;
+ }
+ kd.schedule = NULL;
+ _krb5_DES3_random_to_key(context, kd.key, tmp, keylen);
+ memset(tmp, 0, keylen);
+ free(tmp);
+ ret = _krb5_derive_key(context,
+ et,
+ &kd,
+ "kerberos", /* XXX well known constant */
+ strlen("kerberos"));
+ if (ret) {
+ _krb5_free_key_data(context, &kd, et);
+ return ret;
+ }
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ _krb5_free_key_data(context, &kd, et);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/scache.c b/third_party/heimdal/lib/krb5/scache.c
new file mode 100644
index 0000000..479c58b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/scache.c
@@ -0,0 +1,1602 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+#ifdef HAVE_SCC
+
+#include <sqlite3.h>
+
+typedef struct krb5_scache {
+ char *name;
+ char *file;
+ char *sub;
+ sqlite3 *db;
+
+ sqlite_uint64 cid;
+
+ sqlite3_stmt *icred;
+ sqlite3_stmt *dcred;
+ sqlite3_stmt *iprincipal;
+
+ sqlite3_stmt *icache;
+ sqlite3_stmt *ucachen;
+ sqlite3_stmt *ucachep;
+ sqlite3_stmt *dcache;
+ sqlite3_stmt *scache;
+ sqlite3_stmt *scache_name;
+ sqlite3_stmt *umaster;
+
+} krb5_scache;
+
+#define SCACHE(X) ((krb5_scache *)(X)->data.data)
+
+/*
+ * Because we can't control what permissions SQLite3 (if not in-tree) will use,
+ * and we're a library and can't set the umask. We can't even determine the
+ * current umask in a thread-safe way (not easily), and we can't tell if some
+ * other thread might change it. So what we'll do is put the SQLite3-based
+ * ccache file in its own directory so we can create that directory with
+ * mkdir(2) and the correct permissions.
+ */
+
+#define SCACHE_DEF_NAME "Default-cache"
+#define KRB5_SCACHE_DIR "%{TEMP}/krb5scc_%{uid}"
+#define KRB5_SCACHE_DB KRB5_SCACHE_DIR "scc"
+#define KRB5_SCACHE_NAME "SCC:" KRB5_SCACHE_DB ":" SCACHE_DEF_NAME
+
+#define SCACHE_INVALID_CID ((sqlite_uint64)-1)
+
+/*
+ *
+ */
+
+#define SQL_CMASTER "" \
+ "CREATE TABLE master (" \
+ "oid INTEGER PRIMARY KEY," \
+ "version INTEGER NOT NULL," \
+ "defaultcache TEXT NOT NULL" \
+ ")"
+
+#define SQL_SETUP_MASTER \
+ "INSERT INTO master (version,defaultcache) VALUES(2, \"" SCACHE_DEF_NAME "\")"
+#define SQL_UMASTER "UPDATE master SET defaultcache=? WHERE version=2"
+
+#define SQL_CCACHE "" \
+ "CREATE TABLE caches (" \
+ "oid INTEGER PRIMARY KEY," \
+ "principal TEXT," \
+ "name TEXT NOT NULL" \
+ ")"
+
+#define SQL_TCACHE "" \
+ "CREATE TRIGGER CacheDropCreds AFTER DELETE ON caches " \
+ "FOR EACH ROW BEGIN " \
+ "DELETE FROM credentials WHERE cid=old.oid;" \
+ "END"
+
+#define SQL_ICACHE "INSERT INTO caches (name) VALUES(?)"
+#define SQL_UCACHE_NAME "UPDATE caches SET name=? WHERE OID=?"
+#define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?"
+#define SQL_DCACHE "DELETE FROM caches WHERE OID=?"
+#define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?"
+#define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=? OR " \
+ "(PRINCIPAL IS NOT NULL AND PRINCIPAL=?)"
+
+#define SQL_CCREDS "" \
+ "CREATE TABLE credentials (" \
+ "oid INTEGER PRIMARY KEY," \
+ "cid INTEGER NOT NULL," \
+ "kvno INTEGER NOT NULL," \
+ "etype INTEGER NOT NULL," \
+ "created_at INTEGER NOT NULL," \
+ "cred BLOB NOT NULL" \
+ ")"
+
+#define SQL_TCRED "" \
+ "CREATE TRIGGER credDropPrincipal AFTER DELETE ON credentials " \
+ "FOR EACH ROW BEGIN " \
+ "DELETE FROM principals WHERE credential_id=old.oid;" \
+ "END"
+
+#define SQL_ICRED "INSERT INTO credentials (cid, kvno, etype, cred, created_at) VALUES (?,?,?,?,?)"
+#define SQL_DCRED "DELETE FROM credentials WHERE cid=?"
+
+#define SQL_CPRINCIPALS "" \
+ "CREATE TABLE principals (" \
+ "oid INTEGER PRIMARY KEY," \
+ "principal TEXT NOT NULL," \
+ "type INTEGER NOT NULL," \
+ "credential_id INTEGER NOT NULL" \
+ ")"
+
+#define SQL_IPRINCIPAL "INSERT INTO principals (principal, type, credential_id) VALUES (?,?,?)"
+
+/*
+ * sqlite destructors
+ */
+
+static void
+free_data(void *data)
+{
+ free(data);
+}
+
+static void
+free_krb5(void *str)
+{
+ krb5_xfree(str);
+}
+
+static void
+scc_free(krb5_scache *s)
+{
+ if (!s)
+ return;
+ if (s->file)
+ free(s->file);
+ if (s->sub)
+ free(s->sub);
+ if (s->name)
+ free(s->name);
+
+ if (s->icred)
+ sqlite3_finalize(s->icred);
+ if (s->dcred)
+ sqlite3_finalize(s->dcred);
+ if (s->iprincipal)
+ sqlite3_finalize(s->iprincipal);
+ if (s->icache)
+ sqlite3_finalize(s->icache);
+ if (s->ucachen)
+ sqlite3_finalize(s->ucachen);
+ if (s->ucachep)
+ sqlite3_finalize(s->ucachep);
+ if (s->dcache)
+ sqlite3_finalize(s->dcache);
+ if (s->scache)
+ sqlite3_finalize(s->scache);
+ if (s->scache_name)
+ sqlite3_finalize(s->scache_name);
+ if (s->umaster)
+ sqlite3_finalize(s->umaster);
+
+ if (s->db)
+ sqlite3_close(s->db);
+ free(s);
+}
+
+#ifdef TRACEME
+static void
+trace(void* ptr, const char * str)
+{
+ printf("SQL: %s\n", str);
+}
+#endif
+
+static krb5_error_code
+prepare_stmt(krb5_context context, sqlite3 *db,
+ sqlite3_stmt **stmt, const char *str)
+{
+ int ret;
+
+ ret = sqlite3_prepare_v2(db, str, -1, stmt, NULL);
+ if (ret != SQLITE_OK) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Failed to prepare stmt %s: %s", ""),
+ str, sqlite3_errmsg(db));
+ return ENOENT;
+ }
+ return 0;
+}
+
+static krb5_error_code
+exec_stmt(krb5_context context, sqlite3 *db, const char *str,
+ krb5_error_code code)
+{
+ int ret;
+
+ ret = sqlite3_exec(db, str, NULL, NULL, NULL);
+ if (ret != SQLITE_OK && code) {
+ krb5_set_error_message(context, code,
+ N_("scache execute %s: %s", ""), str,
+ sqlite3_errmsg(db));
+ return code;
+ }
+ return 0;
+}
+
+/* See block comment at the top of this file */
+static krb5_error_code
+make_dir(krb5_context context, const char *name)
+{
+ krb5_error_code ret = 0;
+ char *s, *p;
+
+ /* We really need a dirname() in roken; lib/krb5/fcache.c has one */
+ if ((s = strdup(name)) == NULL)
+ return krb5_enomem(context);
+ for (p = s + strlen(s); p > s; p--) {
+#ifdef WIN32
+ if (*p != '/' && *p != '\\')
+ continue;
+#else
+ if (*p != '/')
+ continue;
+#endif
+ *p = '\0';
+ break;
+ }
+
+ /* If p == s then DB in current directory -- nothing we can do */
+ if (p > s && mkdir(s, 0700) == -1)
+ ret = errno;
+ free(s);
+
+ /* If we created it, we're good, else there's nothing we can do */
+ if (ret == EEXIST)
+ return 0;
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Error making directory for scache file %s", ""),
+ name);
+ return ret;
+}
+
+static krb5_error_code
+default_db(krb5_context context, const char *name, sqlite3 **db, char **file)
+{
+ krb5_error_code ret = 0;
+ char *s = NULL;
+ char *f = NULL;
+
+ if (file)
+ *file = NULL;
+
+ if (name == NULL) {
+ if ((name = krb5_cc_default_name(context))) {
+ if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
+ name += sizeof("SCC:") - 1;
+ }
+ if (name == NULL) {
+ ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s);
+ if (ret)
+ return ret;
+ name = s;
+ }
+ }
+
+ if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
+ name += sizeof("SCC:") - 1;
+
+ if ((f = strdup(name)) == NULL) {
+ free(s);
+ return krb5_enomem(context);
+ }
+ free(s);
+
+ /* Strip off any residue from default name */
+#ifdef WIN32
+ if (f[0] && f[1] == ':' && (s = strrchr(f, ':')) != &f[1])
+ *s = '\0';
+#else
+ if ((s = strrchr(f, ':')))
+ *s = '\0';
+#endif
+
+ ret = make_dir(context, f);
+ if (ret == 0) {
+ int sret;
+
+ sret = sqlite3_open_v2(f, db, SQLITE_OPEN_READWRITE, NULL);
+ if (sret != SQLITE_OK) {
+ if (*db) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Error opening scache file %s: %s (%d)", ""),
+ f, sqlite3_errmsg(*db), sret);
+ sqlite3_close(*db);
+ *db = NULL;
+ } else
+ krb5_set_error_message(context, ENOENT,
+ N_("Error opening scache file %s: %s (%d)", ""),
+ f, sqlite3_errstr(sret), sret);
+ free(f);
+ return ENOENT;
+ }
+ }
+
+#ifndef WIN32
+ /*
+ * Just in case we're using an out-of-tree SQLite3. See block comment at
+ * the top of this file, near KRB5_SCACHE_DIR's definition.
+ */
+ (void) chmod(f, 0600);
+#endif
+
+ if (file)
+ *file = f;
+ else
+ free(f);
+
+#ifdef TRACEME
+ sqlite3_trace(*db, trace, NULL);
+#endif
+
+ return ret;
+}
+
+static krb5_error_code
+get_def_name(krb5_context context, char *filein, char **str, char **file)
+{
+ krb5_error_code ret;
+ sqlite3_stmt *stmt;
+ const char *name;
+ sqlite3 *db;
+
+ ret = default_db(context, filein, &db, file);
+ if (ret)
+ return ret;
+
+ ret = prepare_stmt(context, db, &stmt, "SELECT defaultcache FROM master");
+ if (ret) {
+ sqlite3_close(db);
+ return ret;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_ROW)
+ goto out;
+
+ if (sqlite3_column_type(stmt, 0) != SQLITE_TEXT)
+ goto out;
+
+ name = (const char *)sqlite3_column_text(stmt, 0);
+ if (name == NULL)
+ goto out;
+
+ *str = strdup(name);
+ if (*str == NULL)
+ goto out;
+
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ return 0;
+out:
+ sqlite3_finalize(stmt);
+ sqlite3_close(db);
+ krb5_clear_error_message(context);
+ return ENOENT;
+}
+
+
+
+static krb5_scache * KRB5_CALLCONV
+scc_alloc(krb5_context context,
+ const char *name,
+ const char *sub,
+ int new_unique)
+{
+ krb5_error_code ret = 0;
+ krb5_scache *s;
+ char *freeme = NULL;
+ char *subsidiary;
+
+ ALLOC(s, 1);
+ if(s == NULL)
+ return NULL;
+
+ s->cid = SCACHE_INVALID_CID;
+
+ if (name && *name && sub && *sub) {
+ if ((s->sub = strdup(sub)) == NULL ||
+ (s->file = strdup(name)) == NULL) {
+ free(s->file);
+ free(s);
+ (void) krb5_enomem(context);
+ return NULL;
+ }
+ } else {
+ s->sub = NULL;
+ s->file = NULL;
+ s->name = NULL;
+
+ if (name == NULL)
+ name = krb5_cc_default_name(context);
+ if (name == NULL) {
+ ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB,
+ &freeme);
+ if (ret) {
+ free(s);
+ return NULL;
+ }
+ name = freeme;
+ }
+
+ if (strncmp(name, "SCC:", sizeof("SCC:") - 1) == 0)
+ name += sizeof("SCC:") - 1;
+
+ if ((s->file = strdup(name)) == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ if ((subsidiary = strrchr(s->file, ':'))) {
+#ifdef WIN32
+ if (subsidiary == s->file + 1)
+ subsidiary = NULL;
+ else
+#endif
+ *(subsidiary++) = '\0';
+ }
+
+ if (new_unique) {
+ ret = asprintf(&s->sub, "unique-%p", s) < 0 || s->sub == NULL ?
+ krb5_enomem(context) : 0;
+ } else if (subsidiary == NULL || *subsidiary == '\0') {
+ ret = get_def_name(context, s->file, &s->sub, NULL);
+ if (ret) {
+ if ((s->sub = strdup(SCACHE_DEF_NAME)) == NULL)
+ ret = krb5_enomem(context);
+ else
+ ret = 0;
+ }
+ } else if ((s->sub = strdup(subsidiary)) == NULL) {
+ ret = krb5_enomem(context);
+ }
+ }
+
+ if (ret == 0 && s->file && s->sub &&
+ (asprintf(&s->name, "%s:%s", s->file, s->sub) < 0 || s->name == NULL))
+ ret = krb5_enomem(context);
+
+ out:
+ if (ret || s->file == NULL || s->sub == NULL || s->name == NULL) {
+ scc_free(s);
+ s = NULL;
+ }
+
+ free(freeme);
+ return s;
+}
+
+static krb5_error_code
+open_database(krb5_context context, krb5_scache *s, int flags)
+{
+ krb5_error_code ret;
+ struct stat st;
+ int sret;
+
+
+ if (!(flags & SQLITE_OPEN_CREATE) && stat(s->file, &st) == 0 &&
+ st.st_size == 0)
+ return ENOENT;
+
+ ret = make_dir(context, s->file);
+ if (ret)
+ return ret;
+ sret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL);
+ if (sret != SQLITE_OK) {
+ if (s->db) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Error opening scache file %s: %s (%d)", ""),
+ s->file, sqlite3_errmsg(s->db), sret);
+ sqlite3_close(s->db);
+ s->db = NULL;
+ } else
+ krb5_set_error_message(context, ENOENT,
+ N_("Error opening scache file %s: %s (%d)", ""),
+ s->file, sqlite3_errstr(sret), sret);
+ return ENOENT;
+ }
+ return 0;
+}
+
+static krb5_error_code
+create_cache(krb5_context context, krb5_scache *s)
+{
+ int ret;
+
+ sqlite3_bind_text(s->icache, 1, s->sub, -1, NULL);
+ do {
+ ret = sqlite3_step(s->icache);
+ } while (ret == SQLITE_ROW);
+ if (ret != SQLITE_DONE) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Failed to add scache: %d", ""), ret);
+ return KRB5_CC_IO;
+ }
+ sqlite3_reset(s->icache);
+
+ s->cid = sqlite3_last_insert_rowid(s->db);
+
+ return 0;
+}
+
+static krb5_error_code
+make_database(krb5_context context, krb5_scache *s)
+{
+ int created_file = 0;
+ int ret;
+
+ if (s->db)
+ return 0;
+
+ ret = open_database(context, s, 0);
+ if (ret) {
+ ret = open_database(context, s, SQLITE_OPEN_CREATE);
+ if (ret) goto out;
+
+ created_file = 1;
+
+ ret = exec_stmt(context, s->db, SQL_CMASTER, KRB5_CC_IO);
+ if (ret) goto out;
+ ret = exec_stmt(context, s->db, SQL_CCACHE, KRB5_CC_IO);
+ if (ret) goto out;
+ ret = exec_stmt(context, s->db, SQL_CCREDS, KRB5_CC_IO);
+ if (ret) goto out;
+ ret = exec_stmt(context, s->db, SQL_CPRINCIPALS, KRB5_CC_IO);
+ if (ret) goto out;
+ ret = exec_stmt(context, s->db, SQL_SETUP_MASTER, KRB5_CC_IO);
+ if (ret) goto out;
+
+ ret = exec_stmt(context, s->db, SQL_TCACHE, KRB5_CC_IO);
+ if (ret) goto out;
+ ret = exec_stmt(context, s->db, SQL_TCRED, KRB5_CC_IO);
+ if (ret) goto out;
+ }
+
+#ifdef TRACEME
+ sqlite3_trace(s->db, trace, NULL);
+#endif
+
+ ret = prepare_stmt(context, s->db, &s->icred, SQL_ICRED);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->dcred, SQL_DCRED);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->iprincipal, SQL_IPRINCIPAL);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->icache, SQL_ICACHE);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->ucachen, SQL_UCACHE_NAME);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->ucachep, SQL_UCACHE_PRINCIPAL);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->dcache, SQL_DCACHE);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->scache, SQL_SCACHE);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->scache_name, SQL_SCACHE_NAME);
+ if (ret) goto out;
+ ret = prepare_stmt(context, s->db, &s->umaster, SQL_UMASTER);
+ if (ret) goto out;
+
+#ifndef WIN32
+ /*
+ * Just in case we're using an out-of-tree SQLite3. See block comment at
+ * the top of this file, near KRB5_SCACHE_DIR's definition.
+ */
+ (void) chmod(s->file, 0600);
+#endif
+
+ return 0;
+
+out:
+ if (s->db)
+ sqlite3_close(s->db);
+ if (created_file)
+ unlink(s->file);
+
+ return ret;
+}
+
+static krb5_error_code
+bind_principal(krb5_context context,
+ sqlite3 *db,
+ sqlite3_stmt *stmt,
+ int col,
+ krb5_const_principal principal)
+{
+ krb5_error_code ret;
+ char *str;
+
+ ret = krb5_unparse_name(context, principal, &str);
+ if (ret)
+ return ret;
+
+ ret = sqlite3_bind_text(stmt, col, str, -1, free_krb5);
+ if (ret != SQLITE_OK) {
+ krb5_xfree(str);
+ krb5_set_error_message(context, ENOMEM,
+ N_("scache bind principal: %s", ""),
+ sqlite3_errmsg(db));
+ return ENOMEM;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_name_2(krb5_context context,
+ krb5_ccache id,
+ const char **name,
+ const char **file,
+ const char **sub)
+{
+ if (name)
+ *name = SCACHE(id)->name;
+ if (file)
+ *file = SCACHE(id)->file;
+ if (sub)
+ *sub = SCACHE(id)->sub;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_resolve_2(krb5_context context,
+ krb5_ccache *id,
+ const char *res,
+ const char *sub)
+{
+ krb5_error_code ret;
+ krb5_scache *s;
+
+ s = scc_alloc(context, res, sub, 0);
+ if (s == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ ret = make_database(context, s);
+ if (ret) {
+ scc_free(s);
+ return ret;
+ }
+
+ ret = sqlite3_bind_text(s->scache_name, 1, s->sub, -1, NULL);
+ if (ret != SQLITE_OK) {
+ krb5_set_error_message(context, ENOMEM,
+ "bind principal: %s", sqlite3_errmsg(s->db));
+ scc_free(s);
+ return ENOMEM;
+ }
+
+ if (sqlite3_step(s->scache_name) == SQLITE_ROW) {
+
+ if (sqlite3_column_type(s->scache_name, 0) != SQLITE_INTEGER) {
+ sqlite3_reset(s->scache_name);
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("Cache name of wrong type "
+ "for scache %s", ""),
+ s->name);
+ scc_free(s);
+ return KRB5_CC_END;
+ }
+
+ s->cid = sqlite3_column_int(s->scache_name, 0);
+ } else {
+ s->cid = SCACHE_INVALID_CID;
+ }
+ sqlite3_reset(s->scache_name);
+
+ (*id)->data.data = s;
+ (*id)->data.length = sizeof(*s);
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_scache *s;
+
+ s = scc_alloc(context, NULL, NULL, 1);
+
+ if (s == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ (*id)->data.data = s;
+ (*id)->data.length = sizeof(*s);
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal principal)
+{
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+
+ ret = make_database(context, s);
+ if (ret)
+ return ret;
+
+ ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
+ if (ret) return ret;
+
+ if (s->cid == SCACHE_INVALID_CID) {
+ ret = create_cache(context, s);
+ if (ret)
+ goto rollback;
+ } else {
+ sqlite3_bind_int(s->dcred, 1, s->cid);
+ do {
+ ret = sqlite3_step(s->dcred);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->dcred);
+ if (ret != SQLITE_DONE) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to delete old "
+ "credentials: %s", ""),
+ sqlite3_errmsg(s->db));
+ goto rollback;
+ }
+ }
+
+ ret = bind_principal(context, s->db, s->ucachep, 1, principal);
+ if (ret)
+ goto rollback;
+ sqlite3_bind_int(s->ucachep, 2, s->cid);
+
+ do {
+ ret = sqlite3_step(s->ucachep);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->ucachep);
+ if (ret != SQLITE_DONE) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to bind principal to cache %s", ""),
+ sqlite3_errmsg(s->db));
+ goto rollback;
+ }
+
+ ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
+ if (ret) return ret;
+
+ return 0;
+
+rollback:
+ exec_stmt(context, s->db, "ROLLBACK", 0);
+
+ return ret;
+
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_close(krb5_context context,
+ krb5_ccache id)
+{
+ scc_free(SCACHE(id));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_scache *s = SCACHE(id);
+ int ret;
+
+ if (s->cid == SCACHE_INVALID_CID)
+ return 0;
+
+ sqlite3_bind_int(s->dcache, 1, s->cid);
+ do {
+ ret = sqlite3_step(s->dcache);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->dcache);
+ if (ret != SQLITE_DONE) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Failed to destroy cache %s: %s", ""),
+ s->name, sqlite3_errmsg(s->db));
+ return KRB5_CC_IO;
+ }
+ return 0;
+}
+
+static krb5_error_code
+encode_creds(krb5_context context, krb5_creds *creds, krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ krb5_data_zero(data);
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ ret = krb5_store_creds(sp, creds);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to store credential in scache", ""));
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ ret = krb5_storage_to_data(sp, data);
+ krb5_storage_free(sp);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Failed to encode credential in scache", ""));
+ return ret;
+}
+
+static krb5_error_code
+decode_creds(krb5_context context, const void *data, size_t length,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ sp = krb5_storage_from_readonly_mem(data, length);
+ if (sp == NULL)
+ return krb5_enomem(context);
+
+ ret = krb5_ret_creds(sp, creds);
+ krb5_storage_free(sp);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to read credential in scache", ""));
+ return ret;
+ }
+ return 0;
+}
+
+
+static krb5_error_code KRB5_CALLCONV
+scc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ sqlite_uint64 credid;
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+ krb5_data data;
+
+ ret = make_database(context, s);
+ if (ret)
+ return ret;
+
+ ret = encode_creds(context, creds, &data);
+ if (ret)
+ return ret;
+
+ sqlite3_bind_int(s->icred, 1, s->cid);
+ {
+ krb5_enctype etype = 0;
+ int kvno = 0;
+ Ticket t;
+ size_t len;
+
+ ret = decode_Ticket(creds->ticket.data,
+ creds->ticket.length, &t, &len);
+ if (ret == 0) {
+ if(t.enc_part.kvno)
+ kvno = *t.enc_part.kvno;
+
+ etype = t.enc_part.etype;
+
+ free_Ticket(&t);
+ }
+
+ sqlite3_bind_int(s->icred, 2, kvno);
+ sqlite3_bind_int(s->icred, 3, etype);
+
+ }
+
+ sqlite3_bind_blob(s->icred, 4, data.data, data.length, free_data);
+ sqlite3_bind_int(s->icred, 5, time(NULL));
+
+ ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
+ if (ret) return ret;
+
+ do {
+ ret = sqlite3_step(s->icred);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->icred);
+ if (ret != SQLITE_DONE) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to add credential: %s", ""),
+ sqlite3_errmsg(s->db));
+ goto rollback;
+ }
+
+ credid = sqlite3_last_insert_rowid(s->db);
+
+ {
+ bind_principal(context, s->db, s->iprincipal, 1, creds->server);
+ sqlite3_bind_int(s->iprincipal, 2, 1);
+ sqlite3_bind_int(s->iprincipal, 3, credid);
+
+ do {
+ ret = sqlite3_step(s->iprincipal);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->iprincipal);
+ if (ret != SQLITE_DONE) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to add principal: %s", ""),
+ sqlite3_errmsg(s->db));
+ goto rollback;
+ }
+ }
+
+ {
+ bind_principal(context, s->db, s->iprincipal, 1, creds->client);
+ sqlite3_bind_int(s->iprincipal, 2, 0);
+ sqlite3_bind_int(s->iprincipal, 3, credid);
+
+ do {
+ ret = sqlite3_step(s->iprincipal);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->iprincipal);
+ if (ret != SQLITE_DONE) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("Failed to add principal: %s", ""),
+ sqlite3_errmsg(s->db));
+ goto rollback;
+ }
+ }
+
+ ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO);
+ if (ret) return ret;
+
+ return 0;
+
+rollback:
+ exec_stmt(context, s->db, "ROLLBACK", 0);
+
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+ const char *str;
+
+ *principal = NULL;
+
+ ret = make_database(context, s);
+ if (ret)
+ return ret;
+
+ sqlite3_bind_int(s->scache, 1, s->cid);
+
+ if (sqlite3_step(s->scache) != SQLITE_ROW) {
+ sqlite3_reset(s->scache);
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("No principal for cache SCC:%s", ""),
+ s->name);
+ return KRB5_CC_END;
+ }
+
+ if (sqlite3_column_type(s->scache, 0) != SQLITE_TEXT) {
+ sqlite3_reset(s->scache);
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("Principal data of wrong type "
+ "for SCC:%s", ""),
+ s->name);
+ return KRB5_CC_END;
+ }
+
+ str = (const char *)sqlite3_column_text(s->scache, 0);
+ if (str == NULL) {
+ sqlite3_reset(s->scache);
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("Principal not set for SCC:%s", ""),
+ s->name);
+ return KRB5_CC_END;
+ }
+
+ ret = krb5_parse_name(context, str, principal);
+
+ sqlite3_reset(s->scache);
+
+ return ret;
+}
+
+struct cred_ctx {
+ char *drop;
+ sqlite3_stmt *stmt;
+ sqlite3_stmt *credstmt;
+};
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+ struct cred_ctx *ctx;
+ char *str = NULL, *name = NULL;
+
+ *cursor = NULL;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return krb5_enomem(context);
+
+ ret = make_database(context, s);
+ if (ret) {
+ free(ctx);
+ return ret;
+ }
+
+ if (s->cid == SCACHE_INVALID_CID) {
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("Iterating a invalid scache %s", ""),
+ s->name);
+ free(ctx);
+ return KRB5_CC_END;
+ }
+
+ ret = asprintf(&name, "credIteration%pPid%d",
+ ctx, (int)getpid());
+ if (ret < 0 || name == NULL) {
+ free(ctx);
+ return krb5_enomem(context);
+ }
+
+ ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
+ if (ret < 0 || ctx->drop == NULL) {
+ free(name);
+ free(ctx);
+ return krb5_enomem(context);
+ }
+
+ ret = asprintf(&str, "CREATE TEMPORARY TABLE %s "
+ "AS SELECT oid,created_at FROM credentials WHERE cid = %lu",
+ name, (unsigned long)s->cid);
+ if (ret < 0 || str == NULL) {
+ free(ctx->drop);
+ free(name);
+ free(ctx);
+ return krb5_enomem(context);
+ }
+
+ ret = exec_stmt(context, s->db, str, KRB5_CC_IO);
+ free(str);
+ str = NULL;
+ if (ret) {
+ free(ctx->drop);
+ free(name);
+ free(ctx);
+ return ret;
+ }
+
+ ret = asprintf(&str, "SELECT oid FROM %s ORDER BY created_at", name);
+ if (ret < 0 || str == NULL) {
+ exec_stmt(context, s->db, ctx->drop, 0);
+ free(ctx->drop);
+ free(name);
+ free(ctx);
+ return ret;
+ }
+
+ ret = prepare_stmt(context, s->db, &ctx->stmt, str);
+ free(str);
+ str = NULL;
+ free(name);
+ if (ret) {
+ exec_stmt(context, s->db, ctx->drop, 0);
+ free(ctx->drop);
+ free(ctx);
+ return ret;
+ }
+
+ ret = prepare_stmt(context, s->db, &ctx->credstmt,
+ "SELECT cred FROM credentials WHERE oid = ?");
+ if (ret) {
+ sqlite3_finalize(ctx->stmt);
+ exec_stmt(context, s->db, ctx->drop, 0);
+ free(ctx->drop);
+ free(ctx);
+ return ret;
+ }
+
+ *cursor = ctx;
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ struct cred_ctx *ctx = *cursor;
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+ sqlite_uint64 oid;
+ const void *data = NULL;
+ size_t len = 0;
+
+next:
+ ret = sqlite3_step(ctx->stmt);
+ if (ret == SQLITE_DONE) {
+ krb5_clear_error_message(context);
+ return KRB5_CC_END;
+ } else if (ret != SQLITE_ROW) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("scache Database failed: %s", ""),
+ sqlite3_errmsg(s->db));
+ return KRB5_CC_IO;
+ }
+
+ oid = sqlite3_column_int64(ctx->stmt, 0);
+
+ /* read cred from credentials table */
+
+ sqlite3_bind_int(ctx->credstmt, 1, oid);
+
+ ret = sqlite3_step(ctx->credstmt);
+ if (ret != SQLITE_ROW) {
+ sqlite3_reset(ctx->credstmt);
+ goto next;
+ }
+
+ if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) {
+ krb5_set_error_message(context, KRB5_CC_END,
+ N_("credential of wrong type for SCC:%s", ""),
+ s->name);
+ sqlite3_reset(ctx->credstmt);
+ return KRB5_CC_END;
+ }
+
+ data = sqlite3_column_blob(ctx->credstmt, 0);
+ len = sqlite3_column_bytes(ctx->credstmt, 0);
+
+ ret = decode_creds(context, data, len, creds);
+ sqlite3_reset(ctx->credstmt);
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ struct cred_ctx *ctx = *cursor;
+ krb5_scache *s = SCACHE(id);
+
+ sqlite3_finalize(ctx->stmt);
+ sqlite3_finalize(ctx->credstmt);
+
+ exec_stmt(context, s->db, ctx->drop, 0);
+
+ free(ctx->drop);
+ free(ctx);
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *mcreds)
+{
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+ sqlite3_stmt *stmt;
+ sqlite_uint64 credid = 0;
+ const void *data = NULL;
+ size_t len = 0;
+
+ ret = make_database(context, s);
+ if (ret)
+ return ret;
+
+ ret = prepare_stmt(context, s->db, &stmt,
+ "SELECT cred,oid FROM credentials "
+ "WHERE cid = ?");
+ if (ret)
+ return ret;
+
+ sqlite3_bind_int(stmt, 1, s->cid);
+
+ /* find credential... */
+ while (1) {
+ krb5_creds creds;
+
+ ret = sqlite3_step(stmt);
+ if (ret == SQLITE_DONE) {
+ ret = 0;
+ break;
+ } else if (ret != SQLITE_ROW) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("scache Database failed: %s", ""),
+ sqlite3_errmsg(s->db));
+ break;
+ }
+
+ if (sqlite3_column_type(stmt, 0) != SQLITE_BLOB) {
+ ret = KRB5_CC_END;
+ krb5_set_error_message(context, ret,
+ N_("Credential of wrong type "
+ "for SCC:%s", ""),
+ s->name);
+ break;
+ }
+
+ data = sqlite3_column_blob(stmt, 0);
+ len = sqlite3_column_bytes(stmt, 0);
+
+ ret = decode_creds(context, data, len, &creds);
+ if (ret)
+ break;
+
+ ret = krb5_compare_creds(context, which, mcreds, &creds);
+ krb5_free_cred_contents(context, &creds);
+ if (ret) {
+ credid = sqlite3_column_int64(stmt, 1);
+ ret = 0;
+ break;
+ }
+ }
+
+ sqlite3_finalize(stmt);
+
+ if (ret)
+ return ret;
+
+ ret = prepare_stmt(context, s->db, &stmt,
+ "DELETE FROM credentials WHERE oid=?");
+ if (ret)
+ return ret;
+ sqlite3_bind_int(stmt, 1, credid);
+
+ do {
+ ret = sqlite3_step(stmt);
+ } while (ret == SQLITE_ROW);
+ sqlite3_finalize(stmt);
+ if (ret != SQLITE_DONE) {
+ ret = KRB5_CC_IO;
+ krb5_set_error_message(context, ret,
+ N_("failed to delete scache credental", ""));
+ } else
+ ret = 0;
+
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ return 0; /* XXX */
+}
+
+struct cache_iter {
+ char *drop;
+ char *file;
+ sqlite3 *db;
+ sqlite3_stmt *stmt;
+};
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct cache_iter *ctx;
+ krb5_error_code ret;
+ char *name = NULL, *str = NULL;
+
+ *cursor = NULL;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (ctx == NULL)
+ return krb5_enomem(context);
+
+ ret = default_db(context, NULL, &ctx->db, &ctx->file);
+ if (ret) {
+ free(ctx);
+ return ret;
+ }
+
+ ret = asprintf(&name, "cacheIteration%pPid%d",
+ ctx, (int)getpid());
+ if (ret < 0 || name == NULL) {
+ sqlite3_close(ctx->db);
+ free(ctx);
+ return krb5_enomem(context);
+ }
+
+ ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
+ if (ret < 0 || ctx->drop == NULL) {
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx);
+ return krb5_enomem(context);
+ }
+
+ ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
+ name);
+ if (ret < 0 || str == NULL) {
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx->drop);
+ free(ctx);
+ return krb5_enomem(context);
+ }
+
+ ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
+ free(str);
+ str = NULL;
+ if (ret) {
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx->drop);
+ free(ctx);
+ return ret;
+ }
+
+ ret = asprintf(&str, "SELECT name FROM %s", name);
+ if (ret < 0 || str == NULL) {
+ exec_stmt(context, ctx->db, ctx->drop, 0);
+ sqlite3_close(ctx->db);
+ free(name);
+ free(ctx->drop);
+ free(ctx);
+ return krb5_enomem(context);
+ }
+ free(name);
+
+ ret = prepare_stmt(context, ctx->db, &ctx->stmt, str);
+ free(str);
+ if (ret) {
+ exec_stmt(context, ctx->db, ctx->drop, 0);
+ sqlite3_close(ctx->db);
+ free(ctx->drop);
+ free(ctx);
+ return ret;
+ }
+
+ *cursor = ctx;
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_cache_next(krb5_context context,
+ krb5_cc_cursor cursor,
+ krb5_ccache *id)
+{
+ struct cache_iter *ctx = cursor;
+ krb5_error_code ret;
+ const char *name;
+
+again:
+ ret = sqlite3_step(ctx->stmt);
+ if (ret == SQLITE_DONE) {
+ krb5_clear_error_message(context);
+ return KRB5_CC_END;
+ } else if (ret != SQLITE_ROW) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Database failed: %s", ""),
+ sqlite3_errmsg(ctx->db));
+ return KRB5_CC_IO;
+ }
+
+ if (sqlite3_column_type(ctx->stmt, 0) != SQLITE_TEXT)
+ goto again;
+
+ name = (const char *)sqlite3_column_text(ctx->stmt, 0);
+ if (name == NULL)
+ goto again;
+
+ ret = _krb5_cc_allocate(context, &krb5_scc_ops, id);
+ if (ret == 0)
+ ret = scc_resolve_2(context, id, ctx->file, name);
+ if (ret) {
+ free(*id);
+ *id = NULL;
+ }
+ return ret;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct cache_iter *ctx = cursor;
+
+ exec_stmt(context, ctx->db, ctx->drop, 0);
+ sqlite3_finalize(ctx->stmt);
+ sqlite3_close(ctx->db);
+ free(ctx->file);
+ free(ctx->drop);
+ free(ctx);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_scache *sfrom = SCACHE(from);
+ krb5_scache *sto = SCACHE(to);
+ krb5_error_code ret;
+
+ if (strcmp(sfrom->file, sto->file) != 0) {
+ /* Let upstairs handle the move */
+ return EXDEV;
+ }
+
+ ret = make_database(context, sfrom);
+ if (ret)
+ return ret;
+
+ ret = exec_stmt(context, sfrom->db,
+ "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO);
+ if (ret) return ret;
+
+ if (sto->cid != SCACHE_INVALID_CID) {
+ /* drop old cache entry */
+
+ sqlite3_bind_int(sfrom->dcache, 1, sto->cid);
+ do {
+ ret = sqlite3_step(sfrom->dcache);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(sfrom->dcache);
+ if (ret != SQLITE_DONE) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Failed to delete old cache: %d", ""),
+ (int)ret);
+ goto rollback;
+ }
+ }
+
+ sqlite3_bind_text(sfrom->ucachen, 1, sto->sub, -1, NULL);
+ sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid);
+
+ do {
+ ret = sqlite3_step(sfrom->ucachen);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(sfrom->ucachen);
+ if (ret != SQLITE_DONE) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Failed to update new cache: %d", ""),
+ (int)ret);
+ goto rollback;
+ }
+
+ sto->cid = sfrom->cid;
+
+ ret = exec_stmt(context, sfrom->db, "COMMIT", KRB5_CC_IO);
+ if (ret) return ret;
+
+ krb5_cc_close(context, from);
+ return 0;
+
+rollback:
+ exec_stmt(context, sfrom->db, "ROLLBACK", 0);
+ return KRB5_CC_IO;
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_get_default_name(krb5_context context, char **str)
+{
+ *str = NULL;
+ return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str);
+}
+
+static krb5_error_code KRB5_CALLCONV
+scc_set_default(krb5_context context, krb5_ccache id)
+{
+ krb5_scache *s = SCACHE(id);
+ krb5_error_code ret;
+
+ if (s->cid == SCACHE_INVALID_CID) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Trying to set a invalid cache "
+ "as default %s", ""),
+ s->name);
+ return KRB5_CC_IO;
+ }
+
+ ret = sqlite3_bind_text(s->umaster, 1, s->sub, -1, NULL);
+ if (ret) {
+ sqlite3_reset(s->umaster);
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Failed to set name of default cache", ""));
+ return KRB5_CC_IO;
+ }
+
+ do {
+ ret = sqlite3_step(s->umaster);
+ } while (ret == SQLITE_ROW);
+ sqlite3_reset(s->umaster);
+ if (ret != SQLITE_DONE) {
+ krb5_set_error_message(context, KRB5_CC_IO,
+ N_("Failed to update default cache", ""));
+ return KRB5_CC_IO;
+ }
+
+ return 0;
+}
+
+/**
+ * Variable containing the SCC based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops = {
+ KRB5_CC_OPS_VERSION_5,
+ "SCC",
+ NULL,
+ NULL,
+ scc_gen_new,
+ scc_initialize,
+ scc_destroy,
+ scc_close,
+ scc_store_cred,
+ NULL, /* scc_retrieve */
+ scc_get_principal,
+ scc_get_first,
+ scc_get_next,
+ scc_end_get,
+ scc_remove_cred,
+ scc_set_flags,
+ NULL,
+ scc_get_cache_first,
+ scc_get_cache_next,
+ scc_end_cache_get,
+ scc_move,
+ scc_get_default_name,
+ scc_set_default,
+ NULL,
+ NULL,
+ NULL,
+ scc_get_name_2,
+ scc_resolve_2
+};
+
+#endif
diff --git a/third_party/heimdal/lib/krb5/send_to_kdc.c b/third_party/heimdal/lib/krb5/send_to_kdc.c
new file mode 100644
index 0000000..bcabdd4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/send_to_kdc.c
@@ -0,0 +1,1357 @@
+/*
+ * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 - 2013 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 "send_to_kdc_plugin.h"
+
+/**
+ * @section send_to_kdc Locating and sending packets to the KDC
+ *
+ * The send to kdc code is responsible to request the list of KDC from
+ * the locate-kdc subsystem and then send requests to each of them.
+ *
+ * - Each second a new hostname is tried.
+ * - If the hostname have several addresses, the first will be tried
+ * directly then in turn the other will be tried every 3 seconds
+ * (host_timeout).
+ * - UDP requests are tried 3 times, and it tried with a individual timeout of kdc_timeout / 3.
+ * - TCP and HTTP requests are tried 1 time.
+ *
+ * Total wait time shorter then (number of addresses * 3) + kdc_timeout seconds.
+ *
+ */
+
+static int
+init_port(const char *s, int fallback)
+{
+ int tmp;
+
+ if (s && sscanf(s, "%d", &tmp) == 1)
+ return htons(tmp);
+ return fallback;
+}
+
+struct send_via_plugin_s {
+ krb5_const_realm realm;
+ krb5_krbhst_info *hi;
+ time_t timeout;
+ const krb5_data *send_data;
+ krb5_data *receive;
+};
+
+
+static krb5_error_code KRB5_LIB_CALL
+kdccallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
+ struct send_via_plugin_s *ctx = userctx;
+
+ if (service->send_to_kdc == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+ return service->send_to_kdc(context, plugctx, ctx->hi, ctx->timeout,
+ ctx->send_data, ctx->receive);
+}
+
+static krb5_error_code KRB5_LIB_CALL
+realmcallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
+ struct send_via_plugin_s *ctx = userctx;
+
+ if (service->send_to_realm == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+ return service->send_to_realm(context, plugctx, ctx->realm, ctx->timeout,
+ ctx->send_data, ctx->receive);
+}
+
+static const char *const send_to_kdc_plugin_deps[] = { "krb5", NULL };
+
+static const struct heim_plugin_data
+send_to_kdc_plugin_data = {
+ "krb5",
+ KRB5_PLUGIN_SEND_TO_KDC,
+ KRB5_PLUGIN_SEND_TO_KDC_VERSION_0,
+ send_to_kdc_plugin_deps,
+ krb5_get_instance
+};
+
+static krb5_error_code
+kdc_via_plugin(krb5_context context,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ struct send_via_plugin_s userctx;
+
+ userctx.realm = NULL;
+ userctx.hi = hi;
+ userctx.timeout = timeout;
+ userctx.send_data = send_data;
+ userctx.receive = receive;
+
+ return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
+ &userctx, kdccallback);
+}
+
+static krb5_error_code
+realm_via_plugin(krb5_context context,
+ krb5_const_realm realm,
+ time_t timeout,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ struct send_via_plugin_s userctx;
+
+ userctx.realm = realm;
+ userctx.hi = NULL;
+ userctx.timeout = timeout;
+ userctx.send_data = send_data;
+ userctx.receive = receive;
+
+ return _krb5_plugin_run_f(context, &send_to_kdc_plugin_data, 0,
+ &userctx, realmcallback);
+}
+
+struct krb5_sendto_ctx_data {
+ int flags;
+ int type;
+ krb5_sendto_ctx_func func;
+ void *data;
+ char *hostname;
+ char *sitename;
+ krb5_krbhst_handle krbhst;
+
+ /* context2 */
+ const krb5_data *send_data;
+ krb5_data response;
+ heim_array_t hosts;
+ int stateflags;
+#define KRBHST_COMPLETED 1
+
+ /* prexmit */
+ krb5_sendto_prexmit prexmit_func;
+ void *prexmit_ctx;
+
+ /* stats */
+ struct {
+ struct timeval start_time;
+ struct timeval name_resolution;
+ struct timeval krbhst;
+ unsigned long sent_packets;
+ unsigned long num_hosts;
+ } stats;
+ unsigned int stid;
+};
+
+static void KRB5_CALLCONV
+dealloc_sendto_ctx(void *ptr)
+{
+ krb5_sendto_ctx ctx = (krb5_sendto_ctx)ptr;
+ if (ctx->hostname)
+ free(ctx->hostname);
+ if (ctx->sitename)
+ free(ctx->sitename);
+ heim_release(ctx->hosts);
+ heim_release(ctx->krbhst);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
+{
+ *ctx = heim_alloc(sizeof(**ctx), "sendto-context", dealloc_sendto_ctx);
+ if (*ctx == NULL)
+ return krb5_enomem(context);
+ (*ctx)->hosts = heim_array_create();
+
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
+{
+ ctx->flags |= flags;
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
+{
+ return ctx->flags;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type)
+{
+ ctx->type = type;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
+ krb5_sendto_ctx_func func,
+ void *data)
+{
+ ctx->func = func;
+ ctx->data = data;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_sendto_ctx_set_prexmit(krb5_sendto_ctx ctx,
+ krb5_sendto_prexmit prexmit,
+ void *data)
+{
+ ctx->prexmit_func = prexmit;
+ ctx->prexmit_ctx = data;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_set_hostname(krb5_context context,
+ krb5_sendto_ctx ctx,
+ const char *hostname)
+{
+ char *newname;
+
+ /*
+ * Handle the case where hostname == ctx->hostname by copying it first, and
+ * disposing of any previous value after.
+ */
+ newname = strdup(hostname);
+ if (newname == NULL)
+ return krb5_enomem(context);
+ free(ctx->hostname);
+ ctx->hostname = newname;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_set_sitename(krb5_context context,
+ krb5_sendto_ctx ctx,
+ const char *sitename)
+{
+ char *newname;
+
+ newname = strdup(sitename);
+ if (newname == NULL)
+ return krb5_enomem(context);
+ free(ctx->sitename);
+ ctx->sitename = newname;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_sendto_ctx_set_krb5hst(krb5_context context,
+ krb5_sendto_ctx ctx,
+ krb5_krbhst_handle handle)
+{
+ heim_release(ctx->krbhst);
+ ctx->krbhst = heim_retain(handle);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
+{
+ heim_release(ctx);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
+ const krb5_data *reply, int *action)
+{
+ krb5_error_code ret;
+ KRB_ERROR error;
+
+ if(krb5_rd_error(context, reply, &error))
+ return 0;
+
+ ret = krb5_error_from_rd_error(context, &error, NULL);
+ krb5_free_error_contents(context, &error);
+
+ switch(ret) {
+ case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
+ if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
+ break;
+ krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
+ *action = KRB5_SENDTO_RESET;
+ break;
+ }
+ case KRB5KDC_ERR_SVC_UNAVAILABLE:
+ *action = KRB5_SENDTO_RESET;
+ break;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+
+struct host;
+
+struct host_fun {
+ krb5_error_code (*prepare)(krb5_context, struct host *, const krb5_data *);
+ krb5_error_code (*send_fn)(krb5_context, struct host *);
+ krb5_error_code (*recv_fn)(krb5_context, struct host *, krb5_data *);
+ int ntries;
+};
+
+struct host {
+ enum host_state { CONNECT, CONNECTING, CONNECTED, WAITING_REPLY, DEAD } state;
+ krb5_krbhst_info *hi;
+ struct addrinfo *ai;
+ rk_socket_t fd;
+ const struct host_fun *fun;
+ unsigned int tries;
+ time_t timeout;
+ krb5_data data;
+ unsigned int tid;
+};
+
+static void
+debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+static void
+debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
+{
+ const char *proto = "unknown";
+ const char *state;
+ char name[NI_MAXHOST], port[NI_MAXSERV];
+ char *text = NULL;
+ va_list ap;
+ int ret;
+
+ if (!_krb5_have_debug(context, 5))
+ return;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&text, fmt, ap);
+ va_end(ap);
+ if (ret == -1 || text == NULL)
+ return;
+
+ if (host->hi->proto == KRB5_KRBHST_HTTP)
+ proto = "http";
+ else if (host->hi->proto == KRB5_KRBHST_TCP)
+ proto = "tcp";
+ else if (host->hi->proto == KRB5_KRBHST_UDP)
+ proto = "udp";
+
+ if (getnameinfo(host->ai->ai_addr, host->ai->ai_addrlen,
+ name, sizeof(name), port, sizeof(port), NI_NUMERICHOST) != 0)
+ name[0] = '\0';
+
+ switch (host->state) {
+ case CONNECT: state = "CONNECT"; break;
+ case CONNECTING: state = "CONNECTING"; break;
+ case CONNECTED: state = "CONNECTED"; break;
+ case WAITING_REPLY: state = "WAITING_REPLY"; break;
+ case DEAD: state = "DEAD"; break;
+ default: state = "unknown"; break;
+ }
+
+ _krb5_debug(context, level, "%s: %s %s:%s (%s) state=%s tid: %08x", text,
+ proto, name, port, host->hi->hostname, state, host->tid);
+ free(text);
+}
+
+
+static void HEIM_CALLCONV
+deallocate_host(void *ptr)
+{
+ struct host *host = ptr;
+ if (!rk_IS_BAD_SOCKET(host->fd))
+ rk_closesocket(host->fd);
+ krb5_data_free(&host->data);
+ host->ai = NULL;
+}
+
+static void
+host_dead(krb5_context context, struct host *host, const char *msg)
+{
+ debug_host(context, 5, host, "%s", msg);
+ rk_closesocket(host->fd);
+ host->fd = rk_INVALID_SOCKET;
+ host->state = DEAD;
+}
+
+static krb5_error_code
+send_stream(krb5_context context, struct host *host)
+{
+ ssize_t len;
+
+ len = krb5_net_write(context, &host->fd, host->data.data, host->data.length);
+
+ if (len < 0)
+ return errno;
+ else if (len < host->data.length) {
+ host->data.length -= len;
+ memmove(host->data.data, ((uint8_t *)host->data.data) + len, host->data.length - len);
+ return -1;
+ } else {
+ krb5_data_free(&host->data);
+ return 0;
+ }
+}
+
+static krb5_error_code
+recv_stream(krb5_context context, struct host *host)
+{
+ krb5_error_code ret;
+ size_t oldlen;
+ ssize_t sret;
+ int nbytes;
+
+ if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
+ return HEIM_NET_CONN_REFUSED;
+
+ if (context->max_msg_size - host->data.length < nbytes) {
+ krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
+ N_("TCP message from KDC too large %d", ""),
+ (int)(host->data.length + nbytes));
+ return KRB5KRB_ERR_FIELD_TOOLONG;
+ }
+
+ oldlen = host->data.length;
+
+ ret = krb5_data_realloc(&host->data, oldlen + nbytes + 1 /* NUL */);
+ if (ret)
+ return ret;
+
+ sret = krb5_net_read(context, &host->fd, ((uint8_t *)host->data.data) + oldlen, nbytes);
+ if (sret <= 0) {
+ ret = errno;
+ return ret;
+ }
+ host->data.length = oldlen + sret;
+ /* zero terminate for http transport */
+ ((uint8_t *)host->data.data)[host->data.length] = '\0';
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static void
+host_next_timeout(krb5_context context, struct host *host)
+{
+ host->timeout = context->kdc_timeout / host->fun->ntries;
+ if (host->timeout == 0)
+ host->timeout = 1;
+
+ host->timeout += time(NULL);
+}
+
+/*
+ * connected host
+ */
+
+static void
+host_connected(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
+{
+ krb5_error_code ret;
+
+ host->state = CONNECTED;
+ /*
+ * Now prepare data to send to host
+ */
+ if (ctx->prexmit_func) {
+ krb5_data data;
+
+ krb5_data_zero(&data);
+
+ ret = ctx->prexmit_func(context, host->hi->proto,
+ ctx->prexmit_ctx, host->fd, &data);
+ if (ret == 0) {
+ if (data.length == 0) {
+ host_dead(context, host, "prexmit function didn't send data");
+ return;
+ }
+ ret = host->fun->prepare(context, host, &data);
+ krb5_data_free(&data);
+ }
+
+ } else {
+ ret = host->fun->prepare(context, host, ctx->send_data);
+ }
+ if (ret)
+ debug_host(context, 5, host, "failed to prexmit/prepare");
+}
+
+/*
+ * connect host
+ */
+
+static void
+host_connect(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
+{
+ krb5_krbhst_info *hi = host->hi;
+ struct addrinfo *ai = host->ai;
+
+ debug_host(context, 5, host, "connecting to host");
+
+ if (connect(host->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+#ifdef HAVE_WINSOCK
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ errno = EINPROGRESS;
+#endif /* HAVE_WINSOCK */
+ if (errno == EINPROGRESS && (hi->proto == KRB5_KRBHST_HTTP || hi->proto == KRB5_KRBHST_TCP)) {
+ debug_host(context, 5, host, "connecting to %d", host->fd);
+ host->state = CONNECTING;
+ } else {
+ host_dead(context, host, "failed to connect");
+ }
+ } else {
+ host_connected(context, ctx, host);
+ }
+
+ host_next_timeout(context, host);
+}
+
+/*
+ * HTTP transport
+ */
+
+static krb5_error_code
+prepare_http(krb5_context context, struct host *host, const krb5_data *data)
+{
+ char *str = NULL, *request = NULL;
+ krb5_error_code ret;
+ int len;
+
+ heim_assert(host->data.length == 0, "prepare_http called twice");
+
+ len = rk_base64_encode(data->data, data->length, &str);
+ if(len < 0)
+ return ENOMEM;
+
+ if (context->http_proxy)
+ ret = asprintf(&request, "GET http://%s/%s HTTP/1.0\r\n\r\n", host->hi->hostname, str);
+ else
+ ret = asprintf(&request, "GET /%s HTTP/1.0\r\n\r\n", str);
+ free(str);
+ if(ret < 0 || request == NULL)
+ return ENOMEM;
+
+ host->data.data = request;
+ host->data.length = strlen(request);
+
+ return 0;
+}
+
+static krb5_error_code
+recv_http(krb5_context context, struct host *host, krb5_data *data)
+{
+ krb5_error_code ret;
+ unsigned long rep_len;
+ size_t len;
+ char *p;
+
+ /*
+ * recv_stream returns a NUL terminated stream
+ */
+
+ ret = recv_stream(context, host);
+ if (ret)
+ return ret;
+
+ p = strstr(host->data.data, "\r\n\r\n");
+ if (p == NULL)
+ return -1;
+ p += 4;
+
+ len = host->data.length - (p - (char *)host->data.data);
+ if (len < 4)
+ return -1;
+
+ _krb5_get_int(p, &rep_len, 4);
+ if (len < rep_len)
+ return -1;
+
+ p += 4;
+
+ memmove(host->data.data, p, rep_len);
+ host->data.length = rep_len;
+
+ *data = host->data;
+ krb5_data_zero(&host->data);
+
+ return 0;
+}
+
+/*
+ * TCP transport
+ */
+
+static krb5_error_code
+prepare_tcp(krb5_context context, struct host *host, const krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ heim_assert(host->data.length == 0, "prepare_tcp called twice");
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return ENOMEM;
+
+ ret = krb5_store_data(sp, *data);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+ ret = krb5_storage_to_data(sp, &host->data);
+ krb5_storage_free(sp);
+
+ return ret;
+}
+
+static krb5_error_code
+recv_tcp(krb5_context context, struct host *host, krb5_data *data)
+{
+ krb5_error_code ret;
+ unsigned long pktlen;
+
+ ret = recv_stream(context, host);
+ if (ret)
+ return ret;
+
+ if (host->data.length < 4)
+ return -1;
+
+ _krb5_get_int(host->data.data, &pktlen, 4);
+
+ if (pktlen > host->data.length - 4)
+ return -1;
+
+ memmove(host->data.data, ((uint8_t *)host->data.data) + 4, host->data.length - 4);
+ host->data.length -= 4;
+
+ *data = host->data;
+ krb5_data_zero(&host->data);
+
+ return 0;
+}
+
+/*
+ * UDP transport
+ */
+
+static krb5_error_code
+prepare_udp(krb5_context context, struct host *host, const krb5_data *data)
+{
+ return krb5_data_copy(&host->data, data->data, data->length);
+}
+
+static krb5_error_code
+send_udp(krb5_context context, struct host *host)
+{
+ if (send(host->fd, host->data.data, host->data.length, 0) < 0)
+ return errno;
+ return 0;
+}
+
+static krb5_error_code
+recv_udp(krb5_context context, struct host *host, krb5_data *data)
+{
+ krb5_error_code ret;
+ int nbytes;
+
+
+ if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
+ return HEIM_NET_CONN_REFUSED;
+
+ if (context->max_msg_size < nbytes) {
+ krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
+ N_("UDP message from KDC too large %d", ""),
+ (int)nbytes);
+ return KRB5KRB_ERR_FIELD_TOOLONG;
+ }
+
+ ret = krb5_data_alloc(data, nbytes);
+ if (ret)
+ return ret;
+
+ ret = recv(host->fd, data->data, data->length, 0);
+ if (ret < 0) {
+ ret = errno;
+ krb5_data_free(data);
+ return ret;
+ }
+ data->length = ret;
+
+ return 0;
+}
+
+static const struct host_fun http_fun = {
+ prepare_http,
+ send_stream,
+ recv_http,
+ 1
+};
+static const struct host_fun tcp_fun = {
+ prepare_tcp,
+ send_stream,
+ recv_tcp,
+ 1
+};
+static const struct host_fun udp_fun = {
+ prepare_udp,
+ send_udp,
+ recv_udp,
+ 3
+};
+
+
+/*
+ * Host state machine
+ */
+
+static int
+eval_host_state(krb5_context context,
+ krb5_sendto_ctx ctx,
+ struct host *host,
+ int readable, int writeable)
+{
+ krb5_error_code ret;
+
+ if (host->state == CONNECT) {
+ /* check if its this host time to connect */
+ if (host->timeout < time(NULL))
+ host_connect(context, ctx, host);
+ return 0;
+ }
+
+ if (host->state == CONNECTING && writeable)
+ host_connected(context, ctx, host);
+
+ if (readable) {
+
+ debug_host(context, 5, host, "reading packet");
+
+ ret = host->fun->recv_fn(context, host, &ctx->response);
+ if (ret == -1) {
+ /* not done yet */
+ } else if (ret == 0) {
+ /* if recv_foo function returns 0, we have a complete reply */
+ debug_host(context, 5, host, "host completed");
+ return 1;
+ } else {
+ host_dead(context, host, "host disconnected");
+ }
+ }
+
+ /* check if there is anything to send, state might DEAD after read */
+ if (writeable && host->state == CONNECTED) {
+
+ ctx->stats.sent_packets++;
+
+ debug_host(context, 5, host, "writing packet");
+
+ ret = host->fun->send_fn(context, host);
+ if (ret == -1) {
+ /* not done yet */
+ } else if (ret) {
+ host_dead(context, host, "host dead, write failed");
+ } else
+ host->state = WAITING_REPLY;
+ }
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static krb5_error_code
+submit_request(krb5_context context, krb5_sendto_ctx ctx, krb5_krbhst_info *hi)
+{
+ unsigned long submitted_host = 0;
+ krb5_boolean freeai = FALSE;
+ struct timeval nrstart, nrstop;
+ krb5_error_code ret;
+ struct addrinfo *ai = NULL, *a;
+ struct host *host;
+
+ ret = kdc_via_plugin(context, hi, context->kdc_timeout,
+ ctx->send_data, &ctx->response);
+ if (ret == 0) {
+ return 0;
+ } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
+ _krb5_debug(context, 5, "send via plugin failed %s: %d",
+ hi->hostname, ret);
+ return ret;
+ }
+
+ /*
+ * If we have a proxy, let use the address of the proxy instead of
+ * the KDC and let the proxy deal with the resolving of the KDC.
+ */
+
+ gettimeofday(&nrstart, NULL);
+
+ if (hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
+ char *proxy2 = strdup(context->http_proxy);
+ char *el, *proxy = proxy2;
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+ unsigned short nport;
+
+ if (proxy == NULL)
+ return ENOMEM;
+ if (strncmp(proxy, "http://", 7) == 0)
+ proxy += 7;
+
+ /* check for url terminating slash */
+ el = strchr(proxy, '/');
+ if (el != NULL)
+ *el = '\0';
+
+ /* check for port in hostname, used below as port */
+ el = strchr(proxy, ':');
+ if(el != NULL)
+ *el++ = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* On some systems ntohs(foo(..., htons(...))) causes shadowing */
+ nport = init_port(el, htons(80));
+ snprintf(portstr, sizeof(portstr), "%d", ntohs(nport));
+
+ ret = getaddrinfo(proxy, portstr, &hints, &ai);
+ free(proxy2);
+ if (ret)
+ return krb5_eai_to_heim_errno(ret, errno);
+
+ freeai = TRUE;
+
+ } else {
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret)
+ return ret;
+ }
+
+ /* add up times */
+ gettimeofday(&nrstop, NULL);
+ timevalsub(&nrstop, &nrstart);
+ timevaladd(&ctx->stats.name_resolution, &nrstop);
+
+ ctx->stats.num_hosts++;
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ rk_socket_t fd;
+
+ fd = socket(a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
+ if (rk_IS_BAD_SOCKET(fd))
+ continue;
+ rk_cloexec(fd);
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ if (fd >= FD_SETSIZE) {
+ _krb5_debug(context, 0, "fd too large for select");
+ rk_closesocket(fd);
+ continue;
+ }
+#endif
+ socket_set_nonblocking(fd, 1);
+
+ host = heim_alloc(sizeof(*host), "sendto-host", deallocate_host);
+ if (host == NULL) {
+ if (freeai)
+ freeaddrinfo(ai);
+ rk_closesocket(fd);
+ return ENOMEM;
+ }
+ host->hi = hi;
+ host->fd = fd;
+ host->ai = a;
+ /* next version of stid */
+ host->tid = ctx->stid = (ctx->stid & 0xffff0000) | ((ctx->stid & 0xffff) + 1);
+
+ host->state = CONNECT;
+
+ switch (host->hi->proto) {
+ case KRB5_KRBHST_HTTP :
+ host->fun = &http_fun;
+ break;
+ case KRB5_KRBHST_TCP :
+ host->fun = &tcp_fun;
+ break;
+ case KRB5_KRBHST_UDP :
+ host->fun = &udp_fun;
+ break;
+ default:
+ heim_abort("undefined http transport protocol: %d", (int)host->hi->proto);
+ }
+
+ host->tries = host->fun->ntries;
+
+ /*
+ * Connect directly next host, wait a host_timeout for each next address.
+ * We try host_connect() here, checking the return code because as we do
+ * non-blocking connects, any error here indicates that the address is just
+ * offline. That is, it's something like "No route to host" which is not
+ * worth retrying. And so, we fail directly and immediately to the next
+ * address for this host without enqueueing the address for retries.
+ */
+ if (submitted_host == 0) {
+ host_connect(context, ctx, host);
+ if (host->state == DEAD)
+ continue;
+ } else {
+ debug_host(context, 5, host,
+ "Queuing host in future (in %ds), its the %lu address on the same name",
+ (int)(context->host_timeout * submitted_host), submitted_host + 1);
+ host->timeout = time(NULL) + (submitted_host * context->host_timeout);
+ }
+
+ heim_array_append_value(ctx->hosts, host);
+ heim_release(host);
+ submitted_host++;
+ }
+
+ if (freeai)
+ freeaddrinfo(ai);
+
+ if (submitted_host == 0)
+ return KRB5_KDC_UNREACH;
+
+ return 0;
+}
+
+struct wait_ctx {
+ krb5_context context;
+ krb5_sendto_ctx ctx;
+ fd_set rfds;
+ fd_set wfds;
+ rk_socket_t max_fd;
+ int got_reply;
+ time_t timenow;
+};
+
+static void
+wait_setup(heim_object_t obj, void *iter_ctx, int *stop)
+{
+ struct wait_ctx *wait_ctx = iter_ctx;
+ struct host *h = (struct host *)obj;
+
+ if (h->state == CONNECT) {
+ if (h->timeout >= wait_ctx->timenow)
+ return;
+ host_connect(wait_ctx->context, wait_ctx->ctx, h);
+ }
+
+ /* skip dead hosts */
+ if (h->state == DEAD)
+ return;
+
+ /* if host timed out, dec tries and (retry or kill host) */
+ if (h->timeout < wait_ctx->timenow) {
+ heim_assert(h->tries != 0, "tries should not reach 0");
+ h->tries--;
+ if (h->tries == 0) {
+ host_dead(wait_ctx->context, h, "host timed out");
+ return;
+ } else {
+ debug_host(wait_ctx->context, 5, h, "retrying sending to");
+ host_next_timeout(wait_ctx->context, h);
+ host_connected(wait_ctx->context, wait_ctx->ctx, h);
+ }
+ }
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ heim_assert(h->fd < FD_SETSIZE, "fd too large");
+#endif
+ switch (h->state) {
+ case WAITING_REPLY:
+ FD_SET(h->fd, &wait_ctx->rfds);
+ break;
+ case CONNECTING:
+ case CONNECTED:
+ FD_SET(h->fd, &wait_ctx->rfds);
+ FD_SET(h->fd, &wait_ctx->wfds);
+ break;
+ default:
+ debug_host(wait_ctx->context, 5, h, "invalid sendto host state");
+ heim_abort("invalid sendto host state");
+ }
+ if (h->fd > wait_ctx->max_fd || wait_ctx->max_fd == rk_INVALID_SOCKET)
+ wait_ctx->max_fd = h->fd;
+}
+
+static int
+wait_filter_dead(heim_object_t obj, void *ctx)
+{
+ struct host *h = (struct host *)obj;
+ return (int)((h->state == DEAD) ? true : false);
+}
+
+static void
+wait_accelerate(heim_object_t obj, void *ctx, int *stop)
+{
+ struct host *h = (struct host *)obj;
+
+ if (h->state == CONNECT && h->timeout > 0)
+ h->timeout--;
+}
+
+static void
+wait_process(heim_object_t obj, void *ctx, int *stop)
+{
+ struct wait_ctx *wait_ctx = ctx;
+ struct host *h = (struct host *)obj;
+ int readable, writeable;
+ heim_assert(h->state != DEAD, "dead host resurected");
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ heim_assert(h->fd < FD_SETSIZE, "fd too large");
+#endif
+ readable = FD_ISSET(h->fd, &wait_ctx->rfds);
+ writeable = FD_ISSET(h->fd, &wait_ctx->wfds);
+
+ if (readable || writeable || h->state == CONNECT)
+ wait_ctx->got_reply |= eval_host_state(wait_ctx->context, wait_ctx->ctx, h, readable, writeable);
+
+ /* if there is already a reply, just fall though the array */
+ if (wait_ctx->got_reply)
+ *stop = 1;
+}
+
+static krb5_error_code
+wait_response(krb5_context context, int *action, krb5_sendto_ctx ctx)
+{
+ struct wait_ctx wait_ctx;
+ struct timeval tv;
+ int ret;
+
+ wait_ctx.context = context;
+ wait_ctx.ctx = ctx;
+ FD_ZERO(&wait_ctx.rfds);
+ FD_ZERO(&wait_ctx.wfds);
+ wait_ctx.max_fd = rk_INVALID_SOCKET;
+
+ /* oh, we have a reply, it must be a plugin that got it for us */
+ if (ctx->response.length) {
+ *action = KRB5_SENDTO_FILTER;
+ return 0;
+ }
+
+ wait_ctx.timenow = time(NULL);
+
+ heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_setup);
+ heim_array_filter_f(ctx->hosts, &wait_ctx, wait_filter_dead);
+
+ if (heim_array_get_length(ctx->hosts) == 0) {
+ if (ctx->stateflags & KRBHST_COMPLETED) {
+ _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
+ "trying to pulling more hosts");
+ *action = KRB5_SENDTO_FAILED;
+ } else {
+ _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
+ "and no more hosts -> failure");
+ *action = KRB5_SENDTO_TIMEOUT;
+ }
+ return 0;
+ }
+
+ if (wait_ctx.max_fd == rk_INVALID_SOCKET) {
+ /*
+ * If we don't find a host which can make progress, then
+ * we accelerate the process by moving all of the contestants
+ * up by 1s.
+ */
+ _krb5_debug(context, 5, "wait_response: moving the contestants forward");
+ heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_accelerate);
+ return 0;
+ }
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ret = select(wait_ctx.max_fd + 1, &wait_ctx.rfds, &wait_ctx.wfds, NULL, &tv);
+ if (ret < 0)
+ return errno;
+ if (ret == 0) {
+ *action = KRB5_SENDTO_TIMEOUT;
+ return 0;
+ }
+
+ wait_ctx.got_reply = 0;
+ heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_process);
+ if (wait_ctx.got_reply)
+ *action = KRB5_SENDTO_FILTER;
+ else
+ *action = KRB5_SENDTO_CONTINUE;
+
+ return 0;
+}
+
+static void
+reset_context(krb5_context context, krb5_sendto_ctx ctx)
+{
+ krb5_data_free(&ctx->response);
+ heim_release(ctx->hosts);
+ ctx->hosts = heim_array_create();
+ ctx->stateflags = 0;
+}
+
+
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_context(krb5_context context,
+ krb5_sendto_ctx ctx,
+ const krb5_data *send_data,
+ krb5_const_realm realm,
+ krb5_data *receive)
+{
+ krb5_error_code ret = 0;
+ krb5_krbhst_handle handle = NULL;
+ struct timeval nrstart, nrstop, stop_time;
+ int type, freectx = 0;
+ int action;
+ int numreset = 0;
+
+ krb5_data_zero(receive);
+
+ if (ctx == NULL) {
+ ret = krb5_sendto_ctx_alloc(context, &ctx);
+ if (ret)
+ goto out;
+ freectx = 1;
+ }
+
+ ctx->stid = (context->num_kdc_requests++) << 16;
+
+ memset(&ctx->stats, 0, sizeof(ctx->stats));
+ gettimeofday(&ctx->stats.start_time, NULL);
+
+ type = ctx->type;
+ if (type == 0) {
+ if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
+ type = KRB5_KRBHST_ADMIN;
+ else
+ type = KRB5_KRBHST_KDC;
+ }
+
+ ctx->send_data = send_data;
+
+ if ((int)send_data->length > context->large_msg_size)
+ ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
+
+ /* loop until we get back a appropriate response */
+
+ action = KRB5_SENDTO_INITIAL;
+
+ while (1) {
+ krb5_krbhst_info *hi;
+
+ switch (action) {
+ case KRB5_SENDTO_INITIAL:
+ ret = realm_via_plugin(context, realm, context->kdc_timeout,
+ send_data, &ctx->response);
+ if (ret == 0 || ret != KRB5_PLUGIN_NO_HANDLE) {
+ action = KRB5_SENDTO_DONE;
+ break;
+ }
+ action = KRB5_SENDTO_KRBHST;
+ HEIM_FALLTHROUGH;
+ case KRB5_SENDTO_KRBHST:
+ if (ctx->krbhst == NULL) {
+ ret = krb5_krbhst_init_flags(context, realm, type,
+ ctx->flags, &handle);
+ if (ret)
+ goto out;
+
+ if (ctx->hostname) {
+ ret = krb5_krbhst_set_hostname(context, handle, ctx->hostname);
+ if (ret)
+ goto out;
+ }
+ if (ctx->sitename) {
+ ret = krb5_krbhst_set_sitename(context, handle, ctx->sitename);
+ if (ret)
+ goto out;
+ }
+ } else {
+ handle = heim_retain(ctx->krbhst);
+ }
+ action = KRB5_SENDTO_TIMEOUT;
+ HEIM_FALLTHROUGH;
+ case KRB5_SENDTO_TIMEOUT:
+
+ /*
+ * If we completed, just got to next step
+ */
+
+ if (ctx->stateflags & KRBHST_COMPLETED) {
+ action = KRB5_SENDTO_CONTINUE;
+ break;
+ }
+
+ /*
+ * Pull out next host, if there is no more, close the
+ * handle and mark as completed.
+ *
+ * Collect time spent in krbhst (dns, plugin, etc)
+ */
+
+
+ gettimeofday(&nrstart, NULL);
+
+ ret = krb5_krbhst_next(context, handle, &hi);
+
+ gettimeofday(&nrstop, NULL);
+ timevalsub(&nrstop, &nrstart);
+ timevaladd(&ctx->stats.krbhst, &nrstop);
+
+ action = KRB5_SENDTO_CONTINUE;
+ if (ret == 0) {
+ _krb5_debug(context, 5, "submitting new requests to new host");
+ if (submit_request(context, ctx, hi) != 0)
+ action = KRB5_SENDTO_TIMEOUT;
+ } else {
+ _krb5_debug(context, 5, "out of hosts, waiting for replies");
+ ctx->stateflags |= KRBHST_COMPLETED;
+ }
+
+ break;
+ case KRB5_SENDTO_CONTINUE:
+
+ ret = wait_response(context, &action, ctx);
+ if (ret)
+ goto out;
+
+ break;
+ case KRB5_SENDTO_RESET:
+ /* start over */
+ _krb5_debug(context, 5,
+ "krb5_sendto trying over again (reset): %d",
+ numreset);
+ reset_context(context, ctx);
+ if (handle) {
+ krb5_krbhst_free(context, handle);
+ handle = NULL;
+ }
+ numreset++;
+ if (numreset >= 3)
+ action = KRB5_SENDTO_FAILED;
+ else
+ action = KRB5_SENDTO_KRBHST;
+
+ break;
+ case KRB5_SENDTO_FILTER:
+ /* default to next state, the filter function might modify this */
+ action = KRB5_SENDTO_DONE;
+
+ if (ctx->func) {
+ ret = (*ctx->func)(context, ctx, ctx->data,
+ &ctx->response, &action);
+ if (ret)
+ goto out;
+
+ /*
+ * If we are not done, ask to continue/reset
+ */
+ switch (action) {
+ case KRB5_SENDTO_DONE:
+ break;
+ case KRB5_SENDTO_RESET:
+ case KRB5_SENDTO_CONTINUE:
+ /* free response to clear it out so we don't loop */
+ krb5_data_free(&ctx->response);
+ break;
+ default:
+ ret = KRB5_KDC_UNREACH;
+ krb5_set_error_message(context, ret,
+ "sendto filter funcation return unsupported state: %d", (int)action);
+ goto out;
+ }
+ }
+ break;
+ case KRB5_SENDTO_FAILED:
+ ret = KRB5_KDC_UNREACH;
+ goto out;
+ case KRB5_SENDTO_DONE:
+ ret = 0;
+ goto out;
+ default:
+ heim_abort("invalid krb5_sendto_context state");
+ }
+ }
+
+out:
+ gettimeofday(&stop_time, NULL);
+ timevalsub(&stop_time, &ctx->stats.start_time);
+ if (ret == 0 && ctx->response.length) {
+ *receive = ctx->response;
+ krb5_data_zero(&ctx->response);
+ } else {
+ krb5_data_free(&ctx->response);
+ krb5_clear_error_message (context);
+ ret = KRB5_KDC_UNREACH;
+ krb5_set_error_message(context, ret,
+ N_("unable to reach any KDC in realm %s", ""),
+ realm);
+ }
+
+ _krb5_debug(context, 1,
+ "%s %s done: %d hosts: %lu packets: %lu"
+ " wc: %lld.%06lu nr: %lld.%06lu kh: %lld.%06lu tid: %08x",
+ __func__, realm, ret,
+ ctx->stats.num_hosts, ctx->stats.sent_packets,
+ (long long)stop_time.tv_sec,
+ (unsigned long)stop_time.tv_usec,
+ (long long)ctx->stats.name_resolution.tv_sec,
+ (unsigned long)ctx->stats.name_resolution.tv_usec,
+ (long long)ctx->stats.krbhst.tv_sec,
+ (unsigned long)ctx->stats.krbhst.tv_usec, ctx->stid);
+
+
+ if (freectx)
+ krb5_sendto_ctx_free(context, ctx);
+ else
+ reset_context(context, ctx);
+
+ if (handle)
+ krb5_krbhst_free(context, handle);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/send_to_kdc_plugin.h b/third_party/heimdal/lib/krb5/send_to_kdc_plugin.h
new file mode 100644
index 0000000..30d6892
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/send_to_kdc_plugin.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H
+#define HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H 1
+
+#include <krb5.h>
+#include <heimbase-svc.h>
+
+#define KRB5_PLUGIN_SEND_TO_KDC "send_to_kdc"
+
+#define KRB5_PLUGIN_SEND_TO_KDC_VERSION_0 0
+#define KRB5_PLUGIN_SEND_TO_KDC_VERSION_2 2
+#define KRB5_PLUGIN_SEND_TO_KDC_VERSION KRB5_PLUGIN_SEND_TO_KDC_VERSION_2
+
+typedef krb5_error_code
+(KRB5_CALLCONV *krb5plugin_send_to_kdc_func)(krb5_context,
+ void *,
+ krb5_krbhst_info *,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+typedef krb5_error_code
+(KRB5_CALLCONV *krb5plugin_send_to_realm_func)(krb5_context,
+ void *,
+ krb5_const_realm,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+
+
+typedef struct krb5plugin_send_to_kdc_ftable {
+ HEIM_PLUGIN_FTABLE_COMMON_ELEMENTS(krb5_context);
+ krb5plugin_send_to_kdc_func send_to_kdc;
+ krb5plugin_send_to_realm_func send_to_realm; /* added in version 2 */
+} krb5plugin_send_to_kdc_ftable;
+
+#endif /* HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H */
diff --git a/third_party/heimdal/lib/krb5/sendauth.c b/third_party/heimdal/lib/krb5/sendauth.c
new file mode 100644
index 0000000..a2c8aed
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/sendauth.c
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+/*
+ * The format seems to be:
+ * client -> server
+ *
+ * 4 bytes - length
+ * KRB5_SENDAUTH_V1.0 (including zero)
+ * 4 bytes - length
+ * protocol string (with terminating zero)
+ *
+ * server -> client
+ * 1 byte - (0 = OK, else some kind of error)
+ *
+ * client -> server
+ * 4 bytes - length
+ * AP-REQ
+ *
+ * server -> client
+ * 4 bytes - length (0 = OK, else length of error)
+ * (error)
+ *
+ * if(mutual) {
+ * server -> client
+ * 4 bytes - length
+ * AP-REP
+ * }
+ */
+
+/**
+ * Perform the client side of the sendauth protocol.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context Authentication context of the peer.
+ * @param p_fd Socket associated to the connection.
+ * @param appl_version Server-specific string.
+ * @param client Client principal. If NULL, use the credentials in \a ccache.
+ * @param server Server principal.
+ * @param ap_req_options Options for the AP_REQ message. See the AP_OPTS_* defines in krb5.h.
+ * @param in_data FIXME
+ * @param in_creds FIXME
+ * @param ccache Credentials cache. If NULL, use the default credentials cache.
+ * @param ret_error If not NULL, will be set to the error reported by server, if any.
+ * Must be deallocated with krb5_free_error_contents().
+ * @param rep_result If not NULL, will be set to the EncApRepPart of the AP_REP message.
+ * Must be deallocated with krb5_free_ap_rep_enc_part().
+ * @param out_creds FIXME If not NULL, will be set to FIXME. Must be deallocated with
+ * krb5_free_creds().
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendauth(krb5_context context,
+ krb5_auth_context *auth_context,
+ krb5_pointer p_fd,
+ const char *appl_version,
+ krb5_principal client,
+ krb5_principal server,
+ krb5_flags ap_req_options,
+ krb5_data *in_data,
+ krb5_creds *in_creds,
+ krb5_ccache ccache,
+ krb5_error **ret_error,
+ krb5_ap_rep_enc_part **rep_result,
+ krb5_creds **out_creds)
+{
+ krb5_error_code ret;
+ uint32_t len, net_len;
+ const char *version = KRB5_SENDAUTH_VERSION;
+ u_char repl;
+ krb5_data ap_req, error_data;
+ krb5_creds this_cred;
+ krb5_principal this_client = NULL;
+ krb5_creds *creds;
+ ssize_t sret;
+ krb5_boolean my_ccache = FALSE;
+
+ memset(&this_cred, 0, sizeof(this_cred));
+ len = strlen(version) + 1;
+ net_len = htonl(len);
+ if (krb5_net_write (context, p_fd, &net_len, 4) != 4
+ || krb5_net_write (context, p_fd, version, len) != len) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "write: %s", strerror(ret));
+ return ret;
+ }
+
+ len = strlen(appl_version) + 1;
+ net_len = htonl(len);
+ if (krb5_net_write (context, p_fd, &net_len, 4) != 4
+ || krb5_net_write (context, p_fd, appl_version, len) != len) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "write: %s", strerror(ret));
+ return ret;
+ }
+
+ sret = krb5_net_read (context, p_fd, &repl, sizeof(repl));
+ if (sret < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "read: %s", strerror(ret));
+ return ret;
+ } else if (sret != sizeof(repl)) {
+ krb5_clear_error_message (context);
+ return KRB5_SENDAUTH_BADRESPONSE;
+ }
+
+ if (repl != 0) {
+ krb5_clear_error_message (context);
+ return KRB5_SENDAUTH_REJECTED;
+ }
+
+ if (in_creds == NULL) {
+ if (ccache == NULL) {
+ ret = krb5_cc_default (context, &ccache);
+ if (ret)
+ return ret;
+ my_ccache = TRUE;
+ }
+
+ if (client == NULL) {
+ ret = krb5_cc_get_principal (context, ccache, &this_client);
+ if (ret) {
+ if(my_ccache)
+ krb5_cc_close(context, ccache);
+ return ret;
+ }
+ client = this_client;
+ }
+ this_cred.client = client;
+ this_cred.server = server;
+ this_cred.times.endtime = 0;
+ this_cred.ticket.length = 0;
+ in_creds = &this_cred;
+ }
+ if (in_creds->ticket.length == 0) {
+ ret = krb5_get_credentials (context, 0, ccache, in_creds, &creds);
+ if (ret) {
+ if(my_ccache)
+ krb5_cc_close(context, ccache);
+ return ret;
+ }
+ } else {
+ creds = in_creds;
+ }
+ if(my_ccache)
+ krb5_cc_close(context, ccache);
+ ret = krb5_mk_req_extended (context,
+ auth_context,
+ ap_req_options,
+ in_data,
+ creds,
+ &ap_req);
+
+ if (ret)
+ return ret;
+
+ ret = krb5_write_message (context,
+ p_fd,
+ &ap_req);
+ if (ret)
+ return ret;
+
+ krb5_data_free (&ap_req);
+
+ ret = krb5_read_message (context, p_fd, &error_data);
+ if (ret)
+ return ret;
+
+ if (error_data.length != 0) {
+ KRB_ERROR error;
+
+ ret = krb5_rd_error (context, &error_data, &error);
+ krb5_data_free (&error_data);
+ if (ret == 0) {
+ ret = krb5_error_from_rd_error(context, &error, NULL);
+ if (ret_error != NULL) {
+ *ret_error = malloc (sizeof(krb5_error));
+ if (*ret_error == NULL) {
+ krb5_free_error_contents (context, &error);
+ } else {
+ **ret_error = error;
+ }
+ } else {
+ krb5_free_error_contents (context, &error);
+ }
+ return ret;
+ } else {
+ krb5_clear_error_message(context);
+ return ret;
+ }
+ } else
+ krb5_data_free (&error_data);
+
+ if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
+ krb5_data ap_rep;
+ krb5_ap_rep_enc_part *ignore = NULL;
+
+ krb5_data_zero (&ap_rep);
+ ret = krb5_read_message (context,
+ p_fd,
+ &ap_rep);
+ if (ret)
+ return ret;
+
+ ret = krb5_rd_rep (context, *auth_context, &ap_rep,
+ rep_result ? rep_result : &ignore);
+ krb5_data_free (&ap_rep);
+ if (ret)
+ return ret;
+ if (rep_result == NULL)
+ krb5_free_ap_rep_enc_part (context, ignore);
+ }
+
+ if (out_creds)
+ ret = krb5_copy_creds(context, creds, out_creds);
+
+ this_cred.server = NULL;
+ if (creds == &this_cred)
+ krb5_free_cred_contents(context, creds);
+ else if (creds)
+ krb5_free_creds(context, creds);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/set_default_realm.c b/third_party/heimdal/lib/krb5/set_default_realm.c
new file mode 100644
index 0000000..b3450c4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/set_default_realm.c
@@ -0,0 +1,85 @@
+/*
+ * 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"
+
+/*
+ * Convert the simple string `s' into a NULL-terminated and freshly allocated
+ * list in `list'. Return an error code.
+ */
+
+static krb5_error_code
+string_to_list (krb5_context context, const char *s, krb5_realm **list)
+{
+
+ *list = malloc (2 * sizeof(**list));
+ if (*list == NULL)
+ return krb5_enomem(context);
+ (*list)[0] = strdup (s);
+ if ((*list)[0] == NULL) {
+ free (*list);
+ return krb5_enomem(context);
+ }
+ (*list)[1] = NULL;
+ return 0;
+}
+
+/*
+ * Set the knowledge of the default realm(s) in `context'.
+ * If realm != NULL, that's the new default realm.
+ * Otherwise, the realm(s) are figured out from configuration or DNS.
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_default_realm(krb5_context context,
+ const char *realm)
+{
+ krb5_error_code ret = 0;
+ krb5_realm *realms = NULL;
+
+ if (realm == NULL) {
+ realms = krb5_config_get_strings (context, NULL,
+ "libdefaults",
+ "default_realm",
+ NULL);
+ if (realms == NULL)
+ ret = krb5_get_host_realm(context, NULL, &realms);
+ } else {
+ ret = string_to_list (context, realm, &realms);
+ }
+ if (ret)
+ return ret;
+ krb5_free_host_realm (context, context->default_realms);
+ context->default_realms = realms;
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/sock_principal.c b/third_party/heimdal/lib/krb5/sock_principal.c
new file mode 100644
index 0000000..a43546d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/sock_principal.c
@@ -0,0 +1,68 @@
+/*
+ * 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_sock_to_principal (krb5_context context,
+ int sock,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
+{
+ krb5_error_code ret;
+ struct sockaddr_storage __ss;
+ struct sockaddr *sa = (struct sockaddr *)&__ss;
+ socklen_t salen = sizeof(__ss);
+ char hostname[NI_MAXHOST];
+
+ if (getsockname (sock, sa, &salen) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "getsockname: %s", strerror(ret));
+ return ret;
+ }
+ ret = getnameinfo (sa, salen, hostname, sizeof(hostname), NULL, 0, 0);
+ if (ret) {
+ int save_errno = errno;
+ krb5_error_code ret2 = krb5_eai_to_heim_errno(ret, save_errno);
+ krb5_set_error_message (context, ret2, "getnameinfo: %s", gai_strerror(ret));
+ return ret2;
+ }
+
+ ret = krb5_sname_to_principal (context,
+ hostname,
+ sname,
+ type,
+ ret_princ);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/sp800-108-kdf.c b/third_party/heimdal/lib/krb5/sp800-108-kdf.c
new file mode 100755
index 0000000..4a12067
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/sp800-108-kdf.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, 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"
+
+/*
+ * SP800-108 KDF
+ */
+
+/**
+ * As described in SP800-108 5.1 (for HMAC)
+ *
+ * @param context Kerberos 5 context
+ * @param kdf_K1 Base key material.
+ * @param kdf_label A string that identifies the purpose for the derived key.
+ * @param kdf_context A binary string containing parties, nonce, etc.
+ * @param md Message digest function to use for PRF.
+ * @param kdf_K0 Derived key data.
+ *
+ * @return Return an error code for an failure or 0 on success.
+ * @ingroup krb5_crypto
+ */
+krb5_error_code
+_krb5_SP800_108_HMAC_KDF(krb5_context context,
+ const krb5_data *kdf_K1,
+ const krb5_data *kdf_label,
+ const krb5_data *kdf_context,
+ const EVP_MD *md,
+ krb5_data *kdf_K0)
+{
+ HMAC_CTX c;
+ unsigned char *p = kdf_K0->data;
+ size_t i, n, left = kdf_K0->length;
+ unsigned char hmac[EVP_MAX_MD_SIZE];
+ unsigned int h = EVP_MD_size(md);
+ const size_t L = kdf_K0->length;
+
+ heim_assert(md != NULL, "SP800-108 KDF internal error");
+
+ HMAC_CTX_init(&c);
+
+ n = L / h;
+
+ for (i = 0; i <= n; i++) {
+ unsigned char tmp[4];
+ size_t len;
+
+ if (HMAC_Init_ex(&c, kdf_K1->data, kdf_K1->length, md, NULL) == 0) {
+ HMAC_CTX_cleanup(&c);
+ return krb5_enomem(context);
+ }
+
+ _krb5_put_int(tmp, i + 1, 4);
+ HMAC_Update(&c, tmp, 4);
+ HMAC_Update(&c, kdf_label->data, kdf_label->length);
+ HMAC_Update(&c, (unsigned char *)"", 1);
+ if (kdf_context)
+ HMAC_Update(&c, kdf_context->data, kdf_context->length);
+ _krb5_put_int(tmp, L * 8, 4);
+ HMAC_Update(&c, tmp, 4);
+
+ HMAC_Final(&c, hmac, &h);
+ len = h > left ? left : h;
+ memcpy(p, hmac, len);
+ p += len;
+ left -= len;
+ }
+
+ HMAC_CTX_cleanup(&c);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/store-int.c b/third_party/heimdal/lib/krb5/store-int.c
new file mode 100644
index 0000000..6fe7eb3
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store-int.c
@@ -0,0 +1,67 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_put_int(void *buffer, uint64_t value, size_t size)
+{
+ unsigned char *p = buffer;
+ int i;
+ for (i = size - 1; i >= 0; i--) {
+ p[i] = value & 0xff;
+ value >>= 8;
+ }
+ return size;
+}
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_get_int64(void *buffer, uint64_t *value, size_t size)
+{
+ unsigned char *p = buffer;
+ uint64_t v = 0;
+ size_t i;
+ for (i = 0; i < size; i++)
+ v = (v << 8) + p[i];
+ *value = v;
+ return size;
+}
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_get_int(void *buffer, unsigned long *value, size_t size)
+{
+ uint64_t v64;
+ krb5_ssize_t bytes = _krb5_get_int64(buffer, &v64, size);
+ *value = v64;
+ return bytes;
+}
diff --git a/third_party/heimdal/lib/krb5/store-int.h b/third_party/heimdal/lib/krb5/store-int.h
new file mode 100644
index 0000000..2762d96
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store-int.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#ifndef __store_int_h__
+#define __store_int_h__
+
+struct krb5_storage_data {
+ void *data;
+ ssize_t (*fetch)(struct krb5_storage_data*, void*, size_t);
+ ssize_t (*store)(struct krb5_storage_data*, const void*, size_t);
+ off_t (*seek)(struct krb5_storage_data*, off_t, int);
+ int (*trunc)(struct krb5_storage_data*, off_t);
+ int (*fsync)(struct krb5_storage_data*);
+ void (*free)(struct krb5_storage_data*);
+ krb5_flags flags;
+ int eof_code;
+ size_t max_alloc;
+};
+
+#endif /* __store_int_h__ */
diff --git a/third_party/heimdal/lib/krb5/store-test.c b/third_party/heimdal/lib/krb5/store-test.c
new file mode 100644
index 0000000..3d0f9e0
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store-test.c
@@ -0,0 +1,117 @@
+/*
+ * 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"
+
+static void
+print_data(unsigned char *data, size_t len)
+{
+ int i;
+ for(i = 0; i < len; i++) {
+ if(i > 0 && (i % 16) == 0)
+ printf("\n ");
+ printf("%02x ", data[i]);
+ }
+ printf("\n");
+}
+
+static int
+compare(const char *name, krb5_storage *sp, void *expected, size_t len)
+{
+ int ret = 0;
+ krb5_data data;
+ if (krb5_storage_to_data(sp, &data))
+ errx(1, "krb5_storage_to_data failed");
+ krb5_storage_free(sp);
+ if(data.length != len || memcmp(data.data, expected, len) != 0) {
+ printf("%s mismatch\n", name);
+ printf(" Expected: ");
+ print_data(expected, len);
+ printf(" Actual: ");
+ print_data(data.data, data.length);
+ ret++;
+ }
+ krb5_data_free(&data);
+ return ret;
+}
+
+int
+main(int argc, char **argv)
+{
+ int nerr = 0;
+ krb5_storage *sp;
+ krb5_context context;
+ krb5_principal principal;
+
+
+ krb5_init_context(&context);
+
+ sp = krb5_storage_emem();
+ krb5_store_int32(sp, 0x01020304);
+ nerr += compare("Integer", sp, "\x1\x2\x3\x4", 4);
+
+ sp = krb5_storage_emem();
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+ krb5_store_int32(sp, 0x01020304);
+ nerr += compare("Integer (LE)", sp, "\x4\x3\x2\x1", 4);
+
+ sp = krb5_storage_emem();
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_BE);
+ krb5_store_int32(sp, 0x01020304);
+ nerr += compare("Integer (BE)", sp, "\x1\x2\x3\x4", 4);
+
+ sp = krb5_storage_emem();
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_HOST);
+ krb5_store_int32(sp, 0x01020304);
+ {
+ int test = 1;
+ void *data;
+ if(*(char*)&test)
+ data = "\x4\x3\x2\x1";
+ else
+ data = "\x1\x2\x3\x4";
+ nerr += compare("Integer (host)", sp, data, 4);
+ }
+
+ sp = krb5_storage_emem();
+ krb5_make_principal(context, &principal, "TEST", "foobar", NULL);
+ krb5_store_principal(sp, principal);
+ krb5_free_principal(context, principal);
+ nerr += compare("Principal", sp, "\x0\x0\x0\x1"
+ "\x0\x0\x0\x1"
+ "\x0\x0\x0\x4TEST"
+ "\x0\x0\x0\x6""foobar", 26);
+
+ krb5_free_context(context);
+
+ return nerr ? 1 : 0;
+}
diff --git a/third_party/heimdal/lib/krb5/store.c b/third_party/heimdal/lib/krb5/store.c
new file mode 100644
index 0000000..01961f0
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store.c
@@ -0,0 +1,2058 @@
+/*
+ * 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 "store-int.h"
+
+#define BYTEORDER_IS(SP, V) (((SP)->flags & KRB5_STORAGE_BYTEORDER_MASK) == (V))
+#define BYTEORDER_IS_LE(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_LE)
+#define BYTEORDER_IS_BE(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_BE)
+#define BYTEORDER_IS_HOST(SP) (BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_HOST) || \
+ krb5_storage_is_flags((SP), KRB5_STORAGE_HOST_BYTEORDER))
+#define BYTEORDER_IS_PACKED(SP) BYTEORDER_IS((SP), KRB5_STORAGE_BYTEORDER_PACKED)
+
+/**
+ * Add the flags on a storage buffer by or-ing in the flags to the buffer.
+ *
+ * @param sp the storage buffer to set the flags on
+ * @param flags the flags to set
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_storage_set_flags(krb5_storage *sp, krb5_flags flags)
+{
+ sp->flags |= flags;
+}
+
+/**
+ * Clear the flags on a storage buffer
+ *
+ * @param sp the storage buffer to clear the flags on
+ * @param flags the flags to clear
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_storage_clear_flags(krb5_storage *sp, krb5_flags flags)
+{
+ sp->flags &= ~flags;
+}
+
+/**
+ * Return true or false depending on if the storage flags is set or
+ * not. NB testing for the flag 0 always return true.
+ *
+ * @param sp the storage buffer to check flags on
+ * @param flags The flags to test for
+ *
+ * @return true if all the flags are set, false if not.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_storage_is_flags(krb5_storage *sp, krb5_flags flags)
+{
+ return (sp->flags & flags) == flags;
+}
+
+/**
+ * Set the new byte order of the storage buffer.
+ *
+ * @param sp the storage buffer to set the byte order for.
+ * @param byteorder the new byte order.
+ *
+ * The byte order are: KRB5_STORAGE_BYTEORDER_BE,
+ * KRB5_STORAGE_BYTEORDER_LE and KRB5_STORAGE_BYTEORDER_HOST.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_storage_set_byteorder(krb5_storage *sp, krb5_flags byteorder)
+{
+ sp->flags &= ~KRB5_STORAGE_BYTEORDER_MASK;
+ sp->flags |= byteorder;
+}
+
+/**
+ * Return the current byteorder for the buffer. See krb5_storage_set_byteorder() for the list or byte order contants.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_flags KRB5_LIB_CALL
+krb5_storage_get_byteorder(krb5_storage *sp)
+{
+ return sp->flags & KRB5_STORAGE_BYTEORDER_MASK;
+}
+
+/**
+ * Set the max alloc value
+ *
+ * @param sp the storage buffer set the max allow for
+ * @param size maximum size to allocate, use 0 to remove limit
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_storage_set_max_alloc(krb5_storage *sp, size_t size)
+{
+ sp->max_alloc = size;
+}
+
+/* don't allocate unresonable amount of memory */
+static krb5_error_code
+size_too_large(krb5_storage *sp, size_t size)
+{
+ if (sp->max_alloc && sp->max_alloc < size)
+ return HEIM_ERR_TOO_BIG;
+ return 0;
+}
+
+static krb5_error_code
+size_too_large_num(krb5_storage *sp, size_t count, size_t size)
+{
+ if (sp->max_alloc == 0 || size == 0)
+ return 0;
+ size = sp->max_alloc / size;
+ if (size < count)
+ return HEIM_ERR_TOO_BIG;
+ return 0;
+}
+
+/**
+ * Seek to a new offset.
+ *
+ * @param sp the storage buffer to seek in.
+ * @param offset the offset to seek
+ * @param whence relateive searching, SEEK_CUR from the current
+ * position, SEEK_END from the end, SEEK_SET absolute from the start.
+ *
+ * @return The new current offset
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION off_t KRB5_LIB_CALL
+krb5_storage_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ return (*sp->seek)(sp, offset, whence);
+}
+
+/**
+ * Truncate the storage buffer in sp to offset.
+ *
+ * @param sp the storage buffer to truncate.
+ * @param offset the offset to truncate to.
+ *
+ * @return An Kerberos 5 error code.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_storage_truncate(krb5_storage *sp, off_t offset)
+{
+ return (*sp->trunc)(sp, offset);
+}
+
+/**
+ * Sync the storage buffer to its backing store. If there is no
+ * backing store this function will return success.
+ *
+ * @param sp the storage buffer to sync
+ *
+ * @return A Kerberos 5 error code
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_storage_fsync(krb5_storage *sp)
+{
+ if (sp->fsync != NULL)
+ return sp->fsync(sp);
+ return 0;
+}
+
+/**
+ * Read to the storage buffer.
+ *
+ * @param sp the storage buffer to read from
+ * @param buf the buffer to store the data in
+ * @param len the length to read
+ *
+ * @return The length of data read (can be shorter then len), or negative on error.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+krb5_storage_read(krb5_storage *sp, void *buf, size_t len)
+{
+ return sp->fetch(sp, buf, len);
+}
+
+/**
+ * Write to the storage buffer.
+ *
+ * @param sp the storage buffer to write to
+ * @param buf the buffer to write to the storage buffer
+ * @param len the length to write
+ *
+ * @return The length of data written (can be shorter then len), or negative on error.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+krb5_storage_write(krb5_storage *sp, const void *buf, size_t len)
+{
+ return sp->store(sp, buf, len);
+}
+
+/**
+ * Set the return code that will be used when end of storage is reached.
+ *
+ * @param sp the storage
+ * @param code the error code to return on end of storage
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_storage_set_eof_code(krb5_storage *sp, int code)
+{
+ sp->eof_code = code;
+}
+
+/**
+ * Get the return code that will be used when end of storage is reached.
+ *
+ * @param sp the storage
+ *
+ * @return storage error code
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_storage_get_eof_code(krb5_storage *sp)
+{
+ return sp->eof_code;
+}
+
+/**
+ * Free a krb5 storage.
+ *
+ * @param sp the storage to free.
+ *
+ * @return An Kerberos 5 error code.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_storage_free(krb5_storage *sp)
+{
+ if (sp == NULL)
+ return 0;
+ if(sp->free)
+ (*sp->free)(sp);
+ free(sp->data);
+ free(sp);
+ return 0;
+}
+
+/**
+ * Copy the content of storage to a krb5_data.
+ *
+ * @param sp the storage to copy to a data
+ * @param data the copied data, free with krb5_data_free()
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_storage_to_data(krb5_storage *sp, krb5_data *data)
+{
+ off_t pos, size;
+ krb5_error_code ret;
+
+ pos = sp->seek(sp, 0, SEEK_CUR);
+ if (pos < 0)
+ return HEIM_ERR_NOT_SEEKABLE;
+ size = sp->seek(sp, 0, SEEK_END);
+ ret = size_too_large(sp, size);
+ if (ret)
+ return ret;
+ ret = krb5_data_alloc(data, size);
+ if (ret) {
+ sp->seek(sp, pos, SEEK_SET);
+ return ret;
+ }
+ if (size) {
+ ssize_t bytes;
+
+ sp->seek(sp, 0, SEEK_SET);
+ bytes = sp->fetch(sp, data->data, data->length);
+ sp->seek(sp, pos, SEEK_SET);
+
+ /* sp->fetch() really shouldn't fail */
+ if (bytes < 0)
+ return sp->eof_code;
+
+ /* Maybe the underlying file (or whatever) got truncated? */
+ data->length = bytes;
+ }
+ return 0;
+}
+
+static size_t
+pack_int(uint8_t *p, uint64_t val)
+{
+ size_t l = 0;
+
+ if (val < 128) {
+ *p = val;
+ } else {
+ while (val > 0) {
+ *p-- = val % 256;
+ val /= 256;
+ l++;
+ }
+ *p = 0x80 | l;
+ }
+ return l + 1;
+}
+
+static size_t
+unpack_int_length(uint8_t *v)
+{
+ size_t size;
+
+ if (*v < 128)
+ size = 0;
+ else
+ size = *v & 0x7f;
+
+ return size + 1;
+}
+
+static int
+unpack_int(uint8_t *p, size_t len, uint64_t *val, size_t *size)
+{
+ size_t v;
+
+ if (len == 0)
+ return EINVAL;
+ --len;
+ v = *p++;
+ if (v < 128) {
+ *val = v;
+ *size = 1;
+ } else {
+ int e;
+ size_t l;
+ uint64_t tmp;
+
+ if (v == 0x80) {
+ *size = 1;
+ return EINVAL;
+ }
+ v &= 0x7F;
+ if (len < v)
+ return ERANGE;
+ e = der_get_unsigned64(p, v, &tmp, &l);
+ if (e)
+ return ERANGE;
+ *val = tmp;
+ *size = l + 1;
+ }
+ return 0;
+}
+
+static krb5_error_code
+krb5_store_int(krb5_storage *sp,
+ int64_t value,
+ size_t len)
+{
+ int ret;
+ uint8_t v[9], *p = v;
+
+ if (len > sizeof(value))
+ return EINVAL;
+
+ if (BYTEORDER_IS_PACKED(sp)) {
+ uint64_t mask = ~0ULL >> (64 - len * 8);
+ value &= mask;
+ p += sizeof(v) - 1;
+ len = pack_int(p, value);
+ p = v + sizeof(v) - len;
+ } else
+ _krb5_put_int(v, value, len);
+ ret = sp->store(sp, p, len);
+ if (ret < 0)
+ return errno;
+ if ((size_t)ret != len)
+ return sp->eof_code;
+ return 0;
+}
+
+/**
+ * Store a int32 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_int32(krb5_storage *sp,
+ int32_t value)
+{
+ if(BYTEORDER_IS_HOST(sp))
+ value = htonl(value);
+ else if(BYTEORDER_IS_LE(sp))
+ value = bswap32(value);
+ return krb5_store_int(sp, value, 4);
+}
+
+/**
+ * Store a int64 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_int64(krb5_storage *sp,
+ int64_t value)
+{
+ if (BYTEORDER_IS_HOST(sp))
+#ifdef WORDS_BIGENDIAN
+ ;
+#else
+ value = bswap64(value); /* There's no ntohll() */
+#endif
+ else if (BYTEORDER_IS_LE(sp))
+ value = bswap64(value);
+ return krb5_store_int(sp, value, 8);
+}
+
+/**
+ * Store a uint32 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_uint32(krb5_storage *sp,
+ uint32_t value)
+{
+ return krb5_store_int32(sp, (int32_t)value);
+}
+
+/**
+ * Store a uint64 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_uint64(krb5_storage *sp,
+ uint64_t value)
+{
+ return krb5_store_int64(sp, (int64_t)value);
+}
+
+static krb5_error_code
+krb5_ret_int(krb5_storage *sp,
+ int64_t *value,
+ size_t len)
+{
+ int ret;
+ unsigned char v[9];
+ uint64_t w = 0;
+ *value = 0; /* quiets warnings */
+ if (BYTEORDER_IS_PACKED(sp)) {
+ ret = sp->fetch(sp, v, 1);
+ if (ret < 0)
+ return errno;
+ if (ret != 1)
+ return sp->eof_code;
+
+ len = unpack_int_length(v);
+ if (len < 1)
+ return ERANGE;
+ else if (len > 1) {
+ ret = sp->fetch(sp, v + 1, len - 1);
+ if (ret < 0)
+ return errno;
+ if (ret != len - 1)
+ return sp->eof_code;
+ }
+ ret = unpack_int(v, len, &w, &len);
+ if (ret)
+ return ret;
+ *value = w;
+ return 0;
+ }
+ ret = sp->fetch(sp, v, len);
+ if (ret < 0)
+ return errno;
+ if ((size_t)ret != len)
+ return sp->eof_code;
+ _krb5_get_int64(v, &w, len);
+ *value = w;
+ return 0;
+}
+
+/**
+ * Read a int64 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_int64(krb5_storage *sp,
+ int64_t *value)
+{
+ krb5_error_code ret = krb5_ret_int(sp, value, 8);
+ if(ret)
+ return ret;
+ if(BYTEORDER_IS_HOST(sp))
+#ifdef WORDS_BIGENDIAN
+ ;
+#else
+ *value = bswap64(*value); /* There's no ntohll() */
+#endif
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap64(*value);
+ return 0;
+}
+
+/**
+ * Read a uint64 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_uint64(krb5_storage *sp,
+ uint64_t *value)
+{
+ krb5_error_code ret;
+ int64_t v;
+
+ ret = krb5_ret_int64(sp, &v);
+ if (ret == 0)
+ *value = (uint64_t)v;
+
+ return ret;
+}
+
+/**
+ * Read a int32 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_int32(krb5_storage *sp,
+ int32_t *value)
+{
+ int64_t v;
+
+ krb5_error_code ret = krb5_ret_int(sp, &v, 4);
+ if (ret)
+ return ret;
+ *value = v;
+ if (BYTEORDER_IS_HOST(sp))
+ *value = htonl(*value);
+ else if (BYTEORDER_IS_LE(sp))
+ *value = bswap32(*value);
+ return 0;
+}
+
+/**
+ * Read a uint32 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_uint32(krb5_storage *sp, uint32_t *value)
+{
+ krb5_error_code ret;
+ int32_t v;
+
+ ret = krb5_ret_int32(sp, &v);
+ if (ret == 0)
+ *value = (uint32_t)v;
+
+ return ret;
+}
+
+/**
+ * Store a int16 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_int16(krb5_storage *sp,
+ int16_t value)
+{
+ if(BYTEORDER_IS_HOST(sp))
+ value = htons(value);
+ else if(BYTEORDER_IS_LE(sp))
+ value = bswap16(value);
+ return krb5_store_int(sp, value, 2);
+}
+
+/**
+ * Store a uint16 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_uint16(krb5_storage *sp,
+ uint16_t value)
+{
+ return krb5_store_int16(sp, (int16_t)value);
+}
+
+/**
+ * Read a int16 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_int16(krb5_storage *sp,
+ int16_t *value)
+{
+ int64_t v;
+ int ret;
+ ret = krb5_ret_int(sp, &v, 2);
+ if(ret)
+ return ret;
+ *value = v;
+ if(BYTEORDER_IS_HOST(sp))
+ *value = htons(*value);
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap16(*value);
+ return 0;
+}
+
+/**
+ * Read a int16 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_uint16(krb5_storage *sp,
+ uint16_t *value)
+{
+ krb5_error_code ret;
+ int16_t v;
+
+ ret = krb5_ret_int16(sp, &v);
+ if (ret == 0)
+ *value = (uint16_t)v;
+
+ return ret;
+}
+
+/**
+ * Store a int8 to storage.
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_int8(krb5_storage *sp,
+ int8_t value)
+{
+ int ret;
+
+ ret = sp->store(sp, &value, sizeof(value));
+ if (ret != sizeof(value))
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+/**
+ * Store a uint8 to storage.
+ *
+ * @param sp the storage to write to
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_uint8(krb5_storage *sp,
+ uint8_t value)
+{
+ return krb5_store_int8(sp, (int8_t)value);
+}
+
+/**
+ * Read a int8 from storage
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_int8(krb5_storage *sp,
+ int8_t *value)
+{
+ ssize_t ret;
+
+ ret = sp->fetch(sp, value, sizeof(*value));
+ if (ret < 0 || (size_t)ret != sizeof(*value))
+ return (ret<0)?errno:sp->eof_code;
+ return 0;
+}
+
+/**
+ * Read a uint8 from storage
+ *
+ * @param sp the storage to write to
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_uint8(krb5_storage *sp,
+ uint8_t *value)
+{
+ krb5_error_code ret;
+ int8_t v;
+
+ ret = krb5_ret_int8(sp, &v);
+ if (ret == 0)
+ *value = (uint8_t)v;
+
+ return ret;
+}
+
+/**
+ * Store a data to the storage. The data is stored with an int32 as
+ * lenght plus the data (not padded).
+ *
+ * @param sp the storage buffer to write to
+ * @param data the buffer to store.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_data(krb5_storage *sp,
+ krb5_data data)
+{
+ int ret;
+ ret = krb5_store_int32(sp, data.length);
+ if(ret < 0)
+ return ret;
+ ret = sp->store(sp, data.data, data.length);
+ if(ret < 0)
+ return errno;
+ if((size_t)ret != data.length)
+ return sp->eof_code;
+ return 0;
+}
+
+/**
+ * Store a data blob to the storage. The data is stored with an int32 as
+ * length plus the data (not padded). This function only differs from
+ * krb5_store_data() insofar as it takes a void * and a length as parameters.
+ *
+ * @param sp the storage buffer to write to
+ * @param s the string to store.
+ * @param len length of the string to be stored.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_datalen(krb5_storage *sp, const void *d, size_t len)
+{
+ krb5_data data;
+ data.length = len;
+ data.data = (void *)d;
+ return krb5_store_data(sp, data);
+}
+
+/**
+ * Store a data blob to the storage. The data is stored without a length.
+ *
+ * @param sp the storage buffer to write to
+ * @param s the string to store.
+ * @param len length of the string to be stored.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_bytes(krb5_storage *sp, const void *d, size_t len)
+{
+ ssize_t ssize;
+
+ ssize = krb5_storage_write(sp, d, len);
+ if (ssize != len)
+ return ENOMEM;
+
+ return 0;
+}
+
+/**
+ * Parse a data from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param data the parsed data
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_data(krb5_storage *sp,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ int32_t size;
+
+ ret = krb5_ret_int32(sp, &size);
+ if(ret)
+ return ret;
+ ret = size_too_large(sp, size);
+ if (ret)
+ return ret;
+ ret = krb5_data_alloc (data, size);
+ if (ret)
+ return ret;
+ if (size) {
+ ssize_t bytes;
+
+ bytes = sp->fetch(sp, data->data, size);
+ if (bytes < 0 || bytes != size) {
+ krb5_data_free(data);
+ return (bytes < 0)? errno : sp->eof_code;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Store a string to the buffer. The data is formated as an len:uint32
+ * plus the string itself (not padded).
+ *
+ * @param sp the storage buffer to write to
+ * @param s the string to store.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_string(krb5_storage *sp, const char *s)
+{
+ krb5_data data;
+
+ if (s == NULL)
+ return EINVAL;
+
+ data.length = strlen(s);
+ data.data = rk_UNCONST(s);
+ return krb5_store_data(sp, data);
+}
+
+/**
+ * Parse a string from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param string the parsed string
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_string(krb5_storage *sp,
+ char **string)
+{
+ int ret;
+ krb5_data data;
+
+ *string = NULL;
+ ret = krb5_ret_data(sp, &data);
+ if(ret)
+ return ret;
+ *string = realloc(data.data, data.length + 1);
+ if(*string == NULL){
+ free(data.data);
+ return ENOMEM;
+ }
+ (*string)[data.length] = 0;
+ return 0;
+}
+
+/**
+ * Store a zero terminated string to the buffer. The data is stored
+ * one character at a time until a NUL is stored.
+ *
+ * @param sp the storage buffer to write to
+ * @param s the string to store.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_stringz(krb5_storage *sp, const char *s)
+{
+ size_t len;
+ ssize_t ret;
+
+ if (s == NULL)
+ return EINVAL;
+
+ len = strlen(s) + 1;
+ ret = sp->store(sp, s, len);
+ if(ret < 0)
+ return ret;
+ if((size_t)ret != len)
+ return sp->eof_code;
+ return 0;
+}
+
+/**
+ * Parse zero terminated string from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param string the parsed string
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_stringz(krb5_storage *sp,
+ char **string)
+{
+ char c;
+ char *s = NULL;
+ size_t len = 0;
+ ssize_t ret;
+
+ while((ret = sp->fetch(sp, &c, 1)) == 1){
+ krb5_error_code eret;
+ char *tmp;
+
+ len++;
+ eret = size_too_large(sp, len);
+ if (eret) {
+ free(s);
+ return eret;
+ }
+ tmp = realloc (s, len);
+ if (tmp == NULL) {
+ free (s);
+ return ENOMEM;
+ }
+ s = tmp;
+ s[len - 1] = c;
+ if(c == 0)
+ break;
+ }
+ if(ret != 1){
+ free(s);
+ if(ret == 0)
+ return sp->eof_code;
+ return ret;
+ }
+ *string = s;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_stringnl(krb5_storage *sp, const char *s)
+{
+ size_t len;
+ ssize_t ret;
+
+ if (s == NULL)
+ return EINVAL;
+
+ len = strlen(s);
+ ret = sp->store(sp, s, len);
+ if(ret < 0)
+ return ret;
+ if((size_t)ret != len)
+ return sp->eof_code;
+ ret = sp->store(sp, "\n", 1);
+ if(ret != 1) {
+ if(ret < 0)
+ return ret;
+ else
+ return sp->eof_code;
+ }
+
+ return 0;
+
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_stringnl(krb5_storage *sp,
+ char **string)
+{
+ int expect_nl = 0;
+ char c;
+ char *s = NULL;
+ size_t len = 0;
+ ssize_t ret;
+
+ while((ret = sp->fetch(sp, &c, 1)) == 1){
+ krb5_error_code eret;
+ char *tmp;
+
+ if (c == '\r') {
+ expect_nl = 1;
+ continue;
+ }
+ if (expect_nl && c != '\n') {
+ free(s);
+ return KRB5_BADMSGTYPE;
+ }
+
+ len++;
+ eret = size_too_large(sp, len);
+ if (eret) {
+ free(s);
+ return eret;
+ }
+ tmp = realloc (s, len);
+ if (tmp == NULL) {
+ free (s);
+ return ENOMEM;
+ }
+ s = tmp;
+ if(c == '\n') {
+ s[len - 1] = '\0';
+ break;
+ }
+ s[len - 1] = c;
+ }
+ if(ret != 1){
+ free(s);
+ if(ret == 0)
+ return sp->eof_code;
+ return ret;
+ }
+ *string = s;
+ return 0;
+}
+
+/**
+ * Write a principal block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the principal block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_principal(krb5_storage *sp,
+ krb5_const_principal p)
+{
+ size_t i;
+ int 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;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ret = krb5_store_int32(sp, p->name.name_string.len + 1);
+ else
+ ret = krb5_store_int32(sp, p->name.name_string.len);
+
+ if(ret) return ret;
+ ret = krb5_store_string(sp, p->realm);
+ if(ret) return ret;
+ for(i = 0; i < p->name.name_string.len; i++){
+ ret = krb5_store_string(sp, p->name.name_string.val[i]);
+ if(ret) return ret;
+ }
+ return 0;
+}
+
+/**
+ * Parse principal from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param princ the parsed principal
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_principal(krb5_storage *sp,
+ krb5_principal *princ)
+{
+ int i;
+ int ret;
+ krb5_principal p;
+ int32_t type;
+ int32_t ncomp;
+
+ p = calloc(1, sizeof(*p));
+ if(p == NULL)
+ return ENOMEM;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_NO_NAME_TYPE))
+ type = KRB5_NT_UNKNOWN;
+ else if((ret = krb5_ret_int32(sp, &type))){
+ free(p);
+ return ret;
+ }
+ if((ret = krb5_ret_int32(sp, &ncomp))){
+ free(p);
+ return ret;
+ }
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_PRINCIPAL_WRONG_NUM_COMPONENTS))
+ ncomp--;
+ if (ncomp < 0) {
+ free(p);
+ return EINVAL;
+ }
+ ret = size_too_large_num(sp, ncomp, sizeof(p->name.name_string.val[0]));
+ if (ret) {
+ free(p);
+ return ret;
+ }
+ p->name.name_type = type;
+ p->name.name_string.len = ncomp;
+ ret = krb5_ret_string(sp, &p->realm);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ p->name.name_string.val = calloc(ncomp, sizeof(p->name.name_string.val[0]));
+ if(p->name.name_string.val == NULL && ncomp != 0){
+ free(p->realm);
+ free(p);
+ return ENOMEM;
+ }
+ for(i = 0; i < ncomp; i++){
+ ret = krb5_ret_string(sp, &p->name.name_string.val[i]);
+ if(ret) {
+ while (i >= 0)
+ free(p->name.name_string.val[i--]);
+ free(p->realm);
+ free(p);
+ return ret;
+ }
+ }
+ *princ = p;
+ return 0;
+}
+
+/**
+ * Store a keyblock to the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the keyblock to write
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_keyblock(krb5_storage *sp, krb5_keyblock p)
+{
+ int ret;
+ ret = krb5_store_int16(sp, p.keytype);
+ if(ret) return ret;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE)){
+ /* this should really be enctype, but it is the same as
+ keytype nowadays */
+ ret = krb5_store_int16(sp, p.keytype);
+ if(ret) return ret;
+ }
+
+ ret = krb5_store_data(sp, p.keyvalue);
+ return ret;
+}
+
+/**
+ * Read a keyblock from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the keyblock read from storage, free using krb5_free_keyblock()
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_keyblock(krb5_storage *sp, krb5_keyblock *p)
+{
+ int ret;
+ int16_t tmp;
+
+ ret = krb5_ret_int16(sp, &tmp);
+ if(ret) return ret;
+ p->keytype = tmp;
+
+ if(krb5_storage_is_flags(sp, KRB5_STORAGE_KEYBLOCK_KEYTYPE_TWICE)){
+ ret = krb5_ret_int16(sp, &tmp);
+ if(ret) return ret;
+ }
+
+ ret = krb5_ret_data(sp, &p->keyvalue);
+ return ret;
+}
+
+/**
+ * Write a times block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param times the times block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_times(krb5_storage *sp, krb5_times times)
+{
+ int ret;
+ ret = krb5_store_int32(sp, times.authtime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.starttime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.endtime);
+ if(ret) return ret;
+ ret = krb5_store_int32(sp, times.renew_till);
+ return ret;
+}
+
+/**
+ * Read a times block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param times the times block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_times(krb5_storage *sp, krb5_times *times)
+{
+ int ret;
+ int32_t tmp;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret) return ret;
+ times->authtime = tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret) return ret;
+ times->starttime = tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret) return ret;
+ times->endtime = tmp;
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret) return ret;
+ times->renew_till = tmp;
+ return ret;
+}
+
+/**
+ * Write a address block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the address block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_address(krb5_storage *sp, krb5_address p)
+{
+ int ret;
+ ret = krb5_store_int16(sp, p.addr_type);
+ if(ret) return ret;
+ ret = krb5_store_data(sp, p.address);
+ return ret;
+}
+
+/**
+ * Read a address block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param adr the address block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_address(krb5_storage *sp, krb5_address *adr)
+{
+ int16_t t;
+ int ret;
+ ret = krb5_ret_int16(sp, &t);
+ if(ret) return ret;
+ adr->addr_type = t;
+ ret = krb5_ret_data(sp, &adr->address);
+ return ret;
+}
+
+/**
+ * Write a addresses block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the addresses block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_addrs(krb5_storage *sp, krb5_addresses p)
+{
+ size_t i;
+ int ret;
+ ret = krb5_store_int32(sp, p.len);
+ if(ret) return ret;
+ for(i = 0; i<p.len; i++){
+ ret = krb5_store_address(sp, p.val[i]);
+ if(ret) break;
+ }
+ return ret;
+}
+
+/**
+ * Read a addresses block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param adr the addresses block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_addrs(krb5_storage *sp, krb5_addresses *adr)
+{
+ size_t i;
+ int ret;
+ int32_t tmp;
+
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret) return ret;
+ ret = size_too_large_num(sp, tmp, sizeof(adr->val[0]));
+ if (ret) return ret;
+ adr->len = tmp;
+ ALLOC(adr->val, adr->len);
+ if (adr->val == NULL && adr->len != 0)
+ return ENOMEM;
+ for(i = 0; i < adr->len; i++){
+ ret = krb5_ret_address(sp, &adr->val[i]);
+ if(ret) break;
+ }
+ return ret;
+}
+
+/**
+ * Write a auth data block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param auth the auth data block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_authdata(krb5_storage *sp, krb5_authdata auth)
+{
+ krb5_error_code ret;
+ size_t i;
+ ret = krb5_store_int32(sp, auth.len);
+ if(ret) return ret;
+ for(i = 0; i < auth.len; i++){
+ ret = krb5_store_int16(sp, auth.val[i].ad_type);
+ if(ret) break;
+ ret = krb5_store_data(sp, auth.val[i].ad_data);
+ if(ret) break;
+ }
+ return 0;
+}
+
+/**
+ * Read a auth data from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param auth the auth data block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_authdata(krb5_storage *sp, krb5_authdata *auth)
+{
+ krb5_error_code ret;
+ int32_t tmp;
+ int16_t tmp2;
+ int i;
+ ret = krb5_ret_int32(sp, &tmp);
+ if(ret) return ret;
+ ret = size_too_large_num(sp, tmp, sizeof(auth->val[0]));
+ if (ret) return ret;
+ ALLOC_SEQ(auth, tmp);
+ if (auth->val == NULL && tmp != 0)
+ return ENOMEM;
+ for(i = 0; i < tmp; i++){
+ ret = krb5_ret_int16(sp, &tmp2);
+ if(ret) break;
+ auth->val[i].ad_type = tmp2;
+ ret = krb5_ret_data(sp, &auth->val[i].ad_data);
+ if(ret) break;
+ }
+ return ret;
+}
+
+static int32_t
+bitswap32(int32_t b)
+{
+ int32_t r = 0;
+ int i;
+ for (i = 0; i < 32; i++) {
+ r = r << 1 | (b & 1);
+ b = b >> 1;
+ }
+ return r;
+}
+
+/**
+ * Write a credentials block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the creds block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_creds(krb5_storage *sp, krb5_creds *creds)
+{
+ int ret;
+
+ ret = krb5_store_principal(sp, creds->client);
+ if(ret)
+ return ret;
+ ret = krb5_store_principal(sp, creds->server);
+ if(ret)
+ return ret;
+ ret = krb5_store_keyblock(sp, creds->session);
+ if(ret)
+ return ret;
+ ret = krb5_store_times(sp, creds->times);
+ if(ret)
+ return ret;
+ ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
+ if(ret)
+ return ret;
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ if(ret)
+ return ret;
+ ret = krb5_store_addrs(sp, creds->addresses);
+ if(ret)
+ return ret;
+ ret = krb5_store_authdata(sp, creds->authdata);
+ if(ret)
+ return ret;
+ ret = krb5_store_data(sp, creds->ticket);
+ if(ret)
+ return ret;
+ ret = krb5_store_data(sp, creds->second_ticket);
+ return ret;
+}
+
+/**
+ * Read a credentials block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the credentials block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_creds(krb5_storage *sp, krb5_creds *creds)
+{
+ krb5_error_code ret;
+ int8_t dummy8;
+ int32_t dummy32;
+
+ memset(creds, 0, sizeof(*creds));
+ ret = krb5_ret_principal (sp, &creds->client);
+ if(ret) goto cleanup;
+ ret = krb5_ret_principal (sp, &creds->server);
+ if(ret) goto cleanup;
+ ret = krb5_ret_keyblock (sp, &creds->session);
+ if(ret) goto cleanup;
+ ret = krb5_ret_times (sp, &creds->times);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int8 (sp, &dummy8);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int32 (sp, &dummy32);
+ if(ret) goto cleanup;
+ creds->flags.b = int2TicketFlags(bitswap32(dummy32));
+ ret = krb5_ret_addrs (sp, &creds->addresses);
+ if(ret) goto cleanup;
+ ret = krb5_ret_authdata (sp, &creds->authdata);
+ if(ret) goto cleanup;
+ ret = krb5_ret_data (sp, &creds->ticket);
+ if(ret) goto cleanup;
+ ret = krb5_ret_data (sp, &creds->second_ticket);
+cleanup:
+ if(ret) {
+#if 0
+ krb5_free_cred_contents(context, creds); /* XXX */
+#endif
+ }
+ return ret;
+}
+
+#define SC_CLIENT_PRINCIPAL 0x0001
+#define SC_SERVER_PRINCIPAL 0x0002
+#define SC_SESSION_KEY 0x0004
+#define SC_TICKET 0x0008
+#define SC_SECOND_TICKET 0x0010
+#define SC_AUTHDATA 0x0020
+#define SC_ADDRESSES 0x0040
+
+/**
+ * Write a tagged credentials block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the creds block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_creds_tag(krb5_storage *sp, krb5_creds *creds)
+{
+ int ret;
+ int32_t header = 0;
+
+ if (creds->client)
+ header |= SC_CLIENT_PRINCIPAL;
+ if (creds->server)
+ header |= SC_SERVER_PRINCIPAL;
+ if (creds->session.keytype != ETYPE_NULL)
+ header |= SC_SESSION_KEY;
+ if (creds->ticket.data)
+ header |= SC_TICKET;
+ if (creds->second_ticket.length)
+ header |= SC_SECOND_TICKET;
+ if (creds->authdata.len)
+ header |= SC_AUTHDATA;
+ if (creds->addresses.len)
+ header |= SC_ADDRESSES;
+
+ ret = krb5_store_int32(sp, header);
+ if (ret)
+ return ret;
+
+ if (creds->client) {
+ ret = krb5_store_principal(sp, creds->client);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->server) {
+ ret = krb5_store_principal(sp, creds->server);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->session.keytype != ETYPE_NULL) {
+ ret = krb5_store_keyblock(sp, creds->session);
+ if(ret)
+ return ret;
+ }
+
+ ret = krb5_store_times(sp, creds->times);
+ if(ret)
+ return ret;
+ ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
+ if(ret)
+ return ret;
+
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ if(ret)
+ return ret;
+
+ if (creds->addresses.len) {
+ ret = krb5_store_addrs(sp, creds->addresses);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->authdata.len) {
+ ret = krb5_store_authdata(sp, creds->authdata);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->ticket.data) {
+ ret = krb5_store_data(sp, creds->ticket);
+ if(ret)
+ return ret;
+ }
+
+ if (creds->second_ticket.data) {
+ ret = krb5_store_data(sp, creds->second_ticket);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * Read a tagged credentials block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the credentials block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_creds_tag(krb5_storage *sp,
+ krb5_creds *creds)
+{
+ krb5_error_code ret;
+ int8_t dummy8;
+ int32_t dummy32, header;
+
+ memset(creds, 0, sizeof(*creds));
+
+ ret = krb5_ret_int32 (sp, &header);
+ if (ret) goto cleanup;
+
+ if (header & SC_CLIENT_PRINCIPAL) {
+ ret = krb5_ret_principal (sp, &creds->client);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SERVER_PRINCIPAL) {
+ ret = krb5_ret_principal (sp, &creds->server);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SESSION_KEY) {
+ ret = krb5_ret_keyblock (sp, &creds->session);
+ if(ret) goto cleanup;
+ }
+ ret = krb5_ret_times (sp, &creds->times);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int8 (sp, &dummy8);
+ if(ret) goto cleanup;
+ ret = krb5_ret_int32 (sp, &dummy32);
+ if(ret) goto cleanup;
+ creds->flags.b = int2TicketFlags(bitswap32(dummy32));
+ if (header & SC_ADDRESSES) {
+ ret = krb5_ret_addrs (sp, &creds->addresses);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_AUTHDATA) {
+ ret = krb5_ret_authdata (sp, &creds->authdata);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_TICKET) {
+ ret = krb5_ret_data (sp, &creds->ticket);
+ if(ret) goto cleanup;
+ }
+ if (header & SC_SECOND_TICKET) {
+ ret = krb5_ret_data (sp, &creds->second_ticket);
+ if(ret) goto cleanup;
+ }
+
+cleanup:
+ if(ret) {
+#if 0
+ krb5_free_cred_contents(context, creds); /* XXX */
+#endif
+ }
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_ret_data_at_offset(krb5_storage *sp,
+ size_t offset,
+ size_t length,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ off_t cur, size;
+
+ krb5_data_zero(data);
+
+ cur = sp->seek(sp, 0, SEEK_CUR);
+ if (cur < 0)
+ return HEIM_ERR_NOT_SEEKABLE;
+
+ size = sp->seek(sp, 0, SEEK_END);
+ if (offset + length > size) {
+ ret = ERANGE;
+ goto cleanup;
+ }
+
+ ret = krb5_data_alloc(data, length);
+ if (ret)
+ goto cleanup;
+
+ if (length) {
+ sp->seek(sp, offset, SEEK_SET);
+
+ size = sp->fetch(sp, data->data, length);
+ if (size < 0 || (size_t)size != length)
+ return sp->eof_code;
+ }
+
+cleanup:
+ sp->seek(sp, cur, SEEK_SET);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_ret_utf8_from_ucs2le_at_offset(krb5_storage *sp,
+ off_t offset,
+ size_t length,
+ char **utf8)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t ucs2len = length / 2;
+ uint16_t *ucs2 = NULL;
+ size_t u8len;
+ unsigned int flags = WIND_RW_LE;
+
+ *utf8 = NULL;
+
+ krb5_data_zero(&data);
+
+ ret = _krb5_ret_data_at_offset(sp, offset, length, &data);
+ if (ret)
+ goto out;
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2len);
+ if (ucs2 == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = wind_ucs2read(data.data, data.length, &flags, ucs2, &ucs2len);
+ if (ret)
+ goto out;
+
+ ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len);
+ if (ret)
+ goto out;
+
+ u8len += 1; /* Add space for NUL */
+
+ *utf8 = malloc(u8len);
+ if (*utf8 == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = wind_ucs2utf8(ucs2, ucs2len, *utf8, &u8len);
+ if (ret)
+ goto out;
+
+out:
+ if (ret && *utf8) {
+ free(*utf8);
+ *utf8 = NULL;
+ }
+ free(ucs2);
+ krb5_data_free(&data);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_store_data_at_offset(krb5_storage *sp,
+ size_t offset,
+ const krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_ssize_t nbytes;
+ off_t pos;
+
+ if (offset == (size_t)-1) {
+ if (data == NULL || data->data == NULL) {
+ offset = 0;
+ } else {
+ pos = sp->seek(sp, 0, SEEK_CUR);
+ offset = sp->seek(sp, 0, SEEK_END);
+ sp->seek(sp, pos, SEEK_SET);
+
+ if (offset == (size_t)-1)
+ return HEIM_ERR_NOT_SEEKABLE;
+ }
+ }
+
+ if (offset > 0xFFFF)
+ return ERANGE;
+ else if ((offset != 0) != (data && data->data))
+ return EINVAL;
+ else if (data && data->length > 0xFFFF)
+ return ERANGE;
+
+ ret = krb5_store_uint16(sp, data ? (uint16_t)data->length : 0);
+ if (ret == 0)
+ ret = krb5_store_uint16(sp, (uint16_t)offset);
+ if (ret == 0 && offset) {
+ pos = sp->seek(sp, 0, SEEK_CUR);
+ sp->seek(sp, offset, SEEK_SET);
+ nbytes = krb5_storage_write(sp, data->data, data->length);
+ if ((size_t)nbytes != data->length)
+ ret = sp->eof_code;
+ sp->seek(sp, pos, SEEK_SET);
+ }
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_store_utf8_as_ucs2le_at_offset(krb5_storage *sp,
+ off_t offset,
+ const char *utf8)
+{
+ krb5_error_code ret;
+ size_t ucs2_len, ucs2le_size;
+ uint16_t *ucs2, *ucs2le;
+ unsigned int flags;
+
+ if (utf8) {
+ ret = wind_utf8ucs2_length(utf8, &ucs2_len);
+ if (ret)
+ return ret;
+
+ ucs2 = malloc(sizeof(ucs2[0]) * ucs2_len);
+ if (ucs2 == NULL)
+ return ENOMEM;
+
+ ret = wind_utf8ucs2(utf8, ucs2, &ucs2_len);
+ if (ret) {
+ free(ucs2);
+ return ret;
+ }
+
+ ucs2le_size = (ucs2_len + 1) * 2;
+ ucs2le = malloc(ucs2le_size);
+ if (ucs2le == NULL) {
+ free(ucs2);
+ return ENOMEM;
+ }
+
+ flags = WIND_RW_LE;
+ ret = wind_ucs2write(ucs2, ucs2_len, &flags, ucs2le, &ucs2le_size);
+ free(ucs2);
+ if (ret) {
+ free(ucs2le);
+ return ret;
+ }
+
+ ucs2le_size = ucs2_len * 2;
+ } else {
+ ucs2le = NULL;
+ ucs2le_size = 0;
+ offset = 0;
+ }
+
+ {
+ krb5_data data;
+
+ data.data = ucs2le;
+ data.length = ucs2le_size;
+
+ ret = _krb5_store_data_at_offset(sp, offset, &data);
+ }
+
+ free(ucs2le);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/store_emem.c b/third_party/heimdal/lib/krb5/store_emem.c
new file mode 100644
index 0000000..daef4d7
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store_emem.c
@@ -0,0 +1,222 @@
+/*
+ * 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"
+#include "store-int.h"
+#include <assert.h>
+
+typedef struct emem_storage{
+ unsigned char *base;
+ size_t size;
+ size_t len;
+ unsigned char *ptr;
+}emem_storage;
+
+static ssize_t
+emem_fetch(krb5_storage *sp, void *data, size_t size)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+
+ assert(data != NULL && s->ptr != NULL);
+
+ if((size_t)(s->base + s->len - s->ptr) < size)
+ size = s->base + s->len - s->ptr;
+ memmove(data, s->ptr, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+emem_store(krb5_storage *sp, const void *data, size_t size)
+{
+ emem_storage *s;
+
+ if (size == 0) {
+ sp->seek(sp, 0, SEEK_CUR);
+ return 0;
+ }
+
+ s = (emem_storage*)sp->data;
+
+ assert(data != NULL);
+
+ if(size > (size_t)(s->base + s->size - s->ptr)){
+ void *base;
+ size_t sz, off;
+ off = s->ptr - s->base;
+ sz = off + size;
+ if (sz < 4096)
+ sz *= 2;
+ base = realloc(s->base, sz);
+ if(base == NULL)
+ return -1;
+ s->size = sz;
+ s->base = base;
+ s->ptr = (unsigned char*)base + off;
+ }
+ if (size)
+ memmove(s->ptr, data, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static off_t
+emem_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ switch(whence){
+ case SEEK_SET:
+ if((size_t)offset > s->size)
+ offset = s->size;
+ if(offset < 0)
+ offset = 0;
+ s->ptr = s->base + offset;
+ if((size_t)offset > s->len)
+ s->len = offset;
+ break;
+ case SEEK_CUR:
+ sp->seek(sp,s->ptr - s->base + offset, SEEK_SET);
+ break;
+ case SEEK_END:
+ sp->seek(sp, s->len + offset, SEEK_SET);
+ break;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return s->ptr - s->base;
+}
+
+static int
+emem_trunc(krb5_storage *sp, off_t offset)
+{
+ emem_storage *s = (emem_storage*)sp->data;
+ /*
+ * If offset is larget then current size, or current size is
+ * shrunk more then half of the current size, adjust buffer.
+ */
+ if (offset == 0) {
+ if (s->size > 1024) {
+ void *base;
+
+ base = realloc(s->base, 1024);
+ if (base) {
+ s->base = base;
+ s->size = 1024;
+ }
+ }
+ s->len = 0;
+ s->ptr = s->base;
+ } else if ((size_t)offset > s->size || (s->size / 2) > (size_t)offset) {
+ void *base;
+ size_t off;
+ off = s->ptr - s->base;
+ base = realloc(s->base, offset);
+ if(base == NULL)
+ return ENOMEM;
+ if ((size_t)offset > s->size)
+ memset((char *)base + s->size, 0, offset - s->size);
+ s->size = offset;
+ s->base = base;
+ s->ptr = (unsigned char *)base + off;
+ }
+ s->len = offset;
+ if ((s->ptr - s->base) > offset)
+ s->ptr = s->base + offset;
+ return 0;
+}
+
+
+static void
+emem_free(krb5_storage *sp)
+{
+ emem_storage *s = sp->data;
+
+ assert(s->base != NULL);
+
+ memset_s(s->base, s->len, 0, s->len);
+ free(s->base);
+}
+
+/**
+ * Create a elastic (allocating) memory storage backend. Memory is
+ * allocated on demand. Free returned krb5_storage with
+ * krb5_storage_free().
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_fd()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_emem(void)
+{
+ krb5_storage *sp;
+ emem_storage *s;
+
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL)
+ return NULL;
+
+ s = malloc(sizeof(*s));
+ if (s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->size = 1024;
+ s->base = calloc(1, s->size);
+ if (s->base == NULL) {
+ free(sp);
+ free(s);
+ return NULL;
+ }
+ s->len = 0;
+ s->ptr = s->base;
+ sp->fetch = emem_fetch;
+ sp->store = emem_store;
+ sp->seek = emem_seek;
+ sp->trunc = emem_trunc;
+ sp->fsync = NULL;
+ sp->free = emem_free;
+ sp->max_alloc = UINT32_MAX/64;
+ return sp;
+}
diff --git a/third_party/heimdal/lib/krb5/store_fd.c b/third_party/heimdal/lib/krb5/store_fd.c
new file mode 100644
index 0000000..9184e59
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store_fd.c
@@ -0,0 +1,200 @@
+/*
+ * 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"
+#include "store-int.h"
+
+typedef struct fd_storage {
+ int fd;
+} fd_storage;
+
+#define FD(S) (((fd_storage*)(S)->data)->fd)
+
+static ssize_t
+fd_fetch(krb5_storage * sp, void *data, size_t size)
+{
+ char *cbuf = (char *)data;
+ ssize_t count;
+ size_t rem = size;
+
+ /* similar pattern to net_read() to support pipes */
+ while (rem > 0) {
+ count = read (FD(sp), cbuf, rem);
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else if (rem == size)
+ return count;
+ else
+ return size - rem;
+ } else if (count == 0) {
+ return count;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ return size;
+}
+
+static ssize_t
+fd_store(krb5_storage * sp, const void *data, size_t size)
+{
+ const char *cbuf = (const char *)data;
+ ssize_t count;
+ size_t rem = size;
+
+ /* similar pattern to net_write() to support pipes */
+ while (rem > 0) {
+ count = write(FD(sp), cbuf, rem);
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return size - rem;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ return size;
+}
+
+static off_t
+fd_seek(krb5_storage * sp, off_t offset, int whence)
+{
+ return lseek(FD(sp), offset, whence);
+}
+
+static int
+fd_trunc(krb5_storage * sp, off_t offset)
+{
+ off_t tmpoff;
+
+ if (ftruncate(FD(sp), offset) == -1)
+ return errno;
+
+ tmpoff = lseek(FD(sp), 0, SEEK_CUR);
+ if (tmpoff == -1)
+ return errno;
+
+ if (tmpoff > offset) {
+ tmpoff = lseek(FD(sp), offset, SEEK_SET);
+ if (tmpoff == -1)
+ return errno;
+ }
+
+ return 0;
+}
+
+static int
+fd_sync(krb5_storage * sp)
+{
+ if (fsync(FD(sp)) == -1)
+ return errno;
+ return 0;
+}
+
+static void
+fd_free(krb5_storage * sp)
+{
+ int save_errno = errno;
+ if (close(FD(sp)) == 0)
+ errno = save_errno;
+}
+
+/**
+ *
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_emem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_fd(int fd_in)
+{
+ krb5_storage *sp;
+ int saved_errno;
+ int fd;
+
+#ifdef _MSC_VER
+ /*
+ * This function used to try to pass the input to
+ * _get_osfhandle() to test if the value is a HANDLE
+ * but this doesn't work because doing so throws an
+ * exception that will result in Watson being triggered
+ * to file a Windows Error Report.
+ */
+ fd = _dup(fd_in);
+#else
+ fd = dup(fd_in);
+#endif
+
+ if (fd < 0)
+ return NULL;
+
+ errno = ENOMEM;
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL) {
+ saved_errno = errno;
+ close(fd);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ errno = ENOMEM;
+ sp->data = malloc(sizeof(fd_storage));
+ if (sp->data == NULL) {
+ saved_errno = errno;
+ close(fd);
+ free(sp);
+ errno = saved_errno;
+ return NULL;
+ }
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ FD(sp) = fd;
+ sp->fetch = fd_fetch;
+ sp->store = fd_store;
+ sp->seek = fd_seek;
+ sp->trunc = fd_trunc;
+ sp->fsync = fd_sync;
+ sp->free = fd_free;
+ sp->max_alloc = UINT32_MAX/64;
+ return sp;
+}
diff --git a/third_party/heimdal/lib/krb5/store_mem.c b/third_party/heimdal/lib/krb5/store_mem.c
new file mode 100644
index 0000000..638c341
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store_mem.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1997 - 2000, 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"
+#include "store-int.h"
+
+typedef struct mem_storage{
+ unsigned char *base;
+ size_t size;
+ unsigned char *ptr;
+}mem_storage;
+
+static ssize_t
+mem_fetch(krb5_storage *sp, void *data, size_t size)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if(size > (size_t)(s->base + s->size - s->ptr))
+ size = s->base + s->size - s->ptr;
+ memmove(data, s->ptr, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+mem_store(krb5_storage *sp, const void *data, size_t size)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if(size > (size_t)(s->base + s->size - s->ptr))
+ size = s->base + s->size - s->ptr;
+ memmove(s->ptr, data, size);
+ sp->seek(sp, size, SEEK_CUR);
+ return size;
+}
+
+static ssize_t
+mem_no_store(krb5_storage *sp, const void *data, size_t size)
+{
+ return -1;
+}
+
+static off_t
+mem_seek(krb5_storage *sp, off_t offset, int whence)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ switch(whence){
+ case SEEK_SET:
+ if((size_t)offset > s->size)
+ offset = s->size;
+ if(offset < 0)
+ offset = 0;
+ s->ptr = s->base + offset;
+ break;
+ case SEEK_CUR:
+ return sp->seek(sp, s->ptr - s->base + offset, SEEK_SET);
+ case SEEK_END:
+ return sp->seek(sp, s->size + offset, SEEK_SET);
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+ return s->ptr - s->base;
+}
+
+static int
+mem_trunc(krb5_storage *sp, off_t offset)
+{
+ mem_storage *s = (mem_storage*)sp->data;
+ if((size_t)offset > s->size)
+ return ERANGE;
+ s->size = offset;
+ if ((s->ptr - s->base) > offset)
+ s->ptr = s->base + offset;
+ return 0;
+}
+
+static int
+mem_no_trunc(krb5_storage *sp, off_t offset)
+{
+ return EINVAL;
+}
+
+/**
+ * Create a fixed size memory storage block
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ * @sa krb5_storage_from_socket()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_mem(void *buf, size_t len)
+{
+ krb5_storage *sp = malloc(sizeof(krb5_storage));
+ mem_storage *s;
+ if(sp == NULL)
+ return NULL;
+ s = malloc(sizeof(*s));
+ if(s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->base = buf;
+ s->size = len;
+ s->ptr = buf;
+ sp->fetch = mem_fetch;
+ sp->store = mem_store;
+ sp->seek = mem_seek;
+ sp->trunc = mem_trunc;
+ sp->fsync = NULL;
+ sp->free = NULL;
+ sp->max_alloc = UINT32_MAX/64;
+ return sp;
+}
+
+/**
+ * Create a fixed size memory storage block
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_mem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_fd()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_data(krb5_data *data)
+{
+ return krb5_storage_from_mem(data->data, data->length);
+}
+
+/**
+ * Create a fixed size memory storage block that is read only
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_mem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_readonly_mem(const void *buf, size_t len)
+{
+ krb5_storage *sp = malloc(sizeof(krb5_storage));
+ mem_storage *s;
+ if(sp == NULL)
+ return NULL;
+ s = malloc(sizeof(*s));
+ if(s == NULL) {
+ free(sp);
+ return NULL;
+ }
+ sp->data = s;
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ s->base = rk_UNCONST(buf);
+ s->size = len;
+ s->ptr = rk_UNCONST(buf);
+ sp->fetch = mem_fetch;
+ sp->store = mem_no_store;
+ sp->seek = mem_seek;
+ sp->trunc = mem_no_trunc;
+ sp->fsync = NULL;
+ sp->free = NULL;
+ sp->max_alloc = UINT32_MAX/64;
+ return sp;
+}
diff --git a/third_party/heimdal/lib/krb5/store_sock.c b/third_party/heimdal/lib/krb5/store_sock.c
new file mode 100644
index 0000000..72d3e9d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store_sock.c
@@ -0,0 +1,163 @@
+/*
+ * 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"
+#include "store-int.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+typedef struct socket_storage {
+ krb5_socket_t sock;
+} socket_storage;
+
+#define SOCK(S) (((socket_storage*)(S)->data)->sock)
+
+static ssize_t
+socket_fetch(krb5_storage * sp, void *data, size_t size)
+{
+ return net_read(SOCK(sp), data, size);
+}
+
+static ssize_t
+socket_store(krb5_storage * sp, const void *data, size_t size)
+{
+ return net_write(SOCK(sp), data, size);
+}
+
+static off_t
+socket_seek(krb5_storage * sp, off_t offset, int whence)
+{
+ return lseek(SOCK(sp), offset, whence);
+}
+
+static int
+socket_trunc(krb5_storage * sp, off_t offset)
+{
+ if (ftruncate(SOCK(sp), offset) == -1)
+ return errno;
+ return 0;
+}
+
+static int
+socket_sync(krb5_storage * sp)
+{
+ if (fsync(SOCK(sp)) == -1)
+ return errno;
+ return 0;
+}
+
+static void
+socket_free(krb5_storage * sp)
+{
+ int save_errno = errno;
+ if (rk_IS_SOCKET_ERROR(rk_closesocket(SOCK(sp)))) {
+#ifdef WIN32
+ errno = rk_SOCK_ERRNO;
+#endif
+ } else {
+ errno = save_errno;
+ }
+}
+
+/**
+ *
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_emem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_socket(krb5_socket_t sock_in)
+{
+ krb5_storage *sp;
+ int saved_errno;
+ krb5_socket_t sock;
+
+#ifdef _WIN32
+ WSAPROTOCOL_INFO info;
+
+ sock = rk_INVALID_SOCKET;
+ if (WSADuplicateSocket(sock_in, GetCurrentProcessId(), &info) == 0)
+ {
+
+ sock = WSASocket( FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ &info, 0, 0);
+ }
+#else
+ sock = dup(sock_in);
+#endif
+
+ if (sock == rk_INVALID_SOCKET)
+ return NULL;
+
+ errno = ENOMEM;
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL) {
+ saved_errno = errno;
+ rk_closesocket(sock);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ errno = ENOMEM;
+ sp->data = malloc(sizeof(socket_storage));
+ if (sp->data == NULL) {
+ saved_errno = errno;
+ rk_closesocket(sock);
+ free(sp);
+ errno = saved_errno;
+ return NULL;
+ }
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ SOCK(sp) = sock;
+ sp->fetch = socket_fetch;
+ sp->store = socket_store;
+ sp->seek = socket_seek;
+ sp->trunc = socket_trunc;
+ sp->fsync = socket_sync;
+ sp->free = socket_free;
+ sp->max_alloc = UINT32_MAX/64;
+ return sp;
+}
diff --git a/third_party/heimdal/lib/krb5/store_stdio.c b/third_party/heimdal/lib/krb5/store_stdio.c
new file mode 100644
index 0000000..9244b9e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/store_stdio.c
@@ -0,0 +1,271 @@
+/*
+ * Copyright (c) 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"
+#include "store-int.h"
+
+#ifndef HAVE_FSEEKO
+#define fseeko fseek
+#define ftello ftell
+#endif
+
+typedef struct stdio_storage {
+ FILE *f;
+ off_t pos;
+} stdio_storage;
+
+#define F(S) (((stdio_storage*)(S)->data)->f)
+#define POS(S) (((stdio_storage*)(S)->data)->pos)
+
+static ssize_t
+stdio_fetch(krb5_storage * sp, void *data, size_t size)
+{
+ char *cbuf = (char *)data;
+ ssize_t count;
+ size_t rem = size;
+
+ /* similar pattern to net_read() to support pipes */
+ while (rem > 0) {
+ count = fread(cbuf, 1, rem, F(sp));
+ if (count < 0) {
+ POS(sp) = -1;
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ } else if (count == 0) {
+ if (POS(sp) >= 0)
+ POS(sp) += size - rem;
+ return size - rem;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ if (POS(sp) >= 0)
+ POS(sp) += size;
+ return size;
+}
+
+static ssize_t
+stdio_store(krb5_storage * sp, const void *data, size_t size)
+{
+ const char *cbuf = (const char *)data;
+ ssize_t count;
+ size_t rem = size;
+
+ /*
+ * It's possible we just went from reading to writing if the file was open
+ * for both. Per C99 (N869 final draft) section 7.18.5.3, point 6, when
+ * going from reading to writing [a file opened for both] one must seek.
+ */
+ (void) fseeko(F(sp), 0, SEEK_CUR);
+
+ /* similar pattern to net_write() to support pipes */
+ while (rem > 0) {
+ count = fwrite(cbuf, 1, rem, F(sp));
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ /*
+ * What does it mean to have a short write when using stdio?
+ *
+ * It can't mean much. After all stdio is buffering, so
+ * earlier writes that appeared complete may have failed,
+ * and so we don't know how much we really failed to write.
+ */
+ POS(sp) = -1;
+ return -1;
+ }
+ if (count == 0) {
+ POS(sp) = -1;
+ return -1;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ if (POS(sp) >= 0)
+ POS(sp) += size;
+ return size;
+}
+
+static off_t
+stdio_seek(krb5_storage * sp, off_t offset, int whence)
+{
+ int save_errno = errno;
+
+ if (whence == SEEK_SET && POS(sp) == offset)
+ return POS(sp);
+
+ if (whence == SEEK_CUR && POS(sp) >= 0 && offset == 0)
+ return POS(sp);
+
+ if (fseeko(F(sp), offset, whence) != 0)
+ return -1;
+ errno = save_errno;
+ return POS(sp) = ftello(F(sp));
+}
+
+static int
+stdio_trunc(krb5_storage * sp, off_t offset)
+{
+ off_t tmpoff;
+ int save_errno = errno;
+
+ if (fflush(F(sp)) == EOF)
+ return errno;
+ tmpoff = ftello(F(sp));
+ if (tmpoff < 0)
+ return errno;
+ if (tmpoff > offset)
+ tmpoff = offset;
+ if (ftruncate(fileno(F(sp)), offset) == -1)
+ return errno;
+ if (fseeko(F(sp), 0, SEEK_END) == -1)
+ return errno;
+ if (fseeko(F(sp), tmpoff, SEEK_SET) == -1)
+ return errno;
+ errno = save_errno;
+ POS(sp) = tmpoff;
+ return 0;
+}
+
+static int
+stdio_sync(krb5_storage * sp)
+{
+ if (fflush(F(sp)) == EOF)
+ return errno;
+ if (fsync(fileno(F(sp))) == -1)
+ return errno;
+ return 0;
+}
+
+static void
+stdio_free(krb5_storage * sp)
+{
+ int save_errno = errno;
+
+ if (F(sp) != NULL && fclose(F(sp)) == 0)
+ errno = save_errno;
+ F(sp) = NULL;
+}
+
+/**
+ * Open a krb5_storage using stdio for buffering.
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_emem()
+ * @sa krb5_storage_from_fd()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_stdio_from_fd(int fd_in, const char *mode)
+{
+ krb5_storage *sp;
+ off_t off;
+ FILE *f;
+ int saved_errno = errno;
+ int fd;
+
+ off = lseek(fd_in, 0, SEEK_CUR);
+ if (off == -1)
+ return NULL;
+
+#ifdef _MSC_VER
+ /*
+ * This function used to try to pass the input to
+ * _get_osfhandle() to test if the value is a HANDLE
+ * but this doesn't work because doing so throws an
+ * exception that will result in Watson being triggered
+ * to file a Windows Error Report.
+ */
+ fd = _dup(fd_in);
+#else
+ fd = dup(fd_in);
+#endif
+
+ if (fd < 0)
+ return NULL;
+
+ f = fdopen(fd, mode);
+ if (f == NULL) {
+ (void) close(fd);
+ return NULL;
+ }
+
+ errno = saved_errno;
+
+ if (fseeko(f, off, SEEK_SET) == -1) {
+ saved_errno = errno;
+ (void) fclose(f);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ errno = ENOMEM;
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL) {
+ saved_errno = errno;
+ (void) fclose(f);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ errno = ENOMEM;
+ sp->data = malloc(sizeof(stdio_storage));
+ if (sp->data == NULL) {
+ saved_errno = errno;
+ (void) fclose(f);
+ free(sp);
+ errno = saved_errno;
+ return NULL;
+ }
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ F(sp) = f;
+ POS(sp) = off;
+ sp->fetch = stdio_fetch;
+ sp->store = stdio_store;
+ sp->seek = stdio_seek;
+ sp->trunc = stdio_trunc;
+ sp->fsync = stdio_sync;
+ sp->free = stdio_free;
+ sp->max_alloc = UINT32_MAX/64;
+ return sp;
+}
diff --git a/third_party/heimdal/lib/krb5/string-to-key-test.c b/third_party/heimdal/lib/krb5/string-to-key-test.c
new file mode 100644
index 0000000..8988575
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/string-to-key-test.c
@@ -0,0 +1,140 @@
+/*
+ * 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 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 <err.h>
+
+enum { MAXSIZE = 24 };
+
+static struct testcase {
+ const char *principal_name;
+ const char *password;
+ krb5_enctype enctype;
+ unsigned char res[MAXSIZE];
+} tests[] = {
+#ifdef HEIM_WEAK_CRYPTO
+ {"@", "", ETYPE_DES_CBC_MD5,
+ {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xf1}},
+ {"nisse@FOO.SE", "hej", ETYPE_DES_CBC_MD5,
+ {0xfe, 0x67, 0xbf, 0x9e, 0x57, 0x6b, 0xfe, 0x52}},
+ {"assar/liten@FOO.SE", "hemligt", ETYPE_DES_CBC_MD5,
+ {0x5b, 0x9b, 0xcb, 0xf2, 0x97, 0x43, 0xc8, 0x40}},
+ {"raeburn@ATHENA.MIT.EDU", "password", ETYPE_DES_CBC_MD5,
+ {0xcb, 0xc2, 0x2f, 0xae, 0x23, 0x52, 0x98, 0xe3}},
+ {"danny@WHITEHOUSE.GOV", "potatoe", ETYPE_DES_CBC_MD5,
+ {0xdf, 0x3d, 0x32, 0xa7, 0x4f, 0xd9, 0x2a, 0x01}},
+ {"buckaroo@EXAMPLE.COM", "penny", ETYPE_DES_CBC_MD5,
+ {0x94, 0x43, 0xa2, 0xe5, 0x32, 0xfd, 0xc4, 0xf1}},
+ {"Juri\xc5\xa1i\xc4\x87@ATHENA.MIT.EDU", "\xc3\x9f", ETYPE_DES_CBC_MD5,
+ {0x62, 0xc8, 0x1a, 0x52, 0x32, 0xb5, 0xe6, 0x9d}},
+ {"AAAAAAAA", "11119999", ETYPE_DES_CBC_MD5,
+ {0x98, 0x40, 0x54, 0xd0, 0xf1, 0xa7, 0x3e, 0x31}},
+ {"FFFFAAAA", "NNNN6666", ETYPE_DES_CBC_MD5,
+ {0xc4, 0xbf, 0x6b, 0x25, 0xad, 0xf7, 0xa4, 0xf8}},
+#endif
+#if 0
+ {"@", "", ETYPE_DES3_CBC_SHA1,
+ {0xce, 0xa2, 0x2f, 0x9b, 0x52, 0x2c, 0xb0, 0x15, 0x6e, 0x6b, 0x64,
+ 0x73, 0x62, 0x64, 0x73, 0x4f, 0x6e, 0x73, 0xce, 0xa2, 0x2f, 0x9b,
+ 0x52, 0x57}},
+#endif
+ {"nisse@FOO.SE", "hej", ETYPE_DES3_CBC_SHA1,
+ {0x0e, 0xbc, 0x23, 0x9d, 0x68, 0x46, 0xf2, 0xd5, 0x51, 0x98, 0x5b,
+ 0x57, 0xc1, 0x57, 0x01, 0x79, 0x04, 0xc4, 0xe9, 0xfe, 0xc1, 0x0e,
+ 0x13, 0xd0}},
+ {"assar/liten@FOO.SE", "hemligt", ETYPE_DES3_CBC_SHA1,
+ {0x7f, 0x40, 0x67, 0xb9, 0xbc, 0xc4, 0x40, 0xfb, 0x43, 0x73, 0xd9,
+ 0xd3, 0xcd, 0x7c, 0xc7, 0x67, 0xe6, 0x79, 0x94, 0xd0, 0xa8, 0x34,
+ 0xdf, 0x62}},
+ {"does/not@MATTER", "foo", ETYPE_ARCFOUR_HMAC_MD5,
+ {0xac, 0x8e, 0x65, 0x7f, 0x83, 0xdf, 0x82, 0xbe,
+ 0xea, 0x5d, 0x43, 0xbd, 0xaf, 0x78, 0x00, 0xcc}},
+ {"raeburn@ATHENA.MIT.EDU", "password", ETYPE_DES3_CBC_SHA1,
+ {0x85, 0x0b, 0xb5, 0x13, 0x58, 0x54, 0x8c, 0xd0, 0x5e, 0x86, 0x76, 0x8c, 0x31, 0x3e, 0x3b, 0xfe, 0xf7, 0x51, 0x19, 0x37, 0xdc, 0xf7, 0x2c, 0x3e}},
+ {"danny@WHITEHOUSE.GOV", "potatoe", ETYPE_DES3_CBC_SHA1,
+ {0xdf, 0xcd, 0x23, 0x3d, 0xd0, 0xa4, 0x32, 0x04, 0xea, 0x6d, 0xc4, 0x37, 0xfb, 0x15, 0xe0, 0x61, 0xb0, 0x29, 0x79, 0xc1, 0xf7, 0x4f, 0x37, 0x7a}},
+ {"buckaroo@EXAMPLE.COM", "penny", ETYPE_DES3_CBC_SHA1,
+ {0x6d, 0x2f, 0xcd, 0xf2, 0xd6, 0xfb, 0xbc, 0x3d, 0xdc, 0xad, 0xb5, 0xda, 0x57, 0x10, 0xa2, 0x34, 0x89, 0xb0, 0xd3, 0xb6, 0x9d, 0x5d, 0x9d, 0x4a}},
+ {"Juri\xc5\xa1i\xc4\x87@ATHENA.MIT.EDU", "\xc3\x9f", ETYPE_DES3_CBC_SHA1,
+ {0x16, 0xd5, 0xa4, 0x0e, 0x1c, 0xe3, 0xba, 0xcb, 0x61, 0xb9, 0xdc, 0xe0, 0x04, 0x70, 0x32, 0x4c, 0x83, 0x19, 0x73, 0xa7, 0xb9, 0x52, 0xfe, 0xb0}},
+ {NULL, NULL, 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);
+
+ /* to enable realm-less principal name above */
+
+ krb5_set_default_realm(context, "");
+
+ for (t = tests; t->principal_name; ++t) {
+ krb5_keyblock key;
+ krb5_principal principal;
+ int i;
+
+ ret = krb5_parse_name (context, t->principal_name, &principal);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_parse_name %s",
+ t->principal_name);
+ ret = krb5_string_to_key (context, t->enctype, t->password,
+ principal, &key);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_string_to_key");
+ krb5_free_principal (context, principal);
+ if (memcmp (key.keyvalue.data, t->res, key.keyvalue.length) != 0) {
+ const unsigned char *p = key.keyvalue.data;
+
+ printf ("string_to_key(%s, %s) failed\n",
+ t->principal_name, t->password);
+ printf ("should be: ");
+ for (i = 0; i < key.keyvalue.length; ++i)
+ printf ("%02x", t->res[i]);
+ printf ("\nresult was: ");
+ for (i = 0; i < key.keyvalue.length; ++i)
+ printf ("%02x", p[i]);
+ printf ("\n");
+ val = 1;
+ }
+ krb5_free_keyblock_contents(context, &key);
+ }
+ krb5_free_context(context);
+ return val;
+}
diff --git a/third_party/heimdal/lib/krb5/test_acl.c b/third_party/heimdal/lib/krb5/test_acl.c
new file mode 100644
index 0000000..a11ff3f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_acl.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+
+#define RETVAL(c, r, e, s) \
+ do { if (r != e) krb5_errx(c, 1, "%s", s); } while (0)
+#define STRINGMATCH(c, s, _s1, _s2) \
+ do { \
+ if (_s1 == NULL) \
+ krb5_errx(c, 1, "s1 or s2 is NULL"); \
+ if (strcmp(_s1,_s2) != 0) \
+ krb5_errx(c, 1, "%s", s); \
+ } while (0)
+
+static void
+test_match_string(krb5_context context)
+{
+ krb5_error_code ret;
+ char *s1, *s2;
+
+ ret = krb5_acl_match_string(context, "foo", "s", "foo");
+ RETVAL(context, ret, 0, "single s");
+ ret = krb5_acl_match_string(context, "foo foo", "s", "foo");
+ RETVAL(context, ret, EACCES, "too many strings");
+ ret = krb5_acl_match_string(context, "foo bar", "ss", "foo", "bar");
+ RETVAL(context, ret, 0, "two strings");
+ ret = krb5_acl_match_string(context, "foo bar", "ss", "foo", "bar");
+ RETVAL(context, ret, 0, "two strings double space");
+ ret = krb5_acl_match_string(context, "foo \tbar", "ss", "foo", "bar");
+ RETVAL(context, ret, 0, "two strings space + tab");
+ ret = krb5_acl_match_string(context, "foo", "ss", "foo", "bar");
+ RETVAL(context, ret, EACCES, "one string, two format strings");
+ ret = krb5_acl_match_string(context, "foo", "ss", "foo", "foo");
+ RETVAL(context, ret, EACCES, "one string, two format strings (same)");
+ ret = krb5_acl_match_string(context, "foo \t", "s", "foo");
+ RETVAL(context, ret, 0, "ending space");
+
+ ret = krb5_acl_match_string(context, "foo/bar", "f", "foo/bar");
+ RETVAL(context, ret, 0, "liternal fnmatch");
+ ret = krb5_acl_match_string(context, "foo/bar", "f", "foo/*");
+ RETVAL(context, ret, 0, "foo/*");
+ ret = krb5_acl_match_string(context, "foo/bar.example.org", "f",
+ "foo/*.example.org");
+ RETVAL(context, ret, 0, "foo/*.example.org");
+ ret = krb5_acl_match_string(context, "foo/bar.example.com", "f",
+ "foo/*.example.org");
+ RETVAL(context, ret, EACCES, "foo/*.example.com");
+
+ ret = krb5_acl_match_string(context, "foo/bar/baz", "f", "foo/*/baz");
+ RETVAL(context, ret, 0, "foo/*/baz");
+
+ ret = krb5_acl_match_string(context, "foo", "r", &s1);
+ RETVAL(context, ret, 0, "ret 1");
+ STRINGMATCH(context, "ret 1 match", s1, "foo"); free(s1);
+
+ ret = krb5_acl_match_string(context, "foo bar", "rr", &s1, &s2);
+ RETVAL(context, ret, 0, "ret 2");
+ STRINGMATCH(context, "ret 2 match 1", s1, "foo"); free(s1);
+ STRINGMATCH(context, "ret 2 match 2", s2, "bar"); free(s2);
+
+ ret = krb5_acl_match_string(context, "foo bar", "sr", "bar", &s1);
+ RETVAL(context, ret, EACCES, "ret mismatch");
+ if (s1 != NULL) krb5_errx(context, 1, "s1 not NULL");
+
+ ret = krb5_acl_match_string(context, "foo", "l", "foo");
+ RETVAL(context, ret, EINVAL, "unknown letter");
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ test_match_string(context);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_addr.c b/third_party/heimdal/lib/krb5/test_addr.c
new file mode 100644
index 0000000..79a55ff
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_addr.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+
+static void
+print_addr(krb5_context context, const char *addr)
+{
+ krb5_addresses addresses;
+ krb5_error_code ret;
+ char buf[38];
+ char buf2[1000];
+ size_t len;
+ int i;
+
+ ret = krb5_parse_address(context, addr, &addresses);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_address");
+
+ if (addresses.len < 1)
+ krb5_err(context, 1, ret, "too few addresses");
+
+ for (i = 0; i < addresses.len; i++) {
+ krb5_print_address(&addresses.val[i], buf, sizeof(buf), &len);
+#if 0
+ printf("addr %d: %s (%d/%d)\n", i, buf, (int)len, (int)strlen(buf));
+#endif
+ if (strlen(buf) > sizeof(buf))
+ krb5_err(context, 1, ret, "len %d larger then buf %d",
+ (int)strlen(buf), (int)sizeof(buf));
+ krb5_print_address(&addresses.val[i], buf2, sizeof(buf2), &len);
+#if 0
+ printf("addr %d: %s (%d/%d)\n", i, buf2, (int)len, (int)strlen(buf2));
+#endif
+ if (strlen(buf2) > sizeof(buf2))
+ krb5_err(context, 1, ret, "len %d larger then buf %d",
+ (int)strlen(buf2), (int)sizeof(buf2));
+
+ }
+ krb5_free_addresses(context, &addresses);
+
+}
+
+static void
+truncated_addr(krb5_context context, const char *addr,
+ size_t truncate_len, size_t outlen)
+{
+ krb5_addresses addresses;
+ krb5_error_code ret;
+ char *buf;
+ size_t len;
+
+ buf = ecalloc(1, outlen + 1);
+
+ ret = krb5_parse_address(context, addr, &addresses);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_address");
+
+ if (addresses.len != 1)
+ krb5_err(context, 1, ret, "addresses should be one");
+
+ krb5_print_address(&addresses.val[0], buf, truncate_len, &len);
+
+#if 0
+ printf("addr %s (%d/%d) should be %d\n", buf, (int)len, (int)strlen(buf), (int)outlen);
+#endif
+
+ if (truncate_len > strlen(buf) + 1)
+ krb5_err(context, 1, ret, "%s truncate_len %d larger then strlen %d source %s",
+ buf, (int)truncate_len, (int)strlen(buf), addr);
+
+ if (outlen != len)
+ krb5_err(context, 1, ret, "%s: outlen %d != len %d",
+ buf, (int)outlen, (int)strlen(buf));
+
+ krb5_print_address(&addresses.val[0], buf, outlen + 1, &len);
+
+#if 0
+ printf("addr %s (%d/%d)\n", buf, (int)len, (int)strlen(buf));
+#endif
+
+ if (len != outlen)
+ abort();
+ if (strlen(buf) != len)
+ abort();
+
+ krb5_free_addresses(context, &addresses);
+ free(buf);
+}
+
+static void
+check_truncation(krb5_context context, const char *addr)
+{
+ int i, len = strlen(addr);
+
+ truncated_addr(context, addr, len, len);
+
+ for (i = 0; i < len; i++)
+ truncated_addr(context, addr, i, len);
+}
+
+static void
+match_addr(krb5_context context, const char *range_addr,
+ const char *one_addr, int match)
+{
+ krb5_addresses range, one;
+ krb5_error_code ret;
+
+ ret = krb5_parse_address(context, range_addr, &range);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_address");
+
+ if (range.len != 1)
+ krb5_err(context, 1, ret, "wrong num of addresses");
+
+ ret = krb5_parse_address(context, one_addr, &one);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_address");
+
+ if (one.len != 1)
+ krb5_err(context, 1, ret, "wrong num of addresses");
+
+ if (krb5_address_order(context, &range.val[0], &one.val[0]) == 0) {
+ if (!match)
+ krb5_errx(context, 1, "match when one shouldn't be");
+ } else {
+ if (match)
+ krb5_errx(context, 1, "no match when one should be");
+ }
+
+ krb5_free_addresses(context, &range);
+ krb5_free_addresses(context, &one);
+}
+
+#ifdef _MSC_VER
+
+/* For the truncation tests, calling strcpy_s() or strcat_s() with a
+ size of 0 results in the invalid parameter handler being invoked.
+ For the debug version, the runtime also throws an assert. */
+
+static void
+inv_param_handler(const wchar_t* expression,
+ const wchar_t* function,
+ const wchar_t* file,
+ unsigned int line,
+ uintptr_t pReserved)
+{
+ printf("Invalid parameter handler invoked for: %S in %S(%d) [%S]\n",
+ function, file, line, expression);
+}
+
+static _invalid_parameter_handler _inv_old = NULL;
+
+#define SET_INVALID_PARAM_HANDLER _inv_old = _set_invalid_parameter_handler(inv_param_handler)
+
+#else
+
+#define SET_INVALID_PARAM_HANDLER ((void) 0)
+
+#endif
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+
+ SET_INVALID_PARAM_HANDLER;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ print_addr(context, "RANGE:127.0.0.0/8");
+ print_addr(context, "RANGE:127.0.0.0/24");
+ print_addr(context, "RANGE:IPv4:127.0.0.0-IPv4:127.0.0.255");
+ print_addr(context, "RANGE:130.237.237.4/29");
+#ifdef HAVE_IPV6
+ print_addr(context, "RANGE:2001:db8:1:2:3:4:1428:7ab/64");
+ print_addr(context, "RANGE:IPv6:fe80::209:6bff:fea0:e522/64");
+ print_addr(context, "RANGE:IPv6:fe80::-IPv6:fe80::ffff:ffff:ffff:ffff");
+ print_addr(context, "RANGE:fe80::-fe80::ffff:ffff:ffff:ffff");
+#endif
+
+ check_truncation(context, "IPv4:127.0.0.0");
+ check_truncation(context, "RANGE:IPv4:127.0.0.0-IPv4:127.0.0.255");
+#ifdef HAVE_IPV6
+ check_truncation(context, "IPv6:::");
+ check_truncation(context, "IPv6:::1");
+ check_truncation(context, "IPv6:2001:db8:1:2:3:4:1428:7ab");
+ check_truncation(context, "IPv6:fe80::209:0:0:0");
+ check_truncation(context, "IPv6:fe80::ffff:ffff:ffff:ffff");
+#endif
+
+ match_addr(context, "RANGE:127.0.0.0/8", "inet:127.0.0.0", 1);
+ match_addr(context, "RANGE:127.0.0.0/8", "inet:127.255.255.255", 1);
+ match_addr(context, "RANGE:127.0.0.0/8", "inet:128.0.0.0", 0);
+
+ match_addr(context, "RANGE:130.237.237.8/29", "inet:130.237.237.7", 0);
+ match_addr(context, "RANGE:130.237.237.8/29", "inet:130.237.237.8", 1);
+ match_addr(context, "RANGE:130.237.237.8/29", "inet:130.237.237.15", 1);
+ match_addr(context, "RANGE:130.237.237.8/29", "inet:130.237.237.16", 0);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_alname.c b/third_party/heimdal/lib/krb5/test_alname.c
new file mode 100644
index 0000000..36775ad
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_alname.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <getarg.h>
+#include <err.h>
+
+char localname[1024];
+static size_t lname_size = sizeof (localname);
+static int lname_size_arg = 0;
+static int simple_flag = 0;
+static int verbose_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"lname-size", 0, arg_integer, &lname_size_arg,
+ "set localname size (0 means use default, must be 0..1023)", "integer" },
+ {"simple", 0, arg_flag, &simple_flag, /* Used for scripting */
+ "map the given principal and print the resulting localname", NULL },
+ {"verbose", 0, arg_flag, &verbose_flag,
+ "print the actual principal name as well as the localname", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+test_alname(krb5_context context, krb5_const_realm realm,
+ const char *user, const char *inst,
+ const char *localuser, int ok)
+{
+ krb5_principal p;
+ krb5_error_code ret;
+ char *princ;
+
+ ret = krb5_make_principal(context, &p, realm, user, inst, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_build_principal");
+
+ ret = krb5_unparse_name(context, p, &princ);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ ret = krb5_aname_to_localname(context, p, lname_size, localname);
+ krb5_free_principal(context, p);
+ if (ret) {
+ if (!ok) {
+ free(princ);
+ return;
+ }
+ krb5_err(context, 1, ret, "krb5_aname_to_localname: %s -> %s",
+ princ, localuser);
+ }
+ free(princ);
+
+ if (strcmp(localname, localuser) != 0) {
+ if (ok)
+ errx(1, "compared failed %s != %s (should have succeded)",
+ localname, localuser);
+ } else {
+ if (!ok)
+ errx(1, "compared failed %s == %s (should have failed)",
+ localname, localuser);
+ }
+
+}
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_realm realm;
+ int optidx = 0;
+ char *user;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if (simple_flag) {
+ krb5_principal princ;
+ char *unparsed;
+ int status = 0;
+
+ /* Map then print the result and exit */
+ if (argc != 1)
+ errx(1, "One argument is required and it must be a principal name");
+
+ ret = krb5_parse_name(context, argv[0], &princ);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_build_principal");
+
+ ret = krb5_unparse_name(context, princ, &unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ if (lname_size_arg > 0 && lname_size_arg < 1024)
+ lname_size = lname_size_arg;
+ else if (lname_size_arg != 0)
+ errx(1, "local name size must be between 0 and 1023 (inclusive)");
+
+ ret = krb5_aname_to_localname(context, princ, lname_size, localname);
+ if (ret == KRB5_NO_LOCALNAME) {
+ if (verbose_flag)
+ fprintf(stderr, "No mapping obtained for %s\n", unparsed);
+ exit(1);
+ }
+ switch (ret) {
+ case KRB5_PLUGIN_NO_HANDLE:
+ fprintf(stderr, "Error: KRB5_PLUGIN_NO_HANDLE leaked!\n");
+ status = 2;
+ break;
+ case KRB5_CONFIG_NOTENUFSPACE:
+ fprintf(stderr, "Error: lname-size (%lu) too small\n",
+ (long unsigned)lname_size);
+ status = 3;
+ break;
+ case 0:
+ if (verbose_flag)
+ printf("%s ", unparsed);
+ printf("%s\n", localname);
+ break;
+ default:
+ krb5_err(context, 4, ret, "krb5_aname_to_localname");
+ break;
+ }
+ free(unparsed);
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ exit(status);
+ }
+
+ if (argc != 1)
+ errx(1, "first argument should be a local user that is in root .k5login");
+
+ user = argv[0];
+
+ ret = krb5_get_default_realm(context, &realm);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_default_realm");
+
+ test_alname(context, realm, user, NULL, user, 1);
+ test_alname(context, realm, user, "root", "root", 1);
+
+ test_alname(context, "FOO.BAR.BAZ.KAKA", user, NULL, user, 0);
+ test_alname(context, "FOO.BAR.BAZ.KAKA", user, "root", "root", 0);
+
+ test_alname(context, realm, user, NULL,
+ "not-same-as-user", 0);
+ test_alname(context, realm, user, "root",
+ "not-same-as-user", 0);
+
+ test_alname(context, "FOO.BAR.BAZ.KAKA", user, NULL,
+ "not-same-as-user", 0);
+ test_alname(context, "FOO.BAR.BAZ.KAKA", user, "root",
+ "not-same-as-user", 0);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_ap-req.c b/third_party/heimdal/lib/krb5/test_ap-req.c
new file mode 100644
index 0000000..0fd1078
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_ap-req.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <krb5.h>
+#include <err.h>
+#include <getarg.h>
+#include <roken.h>
+
+static int verify_pac = 1;
+static int server_any = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"verify-pac",0, arg_flag, &verify_pac,
+ "verify the PAC", NULL },
+ {"server-any",0, arg_flag, &server_any,
+ "let server pick the principal", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "...");
+ exit (ret);
+}
+
+
+static void
+test_ap(krb5_context context,
+ krb5_principal target,
+ krb5_principal server,
+ krb5_keytab keytab,
+ krb5_ccache ccache,
+ const krb5_flags client_flags)
+{
+ krb5_error_code ret;
+ krb5_auth_context client_ac = NULL, server_ac = NULL;
+ krb5_data data;
+ krb5_flags server_flags;
+ krb5_ticket *ticket = NULL;
+ int32_t server_seq, client_seq;
+
+ ret = krb5_mk_req_exact(context,
+ &client_ac,
+ client_flags,
+ target,
+ NULL,
+ ccache,
+ &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_mk_req_exact");
+
+ ret = krb5_rd_req(context,
+ &server_ac,
+ &data,
+ server,
+ keytab,
+ &server_flags,
+ &ticket);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_rd_req");
+
+
+ if (server_flags & AP_OPTS_MUTUAL_REQUIRED) {
+ krb5_ap_rep_enc_part *repl;
+
+ krb5_data_free(&data);
+
+ if ((client_flags & AP_OPTS_MUTUAL_REQUIRED) == 0)
+ krb5_errx(context, 1, "client flag missing mutual req");
+
+ ret = krb5_mk_rep (context, server_ac, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_mk_rep");
+
+ ret = krb5_rd_rep (context,
+ client_ac,
+ &data,
+ &repl);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_rd_rep");
+
+ krb5_free_ap_rep_enc_part (context, repl);
+ } else {
+ if (client_flags & AP_OPTS_MUTUAL_REQUIRED)
+ krb5_errx(context, 1, "server flag missing mutual req");
+ }
+
+ krb5_auth_con_getremoteseqnumber(context, server_ac, &server_seq);
+ krb5_auth_con_getremoteseqnumber(context, client_ac, &client_seq);
+ if (server_seq != client_seq)
+ krb5_errx(context, 1, "seq num differ");
+
+ krb5_auth_con_getlocalseqnumber(context, server_ac, &server_seq);
+ krb5_auth_con_getlocalseqnumber(context, client_ac, &client_seq);
+ if (server_seq != client_seq)
+ krb5_errx(context, 1, "seq num differ");
+
+ krb5_data_free(&data);
+ krb5_auth_con_free(context, client_ac);
+ krb5_auth_con_free(context, server_ac);
+
+ if (verify_pac) {
+ krb5_pac pac;
+
+ ret = krb5_ticket_get_authorization_data_type(context,
+ ticket,
+ KRB5_AUTHDATA_WIN2K_PAC,
+ &data);
+ if (ret)
+ krb5_err(context, 1, ret, "get pac");
+
+ ret = krb5_pac_parse(context, data.data, data.length, &pac);
+ if (ret)
+ krb5_err(context, 1, ret, "pac parse");
+
+ krb5_pac_free(context, pac);
+ krb5_data_free(&data);
+ }
+
+ krb5_free_ticket(context, ticket);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0;
+ const char *principal, *keytab, *ccache;
+ krb5_ccache id;
+ krb5_keytab kt;
+ krb5_principal sprincipal, server;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 3)
+ usage(1);
+
+ principal = argv[0];
+ keytab = argv[1];
+ ccache = argv[2];
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_cc_resolve(context, ccache, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+
+ ret = krb5_parse_name(context, principal, &sprincipal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_kt_resolve(context, keytab, &kt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ if (server_any)
+ server = NULL;
+ else
+ server = sprincipal;
+
+ test_ap(context, sprincipal, server, kt, id, 0);
+ test_ap(context, sprincipal, server, kt, id, AP_OPTS_MUTUAL_REQUIRED);
+
+ krb5_cc_close(context, id);
+ krb5_kt_close(context, kt);
+ krb5_free_principal(context, sprincipal);
+
+ krb5_free_context(context);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/test_canon.c b/third_party/heimdal/lib/krb5/test_canon.c
new file mode 100755
index 0000000..8389c02
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_canon.c
@@ -0,0 +1,177 @@
+/*
+ * 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.
+ *
+ */
+
+#include "krb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+
+#if 0
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <krb5.h>
+#endif
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code retval;
+ krb5_context context;
+ krb5_principal princ = NULL;
+ krb5_principal me = NULL;
+ krb5_principal cmp_to_princ = NULL;
+ krb5_ccache cc = NULL;
+ krb5_creds *out_creds = NULL;
+ krb5_keytab kt = NULL;
+ krb5_keytab_entry ktent;
+ krb5_creds in_creds;
+ char *hostname = NULL;
+ char *unparsed = NULL;
+ char *unparsed_canon = NULL;
+ char *during;
+ char *cmp_to = NULL;;
+ int do_kt = 0;
+ int do_get_creds = 0;
+ int opt;
+ int ret = 1;
+
+ memset(&ktent, 0, sizeof(ktent));
+
+ while ((opt = getopt(argc, argv, "hgkc:")) != -1) {
+ switch (opt) {
+ case 'g':
+ do_get_creds++;
+ break;
+ case 'k':
+ do_kt++;
+ break;
+ case 'c':
+ cmp_to = optarg;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, "Usage: %s [-g] [-k] [-c compare-to-principal] "
+ "[principal]\n", argv[0]);
+ return 1;
+ }
+ }
+
+ if (!do_get_creds && !do_kt && !cmp_to)
+ do_get_creds++;
+
+ if (optind < argc)
+ hostname = argv[optind];
+
+ during = "init_context";
+ retval = krb5_init_context(&context);
+ if (retval) goto err;
+
+ during = "sn2p";
+ retval = krb5_sname_to_principal(context, hostname, "host", KRB5_NT_SRV_HST, &princ);
+ if (retval) goto err;
+
+ during = "unparse of sname2princ";
+ retval = krb5_unparse_name(context, princ, &unparsed);
+ if (retval) goto err;
+ printf("krb5_sname_to_principal() output: %s\n", unparsed);
+
+ if (cmp_to) {
+ krb5_boolean eq;
+
+ during = "parsing principal name for comparison compare";
+ retval = krb5_parse_name(context, cmp_to, &cmp_to_princ);
+ if (retval) goto err;
+
+ eq = krb5_principal_compare(context, princ, cmp_to_princ);
+ printf("%s %s %s\n", unparsed, eq ? "==" : "!=", cmp_to);
+ }
+
+ if (do_get_creds) {
+ during = "ccdefault";
+ retval = krb5_cc_default(context, &cc);
+ if (retval) goto err;
+
+ during = "ccprinc";
+ retval = krb5_cc_get_principal(context, cc, &me);
+ if (retval) goto err;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.client = me;
+ in_creds.server = princ;
+
+ during = "getcreds";
+ retval = krb5_get_credentials(context, 0, cc, &in_creds, &out_creds);
+ if (retval) goto err;
+
+ during = "unparsing principal name canonicalized by krb5_get_credentials()";
+ retval = krb5_unparse_name(context, in_creds.server, &unparsed_canon);
+ if (retval) goto err;
+ printf("Principal name as canonicalized by krb5_get_credentials() is %s\n", unparsed_canon);
+ }
+
+ if (do_kt) {
+ during = "getting keytab";
+ retval = krb5_kt_default(context, &kt);
+ if (retval) goto err;
+
+ during = "getting keytab ktent";
+ retval = krb5_kt_get_entry(context, kt, princ, 0, 0, &ktent);
+ if (retval) goto err;
+
+ during = "unparsing principal name canonicalized by krb5_kt_get_entry()";
+ retval = krb5_unparse_name(context, ktent.principal, &unparsed_canon);
+ if (retval) goto err;
+ printf("Principal name as canonicalized by krb5_kt_get_entry() is %s\n", unparsed_canon);
+ }
+
+ ret = 0;
+
+err:
+ krb5_free_principal(context, princ);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, cmp_to_princ);
+ krb5_xfree(unparsed);
+ krb5_xfree(unparsed_canon);
+ if (do_get_creds) {
+ krb5_free_creds(context, out_creds);
+ (void) krb5_cc_close(context, cc);
+ }
+ krb5_kt_free_entry(context, &ktent);
+ if (kt)
+ krb5_kt_close(context, kt);
+ krb5_free_context(context);
+ if (ret)
+ fprintf(stderr, "Failed while doing %s (%d)\n", during, retval);
+ return (ret);
+}
+
diff --git a/third_party/heimdal/lib/krb5/test_cc.c b/third_party/heimdal/lib/krb5/test_cc.c
new file mode 100644
index 0000000..0ca582e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_cc.c
@@ -0,0 +1,1218 @@
+/*
+ * Copyright (c) 2003 - 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 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. */
+
+/*
+ * If this test fails with
+ *
+ * krb5_cc_gen_new: KEYRING: Key has been revoked
+ *
+ * then run
+ *
+ * keyctl new_session
+ */
+
+#include "krb5_locl.h"
+#include <getarg.h>
+#include <err.h>
+
+#ifdef HAVE_KEYUTILS_H
+#include <keyutils.h>
+#endif
+
+static const char *unlink_this;
+static const char *unlink_this2;
+static char *tmpdir;
+static int debug_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+#define TEST_CC_TEMPLATE "%{TEMP}/krb5-cc-test-XXXXXX"
+
+static void
+cleanup(void)
+{
+ char *s = NULL;
+
+ if (asprintf(&s, "%s/cc", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/scc", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/cccol/foobar+lha@H5L.SE", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/cccol/foobar+lha@SU.SE", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/cccol/foobar", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/cccol", tmpdir) > -1 && s != NULL)
+ rmdir(s);
+ free(s);
+
+ if (asprintf(&s, "%s/dcc/tkt.lha@H5L.SE", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/dcc/tkt.lha@SU.SE", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/dcc/tkt", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/dcc/primary", tmpdir) > -1 && s != NULL)
+ unlink(s);
+ free(s);
+
+ if (asprintf(&s, "%s/dcc", tmpdir) > -1 && s != NULL)
+ rmdir(s);
+ free(s);
+
+ if (unlink_this)
+ unlink(unlink_this);
+ unlink_this = NULL;
+ if (unlink_this2)
+ unlink(unlink_this2);
+ unlink_this2 = NULL;
+
+ rmdir(tmpdir);
+}
+
+static void
+make_dir(krb5_context context)
+{
+ krb5_error_code ret;
+ char *template = NULL;
+ char *dcc = NULL;
+
+ ret = _krb5_expand_path_tokens(context, TEST_CC_TEMPLATE, 1, &template);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_expand_path_tokens(%s) failed",
+ TEST_CC_TEMPLATE);
+ if ((tmpdir = mkdtemp(template)) == NULL)
+ krb5_err(context, 1, errno, "mkdtemp(%s) failed", template);
+ if (asprintf(&dcc, "%s/dcc", tmpdir) == -1 || dcc == NULL)
+ krb5_err(context, 1, errno, "asprintf failed");
+ free(dcc);
+ atexit(cleanup);
+}
+
+static void
+test_default_name(krb5_context context)
+{
+ krb5_error_code ret;
+ const char *p;
+ char *test_cc_name = NULL;
+ const char *p3;
+ char *p1, *p2;
+ char *exp_test_cc_name;
+
+ if (asprintf(&test_cc_name, "%s/cc", tmpdir) == -1 || test_cc_name == NULL)
+ krb5_err(context, 1, errno, "out of memory");
+
+ /* Convert slashes to backslashes */
+ ret = _krb5_expand_path_tokens(context, test_cc_name, 1,
+ &exp_test_cc_name);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_expand_path_tokens(%s) failed",
+ test_cc_name);
+ free(test_cc_name);
+ test_cc_name = NULL;
+
+ p = krb5_cc_default_name(context);
+ if (p == NULL)
+ krb5_errx (context, 1, "krb5_cc_default_name 1 failed");
+ p1 = estrdup(p);
+
+ ret = krb5_cc_set_default_name(context, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_set_default_name(NULL) failed");
+
+ p = krb5_cc_default_name(context);
+ if (p == NULL)
+ krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
+ p2 = estrdup(p);
+
+ if (strcmp(p1, p2) != 0)
+ krb5_errx (context, 1, "krb5_cc_default_name no longer same");
+
+ ret = krb5_cc_set_default_name(context, exp_test_cc_name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_set_default_name(%s) failed",
+ exp_test_cc_name);
+
+ p = krb5_cc_default_name(context);
+ if (p == NULL)
+ krb5_errx (context, 1, "krb5_cc_default_name 2 failed");
+
+ if (strncmp(p, "FILE:", sizeof("FILE:") - 1) == 0)
+ p3 = p + sizeof("FILE:") - 1;
+ else
+ p3 = p;
+
+ if (strcmp(exp_test_cc_name, p3) != 0) {
+#ifdef WIN32
+ krb5_warnx(context, 1,
+ "krb5_cc_default_name() returned %s; expected %s",
+ p, exp_test_cc_name);
+#else
+ krb5_errx(context, 1,
+ "krb5_cc_default_name() returned %s; expected %s",
+ p, exp_test_cc_name);
+#endif
+ }
+
+ free(exp_test_cc_name);
+ free(p1);
+ free(p2);
+}
+
+/*
+ * Check that a closed cc still keeps it data and that it's no longer
+ * there when it's destroyed.
+ */
+
+static void
+test_mcache(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_ccache id, id2;
+ const char *nc, *tc;
+ char *c;
+ krb5_principal p, p2;
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique");
+
+ ret = krb5_cc_initialize(context, id, p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ nc = krb5_cc_get_name(context, id);
+ if (nc == NULL)
+ krb5_errx(context, 1, "krb5_cc_get_name");
+
+ tc = krb5_cc_get_type(context, id);
+ if (tc == NULL)
+ krb5_errx(context, 1, "krb5_cc_get_name");
+
+ if (asprintf(&c, "%s:%s", tc, nc) < 0 || c == NULL)
+ errx(1, "malloc");
+
+ krb5_cc_close(context, id);
+
+ ret = krb5_cc_resolve(context, c, &id2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+
+ ret = krb5_cc_get_principal(context, id2, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ if (krb5_principal_compare(context, p, p2) == FALSE)
+ krb5_errx(context, 1, "p != p2");
+
+ krb5_cc_destroy(context, id2);
+ krb5_free_principal(context, p);
+ krb5_free_principal(context, p2);
+
+ ret = krb5_cc_resolve(context, c, &id2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+
+ ret = krb5_cc_get_principal(context, id2, &p2);
+ if (ret == 0)
+ krb5_errx(context, 1, "krb5_cc_get_principal");
+
+ krb5_cc_destroy(context, id2);
+ free(c);
+}
+
+/*
+ * Test that init works on a destroyed cc.
+ */
+
+static void
+test_init_vs_destroy(krb5_context context, const char *type)
+{
+ krb5_error_code ret;
+ krb5_ccache id, id2;
+ krb5_principal p, p2;
+ char *n = NULL;
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_new_unique(context, type, NULL, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique: %s", type);
+
+ if (asprintf(&n, "%s:%s",
+ krb5_cc_get_type(context, id),
+ krb5_cc_get_name(context, id)) < 0 || n == NULL)
+ errx(1, "malloc");
+
+ if (strcmp(krb5_cc_get_type(context, id), "FILE") == 0)
+ unlink_this = krb5_cc_get_name(context, id);
+
+ ret = krb5_cc_resolve(context, n, &id2);
+ free(n);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_resolve");
+
+ krb5_cc_destroy(context, id);
+
+ ret = krb5_cc_initialize(context, id2, p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_get_principal(context, id2, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ krb5_cc_destroy(context, id2);
+ unlink_this = NULL;
+ krb5_free_principal(context, p);
+ krb5_free_principal(context, p2);
+}
+
+static void
+test_cache_remove(krb5_context context, const char *type)
+{
+ krb5_error_code ret;
+ krb5_ccache id;
+ krb5_principal p;
+ krb5_creds cred, found;
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_new_unique(context, type, NULL, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_gen_new: %s", type);
+
+ if (strcmp(krb5_cc_get_type(context, id), "FILE") == 0)
+ unlink_this = krb5_cc_get_name(context, id);
+
+ ret = krb5_cc_initialize(context, id, p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ /* */
+ memset(&cred, 0, sizeof(cred));
+ ret = krb5_parse_name(context, "krbtgt/SU.SE@SU.SE", &cred.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+ ret = krb5_parse_name(context, "lha@SU.SE", &cred.client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+ cred.times.endtime = time(NULL) + 300;
+
+ ret = krb5_cc_store_cred(context, id, &cred);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+ ret = krb5_cc_remove_cred(context, id, 0, &cred);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_remove_cred");
+
+ memset(&found, 0, sizeof(found));
+ ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_MATCH_TIMES,
+ &cred, &found);
+ if (ret == 0)
+ krb5_err(context, 1, ret, "krb5_cc_remove_cred didn't");
+
+ ret = krb5_cc_destroy(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_destroy");
+ unlink_this = NULL;
+
+ krb5_free_principal(context, p);
+ krb5_free_principal(context, cred.server);
+ krb5_free_principal(context, cred.client);
+}
+
+static void
+test_mcc_default(void)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_ccache id, id2;
+ int i;
+
+ for (i = 0; i < 10; i++) {
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_init_context");
+
+ ret = krb5_cc_set_default_name(context, "MEMORY:foo");
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_set_default_name");
+
+ ret = krb5_cc_default(context, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default");
+
+ ret = krb5_cc_default(context, &id2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default");
+
+ ret = krb5_cc_close(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_close");
+
+ ret = krb5_cc_close(context, id2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_close");
+
+ krb5_free_context(context);
+ }
+}
+
+struct {
+ char *str;
+ int fail;
+ char *res;
+} cc_names[] = {
+ { "foo", 0, "foo" },
+ { "foo%}", 0, "foo%}" },
+ { "%{uid}", 0, NULL },
+ { "%{euid}", 0, NULL },
+ { "%{username}", 0, NULL },
+ { "foo%{null}", 0, "foo" },
+ { "foo%{null}bar", 0, "foobar" },
+ { "%{", 1, NULL },
+ { "%{foo %{", 1, NULL },
+ { "%{{", 1, NULL },
+ { "%{{}", 1, NULL },
+ { "%{nulll}", 1, NULL },
+ { "%{does not exist}", 1, NULL },
+ { "%{}", 1, NULL },
+#ifdef WIN32
+ { "%{APPDATA}", 0, NULL },
+ { "%{COMMON_APPDATA}", 0, NULL},
+ { "%{LOCAL_APPDATA}", 0, NULL},
+ { "%{SYSTEM}", 0, NULL},
+ { "%{WINDOWS}", 0, NULL},
+ { "%{TEMP}", 0, NULL},
+ { "%{USERID}", 0, NULL},
+ { "%{uid}", 0, NULL},
+ { "%{USERCONFIG}", 0, NULL},
+ { "%{COMMONCONFIG}", 0, NULL},
+ { "%{LIBDIR}", 0, NULL},
+ { "%{BINDIR}", 0, NULL},
+ { "%{LIBEXEC}", 0, NULL},
+ { "%{SBINDIR}", 0, NULL},
+#endif
+};
+
+static void
+test_def_cc_name(krb5_context context)
+{
+ krb5_error_code ret;
+ char *str;
+ int i;
+
+ for (i = 0; i < sizeof(cc_names)/sizeof(cc_names[0]); i++) {
+ ret = _krb5_expand_default_cc_name(context, cc_names[i].str, &str);
+ if (ret) {
+ if (cc_names[i].fail == 0)
+ krb5_errx(context, 1, "test %d \"%s\" failed",
+ i, cc_names[i].str);
+ } else {
+ if (cc_names[i].fail)
+ krb5_errx(context, 1, "test %d \"%s\" was successful",
+ i, cc_names[i].str);
+ if (cc_names[i].res && strcmp(cc_names[i].res, str) != 0)
+ krb5_errx(context, 1, "test %d %s != %s",
+ i, cc_names[i].res, str);
+ if (debug_flag)
+ printf("%s => %s\n", cc_names[i].str, str);
+ free(str);
+ }
+ }
+}
+
+static void
+test_cache_find(krb5_context context, const char *principal, int find)
+{
+ krb5_principal client;
+ krb5_error_code ret;
+ krb5_ccache id = NULL;
+
+ ret = krb5_parse_name(context, principal, &client);
+ if (ret)
+ krb5_err(context, 1, ret, "parse_name for %s failed", principal);
+
+ ret = krb5_cc_cache_match(context, client, &id);
+ if (ret && find)
+ krb5_err(context, 1, ret, "cc_cache_match for %s failed", principal);
+ if (ret == 0 && !find)
+ krb5_err(context, 1, ret, "cc_cache_match for %s found", principal);
+
+ if (id)
+ krb5_cc_close(context, id);
+ krb5_free_principal(context, client);
+}
+
+
+static void
+test_cache_iter(krb5_context context, const char *type, int destroy)
+{
+ krb5_cc_cache_cursor cursor;
+ krb5_error_code ret;
+ krb5_ccache id;
+
+ ret = krb5_cc_cache_get_first (context, type, &cursor);
+ if (ret == KRB5_CC_NOSUPP)
+ return;
+ else if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_cache_get_first(%s)", type);
+
+
+ while ((ret = krb5_cc_cache_next (context, cursor, &id)) == 0) {
+ krb5_principal principal;
+ char *name;
+
+ heim_assert(id != NULL, "credentials cache is non-NULL");
+ if (debug_flag)
+ printf("name: %s\n", krb5_cc_get_name(context, id));
+ ret = krb5_cc_get_principal(context, id, &principal);
+ if (ret == 0) {
+ ret = krb5_unparse_name(context, principal, &name);
+ if (ret == 0) {
+ if (debug_flag)
+ printf("\tprincipal: %s\n", name);
+ free(name);
+ }
+ krb5_free_principal(context, principal);
+ }
+ if (destroy)
+ krb5_cc_destroy(context, id);
+ else
+ krb5_cc_close(context, id);
+ }
+
+ krb5_cc_cache_end_seq_get(context, cursor);
+}
+
+static void
+test_cache_iter_all(krb5_context context)
+{
+ krb5_cccol_cursor cursor;
+ krb5_error_code ret;
+ krb5_ccache id;
+
+ ret = krb5_cccol_cursor_new (context, &cursor);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cccol_cursor_new");
+
+
+ while ((ret = krb5_cccol_cursor_next (context, cursor, &id)) == 0 && id != NULL) {
+ krb5_principal principal;
+ char *name;
+
+ if (debug_flag)
+ printf("name: %s\n", krb5_cc_get_name(context, id));
+ ret = krb5_cc_get_principal(context, id, &principal);
+ if (ret == 0) {
+ ret = krb5_unparse_name(context, principal, &name);
+ if (ret == 0) {
+ if (debug_flag)
+ printf("\tprincipal: %s\n", name);
+ free(name);
+ }
+ krb5_free_principal(context, principal);
+ }
+ krb5_cc_close(context, id);
+ }
+
+ krb5_cccol_cursor_free(context, &cursor);
+}
+
+
+static void
+test_copy(krb5_context context, const char *from, const char *to)
+{
+ krb5_ccache fromid, toid;
+ krb5_error_code ret;
+ krb5_principal p, p2;
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_new_unique(context, from, NULL, &fromid);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique: %s", from);
+
+ if (strcmp(krb5_cc_get_type(context, fromid), "FILE") == 0)
+ unlink_this = krb5_cc_get_name(context, fromid);
+
+ ret = krb5_cc_initialize(context, fromid, p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ ret = krb5_cc_new_unique(context, to, NULL, &toid);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_gen_new: %s", to);
+
+ if (strcmp(krb5_cc_get_type(context, toid), "FILE") == 0)
+ unlink_this2 = krb5_cc_get_name(context, toid);
+
+ ret = krb5_cc_copy_cache(context, fromid, toid);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_copy_cache");
+
+ ret = krb5_cc_get_principal(context, toid, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ if (krb5_principal_compare(context, p, p2) == FALSE)
+ krb5_errx(context, 1, "p != p2");
+
+ krb5_free_principal(context, p);
+ krb5_free_principal(context, p2);
+
+ krb5_cc_destroy(context, fromid);
+ krb5_cc_destroy(context, toid);
+ unlink_this = unlink_this2 = NULL;
+}
+
+static void
+test_move(krb5_context context, const char *type)
+{
+ const krb5_cc_ops *ops;
+ krb5_ccache fromid, toid;
+ krb5_error_code ret;
+ krb5_principal p, p2;
+ krb5_creds cred, tocred;
+
+ ops = krb5_cc_get_prefix_ops(context, type);
+ if (ops == NULL)
+ return;
+
+ ret = krb5_cc_new_unique(context, type, NULL, &fromid);
+ if (ret == KRB5_CC_NOSUPP)
+ return;
+ else if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique: %s", type);
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_initialize(context, fromid, p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ memset(&cred, 0, sizeof(cred));
+ ret = krb5_parse_name(context, "krbtgt/SU.SE@SU.SE", &cred.server);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+ ret = krb5_parse_name(context, "lha@SU.SE", &cred.client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_store_cred(context, fromid, &cred);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_store_cred");
+
+
+ ret = krb5_cc_new_unique(context, type, NULL, &toid);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique");
+
+ ret = krb5_cc_move(context, fromid, toid);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_move");
+
+ ret = krb5_cc_get_principal(context, toid, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ if (krb5_principal_compare(context, p, p2) == FALSE)
+ krb5_errx(context, 1, "p != p2");
+
+ ret = krb5_cc_retrieve_cred(context, toid, 0, &cred, &tocred);
+ if (ret)
+ krb5_errx(context, 1, "move failed");
+ krb5_free_cred_contents(context, &cred);
+ krb5_free_cred_contents(context, &tocred);
+
+ krb5_free_principal(context, p);
+ krb5_free_principal(context, p2);
+ krb5_cc_destroy(context, toid);
+}
+
+
+static void
+test_prefix_ops(krb5_context context, const char *name, const krb5_cc_ops *ops)
+{
+ const krb5_cc_ops *o;
+
+ o = krb5_cc_get_prefix_ops(context, name);
+ if (o == NULL)
+ krb5_errx(context, 1, "found no match for prefix '%s'", name);
+ if (strcmp(o->prefix, ops->prefix) != 0)
+ krb5_errx(context, 1, "ops for prefix '%s' is not "
+ "the expected %s != %s", name, o->prefix, ops->prefix);
+}
+
+static void
+test_cc_config(krb5_context context, const char *cc_type,
+ const char *cc_name, size_t count)
+{
+ krb5_error_code ret;
+ krb5_principal p;
+ krb5_ccache id;
+ unsigned int i;
+
+ ret = krb5_cc_new_unique(context, cc_type, cc_name, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique");
+
+ ret = krb5_parse_name(context, "lha@SU.SE", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_cc_initialize(context, id, p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_initialize");
+
+ for (i = 0; i < count; i++) {
+ krb5_data data, data2;
+ const char *name = "foo";
+ krb5_principal p1 = NULL;
+
+ if (i & 1)
+ p1 = p;
+
+ data.data = rk_UNCONST(name);
+ data.length = strlen(name);
+
+ /*
+ * Because of how krb5_cc_set_config() this will also test
+ * krb5_cc_remove_cred().
+ */
+ ret = krb5_cc_set_config(context, id, p1, "FriendlyName", &data);
+ if (ret)
+ krb5_errx(context, 1, "krb5_cc_set_config: add");
+
+ ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
+ if (ret)
+ krb5_errx(context, 1, "krb5_cc_get_config: first");
+
+ if (data.length != data2.length ||
+ memcmp(data.data, data2.data, data.length) != 0)
+ krb5_errx(context, 1, "krb5_cc_get_config: did not fetch what was set");
+
+ krb5_data_free(&data2);
+
+ data.data = rk_UNCONST("bar");
+ data.length = strlen("bar");
+
+ ret = krb5_cc_set_config(context, id, p1, "FriendlyName", &data);
+ if (ret)
+ krb5_errx(context, 1, "krb5_cc_set_config: add -second");
+
+ ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
+ if (ret)
+ krb5_errx(context, 1, "krb5_cc_get_config: second");
+
+ if (data.length != data2.length ||
+ memcmp(data.data, data2.data, data.length) != 0)
+ krb5_errx(context, 1, "krb5_cc_get_config: replace failed");
+
+ krb5_data_free(&data2);
+
+ ret = krb5_cc_set_config(context, id, p1, "FriendlyName", NULL);
+ if (ret)
+ krb5_errx(context, 1, "krb5_cc_set_config: delete");
+
+ ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
+ if (ret == 0)
+ krb5_errx(context, 1, "krb5_cc_get_config: non-existant");
+
+ if (data2.length)
+ krb5_errx(context, 1, "krb5_cc_get_config: delete failed");
+ }
+
+ krb5_cc_destroy(context, id);
+ krb5_free_principal(context, p);
+}
+
+static krb5_error_code
+test_cccol(krb5_context context, const char *def_cccol, const char **what)
+{
+ krb5_cc_cache_cursor cursor;
+ krb5_error_code ret;
+ krb5_principal p1, p2;
+ krb5_ccache id, id1, id2;
+ krb5_creds cred1, cred2;
+ size_t match1 = 0;
+ size_t match2 = 0;
+
+ memset(&cred1, 0, sizeof(cred1));
+ memset(&cred2, 0, sizeof(cred2));
+
+ *what = "krb5_parse_name";
+ ret = krb5_parse_name(context, "krbtgt/SU.SE@SU.SE", &cred1.server);
+ if (ret) return ret;
+ ret = krb5_parse_name(context, "lha@SU.SE", &cred1.client);
+ if (ret) return ret;
+ ret = krb5_parse_name(context, "krbtgt/H5L.SE@H5L.SE", &cred2.server);
+ if (ret) return ret;
+ ret = krb5_parse_name(context, "lha@H5L.SE", &cred2.client);
+ if (ret) return ret;
+ *what = "krb5_cc_set_default_name";
+ ret = krb5_cc_set_default_name(context, def_cccol);
+ if (ret) return ret;
+ *what = "krb5_cc_default";
+ ret = krb5_cc_default(context, &id1);
+ if (ret) return ret;
+ *what = "krb5_cc_initialize";
+ ret = krb5_cc_initialize(context, id1, cred1.client);
+ if (ret) return ret;
+ *what = "krb5_cc_store_cred";
+ ret = krb5_cc_store_cred(context, id1, &cred1);
+ if (ret) return ret;
+ *what = "krb5_cc_resolve";
+ ret = krb5_cc_resolve_for(context, NULL, def_cccol, cred2.client, &id2);
+ if (ret) return ret;
+ *what = "krb5_cc_initialize";
+ ret = krb5_cc_initialize(context, id2, cred2.client);
+ if (ret) return ret;
+ *what = "krb5_cc_store_cred";
+ ret = krb5_cc_store_cred(context, id2, &cred2);
+ if (ret) return ret;
+
+ krb5_cc_close(context, id1);
+ krb5_cc_close(context, id2);
+ id1 = id2 = NULL;
+
+ *what = "krb5_cc_default";
+ ret = krb5_cc_default(context, &id1);
+ if (ret) return ret;
+ *what = "krb5_cc_resolve";
+ ret = krb5_cc_resolve_for(context, NULL, def_cccol, cred2.client, &id2);
+ if (ret) return ret;
+
+ *what = "krb5_cc_get_principal";
+ ret = krb5_cc_get_principal(context, id1, &p1);
+ if (ret) return ret;
+ ret = krb5_cc_get_principal(context, id2, &p2);
+ if (ret) return ret;
+
+ if (!krb5_principal_compare(context, p1, cred1.client)) {
+ char *u1 = NULL;
+ char *u2 = NULL;
+
+ (void) krb5_unparse_name(context, p1, &u1);
+ (void) krb5_unparse_name(context, cred1.client, &u2);
+ warnx("Inconsistent principals for ccaches in %s: %s vs %s "
+ "(expected lha@SU.SE)", def_cccol, u1, u2);
+ return EINVAL;
+ }
+ if (!krb5_principal_compare(context, p2, cred2.client)) {
+ char *u1 = NULL;
+ char *u2 = NULL;
+
+ (void) krb5_unparse_name(context, p2, &u1);
+ (void) krb5_unparse_name(context, cred2.client, &u2);
+ warnx("Inconsistent principals for ccaches in %s: %s and %s "
+ "(expected lha@H5L.SE)", def_cccol, u1, u2);
+ return EINVAL;
+ }
+ krb5_free_principal(context, p1);
+ krb5_free_principal(context, p2);
+
+ *what = "krb5_cc_cache_get_first";
+ ret = krb5_cc_cache_get_first(context, NULL, &cursor);
+ if (ret) return ret;
+ *what = "krb5_cc_cache_next";
+ while (krb5_cc_cache_next(context, cursor, &id) == 0) {
+ krb5_principal p;
+
+ *what = "krb5_cc_get_principal";
+ ret = krb5_cc_get_principal(context, id, &p);
+ if (ret) return ret;
+ if (krb5_principal_compare(context, p, cred1.client))
+ match1++;
+ else if (krb5_principal_compare(context, p, cred2.client))
+ match2++;
+ krb5_free_principal(context, p);
+ krb5_cc_close(context, id);
+ }
+ (void) krb5_cc_cache_end_seq_get(context, cursor);
+
+ *what = "cccol iteration inconsistency";
+ if (match1 != 1 || match2 != 1)
+ return EINVAL;
+
+ krb5_cc_close(context, id1);
+ krb5_cc_close(context, id2);
+
+ krb5_free_cred_contents(context, &cred1);
+ krb5_free_cred_contents(context, &cred2);
+
+ return 0;
+}
+
+static void
+test_cccol_dcache(krb5_context context)
+{
+ krb5_error_code ret;
+ char *dcc = NULL;
+ const char *what;
+
+ if (asprintf(&dcc, "DIR:%s/dcc", tmpdir) == -1 || dcc == NULL)
+ krb5_err(context, 1, errno, "asprintf");
+
+ ret = test_cccol(context, dcc, &what);
+ free(dcc);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+}
+
+static void
+test_cccol_scache(krb5_context context)
+{
+ krb5_error_code ret;
+ char *scache = NULL;
+ const char *what;
+ int fd;
+
+ if (asprintf(&scache, "SCC:%s/scache", tmpdir) == -1 || scache == NULL)
+ krb5_err(context, 1, errno, "asprintf");
+ if ((fd = open(scache + sizeof("SCC:") - 1, O_CREAT | O_RDWR, 0600)) == -1)
+ krb5_err(context, 1, errno, "open(%s)", scache + sizeof("SCC:") - 1);
+ (void) close(fd);
+
+ ret = test_cccol(context, scache, &what);
+ (void) unlink(scache + sizeof("SCC:") - 1);
+ free(scache);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+}
+
+
+static struct getargs args[] = {
+ {"debug", 'd', arg_flag, &debug_flag,
+ "turn on debuggin", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "hostname ...");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0;
+ krb5_ccache id1, id2;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ make_dir(context);
+
+ test_cache_remove(context, krb5_cc_type_file);
+ test_cache_remove(context, krb5_cc_type_memory);
+#ifdef USE_SQLITE
+ test_cache_remove(context, krb5_cc_type_scc);
+#endif
+#ifdef HAVE_KEYUTILS_H
+ keyctl_join_session_keyring(NULL);
+ test_cache_remove(context, krb5_cc_type_keyring);
+#endif
+
+ test_default_name(context);
+ test_mcache(context);
+ /*
+ * XXX Make sure to set default ccache names for each cc type!
+ * Otherwise we clobber the user's ccaches.
+ */
+ test_init_vs_destroy(context, krb5_cc_type_memory);
+ test_init_vs_destroy(context, krb5_cc_type_file);
+#if 0
+ test_init_vs_destroy(context, krb5_cc_type_api);
+#endif
+ /*
+ * Cleanup so we can check that the permissions on the directory created by
+ * scc are correct.
+ */
+ cleanup();
+ test_init_vs_destroy(context, krb5_cc_type_scc);
+
+#if defined(S_IRWXG) && defined(S_IRWXO)
+ {
+ struct stat st;
+
+ if (stat(tmpdir, &st) == 0) {
+ if ((st.st_mode & S_IRWXG) ||
+ (st.st_mode & S_IRWXO))
+ krb5_errx(context, 1,
+ "SQLite3 ccache dir perms wrong: %d", st.st_mode);
+ }
+ }
+#endif
+ test_init_vs_destroy(context, krb5_cc_type_dcc);
+#ifdef HAVE_KEYUTILS_H
+ test_init_vs_destroy(context, krb5_cc_type_keyring);
+#endif
+ test_mcc_default();
+ test_def_cc_name(context);
+
+ test_cache_iter_all(context);
+
+ test_cache_iter(context, krb5_cc_type_memory, 0);
+ {
+ krb5_principal p;
+ krb5_cc_new_unique(context, krb5_cc_type_memory, "bar", &id1);
+ krb5_cc_new_unique(context, krb5_cc_type_memory, "baz", &id2);
+ krb5_parse_name(context, "lha@SU.SE", &p);
+ krb5_cc_initialize(context, id1, p);
+ krb5_free_principal(context, p);
+ }
+
+ test_cache_find(context, "lha@SU.SE", 1);
+ test_cache_find(context, "hulabundulahotentot@SU.SE", 0);
+
+ /*
+ * XXX We should compose and krb5_cc_set_default_name() a default ccache
+ * for each cc type that we test with test_cache_iter(), and we should do
+ * that inside test_cache_iter().
+ *
+ * Alternatively we should remove test_cache_iter() in favor of
+ * test_cccol(), which is a much more complete test.
+ */
+ test_cache_iter(context, krb5_cc_type_memory, 0);
+ test_cache_iter(context, krb5_cc_type_memory, 1);
+ test_cache_iter(context, krb5_cc_type_memory, 0);
+ test_cache_iter(context, krb5_cc_type_file, 0);
+ test_cache_iter(context, krb5_cc_type_api, 0);
+ test_cache_iter(context, krb5_cc_type_scc, 0);
+ test_cache_iter(context, krb5_cc_type_scc, 1);
+#if 0
+ test_cache_iter(context, krb5_cc_type_dcc, 0);
+ test_cache_iter(context, krb5_cc_type_dcc, 1);
+#endif
+#ifdef HAVE_KEYUTILS_H
+ test_cache_iter(context, krb5_cc_type_keyring, 0);
+ test_cache_iter(context, krb5_cc_type_keyring, 1);
+#endif
+
+ test_copy(context, krb5_cc_type_file, krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_memory, krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_file, krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_scc, krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_file, krb5_cc_type_scc);
+ test_copy(context, krb5_cc_type_scc, krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, krb5_cc_type_scc);
+#if 0
+ test_copy(context, krb5_cc_type_dcc, krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_dcc, krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_dcc, krb5_cc_type_scc);
+#endif
+#ifdef HAVE_KEYUTILS_H
+ test_copy(context, krb5_cc_type_keyring, krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_file, krb5_cc_type_file);
+ test_copy(context, "KEYRING:", "KEYRING:bar");
+ test_copy(context, "KEYRING:bar", "KEYRING:baz");
+# ifdef HAVE_KEYCTL_GET_PERSISTENT
+ test_copy(context, krb5_cc_type_file, "KEYRING:persistent");
+ test_copy(context, "KEYRING:persistent:", krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_file, "KEYRING:persistent:foo");
+ test_copy(context, "KEYRING:persistent:foo", krb5_cc_type_file);
+# endif
+ test_copy(context, krb5_cc_type_memory, "KEYRING:process:");
+ test_copy(context, "KEYRING:process:", krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, "KEYRING:process:foo");
+ test_copy(context, "KEYRING:process:foo", krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, "KEYRING:thread:");
+ test_copy(context, "KEYRING:thread:", krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, "KEYRING:thread:foo");
+ test_copy(context, "KEYRING:thread:foo", krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, "KEYRING:session:");
+ test_copy(context, "KEYRING:session:", krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_memory, "KEYRING:session:foo");
+ test_copy(context, "KEYRING:session:foo", krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_file, "KEYRING:user:");
+ test_copy(context, "KEYRING:user:", krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_file, "KEYRING:user:foo");
+ test_copy(context, "KEYRING:user:foo", krb5_cc_type_memory);
+#endif /* HAVE_KEYUTILS_H */
+
+ test_move(context, krb5_cc_type_file);
+ test_move(context, krb5_cc_type_memory);
+ test_move(context, krb5_cc_type_scc);
+#if 0
+ test_move(context, krb5_cc_type_dcc);
+#endif
+#ifdef HAVE_KEYUTILS_H
+ test_move(context, krb5_cc_type_keyring);
+# ifdef HAVE_KEYCTL_GET_PERSISTENT
+ test_move(context, "KEYRING:persistent:");
+ test_move(context, "KEYRING:persistent:foo");
+# endif
+ test_move(context, "KEYRING:process:");
+ test_move(context, "KEYRING:process:foo");
+ test_move(context, "KEYRING:thread:");
+ test_move(context, "KEYRING:thread:foo");
+ test_move(context, "KEYRING:session:");
+ test_move(context, "KEYRING:session:foo");
+ test_move(context, "KEYRING:user:");
+ test_move(context, "KEYRING:user:foo");
+#endif /* HAVE_KEYUTILS_H */
+
+ test_prefix_ops(context, "FILE:/tmp/foo", &krb5_fcc_ops);
+ test_prefix_ops(context, "FILE", &krb5_fcc_ops);
+ test_prefix_ops(context, "MEMORY", &krb5_mcc_ops);
+ test_prefix_ops(context, "MEMORY:foo", &krb5_mcc_ops);
+ test_prefix_ops(context, "/tmp/kaka", &krb5_fcc_ops);
+#ifdef HAVE_SCC
+ test_prefix_ops(context, "SCC:", &krb5_scc_ops);
+ test_prefix_ops(context, "SCC:foo", &krb5_scc_ops);
+#endif
+#if 0
+ test_prefix_ops(context, "DIR:", &krb5_dcc_ops);
+ test_prefix_ops(context, "DIR:tkt1", &krb5_dcc_ops);
+#endif
+#ifdef HAVE_KEYUTILS_H
+ test_prefix_ops(context, "KEYRING:", &krb5_krcc_ops);
+ test_prefix_ops(context, "KEYRING:foo", &krb5_krcc_ops);
+#endif /* HAVE_KEYUTILS_H */
+
+ krb5_cc_destroy(context, id1);
+ krb5_cc_destroy(context, id2);
+
+ test_cc_config(context, "MEMORY", "bar", 1000); /* 1000 because fast */
+ test_cc_config(context, "FILE", "/tmp/foocc", 30); /* 30 because slower */
+
+ test_cccol_dcache(context);
+ test_cccol_scache(context);
+#ifdef HAVE_KEYUTILS_H
+ {
+ const char *what;
+
+ ret = test_cccol(context, "KEYRING:legacy:fooccol", &what);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+
+ ret = test_cccol(context, "MEMORY:fooccol", &what);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+ }
+#endif /* HAVE_KEYUTILS_H */
+
+ {
+ const char *what;
+ char *config = NULL;
+ char *fname = NULL;
+ char *d = NULL;
+
+ if (asprintf(&d, "%s/cccol", tmpdir) == -1 || d == NULL)
+ krb5_err(context, 1, errno, "asprintf");
+ if (mkdir(d, 0700) == -1)
+ krb5_err(context, 1, errno, "mkdir(%s)", d);
+ if (asprintf(&fname, "%s/foobar", d) == -1 || fname == NULL ||
+ asprintf(&config,
+ "[libdefaults]\n"
+ "\tdefault_file_cache_collections = FILE:%1$s/cccol/foobar\n"
+ "\tenable_file_cache_iteration = true\n",
+ tmpdir) == -1 || config == NULL)
+ krb5_err(context, 1, errno, "asprintf");
+ ret = krb5_set_config(context, config);
+ if (ret)
+ krb5_err(context, 1, ret,
+ "Could not configure context from string:\n%s\n", config);
+ ret = test_cccol(context, fname, &what);
+ if (ret)
+ krb5_err(context, 1, ret, "%s", what);
+ free(config);
+ free(fname);
+ free(d);
+ }
+
+ krb5_free_context(context);
+
+#if 0
+ sleep(60);
+#endif
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_config.c b/third_party/heimdal/lib/krb5/test_config.c
new file mode 100644
index 0000000..a281633
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_config.c
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+
+static int
+check_config_file(krb5_context context, char *filelist, char **res, int def)
+{
+ krb5_error_code ret;
+ char **pp;
+ int i;
+
+ pp = NULL;
+
+ if (def)
+ ret = krb5_prepend_config_files_default(filelist, &pp);
+ else
+ ret = krb5_prepend_config_files(filelist, NULL, &pp);
+
+ if (ret)
+ krb5_err(context, 1, ret, "prepend_config_files");
+
+ for (i = 0; res[i] && pp[i]; i++)
+ if (strcmp(pp[i], res[i]) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", pp[i], res[i]);
+
+ if (res[i] != NULL)
+ krb5_errx(context, 1, "pp ended before res list");
+
+ if (def) {
+ char **deflist;
+ int j;
+
+ ret = krb5_get_default_config_files(&deflist);
+ if (ret)
+ krb5_err(context, 1, ret, "get_default_config_files");
+
+ for (j = 0 ; pp[i] && deflist[j]; i++, j++)
+ if (strcmp(pp[i], deflist[j]) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", pp[i], deflist[j]);
+
+ if (deflist[j] != NULL)
+ krb5_errx(context, 1, "pp ended before def list");
+ krb5_free_config_files(deflist);
+ }
+
+ if (pp[i] != NULL)
+ krb5_errx(context, 1, "pp ended after res (and def) list");
+
+ krb5_free_config_files(pp);
+
+ return 0;
+}
+
+char *list0[] = { "/tmp/foo", NULL };
+char *list1[] = { "/tmp/foo", "/tmp/foo/bar", NULL };
+char *list2[] = { "", NULL };
+
+struct {
+ char *fl;
+ char **res;
+} test[] = {
+ { "/tmp/foo", NULL },
+ { "/tmp/foo" PATH_SEP "/tmp/foo/bar", NULL },
+ { "", NULL }
+};
+
+static void
+check_config_files(void)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context %d", ret);
+
+ test[0].res = list0;
+ test[1].res = list1;
+ test[2].res = list2;
+
+ for (i = 0; i < sizeof(test)/sizeof(*test); i++) {
+ check_config_file(context, test[i].fl, test[i].res, 0);
+ check_config_file(context, test[i].fl, test[i].res, 1);
+ }
+
+ krb5_free_context(context);
+}
+
+const char *config_string_result0[] = {
+ "A", "B", "C", "D", NULL
+};
+
+const char *config_string_result1[] = {
+ "A", "B", "C D", NULL
+};
+
+const char *config_string_result2[] = {
+ "A", "B", "", NULL
+};
+
+const char *config_string_result3[] = {
+ "A B;C: D", NULL
+};
+
+const char *config_string_result4[] = {
+ "\"\"", "", "\"\"", NULL
+};
+
+const char *config_string_result5[] = {
+ "A\"BQd", NULL
+};
+
+const char *config_string_result6[] = {
+ "efgh\"", "ABC", NULL
+};
+
+const char *config_string_result7[] = {
+ "SnapeKills\\", "Dumbledore", NULL
+};
+
+const char *config_string_result8[] = {
+ "\"TownOf Sandwich: Massachusetts\"Oldest", "Town", "In", "Cape Cod", NULL
+};
+
+const char *config_string_result9[] = {
+ "\"Begins and\"ends", "In", "One", "String", NULL
+};
+
+const char *config_string_result10[] = {
+ "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.",
+ "Why do we test with such long strings? Because some people have config files",
+ "That", "look", "Like this.", NULL
+};
+
+const struct {
+ const char * name;
+ const char ** expected;
+} config_strings_tests[] = {
+ { "foo", config_string_result0 },
+ { "bar", config_string_result1 },
+ { "baz", config_string_result2 },
+ { "quux", config_string_result3 },
+ { "questionable", config_string_result4 },
+ { "mismatch1", config_string_result5 },
+ { "mismatch2", config_string_result6 },
+ { "internal1", config_string_result7 },
+ { "internal2", config_string_result8 },
+ { "internal3", config_string_result9 },
+ { "longer_strings", config_string_result10 }
+};
+
+static void
+check_escaped_strings(void)
+{
+ krb5_context context;
+ krb5_config_section *c = NULL;
+ krb5_error_code ret;
+ int i;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context %d", ret);
+
+ ret = krb5_config_parse_file(context, "test_config_strings.out", &c);
+ if (ret)
+ krb5_errx(context, 1, "krb5_config_parse_file()");
+
+ for (i=0; i < sizeof(config_strings_tests)/sizeof(config_strings_tests[0]); i++) {
+ char **ps;
+ const char **s;
+ const char **e;
+
+ ps = krb5_config_get_strings(context, c, "escapes", config_strings_tests[i].name,
+ NULL);
+ if (ps == NULL)
+ errx(1, "Failed to read string value %s", config_strings_tests[i].name);
+
+ e = config_strings_tests[i].expected;
+
+ for (s = (const char **)ps; *s && *e; s++, e++) {
+ if (strcmp(*s, *e))
+ errx(1,
+ "Unexpected configuration string at value [%s].\n"
+ "Actual=[%s]\n"
+ "Expected=[%s]\n",
+ config_strings_tests[i].name, *s, *e);
+ }
+
+ if (*s || *e)
+ errx(1, "Configuation string list for value [%s] has incorrect length.",
+ config_strings_tests[i].name);
+
+ krb5_config_free_strings(ps);
+ }
+
+ ret = krb5_config_file_free(context, c);
+ if (ret)
+ krb5_errx(context, 1, "krb5_config_file_free()");
+
+ krb5_free_context(context);
+}
+
+int
+main(int argc, char **argv)
+{
+ check_config_files();
+ check_escaped_strings();
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_config_strings.cfg b/third_party/heimdal/lib/krb5/test_config_strings.cfg
new file mode 100644
index 0000000..c62201b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_config_strings.cfg
@@ -0,0 +1,12 @@
+[escapes]
+ foo = A B C D
+ bar = A B "C D"
+ baz = A B ""
+ quux = "A B;C: D"
+ questionable="""" "" """"
+ mismatch1 = A"BQd
+ mismatch2 = efgh" ABC
+ internal1 = "SnapeKills\" "Dumbledore"
+ internal2 = "TownOf Sandwich: Massachusetts"Oldest Town In "Cape Cod"
+ internal3 = "Begins and"ends In One String
+ longer_strings = "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." "Why do we test with such long strings? Because some people have config files" That look "Like this."
diff --git a/third_party/heimdal/lib/krb5/test_crypto.c b/third_party/heimdal/lib/krb5/test_crypto.c
new file mode 100644
index 0000000..34a709a
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_crypto.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2003-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+static void
+time_encryption(krb5_context context, size_t size,
+ krb5_enctype etype, int iterations)
+{
+ struct timeval tv1, tv2;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_crypto crypto;
+ krb5_data data;
+ char *etype_name;
+ void *buf;
+ int i;
+
+ ret = krb5_generate_random_keyblock(context, etype, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ ret = krb5_enctype_to_string(context, etype, &etype_name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_enctype_to_string");
+
+ buf = malloc(size);
+ if (buf == NULL)
+ krb5_errx(context, 1, "out of memory");
+ memset(buf, 0, size);
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+ gettimeofday(&tv1, NULL);
+
+ for (i = 0; i < iterations; i++) {
+ ret = krb5_encrypt(context, crypto, 0, buf, size, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "encrypt: %d", i);
+ krb5_data_free(&data);
+ }
+
+ gettimeofday(&tv2, NULL);
+
+ timevalsub(&tv2, &tv1);
+
+ printf("%s size: %7lu iterations: %d time: %3ld.%06ld\n",
+ etype_name, (unsigned long)size, iterations,
+ (long)tv2.tv_sec, (long)tv2.tv_usec);
+
+ free(buf);
+ free(etype_name);
+ krb5_crypto_destroy(context, crypto);
+ krb5_free_keyblock_contents(context, &key);
+}
+
+static void
+time_s2k(krb5_context context,
+ krb5_enctype etype,
+ const char *password,
+ krb5_salt salt,
+ int iterations)
+{
+ struct timeval tv1, tv2;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_data opaque;
+ char *etype_name;
+ int i;
+
+ ret = krb5_enctype_to_string(context, etype, &etype_name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_enctype_to_string");
+
+ opaque.data = NULL;
+ opaque.length = 0;
+
+ gettimeofday(&tv1, NULL);
+
+ for (i = 0; i < iterations; i++) {
+ ret = krb5_string_to_key_salt_opaque(context, etype, password, salt,
+ opaque, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_key_data_salt_opaque");
+ krb5_free_keyblock_contents(context, &key);
+ }
+
+ gettimeofday(&tv2, NULL);
+
+ timevalsub(&tv2, &tv1);
+
+ printf("%s string2key %d iterations time: %3ld.%06ld\n",
+ etype_name, iterations, (long)tv2.tv_sec, (long)tv2.tv_usec);
+ free(etype_name);
+
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, enciter, s2kiter;
+ int optidx = 0;
+ krb5_salt salt;
+
+ krb5_enctype enctypes[] = {
+#if 0
+ ETYPE_DES_CBC_CRC,
+ ETYPE_DES3_CBC_SHA1,
+ ETYPE_ARCFOUR_HMAC_MD5,
+#endif
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
+ ETYPE_AES256_CTS_HMAC_SHA384_192
+ };
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ salt.salttype = KRB5_PW_SALT;
+ salt.saltvalue.data = NULL;
+ salt.saltvalue.length = 0;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ enciter = 1000;
+ s2kiter = 100;
+
+ for (i = 0; i < sizeof(enctypes)/sizeof(enctypes[0]); i++) {
+
+ krb5_enctype_enable(context, enctypes[i]);
+
+ time_encryption(context, 16, enctypes[i], enciter);
+ time_encryption(context, 32, enctypes[i], enciter);
+ time_encryption(context, 512, enctypes[i], enciter);
+ time_encryption(context, 1024, enctypes[i], enciter);
+ time_encryption(context, 2048, enctypes[i], enciter);
+ time_encryption(context, 4096, enctypes[i], enciter);
+ time_encryption(context, 8192, enctypes[i], enciter);
+ time_encryption(context, 16384, enctypes[i], enciter);
+ time_encryption(context, 32768, enctypes[i], enciter);
+
+ time_s2k(context, enctypes[i], "mYsecreitPassword", salt, s2kiter);
+ }
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_crypto_wrapping.c b/third_party/heimdal/lib/krb5/test_crypto_wrapping.c
new file mode 100644
index 0000000..aff4e8d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_crypto_wrapping.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+static void
+test_wrapping(krb5_context context,
+ size_t min_size,
+ size_t max_size,
+ size_t step,
+ krb5_enctype etype)
+{
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_crypto crypto;
+ krb5_data data;
+ char *etype_name;
+ void *buf;
+ size_t size;
+
+ ret = krb5_generate_random_keyblock(context, etype, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ ret = krb5_enctype_to_string(context, etype, &etype_name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_enctype_to_string");
+
+ buf = malloc(max_size);
+ if (buf == NULL)
+ krb5_errx(context, 1, "out of memory");
+ memset(buf, 0, max_size);
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+ for (size = min_size; size < max_size; size += step) {
+ size_t wrapped_size;
+
+ ret = krb5_encrypt(context, crypto, 0, buf, size, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "encrypt size %lu using %s",
+ (unsigned long)size, etype_name);
+
+ wrapped_size = krb5_get_wrapped_length(context, crypto, size);
+
+ if (wrapped_size != data.length)
+ krb5_errx(context, 1, "calculated wrapped length %lu != "
+ "real wrapped length %lu for data length %lu using "
+ "enctype %s",
+ (unsigned long)wrapped_size,
+ (unsigned long)data.length,
+ (unsigned long)size,
+ etype_name);
+ krb5_data_free(&data);
+ }
+
+ free(etype_name);
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+ krb5_free_keyblock_contents(context, &key);
+}
+
+
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, optidx = 0;
+
+ krb5_enctype enctypes[] = {
+#ifdef HEIM_WEAK_CRYPTO
+ ETYPE_DES_CBC_CRC,
+ ETYPE_DES_CBC_MD4,
+ ETYPE_DES_CBC_MD5,
+#endif
+ ETYPE_DES3_CBC_SHA1,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192
+ };
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ for (i = 0; i < sizeof(enctypes)/sizeof(enctypes[0]); i++) {
+ krb5_enctype_enable(context, enctypes[i]);
+
+ test_wrapping(context, 0, 1024, 1, enctypes[i]);
+ test_wrapping(context, 1024, 1024 * 100, 1024, enctypes[i]);
+ }
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_expand_toks.c b/third_party/heimdal/lib/krb5/test_expand_toks.c
new file mode 100644
index 0000000..9b105fb
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_expand_toks.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <getarg.h>
+#include <err.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0;
+ char *expanded;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = _krb5_expand_path_tokensv(context, "/tmp/%{foo}/%{bar}%{baz}/x", 0,
+ &expanded,
+ "foo", "abc",
+ "bar", "dce",
+ "baz", "fgh",
+ NULL);
+ if (ret)
+ krb5_err(context, ret, 1, "Token expansion failed");
+
+#ifdef _WIN32
+#define EXPANDED_SHOULD_BE "\\tmp\\abc\\dcefgh\\x"
+#else
+#define EXPANDED_SHOULD_BE "/tmp/abc/dcefgh/x"
+#endif
+
+ if (strcmp(expanded, EXPANDED_SHOULD_BE) != 0)
+ krb5_errx(context, 1, "Token expansion incorrect");
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_forward.c b/third_party/heimdal/lib/krb5/test_forward.c
new file mode 100644
index 0000000..3782e50
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_forward.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "hostname");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ const char *hostname;
+ krb5_context context;
+ krb5_auth_context ac;
+ krb5_error_code ret;
+ krb5_creds cred;
+ krb5_ccache id;
+ krb5_data data;
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 1)
+ usage(1);
+
+ hostname = argv[0];
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_cc_default(context, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default failed");
+
+ ret = krb5_auth_con_init(context, &ac);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_auth_con_init failed");
+
+ krb5_auth_con_addflags(context, ac,
+ KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED, NULL);
+
+ ret = krb5_cc_get_principal(context, id, &cred.client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_get_principal");
+
+ ret = krb5_make_principal(context,
+ &cred.server,
+ krb5_principal_get_realm(context, cred.client),
+ KRB5_TGS_NAME,
+ krb5_principal_get_realm(context, cred.client),
+ NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_make_principal(server)");
+
+ ret = krb5_get_forwarded_creds (context,
+ ac,
+ id,
+ KDC_OPT_FORWARDABLE,
+ hostname,
+ &cred,
+ &data);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_forwarded_creds");
+
+ krb5_data_free(&data);
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_fx.c b/third_party/heimdal/lib/krb5/test_fx.c
new file mode 100644
index 0000000..68f00dc
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_fx.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 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 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 <err.h>
+#include <getarg.h>
+
+struct {
+ char *p1;
+ char *pepper1;
+ krb5_enctype e1;
+ char *p2;
+ char *pepper2;
+ krb5_enctype e2;
+ krb5_enctype e3;
+ char *key;
+ size_t len;
+} cf2[] = {
+ {
+ "key1", "a", ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "key2", "b", ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "\x97\xdf\x97\xe4\xb7\x98\xb2\x9e\xb3\x1e\xd7\x28\x02\x87\xa9\x2a",
+ 16
+ },
+ {
+ "key1", "a", ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "key2", "b", ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "\x4d\x6c\xa4\xe6\x29\x78\x5c\x1f\x01\xba\xf5\x5e\x2e\x54\x85\x66"
+ "\xb9\x61\x7a\xe3\xa9\x68\x68\xc3\x37\xcb\x93\xb5\xe7\x2b\x1c\x7b",
+ 32
+ },
+ {
+ "key1", "a", ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "key2", "b", ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "\x97\xdf\x97\xe4\xb7\x98\xb2\x9e\xb3\x1e\xd7\x28\x2\x87\xa9\x2a"
+ "\x1\x96\xfa\xf2\x44\xf8\x11\x20\xc2\x1c\x51\x17\xb3\xe6\xeb\x98",
+ 32
+ },
+ {
+ "key1", "a", ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "key2", "b", ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "\x4d\x6c\xa4\xe6\x29\x78\x5c\x1f\x01\xba\xf5\x5e\x2e\x54\x85\x66",
+ 16
+ },
+ {
+ "key1", "a", ETYPE_AES128_CTS_HMAC_SHA1_96,
+ "key2", "b", ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ "\x88\xbd\xb2\xa9\xf\x3e\x52\x5a\xb0\x5f\x68\xc5\x43\x9a\x4d\x5e"
+ "\x9c\x2b\xfd\x2b\x02\x24\xde\x39\xb5\x82\xf4\xbb\x05\xfe\x2\x2e",
+ 32
+ },
+ {
+ "key1", "a", ETYPE_ARCFOUR_HMAC_MD5,
+ "key2", "b", ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ "\x24\xd7\xf6\xb6\xba\xe4\xe5\xc0\x0d\x20\x82\xc5\xeb\xab\x36\x72",
+ 16
+ },
+ /* We don't yet have a PRF for 1DES in Heimdal */
+ {
+ "key1", "a", ETYPE_DES_CBC_CRC,
+ "key2", "b", ETYPE_DES_CBC_CRC,
+ ETYPE_DES_CBC_CRC,
+ "\x43\xba\xe3\x73\x8c\x94\x67\xe6",
+ 8
+ },
+ {
+ "key1", "a", ETYPE_DES3_CBC_SHA1,
+ "key2", "b", ETYPE_DES3_CBC_SHA1,
+ ETYPE_DES3_CBC_SHA1,
+ "\xe5\x8f\x9e\xb6\x43\x86\x2c\x13\xad\x38\xe5\x29\x31\x34\x62\xa7"
+ "\xf7\x3e\x62\x83\x4f\xe5\x4a\x01",
+ 24
+ },
+};
+
+
+static void
+test_cf2(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_data pw, p1, p2;
+ krb5_salt salt;
+ krb5_keyblock k1, k2, k3;
+ krb5_crypto c1, c2;
+ unsigned int i;
+ unsigned int errors = 0;
+
+ ret = krb5_allow_weak_crypto(context, 1);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_allow_weak_crypto");
+
+ for (i = 0; i < sizeof(cf2)/sizeof(cf2[0]); i++) {
+ pw.data = cf2[i].p1;
+ pw.length = strlen(cf2[i].p1);
+ salt.salttype = (krb5_salttype)KRB5_PADATA_PW_SALT;
+ salt.saltvalue.data = cf2[i].p1;
+ salt.saltvalue.length = strlen(cf2[i].p1);
+
+ ret = krb5_string_to_key_data_salt(context,
+ cf2[i].e1,
+ pw,
+ salt,
+ &k1);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_key_data_salt");
+
+ ret = krb5_crypto_init(context, &k1, 0, &c1);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+ pw.data = cf2[i].p2;
+ pw.length = strlen(cf2[i].p2);
+ salt.saltvalue.data = cf2[i].p2;
+ salt.saltvalue.length = strlen(cf2[i].p2);
+
+ ret = krb5_string_to_key_data_salt(context,
+ cf2[i].e2,
+ pw,
+ salt,
+ &k2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_key_data_salt");
+
+ ret = krb5_crypto_init(context, &k2, 0, &c2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+
+ p1.data = cf2[i].pepper1;
+ p1.length = strlen(cf2[i].pepper1);
+
+ p2.data = cf2[i].pepper2;
+ p2.length = strlen(cf2[i].pepper2);
+
+ ret = krb5_crypto_fx_cf2(context, c1, c2, &p1, &p2, cf2[i].e3, &k3);
+ if (ret == KRB5_PROG_ETYPE_NOSUPP) {
+ krb5_warn(context, ret, "KRB-FX-CF2 not supported for enctype %d",
+ cf2[i].e1);
+ continue;
+ } else if (ret) {
+ krb5_err(context, 1, ret, "krb5_crypto_fx_cf2");
+ }
+
+ if (k3.keytype != cf2[i].e3) {
+ errors++;
+ krb5_warnx(context, "length not right for enctype %d", cf2[i].e3);
+ continue;
+ }
+ if (k3.keyvalue.length != cf2[i].len ||
+ memcmp(k3.keyvalue.data, cf2[i].key, cf2[i].len) != 0) {
+ errors++;
+ krb5_warnx(context, "key not same for enctypes %d %d %d",
+ cf2[i].e1, cf2[i].e2, cf2[i].e3);
+ continue;
+ }
+
+ krb5_crypto_destroy(context, c1);
+ krb5_crypto_destroy(context, c2);
+
+ krb5_free_keyblock_contents(context, &k1);
+ krb5_free_keyblock_contents(context, &k2);
+ krb5_free_keyblock_contents(context, &k3);
+ }
+
+ if (errors)
+ krb5_errx(context, 1, "%u KRB-FX-CF2 vectors failed", errors);
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ test_cf2(context);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_get_addrs.c b/third_party/heimdal/lib/krb5/test_get_addrs.c
new file mode 100644
index 0000000..33ca929
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_get_addrs.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2000 - 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 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 <err.h>
+#include <getarg.h>
+
+/* print all addresses that we find */
+
+static void
+print_addresses (krb5_context context, const krb5_addresses *addrs)
+{
+ int i;
+ char buf[256];
+ size_t len;
+
+ for (i = 0; i < addrs->len; ++i) {
+ krb5_print_address (&addrs->val[i], buf, sizeof(buf), &len);
+ printf ("%s\n", buf);
+ }
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_addresses addrs;
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_get_all_client_addrs (context, &addrs);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_all_client_addrs");
+ printf ("client addresses\n");
+ print_addresses (context, &addrs);
+ krb5_free_addresses (context, &addrs);
+
+ ret = krb5_get_all_server_addrs (context, &addrs);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_get_all_server_addrs");
+ printf ("server addresses\n");
+ print_addresses (context, &addrs);
+ krb5_free_addresses (context, &addrs);
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_gic.c b/third_party/heimdal/lib/krb5/test_gic.c
new file mode 100644
index 0000000..f11777e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_gic.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 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 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 <err.h>
+#include <getarg.h>
+
+static char *password_str;
+
+static krb5_error_code
+lr_proc(krb5_context context, krb5_last_req_entry **e, void *ctx)
+{
+ while (e && *e) {
+ printf("e type: %d value: %d\n", (*e)->lr_type, (int)(*e)->value);
+ e++;
+ }
+ return 0;
+}
+
+static void
+test_get_init_creds(krb5_context context,
+ krb5_principal client)
+{
+ krb5_error_code ret;
+ krb5_get_init_creds_opt *opt;
+ krb5_creds cred;
+
+ ret = krb5_get_init_creds_opt_alloc(context, &opt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
+
+
+ ret = krb5_get_init_creds_opt_set_process_last_req(context,
+ opt,
+ lr_proc,
+ NULL);
+ if (ret)
+ krb5_err(context, 1, ret,
+ "krb5_get_init_creds_opt_set_process_last_req");
+
+ ret = krb5_get_init_creds_password(context,
+ &cred,
+ client,
+ password_str,
+ krb5_prompter_posix,
+ NULL,
+ 0,
+ NULL,
+ opt);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_get_init_creds_password");
+
+ krb5_get_init_creds_opt_free(context, opt);
+}
+
+static char *client_str = NULL;
+static int debug_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"client", 0, arg_string, &client_str,
+ "client principal to use", NULL },
+ {"password",0, arg_string, &password_str,
+ "password", NULL },
+ {"debug", 'd', arg_flag, &debug_flag,
+ "turn on debuggin", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "hostname ...");
+ exit (ret);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0, errors = 0;
+ krb5_principal client;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ if(client_str == NULL)
+ errx(1, "client is not set");
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = krb5_parse_name(context, client_str, &client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name: %d", ret);
+
+ test_get_init_creds(context, client);
+
+ krb5_free_context(context);
+
+ return errors;
+}
diff --git a/third_party/heimdal/lib/krb5/test_hostname.c b/third_party/heimdal/lib/krb5/test_hostname.c
new file mode 100644
index 0000000..f722353
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_hostname.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+static int debug_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+static int
+expand_hostname(krb5_context context, const char *host)
+{
+ krb5_error_code ret;
+ char *h, **r;
+
+ ret = krb5_expand_hostname(context, host, &h);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_expand_hostname(%s)", host);
+
+ if (debug_flag)
+ printf("hostname: %s -> %s\n", host, h);
+
+ free(h);
+
+ ret = krb5_expand_hostname_realms(context, host, &h, &r);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_expand_hostname_realms(%s)", host);
+
+ if (debug_flag) {
+ int j;
+
+ printf("hostname: %s -> %s\n", host, h);
+ for (j = 0; r[j]; j++) {
+ printf("\trealm: %s\n", r[j]);
+ }
+ }
+ free(h);
+ krb5_free_host_realm(context, r);
+
+ return 0;
+}
+
+static int
+test_expand_hostname(krb5_context context)
+{
+ int i, errors = 0;
+
+ struct t {
+ krb5_error_code ret;
+ const char *orig_hostname;
+ const char *new_hostname;
+ } tests[] = {
+ { 0, "pstn1.su.se", "pstn1.su.se" },
+ { 0, "pstnproxy.su.se", "pstnproxy.su.se" },
+ };
+
+ for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+ errors += expand_hostname(context, tests[i].orig_hostname);
+ }
+
+ return errors;
+}
+
+static struct getargs args[] = {
+ {"debug", 'd', arg_flag, &debug_flag,
+ "turn on debuggin", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args), NULL, "hostname ...");
+ exit (ret);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0, errors = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if (argc > 0) {
+ while (argc-- > 0)
+ errors += expand_hostname(context, *argv++);
+ return errors;
+ }
+
+ errors += test_expand_hostname(context);
+
+ krb5_free_context(context);
+
+ return errors;
+}
diff --git a/third_party/heimdal/lib/krb5/test_keytab.c b/third_party/heimdal/lib/krb5/test_keytab.c
new file mode 100644
index 0000000..134b0f1
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_keytab.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+/*
+ * Test that removal entry from of empty keytab doesn't corrupts
+ * memory.
+ */
+
+static void
+test_empty_keytab(krb5_context context, const char *keytab)
+{
+ krb5_error_code ret;
+ krb5_keytab id;
+ krb5_keytab_entry entry;
+
+ ret = krb5_kt_resolve(context, keytab, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ memset(&entry, 0, sizeof(entry));
+
+ krb5_kt_remove_entry(context, id, &entry);
+
+ ret = krb5_kt_have_content(context, id);
+ if (ret == 0)
+ krb5_errx(context, 1, "supposed to be empty keytab isn't");
+
+ ret = krb5_kt_close(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_close");
+}
+
+/*
+ * Test that memory keytab are refcounted.
+ */
+
+static void
+test_memory_keytab(krb5_context context, const char *keytab, const char *keytab2)
+{
+ krb5_error_code ret;
+ krb5_keytab id, id2, id3;
+ krb5_keytab_entry entry, entry2, entry3;
+
+ ret = krb5_kt_resolve(context, keytab, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ memset(&entry, 0, sizeof(entry));
+ ret = krb5_parse_name(context, "lha@SU.SE", &entry.principal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+ entry.vno = 1;
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ &entry.keyblock);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ krb5_kt_add_entry(context, id, &entry);
+
+ ret = krb5_kt_resolve(context, keytab, &id2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ ret = krb5_kt_get_entry(context, id,
+ entry.principal,
+ 0,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ &entry2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_get_entry");
+ krb5_kt_free_entry(context, &entry2);
+
+ ret = krb5_kt_close(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_close");
+
+ ret = krb5_kt_get_entry(context, id2,
+ entry.principal,
+ 0,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ &entry2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_get_entry");
+ krb5_kt_free_entry(context, &entry2);
+
+ ret = krb5_kt_close(context, id2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_close");
+
+
+ ret = krb5_kt_resolve(context, keytab2, &id3);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ memset(&entry3, 0, sizeof(entry3));
+ ret = krb5_parse_name(context, "lha3@SU.SE", &entry3.principal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+ entry3.vno = 1;
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ &entry3.keyblock);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ krb5_kt_add_entry(context, id3, &entry3);
+
+
+ ret = krb5_kt_resolve(context, keytab, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve");
+
+ ret = krb5_kt_get_entry(context, id,
+ entry.principal,
+ 0,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ &entry2);
+ if (ret == 0)
+ krb5_errx(context, 1, "krb5_kt_get_entry when if should fail");
+
+ krb5_kt_remove_entry(context, id, &entry);
+
+ ret = krb5_kt_close(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_close");
+
+ krb5_kt_free_entry(context, &entry);
+
+ krb5_kt_remove_entry(context, id3, &entry3);
+
+ ret = krb5_kt_close(context, id3);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_close");
+
+ krb5_free_principal(context, entry3.principal);
+ krb5_free_keyblock_contents(context, &entry3.keyblock);
+}
+
+static void
+perf_add(krb5_context context, krb5_keytab id, int times)
+{
+}
+
+static void
+perf_find(krb5_context context, krb5_keytab id, int times)
+{
+}
+
+static void
+perf_delete(krb5_context context, krb5_keytab id, int forward, int times)
+{
+}
+
+
+static int version_flag = 0;
+static int help_flag = 0;
+static char *perf_str = NULL;
+static int times = 1000;
+
+static struct getargs args[] = {
+ {"performance", 0, arg_string, &perf_str,
+ "test performance for named keytab", "keytab" },
+ {"times", 0, arg_integer, &times,
+ "number of times to run the perforamce test", "number" },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc != 0)
+ errx(1, "argc != 0");
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if (perf_str) {
+ krb5_keytab id;
+
+ ret = krb5_kt_resolve(context, perf_str, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve: %s", perf_str);
+
+ /* add, find, delete on keytab */
+ perf_add(context, id, times);
+ perf_find(context, id, times);
+ perf_delete(context, id, 0, times);
+
+ /* add and find again on used keytab */
+ perf_add(context, id, times);
+ perf_find(context, id, times);
+
+ ret = krb5_kt_destroy(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_destroy: %s", perf_str);
+
+ ret = krb5_kt_resolve(context, perf_str, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_resolve: %s", perf_str);
+
+ /* try delete backwards */
+#if 0
+ perf_add(context, id, times);
+ perf_delete(context, id, 1, times);
+#endif
+
+ ret = krb5_kt_destroy(context, id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_kt_destroy");
+
+ } else {
+
+ test_empty_keytab(context, "MEMORY:foo");
+ test_empty_keytab(context, "FILE:foo");
+
+ test_memory_keytab(context, "MEMORY:foo", "MEMORY:foo2");
+
+ }
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_kuserok.c b/third_party/heimdal/lib/krb5/test_kuserok.c
new file mode 100644
index 0000000..7319ef7
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_kuserok.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <getarg.h>
+#include <err.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "principal luser");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_principal principal;
+ char *p;
+ int o = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &o))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= o;
+ argv += o;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ if (argc != 2)
+ usage(1);
+
+ ret = krb5_parse_name(context, argv[0], &principal);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_unparse_name(context, principal, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ ret = krb5_kuserok(context, principal, argv[1]);
+
+ krb5_free_principal(context, principal);
+ krb5_free_context(context);
+
+ printf("%s is %sallowed to login as %s\n", p, ret ? "" : "NOT ", argv[1]);
+ free(p);
+
+ if (ret)
+ return 0;
+
+ return 1;
+}
diff --git a/third_party/heimdal/lib/krb5/test_mem.c b/third_party/heimdal/lib/krb5/test_mem.c
new file mode 100644
index 0000000..3333d0d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_mem.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+
+/*
+ * Test run functions, to be used with valgrind to detect memoryleaks.
+ */
+
+static void
+check_log(void)
+{
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ krb5_log_facility *logfacility;
+ krb5_context context;
+ krb5_error_code ret;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ krb5_initlog(context, "test-mem", &logfacility);
+ krb5_addlog_dest(context, logfacility, "0/STDERR:");
+ krb5_set_warn_dest(context, logfacility);
+
+ krb5_free_context(context);
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ setprogname(argv[0]);
+
+ check_log();
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_mkforwardable.c b/third_party/heimdal/lib/krb5/test_mkforwardable.c
new file mode 100644
index 0000000..3f25f13
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_mkforwardable.c
@@ -0,0 +1,191 @@
+/*
+ * 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"
+
+/*
+ * Usage: mkforwardable server out_ccache
+ *
+ * The default cache contains a ticket to server and the default keytab
+ * contains a key to decrypt it, the ticket is decrypted and the forwardable
+ * flag is added, the ticket is then re-encrypted and stored in out_cache.
+ *
+ */
+
+static krb5_context context;
+
+static void
+check(krb5_error_code code)
+{
+ const char *errmsg;
+
+ if (code == 0)
+ return;
+
+ errmsg = krb5_get_error_message(context, code);
+ fprintf(stderr, "%s\n", errmsg);
+ krb5_free_error_message(context, errmsg);
+
+ abort();
+}
+
+static void
+decrypt_ticket_enc_part(EncryptionKey *key,
+ krb5_enctype etype,
+ Ticket *ticket,
+ EncTicketPart *et)
+{
+ krb5_error_code ret;
+ krb5_data plain;
+ size_t len;
+ krb5_crypto crypto;
+
+ check(krb5_crypto_init(context, key, etype, &crypto));
+
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ KRB5_KU_TICKET,
+ &ticket->enc_part,
+ &plain);
+ check(ret);
+
+ check(decode_EncTicketPart(plain.data, plain.length, et, &len));
+
+ krb5_data_free (&plain);
+ krb5_crypto_destroy(context, crypto);
+}
+
+static void
+encrypt_ticket_enc_part(EncryptionKey *key,
+ krb5_enctype etype,
+ krb5_kvno skvno,
+ EncTicketPart *et,
+ Ticket *ticket)
+{
+ size_t len, size;
+ char *buf;
+ krb5_error_code ret;
+ krb5_crypto crypto;
+
+ ASN1_MALLOC_ENCODE(EncTicketPart, buf, len, et, &size, ret);
+ check(ret);
+
+ check(krb5_crypto_init(context, key, etype, &crypto));
+ ret = krb5_encrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ buf,
+ len,
+ skvno,
+ &ticket->enc_part);
+ check(ret);
+
+ free(buf);
+ krb5_crypto_destroy(context, crypto);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_keytab kt;
+ krb5_keytab_entry entry;
+ krb5_enctype etype;
+ krb5_creds mc, cred;
+ krb5_ccache ccache;
+ EncTicketPart et;
+ Ticket ticket;
+ size_t size;
+ krb5_kvno kvno = 0;
+
+ memset(&cred, 0, sizeof(cred));
+
+ if (argc != 3)
+ errx(1, "Usage: mkforwardable server out_ccache");
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %u", ret);
+
+ check(krb5_cc_default(context, &ccache));
+
+ krb5_cc_clear_mcred(&mc);
+
+ check(krb5_parse_name(context, argv[1], &mc.server));
+
+ check(krb5_cc_retrieve_cred(context, ccache, 0, &mc, &cred));
+
+ check(decode_Ticket(cred.ticket.data, cred.ticket.length, &ticket, NULL));
+
+ etype = ticket.enc_part.etype;
+
+ if (ticket.enc_part.kvno != NULL)
+ kvno = *ticket.enc_part.kvno;
+
+ check(krb5_kt_default(context, &kt));
+
+ check(krb5_kt_get_entry(context, kt, mc.server, kvno, etype, &entry));
+
+ decrypt_ticket_enc_part(&entry.keyblock, etype, &ticket, &et);
+
+ et.flags.forwardable = 1;
+ cred.flags.b = et.flags;
+
+ free_EncryptedData(&ticket.enc_part);
+
+ encrypt_ticket_enc_part(&entry.keyblock, etype, kvno, &et, &ticket);
+
+ krb5_data_free(&cred.ticket);
+ ASN1_MALLOC_ENCODE(Ticket, cred.ticket.data, cred.ticket.length, &ticket,
+ &size, ret);
+ check(ret);
+
+ krb5_cc_close(context, ccache);
+
+ check(krb5_cc_resolve(context, argv[2], &ccache));
+ check(krb5_cc_initialize(context, ccache, cred.client));
+
+ check(krb5_cc_store_cred(context, ccache, &cred));
+
+ free_Ticket(&ticket);
+ free_EncTicketPart(&et);
+ krb5_cc_close(context, ccache);
+ krb5_free_principal(context, mc.server);
+ krb5_free_cred_contents(context, &cred);
+ krb5_kt_free_entry(context, &entry);
+ krb5_kt_close(context, kt);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_pac.c b/third_party/heimdal/lib/krb5/test_pac.c
new file mode 100644
index 0000000..d0d940f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_pac.c
@@ -0,0 +1,1270 @@
+/*
+ * 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"
+
+/*
+ * This PAC and keys are copied (with permission) from Samba torture
+ * regression test suite, they where created by Andrew Bartlet.
+ */
+
+static const unsigned char saved_pac[] = {
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xd8, 0x01, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x40, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x10, 0x08, 0x00, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xc8, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x30, 0xdf, 0xa6, 0xcb,
+ 0x4f, 0x7d, 0xc5, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x7f, 0xc0, 0x3c, 0x4e, 0x59, 0x62, 0x73, 0xc5, 0x01, 0xc0, 0x3c, 0x4e, 0x59,
+ 0x62, 0x73, 0xc5, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x16, 0x00, 0x16, 0x00,
+ 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x14, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x65, 0x00, 0x00, 0x00,
+ 0xed, 0x03, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x02, 0x00,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x16, 0x00, 0x20, 0x00, 0x02, 0x00, 0x16, 0x00, 0x18, 0x00,
+ 0x24, 0x00, 0x02, 0x00, 0x28, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00,
+ 0x57, 0x00, 0x32, 0x00, 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4e, 0x00,
+ 0x41, 0x00, 0x4c, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x57, 0x00, 0x32, 0x00,
+ 0x30, 0x00, 0x30, 0x00, 0x33, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x41, 0x00, 0x4c, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x57, 0x00, 0x49, 0x00,
+ 0x4e, 0x00, 0x32, 0x00, 0x4b, 0x00, 0x33, 0x00, 0x54, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4e, 0x00,
+ 0x4b, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
+ 0x15, 0x00, 0x00, 0x00, 0x11, 0x2f, 0xaf, 0xb5, 0x90, 0x04, 0x1b, 0xec, 0x50, 0x3b, 0xec, 0xdc,
+ 0x01, 0x00, 0x00, 0x00, 0x30, 0x00, 0x02, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x66, 0x28, 0xea, 0x37, 0x80, 0xc5, 0x01, 0x16, 0x00, 0x77, 0x00, 0x32, 0x00, 0x30, 0x00,
+ 0x30, 0x00, 0x33, 0x00, 0x66, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x24, 0x00,
+ 0x76, 0xff, 0xff, 0xff, 0x37, 0xd5, 0xb0, 0xf7, 0x24, 0xf0, 0xd6, 0xd4, 0xec, 0x09, 0x86, 0x5a,
+ 0xa0, 0xe8, 0xc3, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x76, 0xff, 0xff, 0xff, 0xb4, 0xd8, 0xb8, 0xfe,
+ 0x83, 0xb3, 0x13, 0x3f, 0xfc, 0x5c, 0x41, 0xad, 0xe2, 0x64, 0x83, 0xe0, 0x00, 0x00, 0x00, 0x00
+};
+
+static int type_1_length = 472;
+
+static const krb5_keyblock kdc_keyblock = {
+ ETYPE_ARCFOUR_HMAC_MD5,
+ { 16, "\xB2\x86\x75\x71\x48\xAF\x7F\xD2\x52\xC5\x36\x03\xA1\x50\xB7\xE7" }
+};
+
+static const krb5_keyblock member_keyblock = {
+ ETYPE_ARCFOUR_HMAC_MD5,
+ { 16, "\xD2\x17\xFA\xEA\xE5\xE6\xB5\xF9\x5C\xCC\x94\x07\x7A\xB8\xA5\xFC" }
+};
+
+static time_t authtime = 1120440609;
+static const char *user = "w2003final$";
+
+/*
+ * This pac from Christan Krause
+ */
+
+static const unsigned char saved_pac2[] =
+ "\x05\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xc8\x01\x00\x00"
+ "\x58\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x18\x00\x00\x00"
+ "\x20\x02\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x70\x00\x00\x00"
+ "\x38\x02\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x14\x00\x00\x00"
+ "\xa8\x02\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x14\x00\x00\x00"
+ "\xc0\x02\x00\x00\x00\x00\x00\x00\x01\x10\x08\x00\xcc\xcc\xcc\xcc"
+ "\xb8\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x7d\xee\x09\x76"
+ "\xf2\x39\xc9\x01\xff\xff\xff\xff\xff\xff\xff\x7f\xff\xff\xff\xff"
+ "\xff\xff\xff\x7f\x6d\x49\x38\x62\xf2\x39\xc9\x01\x6d\x09\xa2\x8c"
+ "\xbb\x3a\xc9\x01\xff\xff\xff\xff\xff\xff\xff\x7f\x0e\x00\x0e\x00"
+ "\x04\x00\x02\x00\x10\x00\x10\x00\x08\x00\x02\x00\x00\x00\x00\x00"
+ "\x0c\x00\x02\x00\x00\x00\x00\x00\x10\x00\x02\x00\x00\x00\x00\x00"
+ "\x14\x00\x02\x00\x00\x00\x00\x00\x18\x00\x02\x00\x02\x01\x00\x00"
+ "\x52\x04\x00\x00\x01\x02\x00\x00\x03\x00\x00\x00\x1c\x00\x02\x00"
+ "\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x10\x00\x12\x00\x20\x00\x02\x00\x0e\x00\x10\x00"
+ "\x24\x00\x02\x00\x28\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x10\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x07\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00"
+ "\x6f\x00\x70\x00\x65\x00\x6e\x00\x6d\x00\x73\x00\x70\x00\x00\x00"
+ "\x08\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x6f\x00\x70\x00"
+ "\x65\x00\x6e\x00\x20\x00\x6d\x00\x73\x00\x70\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00"
+ "\x60\x04\x00\x00\x07\x00\x00\x00\x01\x02\x00\x00\x07\x00\x00\x00"
+ "\x5e\x04\x00\x00\x07\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00"
+ "\x08\x00\x00\x00\x43\x00\x48\x00\x4b\x00\x52\x00\x2d\x00\x41\x00"
+ "\x44\x00\x53\x00\x08\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00"
+ "\x4d\x00\x53\x00\x50\x00\x2d\x00\x41\x00\x44\x00\x53\x00\x00\x00"
+ "\x04\x00\x00\x00\x01\x04\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00"
+ "\x91\xad\xdc\x4c\x63\xb8\xb5\x48\xd5\x53\xd2\xd1\x00\x00\x00\x00"
+ "\x00\x66\xeb\x75\xf2\x39\xc9\x01\x0e\x00\x6f\x00\x70\x00\x65\x00"
+ "\x6e\x00\x6d\x00\x73\x00\x70\x00\x38\x00\x10\x00\x28\x00\x48\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x6f\x00\x70\x00\x65\x00\x6e\x00"
+ "\x6d\x00\x73\x00\x70\x00\x40\x00\x6d\x00\x73\x00\x70\x00\x2d\x00"
+ "\x61\x00\x64\x00\x73\x00\x2e\x00\x70\x00\x65\x00\x70\x00\x70\x00"
+ "\x65\x00\x72\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x64\x00\x65\x00"
+ "\x4d\x00\x53\x00\x50\x00\x2d\x00\x41\x00\x44\x00\x53\x00\x2e\x00"
+ "\x50\x00\x45\x00\x50\x00\x50\x00\x45\x00\x52\x00\x43\x00\x4f\x00"
+ "\x4e\x00\x2e\x00\x44\x00\x45\x00\x76\xff\xff\xff\xb3\x56\x15\x29"
+ "\x37\xc6\x5c\xf7\x97\x35\xfa\xec\x59\xe8\x96\xa0\x00\x00\x00\x00"
+ "\x76\xff\xff\xff\x50\x71\xa2\xb1\xa3\x64\x82\x5c\xfd\x23\xea\x3b"
+ "\xb0\x19\x12\xd4\x00\x00\x00\x00";
+
+
+static const krb5_keyblock member_keyblock2 = {
+ ETYPE_DES_CBC_MD5,
+ { 8, "\x9e\x37\x83\x25\x4a\x7f\xf2\xf8" }
+};
+
+static time_t authtime2 = 1225304188;
+static const char *user2 = "openmsp";
+
+
+/* PAC ticket-signature test data. */
+
+static const krb5_keyblock rwdc_tgt_key = {
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ { 32, "\x27\x86\x34\x1d\xd3\x15\x06\x0d\x6f\xd6\x40\xfa\x03\xb1\x95\x32"
+ "\x91\x22\xe8\x6b\x0f\x47\xe0\xb5\xfe\xda\xef\x54\x98\xdc\x07\x5a" }
+};
+
+static const krb5_keyblock rwdc_tgt_pac_key = {
+ ENCTYPE_ARCFOUR_HMAC,
+ { 16, "\xb9\xf0\x39\x8d\xe9\x60\xb8\x40\x8c\x54\x0b\x61\xf9\xd2\xf1\x16" }
+};
+
+static const krb5_keyblock rodc_tgt_key = {
+ ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ { 32, "\x0c\x7e\x63\x47\xc8\x70\x7f\x58\x7c\x91\x59\xba\xc9\xbf\x50\xb4"
+ "\xe2\xd6\x49\xb6\x85\xd3\xd9\xf3\x80\xba\xe9\x02\x46\x51\xab\x23" }
+};
+
+static const krb5_keyblock rodc_tgt_pac_key = {
+ ENCTYPE_ARCFOUR_HMAC,
+ { 16, "\x80\x5d\x66\xb9\x5f\x66\xd6\x80\xc3\x5a\x07\x41\xe8\x97\xcc\x66" }
+};
+
+static const krb5_keyblock server_key = {
+ ENCTYPE_ARCFOUR_HMAC,
+ { 16, "\xed\x23\x11\x20\x7a\x21\x44\x20\xbf\xc0\x8d\x36\xf7\xf6\xb2\x3e" }
+};
+
+
+static const unsigned char tgt_ticket[] =
+ "\x61\x82\x03\xe1\x30\x82\x03\xdd\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x1d\x30\x1b\xa0\x03\x02"
+ "\x01\x02\xa1\x14\x30\x12\x1b\x06\x6b\x72\x62\x74\x67\x74\x1b\x08"
+ "\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa3\x82\x03\xa9\x30\x82\x03\xa5"
+ "\xa0\x03\x02\x01\x12\xa1\x03\x02\x01\x02\xa2\x82\x03\x97\x04\x82"
+ "\x03\x93\xfb\x16\x6b\x43\xca\x54\x6a\xed\xc8\xa4\x1f\x9e\xde\x88"
+ "\xa0\xa2\xc8\x86\x50\x17\x67\xbc\x0b\x13\xb8\x00\x80\x48\x54\x77"
+ "\xb2\x4c\x5f\xff\x34\xb6\x05\xc4\xff\xdb\x68\x00\x7c\xf4\x4f\x65"
+ "\x56\x69\x1a\x2f\x32\x24\x19\xef\x0a\xf8\xcd\x22\x4a\xe9\xcc\x2b"
+ "\x65\xfb\x3a\xff\x3d\xb3\xdc\xea\x5a\x9b\xb0\x14\xd0\xa6\x1d\x7c"
+ "\x82\xa7\x18\x23\xad\xd3\x43\x6c\x23\x41\x6e\x7c\x84\x01\xc7\xd6"
+ "\x60\xdf\xbb\x19\xac\xf3\x5b\x52\xea\xd1\xdb\xa5\x49\xc6\xf1\x44"
+ "\x59\x41\x15\x78\x13\xbc\x85\x73\xba\xd7\xde\x55\xe9\x84\x30\x11"
+ "\x5e\x00\x47\xff\x01\x87\x46\xda\xe3\xb4\xe3\xef\xac\x67\xa8\x8d"
+ "\x06\xf4\x04\xa3\x15\x5f\x51\x59\xe9\xb4\x65\x00\x1f\x45\x7c\x2a"
+ "\xcf\x17\x78\x75\xad\xc8\x65\x92\xe7\x20\x89\x41\x43\x5e\x08\x1d"
+ "\xc9\x4b\xa1\x57\x26\x7f\x9a\x64\xc7\xe3\x90\xbf\xa8\x9f\x86\x89"
+ "\xbf\x37\xd0\x1a\x9c\xe7\x32\xbb\x9f\x8d\x38\xfd\xe8\xf4\x88\x54"
+ "\x3d\xe7\xd0\xfb\x73\x1a\x97\xee\xa5\x32\x67\x62\x4d\x1c\x28\x85"
+ "\x01\x4f\x76\x4c\xf6\xe5\x73\x93\x0e\xd8\x0a\xf1\x6f\xf0\x8c\xbf"
+ "\x65\x80\x87\xd1\xe8\xbc\xc5\x8f\x03\xb8\xfe\x7e\xd6\xde\x80\xa5"
+ "\x27\x8c\x26\x14\x66\xf3\xd2\x4d\x51\x51\xe7\x47\xac\xa5\x93\xd7"
+ "\x01\x5b\x6b\xca\x6c\xd5\x19\x7b\xad\x97\xf2\xa6\x18\x0e\xe3\xea"
+ "\x5e\x81\xe4\xd7\xf5\xb0\xca\x0f\x04\x13\xc4\xed\x5b\x0c\xb2\xc4"
+ "\x5e\xd3\xc2\xb6\x0f\x0a\x76\x8a\x7d\x1c\x79\x62\xb5\x68\x47\x33"
+ "\x5e\x28\x3a\xd6\x78\x89\xb3\xbd\x34\x72\x7f\xe1\x8e\x5e\xa9\x89"
+ "\xfe\xc5\xba\x5f\x76\x00\x27\x29\x88\x79\xb4\x42\x33\xe7\x4c\xce"
+ "\x47\x39\xac\xfa\x8e\x93\x7a\x92\x7f\xbd\x91\xc8\xff\xe4\x7b\x04"
+ "\xe8\xf7\x29\x3b\xb4\x41\xb9\xe0\x61\x14\x2a\xc9\x5b\x02\xc2\x01"
+ "\x6f\x61\xa8\x1a\x5e\x2d\x0b\x3f\xc3\x1d\x6a\xb0\x9e\xf1\x41\xaf"
+ "\xc6\xe0\x11\xb3\x47\xb2\x43\x42\xb4\x6e\xdb\x16\x41\x6a\x7b\x53"
+ "\x97\x01\x7f\x3e\x9d\x47\x27\x14\x29\x7d\xc7\xa5\x40\xaf\x77\xcb"
+ "\xc9\x3e\x2e\x1f\xa3\xc8\x69\x98\xf7\xb2\x18\xa7\xcf\x45\x87\xba"
+ "\x48\x0b\xb8\x03\x10\x46\x2d\x95\x59\x5b\x9e\xe3\xe4\x20\x08\x80"
+ "\x97\x39\x16\x0c\x34\x27\x35\xd3\xd9\x71\x7b\xc2\x7d\x16\x36\xad"
+ "\xa0\x95\x12\x49\x5a\x3b\xea\xcf\x85\x75\x9e\xa4\x96\x51\x6c\x34"
+ "\x60\xd5\x3a\x9d\x60\x80\x53\x71\xac\x21\xfe\xea\xb3\xc5\x74\x65"
+ "\x7b\x98\xb6\x63\x58\xb7\xc9\x0c\x80\xad\x54\x9b\x1c\xdf\x84\xf9"
+ "\x97\xac\x73\x72\x4f\xbe\x10\x72\x6a\xec\x36\xfc\x7c\x98\xc6\x98"
+ "\x81\xde\xab\x2b\x8a\x68\x17\xd6\xb0\xac\xfd\xed\x5d\x67\xd3\x5a"
+ "\xbe\x7e\x88\x63\x5f\xc3\x1f\xf7\x04\xd9\xcd\x0a\xc7\x70\x83\x93"
+ "\x60\x86\x0c\x08\x21\xbb\x2f\x29\x28\xaf\x3f\x93\x24\x5f\x3a\x1d"
+ "\xf3\x81\x95\xb4\xef\xd6\x8c\x79\xdf\xf7\x9d\x4a\x0a\x49\xa7\x46"
+ "\xcd\x05\x30\x6e\x92\xa2\x26\x23\x8e\xf3\xe1\x38\x41\x63\x2f\xe6"
+ "\xd1\x59\x99\x8d\x7a\x8c\x4d\x57\x19\x5c\xee\x75\x7a\xd8\x0a\x53"
+ "\x43\xb0\x92\x01\x4a\xf7\xc4\x54\x8e\xe8\xc6\x43\x0b\x43\x9a\xa3"
+ "\xfd\x5c\x0c\x47\x52\xdf\xc6\x47\xa0\x14\xef\x45\xcf\xdf\xb7\x04"
+ "\xca\x3e\x5d\x8b\x32\x9b\x81\xa3\x8b\x9d\x7c\x0f\x11\x13\xf1\x0d"
+ "\xc0\xd0\xe2\xc1\xf7\x65\x03\xe0\x50\x6c\xbb\x3c\xd2\xc1\xad\x15"
+ "\xa3\xcd\x24\x85\xc0\x94\x54\x12\x2d\x63\x01\x47\x8b\x51\xee\xd1"
+ "\x46\xc0\x47\xcf\xce\xa1\x81\x5a\x5c\xe6\x59\x99\xb1\xaf\x80\x05"
+ "\xc3\x22\x69\xb3\x19\xec\x0e\xdd\x72\x0a\xcb\xf5\x4e\x90\x65\x41"
+ "\x68\x46\x8e\xab\xb3\xcc\xb6\xe2\xbd\xbf\xc0\x04\x12\x35\x84\xe5"
+ "\xef\xa5\x7f\x98\x8e\x0d\xac\x92\xae\x8f\x9c\x41\xce\x41\xb5\xcc"
+ "\x59\x00\xa5\x2c\x71\xe3\xdd\x25\x29\xd2\xac\x0f\x9b\x23\xbd\x1e"
+ "\x25\xe5\x06\xbd\x43\x46\x46\x4f\x81\x08\x1f\x57\xa9\x93\x24\xe4"
+ "\x83\x42\x13\x00\xce\x95\xdb\xde\x95\xc5\x8c\xd8\x29\x4e\x43\xb2"
+ "\x64\xe6\x6c\x0e\x40\x86\x35\xef\xac\x5b\x15\x92\xfb\x3c\xf0\x94"
+ "\xd8\x1f\xf9\x90\x8a\xd2\xa8\x4a\x1d\x77\x57\x4c\x65\xb3\x4f\xe4"
+ "\x03\x13\x3d\x52\x8b\xe4\x9e\x98\x53\xf1\xad\xc1\x2a\x3c\xda\x4f"
+ "\x24\xbf\x24\xb2\xb7\x34\xc7\xde\xfb\xe6\xe9\x82\x05\x07\x25\xd9"
+ "\x8e\xea\xd8\xb7\x0d\x1d\x0d\xf1\x0c\x99\x1a\x6a\xa7\xe7\x27\x49"
+ "\x26\x2a\x75\xd5\x84";
+
+static const unsigned char service_ticket[] =
+ "\x61\x82\x03\xf3\x30\x82\x03\xef\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x13\x30\x11\xa0\x03\x02"
+ "\x01\x01\xa1\x0a\x30\x08\x1b\x06\x61\x70\x61\x63\x68\x65\xa3\x82"
+ "\x03\xc5\x30\x82\x03\xc1\xa0\x03\x02\x01\x17\xa1\x03\x02\x01\x02"
+ "\xa2\x82\x03\xb3\x04\x82\x03\xaf\x7e\x88\x85\x77\x22\x5e\x30\x7a"
+ "\xaf\x63\xc4\x2b\xc1\xae\x88\x87\x27\x65\x0e\x1e\xa7\x94\x67\xf9"
+ "\x9d\x49\x5b\x6b\xba\xfb\x51\x04\xc4\xe8\x44\xc9\xdf\x8d\x8d\x58"
+ "\x61\xdb\x1e\x82\x98\xc7\x68\x9e\x81\xf0\xc4\x88\x87\xd6\xf5\x7b"
+ "\x44\x8e\x4a\x65\x4f\x37\x49\x8a\x81\x67\x00\x7e\xf3\x7e\x75\x9d"
+ "\x61\x18\x6d\x6f\x21\x73\xe3\xab\xe2\x86\xc0\xf1\x8b\x9f\x25\xc2"
+ "\xdc\xc4\x65\xeb\xba\x24\x3d\x2d\x5a\x9b\xbe\xc1\x24\x8c\xf1\x78"
+ "\xfc\xf9\xca\x14\x8e\xd4\xe3\xb3\x54\x87\xce\x97\x0c\x7b\x11\x2e"
+ "\x38\x30\xed\x8c\x41\x28\x90\x5b\xc6\x3d\xba\x29\x7b\x11\xfa\x85"
+ "\x54\x13\xab\x22\xe9\x52\xdd\xad\x5a\xdc\xa7\xca\x4f\x19\x49\x81"
+ "\x53\x09\x10\xca\x6b\x1a\x44\x13\xe6\x88\xec\x14\xd2\x15\x5a\x65"
+ "\x8a\xef\x55\x6a\xb1\xda\xc4\xe6\x0d\xc8\x2d\x8b\x0c\x00\x71\x6a"
+ "\x7b\x06\x34\xe0\x93\xa4\x5d\x5b\xa9\x43\x65\x42\xc5\x13\x64\xee"
+ "\x51\xd3\xd9\xc8\x3d\x52\xe2\xba\xb2\x81\xe6\x86\x25\x6f\xa6\x22"
+ "\x25\x97\xbb\xf1\xe4\x6e\xe1\x9a\xfa\xa4\xa0\x8b\xd6\x7b\x4a\x8a"
+ "\x62\x3d\x21\x1c\x08\x16\x8d\x29\x58\x7e\xfd\x43\x48\xba\xd7\x19"
+ "\x7c\xdd\x57\xe4\x8f\x94\x6e\x97\xed\x8d\xf2\x68\xe4\x89\xde\xc3"
+ "\xed\xa6\x7b\xcd\xff\x0c\xcf\xac\xad\xfa\x54\x89\xfc\xd0\x94\xd9"
+ "\x48\x25\x61\x71\x89\x32\xb8\xdb\xf9\xfb\xb9\xf4\x8b\x7e\x9c\x95"
+ "\x5d\xa3\x03\x13\xaa\x50\x28\xfa\x0b\x54\x5e\x0a\x3a\xb6\x4e\x58"
+ "\xee\x3f\xbc\xd2\x23\x81\x82\x82\xab\xef\x6a\xf8\x10\x56\x0e\x43"
+ "\xc2\x4a\x30\xa9\x17\x1a\x46\xb2\xdd\xcf\x7d\x20\x46\x0e\xc8\xff"
+ "\x54\xf5\xa1\xa1\x43\x8b\x02\x00\x0c\x31\xbb\x66\xcd\x17\xf0\x12"
+ "\x83\x39\x6c\xcf\x9c\xe9\x68\x26\x68\x86\xd4\xa4\xe6\x33\xa1\xdb"
+ "\x74\x1d\x56\x1c\x2b\xff\x5e\xdd\xe7\xdd\x3a\x3e\x3a\x13\xbf\x36"
+ "\x5a\x3f\x0d\x21\x9d\x9c\xd5\x20\x13\x1d\x86\xb4\xb2\xa5\x34\xa2"
+ "\x0b\x9a\x0c\xa0\xac\x92\x9b\x02\xf2\x68\x19\x9e\x1c\x66\x40\x29"
+ "\x81\x13\xf3\x06\x49\xa4\xf5\xd9\xe9\xd1\x2f\x89\xac\x1d\x20\x9e"
+ "\xb1\x2e\xf3\xde\x09\x7c\xf4\xe6\x2b\x61\x3f\x35\x0f\x83\xa9\x24"
+ "\xd3\xbd\x14\x0f\x48\xcb\xe4\x98\x02\x7c\x83\x1b\x61\x59\x34\x7d"
+ "\x32\x59\xbc\xb8\xe2\xb3\x99\x80\x75\x4b\x4b\xb5\x2b\x6d\x07\x66"
+ "\x3c\x8a\xbc\x6d\x61\x6e\xcb\x12\x53\xe2\x07\x38\x4e\x2f\xdb\xe0"
+ "\x15\x4c\x8b\xdb\x7e\xeb\x61\x96\xfb\x4f\x3d\x5f\xbe\xdb\x34\xb0"
+ "\x73\x7a\xe6\x10\x8a\xe7\x37\xa3\x15\x68\x44\x85\xc2\xc1\x4f\x3e"
+ "\x4c\xc8\x51\x54\x2c\x7d\x50\x86\xf2\xbd\x19\x63\x51\x4c\xb4\xd4"
+ "\x29\x6a\x03\xae\x38\x77\x25\xbc\x15\xba\xa8\x65\x29\x29\xdd\xb1"
+ "\xaa\xe8\x9a\xbf\x4c\x15\xcf\x13\x11\x0b\x86\x5c\x44\xca\x82\x49"
+ "\x8c\x1b\x77\x28\x36\x87\xf9\xad\x6d\xe0\x05\x0f\x93\x00\x1f\xaf"
+ "\xe4\xe9\x5a\x8f\x61\x41\x46\x2f\x52\x4d\x16\x4f\x9c\xca\x94\xff"
+ "\x19\x2e\xa5\x65\x09\x4f\x58\xfb\x4b\xbe\x89\xb3\x1a\x3b\x9b\xe4"
+ "\xb9\x7b\x58\xcd\xa3\x73\x41\xe0\xcb\xbd\xce\x97\x87\x1f\x60\xc8"
+ "\xbb\x8c\xe9\xd7\x4f\x9a\x2d\xb5\xd4\x73\x4e\x25\xf3\x36\xbc\x3f"
+ "\x81\x23\x82\x1e\xde\xbe\x9b\x35\x67\x99\x9f\x9c\x02\x9c\x75\x0c"
+ "\xed\xb1\xcb\x82\x7f\xf5\x31\x66\x76\x01\x5a\x61\x3c\xa4\xe9\xae"
+ "\x1a\xc1\x3f\x35\x7d\xd4\x41\xdb\x7f\xd2\xc5\x5f\xbe\x8c\x13\xa6"
+ "\x1a\xd3\x2e\xf6\x0f\x91\xd5\x20\xd5\x36\x57\xa8\x40\x17\x3b\x49"
+ "\xf6\xe5\x55\x04\xcf\xf3\x72\x3d\xa6\x99\x3a\x40\x49\xdf\x5a\x69"
+ "\xfb\x35\x43\x3a\x49\x8e\xb9\x6d\x1b\xd3\xb9\x6a\xcb\xcd\x6c\xda"
+ "\x68\x3f\xc1\xd4\x0e\xdb\x7a\xd5\x05\x77\xac\xc2\x95\xf4\x7c\x83"
+ "\x3c\xaf\x55\x9f\x29\x52\xee\xe4\x34\x2d\x4c\x5f\xfa\x76\x0a\xe2"
+ "\x8e\xe3\xb7\x12\x37\x79\x9e\xfb\xe2\xaf\x3f\xbc\x2a\x2e\x35\x79"
+ "\xd4\x65\x05\x88\xb7\x1d\xc6\xf8\x89\xe7\xe9\xa1\xe0\xf0\x18\x3a"
+ "\xd4\x06\x47\x22\xf2\xb7\xbf\x67\x41\x24\xaf\x19\x85\x32\xf5\x91"
+ "\x8a\x16\xd8\x99\xd5\x27\x0e\x08\xfd\x07\x42\xdd\xd9\x41\xd7\x1c"
+ "\x7e\x64\x09\x9b\xe7\x51\xff\x02\x37\x32\x12\x6d\x60\x3f\x36\x25"
+ "\xd0\x53\xd8\xbd\x22\x6f\x2c\xec\x7c\xe4\x65\x7b\xd4\xcc\xce\xc4"
+ "\x8d\xee\x53\x48\x26\x8c\x97\x18\x0a\x06\x62\xaf\x31\x0c\x85\xc8"
+ "\x27\x11\xb3\x18\x9b\x24\x57\x38\x20\xf6\xe5\x42\x72\x1c\x3c\xfb"
+ "\xce\xd4\x26\x65\x92\x21\x49\x0b\x5d\x62\x72\xeb\xb3\x53\x89\x55"
+ "\xb6\x81\xef\xfd\xd2\xb6\xed";
+
+static const unsigned char s4u2self_ticket[] =
+ "\x61\x82\x03\xf2\x30\x82\x03\xee\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x13\x30\x11\xa0\x03\x02"
+ "\x01\x01\xa1\x0a\x30\x08\x1b\x06\x61\x70\x61\x63\x68\x65\xa3\x82"
+ "\x03\xc4\x30\x82\x03\xc0\xa0\x03\x02\x01\x17\xa1\x03\x02\x01\x02"
+ "\xa2\x82\x03\xb2\x04\x82\x03\xae\x41\x12\xbe\x9e\x19\x0b\x27\xfe"
+ "\x38\xd3\x9b\x98\x8d\xcf\xc4\xe5\x34\x43\x64\x4a\x96\x00\x12\x52"
+ "\x92\x24\xcc\x8a\xe2\x80\x2a\x9e\xc1\x67\x6d\xd2\x77\x41\xe3\xc3"
+ "\xb7\xde\xf9\xdf\x8f\xdb\xe8\xd6\x78\x84\x83\xa0\x99\x2a\xbb\x24"
+ "\x25\x9d\x3a\x4d\xb0\x89\xe8\xa7\x67\xbe\x4e\x7b\xd4\x20\xad\xa6"
+ "\xb5\xa2\xab\xf4\x07\xf9\x14\x2e\x08\xc5\xa8\x43\xc2\xaf\x24\x53"
+ "\xb3\xaf\x7b\x83\xb5\xd1\xca\x8a\x99\x83\x14\x08\x37\x1f\x20\x03"
+ "\xfc\xd4\xb4\xce\xb3\x9a\xc1\xbd\x31\x54\x21\xb6\xbe\x7c\x82\xbc"
+ "\x30\x61\x0d\x9a\x10\x90\x00\x69\xee\xd5\xd3\xf6\x7c\xfa\x70\xa7"
+ "\xd4\xa1\xe6\xbd\x0b\xb1\xe2\xcc\x4e\xc6\x53\xc7\x89\x39\x57\x63"
+ "\xca\xfb\x4f\x0d\x73\xf7\x2a\xda\x94\x40\xdd\x0a\xd6\x00\x14\xc2"
+ "\x7b\x5f\xda\x27\x7c\x95\x32\xab\xcf\xd1\xac\xd0\xfc\x4a\xb4\x82"
+ "\x80\x5f\x56\xa5\xac\xb9\xb4\xc8\xb9\x08\x0a\x52\x9b\x2e\x80\xf4"
+ "\x9d\x85\x73\x09\xb1\x9a\xd6\x50\x59\x47\x0f\x8f\x72\xd5\x91\x76"
+ "\x89\xba\x49\xe3\x5e\x76\x07\x84\x8b\xf2\xc0\x57\xd4\xd2\x31\xfe"
+ "\xfd\xa3\xcd\x96\x5a\xbe\x19\x18\x97\x8e\xbf\x18\x1b\xdf\xf9\x78"
+ "\x5b\xac\x3c\x1f\xff\x4c\x64\xef\x1d\x5e\x69\x04\xcf\x68\xf6\x97"
+ "\xc4\xff\x16\x8e\xb2\x22\xf3\xc5\x84\x67\x41\x04\xce\x72\xdd\x76"
+ "\x65\x2e\xee\x84\xfb\x85\x83\x4b\x41\xdf\x24\x1e\xc7\x31\xf0\xc4"
+ "\xf1\xf7\x83\xfe\x46\x6d\x6f\x45\xf6\xb4\x40\xbb\x2f\x09\xf7\xe2"
+ "\x6f\x19\xd3\x3e\xa6\x94\x76\x46\x6b\x78\x43\x08\x4c\xfc\x15\xc2"
+ "\x4d\xef\x12\xe0\x38\x6a\xdb\x49\x7a\x71\x6c\xeb\xe3\xdf\xe7\x57"
+ "\x25\x23\x09\x4d\x74\xac\x93\x95\x8a\x6c\x01\x70\x0a\xb7\x42\xee"
+ "\xf5\x5c\x65\x64\xc6\xd7\x3e\x57\x7e\x0f\x9b\xdd\xf0\xfb\xdb\x92"
+ "\xa6\x1c\x75\x18\x88\x29\x03\xf9\x58\xf0\xd0\xc2\x91\x9c\xf6\x58"
+ "\x4e\x15\xe1\xab\x46\x1e\x23\x7c\xdc\xa5\xdd\xaf\x59\xae\x22\xbb"
+ "\xc3\x6d\x02\x0f\x02\x9e\x4a\xc1\x6d\x55\x4b\x35\x69\xd5\xaa\x92"
+ "\x61\xef\x0d\x50\x42\x49\xc0\xb5\x9d\x57\x3f\x50\x4d\xc2\x17\xda"
+ "\xc4\x43\xd1\x1f\x8c\x77\x4c\xa1\x37\x5c\x39\xe7\x51\x7e\x52\x68"
+ "\x0f\x6d\x8e\x1e\xb7\x81\x14\xc1\x11\x17\xa9\x37\x75\x48\x72\x35"
+ "\xca\xeb\xcb\x0d\x84\xbb\xf1\x08\x79\xde\x31\x00\x7f\x54\xc8\xbc"
+ "\x38\x1f\x2c\x56\x3b\xfc\x9d\x8a\xbc\x2d\x72\x8b\x32\x29\xf7\x52"
+ "\x96\x60\x34\x70\x13\x6f\x5a\x98\x7f\x69\xdc\x00\x21\x4c\x9d\x21"
+ "\x52\xd9\x71\xa3\xc6\xea\xd9\xdb\xeb\xd5\x78\xe5\x8f\x5e\x1d\xa8"
+ "\xa6\x0a\x58\x9c\xc9\x45\xc0\xb6\xb1\xe5\xfb\x49\xd2\x71\x41\x52"
+ "\xae\x52\x84\xd6\x90\x1e\x9a\xc2\xdb\x5b\xf8\x18\xf8\x47\x25\x70"
+ "\x1b\x07\x64\x8c\x4a\x94\xb0\x52\x50\xf0\x99\x3b\x0d\x11\x33\xb9"
+ "\xd6\xdf\x43\xfe\x34\x92\x3f\xa9\x1c\x9e\xfc\x89\x11\xdb\xe5\x15"
+ "\xae\x74\xd2\x1b\xd0\x42\x5a\x19\x0c\x03\x8e\x09\x16\xd2\xcf\xe8"
+ "\xd5\x02\xa4\x58\xe3\xca\xef\xe3\xa5\x36\x4f\x79\x6e\xc2\x57\x7f"
+ "\x97\xcf\x87\xec\x10\x57\x35\x15\xd8\x67\x4c\xaf\x2c\x6b\x50\x04"
+ "\xd6\xf7\x5f\x83\x1a\x4d\xfe\x44\x67\x60\x3f\x98\xfe\xa5\xd0\x56"
+ "\xf1\x46\x1e\xa0\x38\x21\x62\x0e\xce\xd5\x9a\x41\x4b\xb1\x9a\x7d"
+ "\xfe\x7d\x2d\x88\x16\x82\x73\x27\x4d\x9f\xad\x06\x1e\x93\x0f\xe7"
+ "\xd0\x98\xc5\x2e\xd8\xfc\x97\x4c\xed\x3b\x89\x94\xf4\x71\x88\x9f"
+ "\xc6\x5a\x73\x6e\x00\x4a\xd4\x99\xe2\x87\x32\x62\x03\x20\x73\xe3"
+ "\xba\xcc\x5d\x35\xa8\x20\x10\xb8\x60\xff\xed\x8c\x44\x21\x31\x1a"
+ "\x41\xb1\x3b\xa3\x44\x15\x45\x99\xa2\x89\x47\xd1\x80\xfd\xee\xc7"
+ "\xe8\xa2\xaa\xb3\x90\x2b\x04\x3c\x78\xf9\xcd\x0a\x90\xd5\x46\x28"
+ "\x52\x62\xbb\x08\x46\x19\x82\xb6\xe6\x2d\x3e\x04\x95\xd5\x0e\x7e"
+ "\xb3\x5f\x0e\x30\xbe\xf0\xdb\x36\x15\x69\xd5\x2d\x38\x95\x17\x27"
+ "\x2b\xa3\xc8\x68\x00\x55\xde\x2e\xd6\x07\xb4\x7a\x89\x1c\xe7\xc2"
+ "\x3c\xc4\x57\x9f\x45\xea\x77\xaf\xd8\xf1\xbe\x2a\x05\x46\x04\x51"
+ "\x83\x21\xa7\x1a\xa8\x23\x2f\x62\xa9\x67\xe6\xf3\xab\x47\xb9\xa6"
+ "\xec\x98\x7c\x7b\xd3\x42\x5d\x73\x37\xab\x46\x75\x2b\x33\xda\xf8"
+ "\xad\x2c\x62\x36\x2a\x91\x8f\x2c\x12\x4d\x00\xd5\x02\x98\x58\xfe"
+ "\xd2\x3d\x38\xc1\xe0\x2d\xeb\x7f\x22\xe6\x12\x72\xcd\xc7\x4b\xac"
+ "\x6d\x70\xc1\xcc\xda\x6d\xf1\x21\x44\xb4\x92\xec\xce\xd7\x6a\xde"
+ "\xea\x62\x63\xa9\x15\xe8\xf3\xea\xc6\xc8\x26\x39\xef\xa0\x76\x66"
+ "\x54\x45\xb6\xdd\x4b\xff";
+
+static const unsigned char s4u2proxy_ticket[] =
+ "\x61\x82\x04\x9f\x30\x82\x04\x9b\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x20\x30\x1e\xa0\x03\x02"
+ "\x01\x03\xa1\x17\x30\x15\x1b\x03\x73\x71\x6c\x1b\x0e\x6d\x79\x73"
+ "\x71\x6c\x2e\x61\x63\x6d\x65\x2e\x63\x6f\x6d\xa3\x82\x04\x64\x30"
+ "\x82\x04\x60\xa0\x03\x02\x01\x17\xa1\x03\x02\x01\x02\xa2\x82\x04"
+ "\x52\x04\x82\x04\x4e\xaa\x63\x2e\xe0\x3a\x24\xed\x98\x29\x42\xa4"
+ "\x17\x2b\x4a\x17\x1d\xea\x46\x2a\xc9\xeb\x10\xeb\x02\xe3\xa2\x40"
+ "\xf5\x79\xaa\x1c\x83\x20\xe0\x24\x9b\x6f\x06\xc4\x31\x82\xc8\x52"
+ "\xdb\x9a\x04\x36\xff\x5c\x7a\xa5\xfe\x5c\xf3\x40\xcf\x7c\x31\x0f"
+ "\xda\x2a\x20\xa1\x7e\xdb\xf4\x05\x54\xcb\x51\xa0\x54\x25\xc8\x9f"
+ "\x4d\x23\x0a\xef\x56\x4e\xa0\xdf\xf5\x0b\xce\x75\x93\x5b\xf9\xe5"
+ "\x3a\xdf\x08\x3f\xce\x1c\x75\x6b\x4f\x38\x1a\x90\x4c\x3e\x99\x49"
+ "\x6c\x7a\x55\xf5\xdd\x59\x64\xab\x43\xfd\x3f\x45\x5a\xd3\x0f\x4f"
+ "\x37\x44\x03\x42\x0e\x83\xac\xf9\x04\x83\x43\xfc\x60\xc3\x1b\xc4"
+ "\xc0\x2a\x6c\x6e\x65\xf4\x8f\x2d\x52\x1f\x18\xbe\x09\x17\x10\x1d"
+ "\xd4\xc7\xe6\xa9\x9f\x70\x90\x65\x8a\x30\x63\x26\xfd\xb4\x63\xd0"
+ "\xaa\x3d\x80\x03\x79\x74\xc7\x5d\xe8\x50\x9e\x1b\x0a\xa7\x69\x62"
+ "\x91\xda\xcb\x80\xe8\xa3\x0d\xd8\xef\x05\x46\x2a\xb8\x14\x6f\x45"
+ "\x4e\xa6\x76\x28\x1b\xbe\x50\x0b\x5b\xf9\xe1\x61\x66\x5b\xe8\xb7"
+ "\xa1\x9c\xea\x2a\x8b\x28\x63\x8e\x20\x41\xaa\xdc\xca\x61\xa9\x1f"
+ "\x1b\xf1\xf3\x39\x47\xdf\x1f\xc3\x0d\xdd\xe7\x40\xb1\x8d\xbf\x28"
+ "\x22\xd4\x5d\x82\x63\x80\xc6\x75\x45\xa3\xe2\x79\x71\xa9\x5d\x9a"
+ "\x29\xa4\xc5\x3d\x1a\x56\x99\x2a\x3f\x4c\x97\x02\x85\xd8\x21\x15"
+ "\xd7\xaa\x8c\x6e\xa9\x0f\xb8\xa7\x1b\x43\xa4\xb5\xf0\xab\x2f\x77"
+ "\xa0\xa9\x23\xb2\x81\xf4\x0d\x02\x0e\x17\xa5\xb7\xa6\x54\xc6\x3b"
+ "\x12\xd9\x2e\x30\xdc\xb7\x84\x46\x9b\x28\x89\x15\x48\xd0\xe4\x44"
+ "\xd5\x22\x67\x3e\x58\x96\xcf\xd1\x0f\x5e\xd1\x5c\xa5\xdd\x5a\x60"
+ "\xf2\xf9\x36\xad\xe0\x39\x2a\x56\x98\x71\x37\x67\xca\x3a\xc4\x7f"
+ "\xda\x43\x22\x7b\xe9\xf3\x8c\x0d\x31\x9e\xc3\x8d\xf8\x49\xf1\x81"
+ "\xe0\xb3\x4d\x47\xfa\x91\xdd\x46\xfb\x03\x84\x13\x37\x9c\x12\x0c"
+ "\x80\x4f\x01\xc6\x19\x62\x0e\xba\x94\x0d\x89\x24\x0c\x14\xbe\x0f"
+ "\x06\xc2\x8d\x54\xfb\x84\x64\xa2\x57\x9e\x85\x54\x98\xad\x25\x2c"
+ "\xa0\x73\x39\x14\x47\xf4\xa6\x1d\xf8\x1e\x94\x4c\x2e\x0f\xd6\x33"
+ "\xa6\x1b\x58\x7a\xc1\x29\x8d\xc8\x7e\x79\xb7\x44\x92\x8a\xf3\x69"
+ "\x37\x34\x74\xe6\x29\xf7\x38\xb1\xb5\xad\x4a\x2a\x56\x86\xa5\xc5"
+ "\x65\xcf\x0b\x9b\x2a\x54\x31\x1d\xad\xc0\xac\xd0\xf3\xaf\x41\x9a"
+ "\x4c\x5b\x42\x2c\x4c\x38\x72\xa4\x74\xcb\xfb\x77\xe1\xc7\x08\xba"
+ "\x72\xc1\x8e\xa7\x60\xeb\x6a\x63\xeb\x75\x0f\xe6\xd0\x82\x43\xa3"
+ "\x49\x7c\x74\xac\x50\xbb\x30\xf8\x59\x31\xa6\xcf\xaf\xc9\x08\x1c"
+ "\x21\x6d\x42\xa6\x84\x7a\xc9\x3e\x1d\x65\xc8\x8a\x6f\x28\xaa\x54"
+ "\x0a\x87\xed\x09\xa4\x90\xe1\x4a\xda\x71\x9c\x3d\x06\x82\x86\xf6"
+ "\xc9\x0d\xe6\xcd\xdc\xe7\xe8\x9f\x07\x4b\x6b\xf3\x50\x30\xee\xdb"
+ "\xd6\x7c\x97\xa5\x53\x09\x91\xa8\xc1\x06\xed\xc2\x30\xd5\x66\x07"
+ "\x5c\x1c\xb2\x04\x81\x09\x9f\x7c\xdf\x47\xc7\xfa\x04\x4d\x73\xbd"
+ "\x1a\x28\x8c\x83\x2a\x4b\xcd\xcc\x93\x41\xc9\x9f\x05\x7a\xb6\xff"
+ "\x98\x58\xa4\x11\xf1\x3d\xca\x94\x71\xb6\x0e\x6e\xeb\x30\x3c\xfc"
+ "\x5a\xf6\x0e\x0b\x1a\x0a\x3d\xd0\x9a\x08\xd4\x43\xaf\x06\xbe\x8f"
+ "\xc0\xf2\xfb\xe7\x94\xa0\x5d\x9c\x44\x7f\xca\xbd\x1e\x83\xf8\x22"
+ "\xe8\x6e\x7f\xd4\xc6\xaa\x8a\x45\x76\x37\xf7\x45\xdb\xcb\x20\xa9"
+ "\xcc\x74\x62\x52\xe2\x88\xbf\x95\x4d\xe8\x1e\xac\x30\xfa\x9c\x1e"
+ "\xe7\xe6\xeb\x18\x75\xe3\x0f\xb8\xfc\xbf\x6c\x6f\x59\x51\xd4\x38"
+ "\xb0\xab\x85\x73\x16\x51\x5d\x27\x94\x83\x03\xd2\x86\x28\x99\xa1"
+ "\x8c\x04\x4c\xfb\x21\x79\x7b\x95\x96\x21\x47\x81\xc9\xba\xef\x13"
+ "\x18\xd6\x8f\xa9\xf4\x89\x25\x5b\xa4\xae\xbc\x6a\x59\x1e\x7b\x15"
+ "\xc9\xa5\xc8\x8a\x8c\x35\xec\xee\xe2\x8a\x0e\x1b\x8e\xaf\x4b\x51"
+ "\x9e\x66\xe7\x94\x56\xf6\x37\x93\x22\x5d\xe3\x9e\x33\x5e\xb4\x74"
+ "\xf1\x69\xe3\x93\xc3\xe8\xc7\x61\x09\x6a\xc1\xb7\x42\xcc\x3b\x5b"
+ "\x24\x40\x28\xf7\x9b\xef\x5c\x98\xb9\xe7\x2c\x01\xed\x99\x66\xa7"
+ "\x06\xeb\x08\x89\xd6\x5e\xce\x85\x08\x1a\x90\xc4\x92\x40\x53\xc5"
+ "\x53\x8b\xcb\x1a\xe9\x30\x8c\x48\xdb\x66\xc3\x98\x24\x1d\x5f\xee"
+ "\x6c\x2c\xc1\x3e\xcd\xcd\xb9\x1a\x5d\x8d\xb9\x64\xd9\x4c\x33\x83"
+ "\xa4\xa5\x08\x0a\xe3\x73\x0c\xe9\xd9\x6f\xb8\x78\x9a\x46\xdf\x6f"
+ "\x7f\x9f\x38\x69\x4b\x44\x00\x7e\x5b\xee\xf6\x2b\xba\xdd\xf7\x8a"
+ "\x0e\xa3\xff\xfc\x09\x5a\xd0\x44\x7e\x58\x2f\xfa\xe9\x1d\x1d\xc8"
+ "\x39\xc7\x2c\x62\xff\xf5\x22\xca\x8d\xae\xfd\x86\xef\x3b\x8f\xac"
+ "\x1b\x11\xb8\x6a\x4e\xf9\x6a\x93\xb1\xda\x8d\x80\xb9\xfa\xca\x52"
+ "\x0d\x4a\x45\xf1\x43\x5b\xca\x74\x1f\x94\xe6\x2a\x83\x2f\x76\x8c"
+ "\x70\x00\xcf\xa5\x95\x3c\x31\x10\x75\xd2\x75\xf8\x83\x09\x58\x11"
+ "\xff\xf9\x82\x32\x26\xad\x72\x85\x13\x64\x6e\xf6\xc1\x71\x55\xe3"
+ "\x51\x98\xda\x03\x76\xf0\x5b\x5f\xbe\x5c\xf3\x12\xe5\x6f\x03\xc4"
+ "\x36\x84\x66\xb7\xf8\xd5\xb9\x99\xeb\x66\x84\xbc\xf2\x78\x23\x87"
+ "\xb2\xa5\x64\xc4\x4a\xa1\x30\x93\x7f\x3a\x48\xe1\xad\xc0\x14\xc2"
+ "\x28\x14\x77\x7f\x23\xc7\xa8\xa2\x6c\xab\xd4\x74\xdf\xb1\x42\x84"
+ "\xa7\x11\x73\xef\x2d\xf6\x7a\xfb\xe1\xe6\xdb\xac\x57\x3b\xc7\xba"
+ "\x8c\x83\x19";
+
+static const unsigned char tgt_rodc[] =
+ "\x61\x82\x03\xe4\x30\x82\x03\xe0\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x1d\x30\x1b\xa0\x03\x02"
+ "\x01\x02\xa1\x14\x30\x12\x1b\x06\x6b\x72\x62\x74\x67\x74\x1b\x08"
+ "\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa3\x82\x03\xac\x30\x82\x03\xa8"
+ "\xa0\x03\x02\x01\x12\xa1\x06\x02\x04\xb6\x51\x00\x01\xa2\x82\x03"
+ "\x97\x04\x82\x03\x93\xe2\x97\x36\xa2\x9f\x18\x79\x44\x87\xbf\xc8"
+ "\x27\x59\x40\xd1\x9d\x3b\x3d\x1d\x8b\xc8\x86\xfb\xc5\x4a\xf8\x27"
+ "\x5b\xee\x59\xcc\x3a\xcd\x2f\x0d\xb3\x9f\xb7\x87\xe2\x83\xdb\x36"
+ "\xee\xa0\x1c\xdb\x91\x9b\xe1\x50\x3e\xaa\x3d\xea\xae\x93\x65\xdf"
+ "\xb3\x23\xfb\xa6\x00\xaf\x55\x49\xf5\x6e\xd9\x2c\xc1\x61\x97\x37"
+ "\x19\x83\xe6\x60\xd8\x46\x2d\xdb\x0c\xc4\x55\x2e\x0a\x52\x0d\x64"
+ "\xb4\x80\xa9\x3d\x37\xb6\xed\x06\xdc\x32\xfd\x6a\x40\x20\xc4\x69"
+ "\x59\xf5\x80\x9c\x7f\x7a\x68\x88\x1c\x67\x37\x52\x85\x1a\x2f\x01"
+ "\xf8\x46\xd7\x7a\x24\xfb\x14\x31\xa8\x23\xd0\xac\x13\x85\x1c\xef"
+ "\xb1\xe4\x26\x20\x4f\x0c\xbb\xcc\xbb\x86\x40\xb3\x2e\x76\x25\x26"
+ "\x2e\x0f\x33\xec\xca\xac\x32\xa2\x35\x98\x1a\x9d\x34\x30\xe8\xaa"
+ "\x85\x8c\xa0\x0a\xf6\xe9\x60\x4f\x59\x20\xed\x51\x9d\xcf\xb4\xc1"
+ "\xdf\x1e\x8f\x91\x3e\xe2\x32\x9f\x68\x14\x41\x22\x2e\x05\xa7\x6d"
+ "\x1d\xa5\x55\xd8\x3b\x1c\xa4\x0b\x80\xf1\x43\x5f\xf7\xc2\xef\xa6"
+ "\x28\xcf\xbb\x2a\xbc\x0f\xbc\x20\x11\xff\xc1\x1b\x75\x5b\x49\x6f"
+ "\xc5\xc4\xa8\x3c\x46\xb3\xfd\x41\x70\x2a\xd8\x8b\xa6\xc1\x54\xad"
+ "\x15\x3e\x96\xca\x28\x3e\xca\x06\xe6\x0d\xad\xc8\x74\x32\x9a\x0d"
+ "\x80\x65\xd6\x49\x35\xc0\xd8\x75\xed\xb7\x4b\x9d\xb5\xd5\x3b\x3b"
+ "\xd3\x2e\x9f\xed\xbe\xd3\x83\x68\xe1\x3a\x25\x2c\xb5\xfe\xbd\x89"
+ "\xf3\x0e\xe3\x5e\xb3\x15\x2e\x0e\xb5\x2b\x97\x47\x6c\x6d\x88\x82"
+ "\x42\x54\x0d\x97\x52\x17\x56\x16\x9b\x5e\xaa\x63\xb0\xcb\x6f\xe6"
+ "\x0f\x9c\x9e\x6f\x3f\x49\x31\x60\x88\x2f\x25\xae\xff\x1c\x90\x55"
+ "\x9d\x63\x50\x56\x4d\x6e\x55\x5c\x48\x84\x1d\xf1\x0a\x03\xa6\x99"
+ "\x90\x13\xd6\x9a\xf1\x43\x5d\x0d\x69\x74\x3d\x60\xf5\xa0\x58\x29"
+ "\x98\x74\x10\x35\x2f\x6b\x35\x0b\x9a\xed\x3d\x7c\x4f\x00\x55\xec"
+ "\x0e\x20\xd0\x77\x46\x45\x7c\x0f\xfa\xf6\x55\xd9\x9f\xf8\x4b\x81"
+ "\xfa\x7a\x36\x05\xb9\x8e\xaf\xd8\xd3\x40\x6c\x0f\x72\x0d\x06\xba"
+ "\x91\x65\xbe\x5a\xfd\xf4\xb4\x49\xfa\x41\x83\xcb\xcf\x8d\x8a\xb0"
+ "\xf6\x3b\xcc\x08\xf8\x26\x41\x0d\x7e\xc6\xb3\x29\x8c\x33\x32\xb3"
+ "\x44\x7c\xbd\x0a\xc7\x0a\x58\x75\xfb\x2d\xf0\x53\x6c\xba\x89\x77"
+ "\xfd\x71\x7f\xe2\x9d\x9d\x35\x1d\x6b\xb2\x6f\x72\x27\x9b\xe5\x9e"
+ "\xc0\x21\x6f\x4a\x5f\x15\xf3\x13\xa4\xd9\x76\x96\xc1\xb3\xcf\x95"
+ "\xad\x47\x39\xa7\x10\x39\x7b\xfd\x9d\x71\xe3\xb8\xc4\xdf\xea\x96"
+ "\x40\x5e\xbb\x91\xfe\x99\x78\xc7\x97\x89\xca\x38\xd5\xdc\x7f\xad"
+ "\x3c\xf8\x59\x46\x05\x54\xb0\xfd\x33\x00\xd8\xa8\xb2\xf6\x44\xad"
+ "\xd5\xfc\x2d\xce\x92\x59\x85\x61\x5c\x53\xba\xf9\xe6\xc8\x47\x2f"
+ "\x50\xa3\x4a\xa6\x69\x70\xc2\x03\x5a\x44\x84\x20\x4b\x0a\x37\xa1"
+ "\x3b\xa8\x46\x96\xa6\x95\xfa\x59\x01\x38\xb5\xc7\xdb\x7c\xa8\x24"
+ "\xf9\x75\x90\xa9\x3d\x56\x5f\x95\x14\x17\xc7\xa3\x6f\xe7\xed\x0f"
+ "\x6b\xc9\x24\xfa\x0e\x4b\x33\x32\x0f\xd4\xe8\x3d\xae\x03\x9d\xfd"
+ "\xbf\x68\xef\x1b\x5a\xfa\x4b\x2e\x7f\x70\xd8\x95\x69\x03\x58\x1b"
+ "\xae\xf2\xde\xdb\x1e\xbd\x8a\xb0\xe5\xb3\x1a\x19\xe6\x1c\xf3\xf1"
+ "\xa5\xea\x8e\x61\x47\xd6\x4a\x5c\xb1\x2d\x4c\xcb\x22\xb5\x5a\x41"
+ "\xac\xad\xdc\x94\x71\xd4\x53\xcf\x67\xc6\xfa\xd4\x6b\x60\xe7\xf6"
+ "\x8e\xaf\xae\x98\x1c\x55\xd8\xed\xff\x48\x05\x69\xf4\x63\x4b\x0b"
+ "\x1a\xa4\x50\x3e\xff\x61\x72\x23\x5d\x8a\x19\x9e\x7b\x32\x79\x81"
+ "\xab\x4e\x5b\x5d\x06\x89\x2a\x26\x13\x20\x6d\xeb\xb2\x69\xb0\xd9"
+ "\xa3\x17\x04\xfd\x85\xfb\x54\x12\x02\xa8\x1d\xc4\xd8\x17\x15\xe0"
+ "\x67\xc0\x17\x81\x91\xab\x8a\x4b\x72\xe0\x10\x17\xc2\xd3\xe2\x5e"
+ "\x08\x24\xfe\x6f\x9d\xc0\x57\x03\x19\x4b\x62\x0a\xee\x3c\xd3\xc6"
+ "\xa7\x70\xe6\xca\x02\xea\x91\xf6\x31\xcd\x02\x5b\x55\xd2\xf4\x76"
+ "\x2b\x24\xbd\x09\x45\xe8\x00\x63\xbd\x3b\xf8\xeb\xe7\x17\x20\x24"
+ "\xaf\x11\x65\xaa\x52\xd1\xf5\x74\xf5\xed\xd2\x5b\x60\x86\x9a\xfc"
+ "\xa4\x63\x10\x25\x50\xb4\x40\x14\x97\x20\xf0\x53\xbe\x0f\x2a\x28"
+ "\x09\xac\x4d\x9d\x45\x52\x77\xcd\x65\x9a\x2c\xf2\x49\xfe\x92\x1a"
+ "\x6d\xb9\xf3\x29\x6c\xcd\x5b\xee\x73\x28\x6b\x2c\x14\xec\x6a\x25"
+ "\x64\xac\xcb\xdf\x1a\xe0\xaf\x56\xf6\x49\xea\xad\x06\x9c\xa3\x60"
+ "\xb2\xcf\x2c\xad\x19\xeb\xc3\x0f";
+
+static const unsigned char service_rodc[] =
+ "\x61\x82\x03\xf3\x30\x82\x03\xef\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x13\x30\x11\xa0\x03\x02"
+ "\x01\x01\xa1\x0a\x30\x08\x1b\x06\x61\x70\x61\x63\x68\x65\xa3\x82"
+ "\x03\xc5\x30\x82\x03\xc1\xa0\x03\x02\x01\x17\xa1\x03\x02\x01\x02"
+ "\xa2\x82\x03\xb3\x04\x82\x03\xaf\xaf\x34\x2b\xc2\xe0\x1a\x4d\x2d"
+ "\xb9\xda\x8c\x44\x52\xa3\xc1\xbf\xe2\xb3\xc9\x27\x29\xd2\x90\xa4"
+ "\xca\x87\xd7\xae\x3d\x10\x1a\xc4\x88\x6c\x22\x78\x9a\xb8\xd9\xb0"
+ "\x64\xa8\xfc\x49\x8b\xcd\x95\xf6\xc0\xeb\x32\xaa\x70\x06\xdf\xa8"
+ "\x98\x9f\xa5\x56\x42\xdf\x33\xd5\x6d\x15\x9d\xea\x6c\x04\x0d\xef"
+ "\x06\x13\x52\xe2\xa7\xc3\x76\xb9\xec\x95\x94\xc4\xad\xad\xb5\xa0"
+ "\xf5\x44\x69\x69\x96\xf3\xfb\x9e\x99\x48\xca\x5d\x85\x34\x10\xfb"
+ "\x51\xb1\x6f\x59\x70\x98\x06\x39\x55\xdc\xa3\x1f\xc8\x36\x63\x47"
+ "\xc8\xfc\xc7\x8e\x44\xaf\x5c\x20\x79\x3c\x8f\x54\xb5\x3b\x38\xb4"
+ "\xda\xcf\x6c\xea\xcd\x33\x0d\xc6\x86\x07\x08\xa7\x37\x3f\xed\xc4"
+ "\x3a\xa6\x98\xcd\x5e\xf8\x06\x3c\x47\xea\x04\xaf\x1e\xd3\x88\x1e"
+ "\x17\xf9\xac\xa7\x9e\xf2\x92\x70\xef\xd3\xb7\x39\x24\x65\x0c\x32"
+ "\x7e\x03\x2f\x36\x31\xa1\x9b\xe4\x58\x8f\x78\xde\xec\xa5\xba\x54"
+ "\x68\xb4\x4c\x17\x23\x0a\x62\xc6\xb0\x22\x3d\x21\x35\xfa\x4f\xf4"
+ "\x6d\x9e\x46\xb7\xf5\xcc\x28\xe4\x78\x3a\x36\x6a\x44\xc5\x34\xb7"
+ "\x34\x0d\xac\x4f\x02\x41\x8f\xfd\xcb\xd4\x4c\xb6\x4e\xf1\xd1\xe7"
+ "\x4a\x1b\xf1\x96\xa5\xd3\x15\x74\xd4\x52\xbe\x1b\xbd\xe1\x17\xe8"
+ "\x77\x79\xf6\x51\xc4\xc3\xd9\x6a\xe9\x4c\x70\x10\x33\xed\x59\xa5"
+ "\x7d\x05\x17\xde\x61\x65\x5f\xf5\xde\x06\xa4\xd6\x39\xe4\x3f\x83"
+ "\xdd\x95\xa3\xb3\x57\x60\x1c\xf3\x42\xdb\xdc\xd5\x39\x76\x68\xd3"
+ "\x6e\xc8\x06\xff\x6b\x5b\x1d\x72\x7d\xb4\x4d\xec\x3e\xb8\x6d\x09"
+ "\xff\xa5\x0e\x30\xe8\x12\x72\x31\xce\xf3\xdd\x78\xee\x43\x53\x75"
+ "\x26\x43\xc2\x29\x1f\x78\x2f\x03\x4f\x2d\x82\xe3\x7c\x82\xb0\x1d"
+ "\x64\xdc\x67\xb3\x53\xa6\x7f\x17\xd5\x11\xc1\x0d\xde\x79\x28\x07"
+ "\x40\x08\xe0\xfa\x41\x2b\xeb\x3e\xa9\xf0\x92\xdd\x4f\xfe\x13\x05"
+ "\x73\x1a\xc7\xfe\x14\x56\x86\x32\x27\x4d\xe5\x4e\x15\x35\x1e\xe5"
+ "\x54\x87\x8f\x75\x2a\xcb\x48\x5d\xc4\xce\xd3\x71\x25\x3b\xc6\x51"
+ "\xe2\x4f\x83\x87\x05\x97\xc9\x35\x4a\xce\xd7\x5a\x82\xcc\xe5\xc1"
+ "\xf6\xf6\x40\x8a\x91\xce\x05\x3a\x6a\xba\x9b\x43\xc6\x9f\x3b\x59"
+ "\xb4\xb7\x9b\x24\x6e\xad\x98\xdf\x3e\x12\x9f\x17\x31\x0c\xc7\x7d"
+ "\x31\x0b\x6c\xc0\x7b\x72\x1f\xb3\xcf\x24\x80\x64\x49\x95\x5d\xc0"
+ "\x0a\x84\xfc\x66\xf7\xcc\xc6\x82\x78\xf4\x23\x69\xfd\x34\x09\x94"
+ "\x9e\xd6\x26\xd3\xb6\x4f\x61\x50\xd0\x38\x72\x6d\x06\xef\xa1\x0f"
+ "\x3d\x3d\x46\xb0\x4b\xd9\xe8\xe9\x76\x73\xfe\x53\x82\x84\xbd\xea"
+ "\x4b\x1b\x97\x5e\x23\x55\x8d\xe6\x2d\x14\xe2\x9c\xbd\x07\xd2\xc2"
+ "\x98\x18\xbe\x02\xda\x19\x89\x95\x05\x06\xdb\x59\x4b\xd3\xfc\x18"
+ "\x9a\xf7\x39\x98\x9b\x79\x9c\xe1\xbe\x96\xf5\x55\xd7\x2c\x1b\xbb"
+ "\x84\x1b\xd3\xad\x8c\x26\x50\xbf\x86\x1b\x59\xfa\xab\xf5\xaf\x2b"
+ "\x04\x75\xf9\x33\xcf\xed\x01\x46\xbc\x5c\xd8\x9a\x3c\x1e\x11\x9e"
+ "\x03\x86\xf7\xed\x72\x93\xde\xf4\x5f\xb7\x63\x9c\x13\x0a\xa9\xba"
+ "\x51\x12\x3d\x13\xa5\xcf\x3e\x4b\x0d\x09\xfe\x97\x87\xde\xdf\x23"
+ "\x8d\x2a\x5a\x97\x7d\xfc\xe0\x40\x1a\x99\x75\x02\x7a\xb9\xae\xb4"
+ "\x51\x5d\x48\xcd\x98\xb1\xa7\x1e\x91\x06\xf5\xb7\x85\xb5\xd3\xe0"
+ "\x9e\xa2\x44\x08\xd7\xed\xbf\x3f\x6c\x39\x19\xfe\xd9\x80\x24\xe4"
+ "\x16\x7d\xd8\x66\x1f\xbb\x85\xc1\x38\x03\x1f\x36\xce\x77\x18\x11"
+ "\x99\x22\x7e\xb1\x7c\x47\x19\xd7\x8d\x79\x39\xef\xf5\xbd\xa0\xf7"
+ "\xca\x88\x79\x7e\xc9\x2b\xd7\x99\x2c\x87\x78\x7b\x4f\x4c\x38\xd8"
+ "\x02\xcd\x7c\xd4\x07\x33\x74\x89\x61\xa7\x11\x04\x98\xb2\x5f\x95"
+ "\x0c\xc3\x17\x35\xf6\xc3\x38\xee\xc3\xf6\xcf\x46\x68\xeb\x95\xb4"
+ "\xa3\x92\xbb\xe9\xd7\x7c\x92\x7f\xd0\x67\x64\x7d\x93\xc6\x37\xb3"
+ "\xb4\xe2\xf6\x31\x89\x22\x4b\xd7\xa6\x9d\xad\x14\x0c\x00\xc4\x7c"
+ "\x44\xfa\x2c\x89\x3a\x78\xc0\xe8\xb2\x6e\x4f\xde\x37\x07\xca\x5e"
+ "\x4a\x7f\x27\xbc\x2e\x96\x5b\xaf\xd0\x6b\xa4\x31\xad\x21\x2d\x0e"
+ "\x4c\x25\x97\xde\x42\x07\xc3\x97\xf0\xf5\x3b\x69\x8c\x89\x34\x8f"
+ "\xd4\xe7\x7c\xca\x45\x83\x7f\xf7\x7b\x4e\x75\x86\x39\x3b\x67\xbe"
+ "\x57\x76\xa1\x26\x65\x1d\x49\x8c\x33\x5c\xdf\x05\x9b\xcf\x4d\xce"
+ "\x06\xc9\xaa\xd5\xee\xf0\x3a\x42\x75\x02\x93\xf5\xeb\x00\xa7\xa4"
+ "\xfc\xe6\xff\x9e\xda\xb6\xbd\xc9\xe0\x67\xac\x99\x74\x99\xd8\x3e"
+ "\xf1\x13\xa8\x2f\xd7\x9c\x26\x95\x0e\xbe\x52\x79\x9b\xda\x8a\x78"
+ "\x7e\x2b\x9f\x22\xf3\xfb\x0f";
+
+static const unsigned char s4u2self_rodc[] =
+ "\x61\x82\x03\xf2\x30\x82\x03\xee\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x13\x30\x11\xa0\x03\x02"
+ "\x01\x01\xa1\x0a\x30\x08\x1b\x06\x61\x70\x61\x63\x68\x65\xa3\x82"
+ "\x03\xc4\x30\x82\x03\xc0\xa0\x03\x02\x01\x17\xa1\x03\x02\x01\x02"
+ "\xa2\x82\x03\xb2\x04\x82\x03\xae\x4d\x90\xed\x46\x56\x94\x72\x5e"
+ "\x2a\x3d\x13\xad\xfa\xfa\x0e\xd6\x75\x91\x38\x57\xd4\x31\x6b\x07"
+ "\x75\x4b\x5c\x3c\x2c\x7a\xdc\x71\xd9\x36\x27\xf9\x27\xbf\xf0\xa9"
+ "\xb5\x16\xa0\x12\x3c\x36\x36\x16\x6e\xa5\x19\xcb\x28\xf9\xbb\xe3"
+ "\x66\xf7\x02\xad\xa8\xf9\xca\xe0\x84\x27\x7e\x9f\x81\x02\xea\xba"
+ "\x85\x6c\x57\x56\x39\x59\xfd\xce\x0f\xb6\x7d\x11\x1c\xc4\x9c\x92"
+ "\xb7\xbc\xe0\xc9\x51\xb8\x0f\xd4\x03\x84\xc4\x25\x0b\x9d\x44\x56"
+ "\x3f\xc2\xf3\x63\x7b\x8d\x2b\x61\xf2\x5f\x5b\x51\x46\x9f\x81\x1e"
+ "\xfc\xc6\x96\x71\x81\x63\xf9\xd8\x20\x57\x08\x6a\xf2\xce\xf7\x4b"
+ "\x7f\x92\x3b\x20\x21\xac\x1e\x30\x9e\x88\x71\x55\x00\x12\xef\xe4"
+ "\xfe\xf1\x6c\xe1\xab\x2f\x7c\xd9\xea\x53\xac\xd8\x74\x36\x04\xe7"
+ "\x40\x9e\x2d\x49\xc4\x9a\x6d\xdb\x95\xd1\xa1\x41\x27\xf9\x9d\xa7"
+ "\x16\x44\xd3\x58\x50\x95\x93\xdd\xba\xd0\x81\x17\xaf\x37\x19\x99"
+ "\x66\x3d\xc8\x3b\x75\xa0\xef\xd8\xa6\x8d\xa5\xe9\x94\xba\x53\x1c"
+ "\x0f\xc7\xcc\x34\x34\x23\x4f\xd5\x63\xda\x33\x49\x9c\x89\xd9\xb7"
+ "\xef\x3d\xf3\x9b\x44\x44\xcf\x84\x72\x0d\x54\xca\x40\x67\x42\x81"
+ "\xd9\xd8\xd4\x83\x6c\x52\x94\x1b\xdc\x36\x81\xee\x51\x9f\x82\x1c"
+ "\xda\x04\x84\x26\x0c\x83\xd2\xe0\x56\xbe\x53\x6b\x50\x43\xc2\x1e"
+ "\xaa\x08\xce\x37\x6b\xa8\xa8\xf8\xf2\x03\x69\xfb\xfa\xcb\xfc\xa4"
+ "\x89\x47\xbb\xd2\x68\xe5\x2d\x00\x3e\xb2\x06\x71\xda\x4c\x35\xe9"
+ "\x6d\xfc\x17\x2a\xdf\xc2\xd5\x96\x67\x03\x39\x75\xe9\xa7\x1f\x77"
+ "\xb2\xa8\x0d\xc1\x50\xc2\xe5\xed\x1a\x88\xc3\x73\x81\xa9\x86\xd2"
+ "\xc6\x34\xc7\xaa\x1c\x7f\xe6\x47\xbf\x47\xc9\x90\x39\xb3\x2c\x31"
+ "\xa0\x26\xee\x7d\xbc\x61\xe0\x26\xaa\xf6\xce\x73\x6f\xdc\xed\x27"
+ "\xdd\x13\x53\x0e\x21\x52\x1f\xd3\x75\xdb\x07\x62\x14\x00\xda\x85"
+ "\x76\x77\x16\x34\x30\x90\x7f\x94\xc5\x54\x9d\xc6\xce\xab\x11\x0b"
+ "\x98\xa9\xe6\x5f\x82\xb7\x1b\x13\xcd\xb0\xcc\xa6\xb8\x27\xb3\xd7"
+ "\x0f\x1f\xd8\x75\x56\x0e\xc5\x73\x95\x6f\x90\xda\x49\x12\xd8\x2d"
+ "\x23\x49\x13\x43\x49\x1e\xe3\xbf\x80\x4a\xde\xd6\x97\x1e\x33\x50"
+ "\x44\x6b\x7b\x00\x05\x90\xab\xfe\x94\xa8\x7c\x40\x86\x42\x4a\x6b"
+ "\xc7\x26\x21\x12\x19\x98\x01\xd5\x64\x7d\xdb\xf7\xe3\xf7\xea\xca"
+ "\x5d\xf8\xcb\x0a\x3f\x83\xf2\x6b\xe7\x2e\x3c\x71\x73\xc9\x3c\xc0"
+ "\x82\xc5\x2d\x7b\xd0\x03\xa2\xe2\xd9\x6d\xcd\x15\xd5\xd4\xbf\x90"
+ "\xc6\xf4\x19\x5b\x72\x0f\xaf\x42\xd4\x28\x58\x4f\xbe\xff\x63\x6a"
+ "\x0d\x42\x9f\x05\xfe\xbd\xdb\x81\x18\xaa\x73\x4b\x16\x96\x1b\x62"
+ "\xe8\x21\x88\xde\x9a\x57\x70\xcc\xab\x49\x4e\x0c\x31\xec\x8d\xce"
+ "\x82\x20\x3c\x44\x9b\xbc\x36\xb2\x8f\x7d\xe5\xea\x11\xf6\x71\x16"
+ "\x8e\x66\xcb\xfb\xc2\x92\xf5\xc1\x09\xac\x90\x68\xc6\xd7\x93\x21"
+ "\xcb\xb8\x57\x47\x56\x8c\x13\x7e\x78\x81\x08\x25\x3d\x4c\x1d\xeb"
+ "\x39\x2c\xf5\x9b\x67\x5d\xb4\xee\xb5\x71\xba\xcf\x29\x7a\x6b\xa5"
+ "\x30\x4a\xfd\x16\x33\x5d\x68\x95\x62\xe7\x76\xca\x06\xb8\x94\x51"
+ "\x1f\x9a\x71\x99\x78\xaa\xec\xcf\x27\xc9\x16\xcb\x91\xa0\xbe\xca"
+ "\xf9\x9c\xbe\x4d\xb0\xb9\x98\x21\x01\xb2\xa7\x7a\x8a\x0d\x1c\x62"
+ "\xeb\x7d\x52\x81\xf9\xf5\x94\x0b\x27\x6a\x1c\xfc\xb6\xb2\x3a\x52"
+ "\x39\xd2\x6c\xca\x23\x43\xd1\xbf\x44\x54\x85\xb6\xd5\xb9\x93\xb9"
+ "\x91\x86\x59\x7f\xd2\x23\x1c\xeb\x2c\x5d\xb0\xcd\x09\x2d\x83\x62"
+ "\x24\x94\x65\x64\x13\x00\xe0\x1d\xd0\xf5\x56\x79\xff\xcc\xa9\xb9"
+ "\xda\xd4\x6a\xab\xe9\x2a\x91\x40\x44\xed\x7b\x8c\xed\xb1\x39\x70"
+ "\xd0\x51\x51\x6c\x45\x5f\xd4\xed\x0d\x36\x17\xf0\x69\xf6\x92\x72"
+ "\xca\xc8\x90\x1e\xe0\x44\xf0\x19\x2e\x2c\x1f\x12\x42\xa3\x15\x9d"
+ "\x37\x86\xa3\xa3\x6e\x04\xfc\x84\x73\xf8\x1d\x13\x30\xe5\x0f\x32"
+ "\x58\xab\x51\x0e\xad\x0a\xcc\xb9\x32\x81\x44\x7c\x6e\xc1\xec\xcd"
+ "\x5d\x6a\x9a\xb7\x27\x53\x98\xcc\x2b\x8d\x3c\xac\xbe\x12\xcb\x01"
+ "\xeb\xe7\xbe\xb5\x10\x16\x22\xc1\x92\x86\xe7\xa6\x1f\x88\xfa\x6a"
+ "\x70\xec\xd8\x38\x31\x85\x6f\x08\x33\x3c\x1b\x49\x68\xd4\xa5\x4c"
+ "\x7e\xf1\xbc\x25\x4f\x2f\xef\x39\xad\x78\x19\x23\x31\x47\xa1\x96"
+ "\xae\x7b\x8d\x8b\xf1\xf4\xc4\x3b\x06\xc0\xf6\x6e\x0f\xd2\xe0\x18"
+ "\xca\xc4\x09\x1b\x34\xbe\x1f\x12\x21\x69\x79\x9c\xaf\xe2\x78\xb4"
+ "\x19\x85\x83\xb7\x9c\x2d\x1a\x14\x1d\x64\xc8\x36\x48\xf2\x3d\xf6"
+ "\x26\x8c\x5a\x4c\x03\xae";
+
+static const unsigned char s4u2proxy_rodc[] =
+ "\x61\x82\x04\x9f\x30\x82\x04\x9b\xa0\x03\x02\x01\x05\xa1\x0a\x1b"
+ "\x08\x41\x43\x4d\x45\x2e\x43\x4f\x4d\xa2\x20\x30\x1e\xa0\x03\x02"
+ "\x01\x03\xa1\x17\x30\x15\x1b\x03\x73\x71\x6c\x1b\x0e\x6d\x79\x73"
+ "\x71\x6c\x2e\x61\x63\x6d\x65\x2e\x63\x6f\x6d\xa3\x82\x04\x64\x30"
+ "\x82\x04\x60\xa0\x03\x02\x01\x17\xa1\x03\x02\x01\x02\xa2\x82\x04"
+ "\x52\x04\x82\x04\x4e\xd2\x92\xec\x00\xf9\xc9\x37\xde\x46\xf6\x35"
+ "\x62\xb7\xa1\x77\x0c\x9a\x4f\x6d\x17\x2e\xfd\x59\x24\xfe\x85\x32"
+ "\x3f\xf2\x40\x1e\xcd\xf1\xa6\xd2\xeb\xba\x26\xa1\x87\xee\x0b\xee"
+ "\xbf\x3e\xad\x73\x07\xf1\xc8\xb9\x2b\xc7\x16\x31\xf3\x63\x95\x56"
+ "\x5d\x5d\x00\x6a\xff\xa0\x20\x05\x14\x0b\x50\x0b\x1e\x1a\xf2\x61"
+ "\x29\x1b\x49\x54\xf9\xac\x6c\x08\x91\xdf\x44\x3e\x43\x42\xbe\x4a"
+ "\x5a\x7d\xaf\xd5\xb9\x26\xe4\x91\x5f\xf0\xcc\x55\x3d\xc7\xc4\xc0"
+ "\x46\x3b\x32\x1f\xca\xcf\x16\xec\x18\x1b\x29\xe9\xb1\x4c\xdd\x0f"
+ "\xb3\x53\x2b\x0a\x7a\x5a\xff\xa0\xf4\x62\xcf\xf1\x2c\xc9\x64\xfa"
+ "\x35\xad\xb7\x18\x9b\x53\xb2\xe4\xdc\x81\x59\x8b\x2a\xb7\x94\xe0"
+ "\xf5\x99\x7d\x04\xb6\x49\xa2\x9a\x6a\xcc\x17\x62\x63\x5d\x2e\xf2"
+ "\x24\x56\x58\x00\x68\x6a\x60\x37\xf9\xa7\x55\xac\xb8\x95\xcb\x22"
+ "\x6a\xd6\x6c\x91\x99\x22\xc4\x33\x38\xfa\xf4\x7f\xf0\xf1\xae\x2e"
+ "\x5c\xa5\x6c\x6b\x98\x3e\x82\x94\xee\x2e\x4c\xbd\x41\xb8\x4f\xeb"
+ "\x0a\xca\xb1\x0b\x88\xfb\x51\x76\x87\x69\x7c\x0b\x61\xc3\xe6\xe1"
+ "\xd8\x2a\xc3\xee\xde\x13\x70\xbf\x6a\x9f\x7d\x2a\x6f\x7b\x57\x28"
+ "\xd3\x9e\x39\xf8\xc7\x05\xd5\xc0\xe3\x11\x11\x05\xeb\x2f\x08\x14"
+ "\x1c\x98\xae\x7e\x56\x8a\xb7\xcc\x2b\x74\xda\x01\x69\xeb\x56\x3c"
+ "\x89\x4f\x5d\xf8\xf7\xdb\x73\xa4\x84\x29\x91\x79\x32\xcc\xa0\xc1"
+ "\x28\xad\x9d\x4e\xcf\x45\x00\x6e\x8a\xe7\xf8\xd5\xeb\x87\x51\x37"
+ "\xcb\xb9\x4f\xc7\x08\x27\x2a\xf3\xaa\x24\x26\x0b\xed\x40\xcf\x74"
+ "\xae\xa9\xa3\xed\x1e\x97\xd0\x05\xb5\xae\xdf\x37\xb5\x24\x52\xa1"
+ "\xac\xd7\x31\xa2\xef\xc8\xab\x8a\xc4\x8c\x3d\x7c\xf3\x00\xf0\xc3"
+ "\xe7\xa3\x6d\x93\x83\xe4\x08\x2e\xa1\xb5\xc4\xdc\x4b\x3f\x06\x23"
+ "\x40\xaa\x0a\x72\xe6\x5a\xad\x65\x53\x3d\x48\x6c\x6f\xe3\xc5\x8b"
+ "\xa3\xba\x54\x73\x78\x07\xf8\x88\xd5\xf4\x87\x04\x5d\x88\x1b\xba"
+ "\x4e\x67\x9f\x92\xe8\x9c\xf0\xc4\xba\xfb\xe3\x78\xe2\xd9\xa3\x38"
+ "\xdb\xd6\x41\x4c\xa4\x76\x92\x36\xc1\xd7\x30\x05\x3f\xf8\x9f\xd1"
+ "\xf8\x79\x13\xeb\x98\xc2\x7a\xa3\x25\xab\x32\x1e\x14\x10\x87\xc0"
+ "\x00\x68\x26\xf8\x8d\x4d\xb5\x96\xb7\x35\x81\xe5\x0c\x3f\x45\x2c"
+ "\xcf\x4e\xdd\xda\xe6\xd4\xb3\xcb\x50\xf1\xe5\x48\x4c\xec\xcc\x10"
+ "\x33\xa0\x11\x53\x0d\xf3\x2c\x98\xcb\x76\xbf\x6e\xd7\xe9\x20\xe7"
+ "\xdb\xbd\xae\xcf\x69\x0e\xd1\xce\x47\xae\x5a\xe1\x21\x0d\xe9\xd5"
+ "\x2f\x09\xc4\x36\x53\x24\x4a\x5c\xac\x07\xff\xd8\xac\xfe\xae\x91"
+ "\x93\x92\xbf\xc6\x3b\xa4\xdb\x28\x52\x23\x58\x7d\xcb\xbd\x39\x34"
+ "\x07\xeb\x56\x1a\xf9\x47\xf8\x70\xee\x60\x51\x2e\x80\x92\xd4\xcf"
+ "\xd8\x9d\x75\x16\x50\xf1\xb0\x02\x61\x99\x51\x7c\x46\x48\xf9\x6b"
+ "\x84\xdf\x5c\xef\xe1\x1f\x0c\x22\x9b\xdc\xbc\x76\x7f\x3f\x6d\xfe"
+ "\xb8\x2f\x93\xb9\x27\x58\xa9\x93\x42\xda\xf2\x67\xf7\x01\xbe\xd8"
+ "\xa2\x18\xec\x1e\x40\x3d\x3b\x6f\xfe\x50\xab\xb9\x35\xb7\xdd\x2a"
+ "\xe1\x51\xf9\xce\xea\xf5\x1d\xed\x6c\x2b\xbc\x86\xc9\x53\x41\x8b"
+ "\x2e\x06\xc8\xc0\x08\x48\x37\x40\x8c\xf8\xe9\x4b\xc7\xc0\x6f\x1b"
+ "\xe1\x0c\x8c\x0e\xf4\x73\x19\x7e\xc6\x36\x84\xa2\x8f\x72\x2e\x59"
+ "\x53\x25\x2c\x92\x95\x04\x9b\x13\x97\x7a\xc7\x53\xa4\xa6\x0f\xf3"
+ "\x06\x59\x25\xe7\xd5\x35\x0e\xe9\x10\x95\x60\x0f\x53\xd4\x24\x13"
+ "\x72\xf6\x6d\x17\xb8\x43\xb2\xac\x40\x15\x11\xb6\x1b\xbb\x13\xb3"
+ "\x90\x66\x85\x92\xcb\xb4\xf5\x9c\x15\x89\x41\xbc\x07\xb0\x37\x21"
+ "\xff\x13\x93\x55\xe9\xe0\x2d\x32\x64\x62\x40\x45\xb5\x41\x80\xdd"
+ "\xb8\xf2\xe9\x64\x3e\xfb\xe6\x69\x1c\xd0\xb4\xb1\xa2\x58\x95\xe5"
+ "\xf7\x48\x8e\x10\x4c\x8b\x05\x96\xfa\x6f\x36\x34\xe1\x29\x1c\x65"
+ "\x65\x8d\xb0\x39\x50\x27\x00\xac\xa6\x4f\x5b\xd2\xb0\xfe\x69\x17"
+ "\xa2\xa8\x19\x34\x78\x9b\xef\x8c\xc2\xb9\xf1\xb6\xee\x28\xf4\x74"
+ "\xd6\x18\xe0\x0f\x36\x91\xf4\x56\xbc\x5d\x16\x21\x0e\x14\xb2\x5e"
+ "\x42\x38\xe2\x10\x81\x31\x54\x4b\xf5\xfd\x62\x72\xb0\x05\x16\x19"
+ "\x88\xe0\xd4\x5d\x5f\x10\x5c\x0d\x1f\x6a\x9f\x27\x48\x58\x18\xd1"
+ "\x60\x46\x17\xf3\x5e\xbc\xf1\x3a\xcd\x4b\x01\x08\x2e\x34\x5e\xa6"
+ "\x91\xa3\x87\x0f\xe7\x34\x9c\xcb\x87\x95\x40\xee\x22\x1c\x41\x55"
+ "\xc1\x6b\x36\x22\xa2\xbb\xf2\x55\x04\xb7\x79\x0f\xe1\xb8\x95\xcf"
+ "\x7b\x8a\x37\x6d\x1e\x78\x09\x5c\x1a\x6c\xa3\x93\x13\x70\xfe\x37"
+ "\xcc\x4a\xe7\x58\xda\xaa\x14\xe1\x32\xac\x64\xdb\x52\xef\x33\x71"
+ "\xf3\x27\xa5\x2e\x2c\x69\xe3\xa6\xab\x15\xe5\xdc\x27\xf4\xe8\x6d"
+ "\x7a\x66\x9a\x1e\x69\x46\xa9\xfa\x02\xbb\x35\x6e\x16\xc0\x06\xdf"
+ "\xd7\xcf\x8b\xab\x3e\xfc\x62\x7d\x35\x95\xb8\x15\xe2\xee\x6d\x61"
+ "\x5b\x7b\xb5\x88\x03\x45\x1c\xa0\x79\xff\x81\x4f\x75\xa9\xe7\x0a"
+ "\xed\x81\xac\xe6\x2f\x86\xb2\x23\x9b\x5a\xfe\x5c\xee\x18\x5a\x4a"
+ "\x0f\x4a\xf4\x49\x8d\xbc\x7f\x3f\xc4\xbc\x7f\xbf\x41\x95\x62\x9e"
+ "\xc6\x73\x5f\x5a\x5d\x41\x53\xe2\xaa\x03\xc8\x00\x68\xad\x26\xf2"
+ "\x8f\x66\x78\x10\x3f\xa8\x56\x4b\x77\xb9\x0d\x94\x49\x54\x47\xd5"
+ "\x69\x9d\x4f\x44\xce\xc3\x6d\xae\x51\x20\x24\x61\xb6\x6f\xff\x27"
+ "\xc4\x36\xb1";
+
+static const unsigned char extra_logon_info[] =
+ "\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\xf0\x01\x00\x00"
+ "\x88\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x1e\x00\x00\x00"
+ "\x78\x02\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x98\x00\x00\x00"
+ "\x98\x02\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00"
+ "\x30\x03\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x08\x00\x00\x00"
+ "\x30\x03\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x1c\x00\x00\x00"
+ "\x38\x03\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00\x10\x00\x00\x00"
+ "\x58\x03\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x10\x00\x00\x00"
+ "\x68\x03\x00\x00\x00\x00\x00\x00\x01\x10\x08\x00\xcc\xcc\xcc\xcc"
+ "\xe0\x01\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x7f\xff\xff\xff\xff"
+ "\xff\xff\xff\x7f\x59\xa1\x0f\x59\x77\xf5\xd7\x01\x59\xa1\x0f\x59"
+ "\x77\xf5\xd7\x01\x59\x21\x69\x4e\x78\x16\xd8\x01\x14\x00\x14\x00"
+ "\x04\x00\x02\x00\x00\x00\x00\x00\x08\x00\x02\x00\x00\x00\x00\x00"
+ "\x0c\x00\x02\x00\x00\x00\x00\x00\x10\x00\x02\x00\x00\x00\x00\x00"
+ "\x14\x00\x02\x00\x00\x00\x00\x00\x18\x00\x02\x00\x00\x00\x00\x00"
+ "\x4d\x04\x00\x00\x01\x02\x00\x00\x01\x00\x00\x00\x1c\x00\x02\x00"
+ "\x20\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x0e\x00\x10\x00\x20\x00\x02\x00\x0e\x00\x10\x00"
+ "\x24\x00\x02\x00\x28\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x02\x00\x00\x00\x2c\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x0a\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00"
+ "\x63\x00\x39\x00\x64\x00\x38\x00\x30\x00\x31\x00\x61\x00\x38\x00"
+ "\x5f\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x01\x00\x00\x00\x01\x02\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00"
+ "\x00\x00\x00\x00\x07\x00\x00\x00\x54\x00\x45\x00\x53\x00\x54\x00"
+ "\x2d\x00\x44\x00\x43\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00"
+ "\x07\x00\x00\x00\x45\x00\x58\x00\x41\x00\x4d\x00\x50\x00\x4c\x00"
+ "\x45\x00\x00\x00\x04\x00\x00\x00\x01\x04\x00\x00\x00\x00\x00\x05"
+ "\x15\x00\x00\x00\xa1\xa5\x92\x7f\x29\x19\xc5\x3b\xbb\x56\xb0\x05"
+ "\x02\x00\x00\x00\x30\x00\x02\x00\x07\x00\x00\x00\x34\x00\x02\x00"
+ "\x07\x00\x00\x00\x05\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05"
+ "\x15\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\xf1\x01\x00\x00\x01\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x12"
+ "\x01\x00\x00\x00\x00\x00\x00\x00\x00\xb6\x8f\x5a\x77\xf5\xd7\x01"
+ "\x14\x00\x63\x00\x39\x00\x64\x00\x38\x00\x30\x00\x31\x00\x61\x00"
+ "\x38\x00\x5f\x00\x30\x00\x00\x00\x2c\x00\x18\x00\x16\x00\x48\x00"
+ "\x03\x00\x00\x00\x14\x00\x60\x00\x1c\x00\x78\x00\x00\x00\x00\x00"
+ "\x63\x00\x39\x00\x64\x00\x38\x00\x30\x00\x31\x00\x61\x00\x38\x00"
+ "\x5f\x00\x30\x00\x40\x00\x45\x00\x58\x00\x41\x00\x4d\x00\x50\x00"
+ "\x4c\x00\x45\x00\x2e\x00\x43\x00\x4f\x00\x4d\x00\x00\x00\x00\x00"
+ "\x45\x00\x58\x00\x41\x00\x4d\x00\x50\x00\x4c\x00\x45\x00\x2e\x00"
+ "\x43\x00\x4f\x00\x4d\x00\x00\x00\x63\x00\x39\x00\x64\x00\x38\x00"
+ "\x30\x00\x31\x00\x61\x00\x38\x00\x5f\x00\x30\x00\x00\x00\x00\x00"
+ "\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xa1\xa5\x92\x7f"
+ "\x29\x19\xc5\x3b\xbb\x56\xb0\x05\x4d\x04\x00\x00\x00\x00\x00\x00"
+ "\x02\x00\x00\x00\x01\x00\x00\x00\x01\x05\x00\x00\x00\x00\x00\x05"
+ "\x15\x00\x00\x00\xa1\xa5\x92\x7f\x29\x19\xc5\x3b\xbb\x56\xb0\x05"
+ "\x4d\x04\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xfd\x39\xd8\x32"
+ "\x3b\x8c\xa5\xfe\x6b\x3d\x27\xff\x10\x00\x00\x00\x2f\x99\x7e\x57"
+ "\xca\xe4\xe1\xc7\x1b\xc0\xbd\xee";
+
+struct test_pac_ticket {
+ const char *name;
+ const unsigned char *ticket;
+ size_t ticket_len;
+ uint16_t rodc_id;
+ const krb5_keyblock *key;
+ const krb5_keyblock *kdc_key;
+};
+
+static const struct test_pac_ticket pac_tickets[] = {
+ { "tgt_ticket", tgt_ticket, sizeof(tgt_ticket), 0, &rwdc_tgt_key, &rwdc_tgt_pac_key },
+ { "service_ticket", service_ticket, sizeof(service_ticket), 0, &server_key, &rwdc_tgt_pac_key },
+ { "s4u2self_ticket", s4u2self_ticket, sizeof(s4u2self_ticket), 0, &server_key, &rwdc_tgt_pac_key },
+ { "s4u2proxy_ticket", s4u2proxy_ticket, sizeof(s4u2proxy_ticket), 0, &server_key, &rwdc_tgt_pac_key },
+ { "tgt_rodc", tgt_rodc, sizeof(tgt_rodc), 46673, &rodc_tgt_key, &rodc_tgt_pac_key },
+ { "service_rodc", service_rodc, sizeof(service_rodc), 46673, &server_key, &rodc_tgt_pac_key },
+ { "s4u2self_rodc", s4u2self_rodc, sizeof(s4u2self_rodc), 46673, &server_key, &rodc_tgt_pac_key },
+ { "s4u2proxy_rodc", s4u2proxy_rodc, sizeof(s4u2proxy_rodc), 46673, &server_key, &rodc_tgt_pac_key },
+ { NULL, NULL, 0, 0, NULL, NULL }
+};
+
+static void
+t_err(krb5_context context,
+ const char *test,
+ const char *func,
+ krb5_error_code error)
+{
+ krb5_err(context, 1, error, "test %s failed in %s", test, func);
+}
+
+static void
+check_ticket_signature(krb5_context context,
+ const struct test_pac_ticket *tkt)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ krb5_data data, orig_pac_ad;
+ Ticket ticket;
+ AuthorizationDataElement ad;
+ EncTicketPart et;
+ krb5_principal client;
+ krb5_pac pac;
+ krb5_boolean signedticket;
+ krb5_cksumtype cstype;
+ uint16_t rodc_id;
+
+ ret = decode_Ticket(tkt->ticket, tkt->ticket_len, &ticket, NULL);
+ if (ret)
+ t_err(context, tkt->name, "decode_Ticket", ret);
+
+ ret = krb5_crypto_init(context, tkt->key, tkt->key->keytype, &crypto);
+ if (ret)
+ t_err(context, tkt->name, "krb5_crypto_init", ret);
+
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_TICKET,
+ &ticket.enc_part,
+ &data);
+ if (ret)
+ t_err(context, tkt->name, "krb5_decrypt_EncryptedData", ret);
+
+ ret = decode_EncTicketPart(data.data, data.length, &et, NULL);
+ if (ret)
+ t_err(context, tkt->name, "decode_EncTicketPart", ret);
+
+ ret = _krb5_principalname2krb5_principal(context, &client, et.cname,
+ et.crealm);
+ if (ret)
+ t_err(context, tkt->name, "_krb5_principalname2krb5_principal", ret);
+
+ ret = _krb5_kdc_pac_ticket_parse(context, &et, &signedticket, &pac);
+ if (ret)
+ t_err(context, tkt->name, "_krb5_kdc_pac_ticket_parse", ret);
+
+ heim_assert(!krb5_principalname_is_krbtgt(context,
+ &ticket.sname) == !!signedticket,
+ "ticket-signature");
+
+ /*
+ * We have to not verify the KDC checksum as the saved PAC has no
+ * full checksum, and krb5_pac_verify requires this now
+ */
+ ret = krb5_pac_verify(context, pac, et.authtime, client,
+ tkt->key, NULL);
+ if (ret)
+ t_err(context, tkt->name, "krb5_pac_verify ticket-sig", ret);
+
+ ret = krb5_pac_get_kdc_checksum_info(context, pac, &cstype, &rodc_id);
+ if (ret)
+ t_err(context, tkt->name, "krb5_pac_get_kdc_checksum_info", ret);
+
+ heim_assert(cstype == CKSUMTYPE_HMAC_MD5, "Wrong checksum type");
+ heim_assert(rodc_id == tkt->rodc_id, "Wrong RODCIdentifier");
+
+ /* Try to resign the PAC */
+ ret = copy_AuthorizationDataElement(&et.authorization_data->val[0], &ad);
+ if (ret)
+ t_err(context, tkt->name, "remove_AuthorizationData", ret);
+ orig_pac_ad = ad.ad_data;
+
+ ret = remove_AuthorizationData(et.authorization_data, 0);
+ if (ret)
+ t_err(context, tkt->name, "remove_AuthorizationData", ret);
+
+ /*
+ * While we should do a full PAC signature in the same case as
+ * signedticket, the saved examples do not have one
+ */
+ ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key,
+ tkt->kdc_key, tkt->rodc_id,
+ NULL, NULL, signedticket, FALSE, &et, NULL);
+ if (ret)
+ t_err(context, tkt->name, "_krb5_kdc_pac_sign_ticket", ret);
+
+ heim_assert(krb5_data_cmp(&et.authorization_data->val[0].ad_data,
+ &orig_pac_ad) == 0, "PACs differ");
+
+ free_AuthorizationDataElement(&ad);
+
+ /* Sign and verify a clean PAC */
+ krb5_pac_free(context, pac);
+ ret = krb5_pac_init(context, &pac);
+ if (ret)
+ t_err(context, tkt->name, "krb5_pac_init", ret);
+
+ ret = remove_AuthorizationData(et.authorization_data, 0);
+ if (ret)
+ t_err(context, tkt->name, "remove_AuthorizationData 2", ret);
+
+ /*
+ * This time we will not be doing a krb5_data_cmp() so we add the
+ * full signature so that we can run that check in
+ * krb5_pac_verify()
+ */
+ ret = _krb5_kdc_pac_sign_ticket(context, pac, client, tkt->key,
+ tkt->kdc_key, tkt->rodc_id,
+ NULL, NULL, signedticket, TRUE, &et, NULL);
+ if (ret)
+ t_err(context, tkt->name, "_krb5_kdcsignedticketsign_ticket 2", ret);
+
+ krb5_pac_free(context, pac);
+
+ ret = _krb5_kdc_pac_ticket_parse(context, &et, &signedticket, &pac);
+ if (ret)
+ t_err(context, tkt->name, "_krb5_kdc_pac_ticket_parse 2", ret);
+
+ heim_assert(!krb5_principalname_is_krbtgt(context,
+ &ticket.sname) == !!signedticket,
+ "ticket-signature");
+
+ ret = krb5_pac_verify(context, pac, et.authtime, client, tkt->key,
+ tkt->kdc_key);
+ if (ret)
+ t_err(context, tkt->name, "krb5_pac_verify ticket-sig 2", ret);
+
+ ret = krb5_pac_get_kdc_checksum_info(context, pac, &cstype, &rodc_id);
+ if (ret)
+ t_err(context, tkt->name, "krb5_pac_get_kdc_checksum_info 2", ret);
+
+ heim_assert(cstype == CKSUMTYPE_HMAC_MD5, "Wrong checksum type 2");
+ heim_assert(rodc_id == tkt->rodc_id, "Wrong RODCIdentifier 2");
+
+ krb5_pac_free(context, pac);
+ krb5_free_principal(context, client);
+ krb5_crypto_destroy(context, crypto);
+ free_EncTicketPart(&et);
+ krb5_data_free(&data);
+ free_Ticket(&ticket);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_pac pac;
+ krb5_data data;
+ krb5_principal p, p2;
+ unsigned char bad_pac[sizeof(saved_pac)];
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context");
+
+ krb5_enctype_enable(context, ETYPE_DES_CBC_MD5);
+
+ ret = krb5_parse_name_flags(context, user,
+ KRB5_PRINCIPAL_PARSE_NO_REALM, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ /* Attempt to parse a truncated PAC */
+ ret = krb5_pac_parse(context, saved_pac, sizeof(saved_pac) >> 1, &pac);
+ if (ret == 0)
+ krb5_err(context, 1, ret, "krb5_pac_parse parsed a short PAC");
+
+ /* Attempt to parse a PAC with a buffer claiming too large a length */
+ memcpy(bad_pac, saved_pac, sizeof(saved_pac));
+ bad_pac[13] += 1;
+
+ ret = krb5_pac_parse(context, bad_pac, sizeof(saved_pac), &pac);
+ if (ret == 0)
+ krb5_err(context, 1, ret, "krb5_pac_parse parsed a malicious PAC");
+
+ /* Attempt to parse a PAC with a buffer offset too far in */
+ memcpy(bad_pac, saved_pac, sizeof(saved_pac));
+ bad_pac[16] += 1;
+
+ ret = krb5_pac_parse(context, bad_pac, sizeof(saved_pac), &pac);
+ if (ret == 0)
+ krb5_err(context, 1, ret, "krb5_pac_parse parsed a malicious PAC");
+
+ /* Attempt to parse a PAC with a buffer offset too far back */
+ memcpy(bad_pac, saved_pac, sizeof(saved_pac));
+ bad_pac[16] -= 1;
+
+ ret = krb5_pac_parse(context, bad_pac, sizeof(saved_pac), &pac);
+ if (ret == 0)
+ krb5_err(context, 1, ret, "krb5_pac_parse parsed a malicious PAC");
+
+ /* Attempt to parse a PAC with an incorrect buffer count */
+ memcpy(bad_pac, saved_pac, sizeof(saved_pac));
+ bad_pac[0] += 1;
+
+ ret = krb5_pac_parse(context, bad_pac, sizeof(saved_pac), &pac);
+ if (ret == 0)
+ krb5_err(context, 1, ret, "krb5_pac_parse parsed a malicious PAC");
+
+ /* Parse a well-formed PAC */
+ ret = krb5_pac_parse(context, saved_pac, sizeof(saved_pac), &pac);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_parse");
+
+ /*
+ * We have to not verify the KDC checksum as the saved PAC has no
+ * full checksum, and krb5_pac_verify requires this now
+ */
+ ret = krb5_pac_verify(context, pac, authtime, p,
+ &member_keyblock, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_verify");
+
+ ret = _krb5_pac_sign(context, pac, authtime, p,
+ &member_keyblock, &kdc_keyblock, 0, NULL, NULL,
+ TRUE,
+ NULL, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_pac_sign");
+
+ krb5_pac_free(context, pac);
+
+ ret = krb5_pac_parse(context, data.data, data.length, &pac);
+ krb5_data_free(&data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_parse 2");
+
+ ret = krb5_pac_verify(context, pac, authtime, p,
+ &member_keyblock, &kdc_keyblock);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_verify 2");
+
+ /* make a copy and try to reproduce it */
+ {
+ uint32_t *list;
+ size_t len, i;
+ krb5_pac pac2;
+
+ ret = krb5_pac_init(context, &pac2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_init");
+
+ /* our two user buffer plus the four "system" buffers */
+ ret = krb5_pac_get_types(context, pac, &len, &list);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_get_types");
+
+ for (i = 0; i < len; i++) {
+ /* skip server_cksum, privsvr_cksum, and logon_name */
+ if (list[i] == 6 || list[i] == 7 || list[i] == 10 || list[i] == 19)
+ continue;
+
+ ret = krb5_pac_get_buffer(context, pac, list[i], &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_get_buffer");
+
+ if (list[i] == 1) {
+ if (type_1_length != data.length)
+ krb5_errx(context, 1, "type 1 have wrong length: %lu",
+ (unsigned long)data.length);
+ } else
+ krb5_errx(context, 1, "unknown type %lu",
+ (unsigned long)list[i]);
+
+ ret = krb5_pac_add_buffer(context, pac2, list[i], &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_add_buffer");
+ krb5_data_free(&data);
+ }
+ free(list);
+
+ ret = _krb5_pac_sign(context, pac2, authtime, p,
+ &member_keyblock, &kdc_keyblock, 0,
+ NULL, NULL, TRUE, NULL, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_pac_sign 4");
+
+ krb5_pac_free(context, pac2);
+
+ ret = krb5_pac_parse(context, data.data, data.length, &pac2);
+ krb5_data_free(&data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_parse 4");
+
+ ret = krb5_pac_verify(context, pac2, authtime, p,
+ &member_keyblock, &kdc_keyblock);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_verify 4");
+
+ krb5_pac_free(context, pac2);
+ }
+
+ krb5_pac_free(context, pac);
+
+ /*
+ * check pac from Christian
+ */
+
+ ret = krb5_parse_name_flags(context, user2,
+ KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_pac_parse(context, saved_pac2, sizeof(saved_pac2) -1, &pac);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_parse");
+
+ ret = krb5_pac_verify(context, pac, authtime2, p2,
+ &member_keyblock2, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_verify c1");
+
+ krb5_pac_free(context, pac);
+ krb5_free_principal(context, p2);
+
+ /*
+ * Test empty free
+ */
+
+ ret = krb5_pac_init(context, &pac);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_init");
+ krb5_pac_free(context, pac);
+
+ /*
+ * Test add remove buffer
+ */
+
+ ret = krb5_pac_init(context, &pac);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_init");
+
+ {
+ const krb5_data cdata = { 2, "\x00\x01" } ;
+
+ ret = krb5_pac_add_buffer(context, pac, 1, &cdata);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_add_buffer");
+ }
+ {
+ ret = krb5_pac_get_buffer(context, pac, 1, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_get_buffer");
+ if (data.length != 2 || memcmp(data.data, "\x00\x01", 2) != 0)
+ krb5_errx(context, 1, "krb5_pac_get_buffer data not the same");
+ krb5_data_free(&data);
+ }
+
+ {
+ const krb5_data cdata = { 2, "\x02\x00" } ;
+
+ ret = krb5_pac_add_buffer(context, pac, 2, &cdata);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_add_buffer");
+ }
+ {
+ ret = krb5_pac_get_buffer(context, pac, 1, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_get_buffer");
+ if (data.length != 2 || memcmp(data.data, "\x00\x01", 2) != 0)
+ krb5_errx(context, 1, "krb5_pac_get_buffer data not the same");
+ krb5_data_free(&data);
+ /* */
+ ret = krb5_pac_get_buffer(context, pac, 2, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_get_buffer");
+ if (data.length != 2 || memcmp(data.data, "\x02\x00", 2) != 0)
+ krb5_errx(context, 1, "krb5_pac_get_buffer data not the same");
+ krb5_data_free(&data);
+ }
+
+ ret = _krb5_pac_sign(context, pac, authtime, p,
+ &member_keyblock, &kdc_keyblock, 0,
+ NULL, NULL, TRUE, NULL, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_pac_sign");
+
+ krb5_pac_free(context, pac);
+
+ ret = krb5_pac_parse(context, data.data, data.length, &pac);
+ krb5_data_free(&data);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_parse 3");
+
+ ret = krb5_pac_verify(context, pac, authtime, p,
+ &member_keyblock, &kdc_keyblock);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_verify 3");
+
+ {
+ uint32_t *list;
+ size_t len;
+
+ /* our two user buffer plus the four "system" buffers */
+ ret = krb5_pac_get_types(context, pac, &len, &list);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_get_types");
+ if (len != 6)
+ krb5_errx(context, 1, "list wrong length");
+ free(list);
+ }
+
+ krb5_pac_free(context, pac);
+ krb5_free_principal(context, p);
+
+ /*
+ * check extra logon info PAC
+ */
+ ret = krb5_pac_parse(context, extra_logon_info,
+ sizeof(extra_logon_info) - 1, &pac);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_parse");
+
+ ret = krb5_pac_verify(context, pac, 0, NULL, NULL, NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_pac_verify");
+
+ ret = krb5_parse_name(context, "c9d801a8_0@EXAMPLE.COM", &p);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_pac_get_canon_principal");
+
+ ret = _krb5_pac_get_canon_principal(context, pac, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_pac_get_canon_principal");
+
+ if (!krb5_principal_compare(context, p, p2))
+ krb5_errx(context, 1, "canon principal doesn't match");
+
+ krb5_pac_free(context, pac);
+ krb5_free_principal(context, p);
+ krb5_free_principal(context, p2);
+
+ /* Test PAC ticket-signature */
+ {
+ const struct test_pac_ticket *tkt;
+
+ for (tkt = pac_tickets; tkt->name != NULL; tkt++)
+ check_ticket_signature(context, tkt);
+ }
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_pkinit_dh2key.c b/third_party/heimdal/lib/krb5/test_pkinit_dh2key.c
new file mode 100644
index 0000000..e18fd17
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_pkinit_dh2key.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+static void
+test_dh2key(int i,
+ krb5_context context,
+ const heim_octet_string *dh,
+ const heim_octet_string *c_n,
+ const heim_octet_string *k_n,
+ krb5_enctype etype,
+ const heim_octet_string *result)
+{
+ krb5_error_code ret;
+ krb5_keyblock key;
+
+ ret = _krb5_pk_octetstring2key(context,
+ etype,
+ dh->data, dh->length,
+ c_n,
+ k_n,
+ &key);
+ if (ret != 0)
+ krb5_err(context, 1, ret, "_krb5_pk_octetstring2key: %d", i);
+
+ if (key.keyvalue.length != result->length ||
+ memcmp(key.keyvalue.data, result->data, result->length) != 0)
+ krb5_errx(context, 1, "resulting key wrong: %d", i);
+
+ krb5_free_keyblock_contents(context, &key);
+}
+
+
+struct {
+ krb5_enctype type;
+ krb5_data X;
+ krb5_data key;
+} tests[] = {
+ /* 0 */
+ {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ {
+ 256,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ },
+ {
+ 32,
+ "\x5e\xe5\x0d\x67\x5c\x80\x9f\xe5\x9e\x4a\x77\x62\xc5\x4b\x65\x83"
+ "\x75\x47\xea\xfb\x15\x9b\xd8\xcd\xc7\x5f\xfc\xa5\x91\x1e\x4c\x41"
+ }
+ },
+ /* 1 */
+ {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ {
+ 128,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ },
+ {
+ 32,
+ "\xac\xf7\x70\x7c\x08\x97\x3d\xdf\xdb\x27\xcd\x36\x14\x42\xcc\xfb"
+ "\xa3\x55\xc8\x88\x4c\xb4\x72\xf3\x7d\xa6\x36\xd0\x7d\x56\x78\x7e"
+ }
+ },
+ /* 2 */
+ {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ {
+ 128,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
+ "\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
+ "\x0e\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
+ "\x0d\x0e\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b"
+ "\x0c\x0d\x0e\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a"
+ "\x0b\x0c\x0d\x0e\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09"
+ "\x0a\x0b\x0c\x0d\x0e\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08"
+ },
+ {
+ 32,
+ "\xc4\x42\xda\x58\x5f\xcb\x80\xe4\x3b\x47\x94\x6f\x25\x40\x93\xe3"
+ "\x73\x29\xd9\x90\x01\x38\x0d\xb7\x83\x71\xdb\x3a\xcf\x5c\x79\x7e"
+ }
+ },
+ /* 3 */
+ {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ {
+ 77,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+ "\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
+ "\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
+ "\x0e\x0f\x10\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c"
+ "\x0d\x0e\x0f\x10\x00\x01\x02\x03"
+ "\x04\x05\x06\x07\x08"
+ },
+ {
+ 32,
+ "\x00\x53\x95\x3b\x84\xc8\x96\xf4\xeb\x38\x5c\x3f\x2e\x75\x1c\x4a"
+ "\x59\x0e\xd6\xff\xad\xca\x6f\xf6\x4f\x47\xeb\xeb\x8d\x78\x0f\xfc"
+ }
+ }
+};
+
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
+ test_dh2key(i, context, &tests[i].X, NULL, NULL,
+ tests[i].type, &tests[i].key);
+ }
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_pknistkdf.c b/third_party/heimdal/lib/krb5/test_pknistkdf.c
new file mode 100644
index 0000000..f97dddf
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_pknistkdf.c
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <pkinit_asn1.h>
+#include <err.h>
+#include <getarg.h>
+#include <hex.h>
+
+static int verbose_flag = 0;
+
+struct testcase {
+ const heim_oid *oid;
+ krb5_data Z;
+ const char *client;
+ const char *server;
+ krb5_enctype enctype;
+ krb5_data as_req;
+ krb5_data pk_as_rep;
+ krb5_data ticket;
+
+ krb5_data key;
+} tests[] = {
+ /* 0 */
+ {
+ NULL, /* AlgorithmIdentifier */
+ { /* Z */
+ 256,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ },
+ "lha@SU.SE", /* client, partyUInfo */
+ "krbtgt/SU.SE@SU.SE", /* server, partyVInfo */
+ ETYPE_AES256_CTS_HMAC_SHA1_96, /* enctype */
+ { /* as_req */
+ 10,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ },
+ { /* pk_as_rep */
+ 9,
+ "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB"
+ },
+ { /* ticket */
+ 55,
+ "\x61\x35\x30\x33\xa0\x03\x02\x01\x05\xa1\x07\x1b\x05\x53\x55\x2e"
+ "\x53\x45\xa2\x10\x30\x0e\xa0\x03\x02\x01\x01\xa1\x07\x30\x05\x1b"
+ "\x03\x6c\x68\x61\xa3\x11\x30\x0f\xa0\x03\x02\x01\x12\xa2\x08\x04"
+ "\x06\x68\x65\x6a\x68\x65\x6a"
+ },
+ { /* key */
+ 32,
+ "\xc7\x62\x89\xec\x4b\x28\xa6\x91\xff\xce\x80\xbb\xb7\xec\x82\x41"
+ "\x52\x3f\x99\xb1\x90\xcf\x2d\x34\x8f\x54\xa8\x65\x81\x2c\x32\x73"
+ }
+ },
+ /* 1 */
+ {
+ NULL, /* AlgorithmIdentifier */
+ { /* Z */
+ 256,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ },
+ "lha@SU.SE", /* client, partyUInfo */
+ "krbtgt/SU.SE@SU.SE", /* server, partyVInfo */
+ ETYPE_AES256_CTS_HMAC_SHA1_96, /* enctype */
+ { /* as_req */
+ 10,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ },
+ { /* pk_as_rep */
+ 9,
+ "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB"
+ },
+ { /* ticket */
+ 55,
+ "\x61\x35\x30\x33\xa0\x03\x02\x01\x05\xa1\x07\x1b\x05\x53\x55\x2e"
+ "\x53\x45\xa2\x10\x30\x0e\xa0\x03\x02\x01\x01\xa1\x07\x30\x05\x1b"
+ "\x03\x6c\x68\x61\xa3\x11\x30\x0f\xa0\x03\x02\x01\x12\xa2\x08\x04"
+ "\x06\x68\x65\x6a\x68\x65\x6a"
+ },
+ { /* key */
+ 32,
+ "\x59\xf3\xca\x77\x5b\x20\x17\xe9\xad\x36\x3f\x47\xca\xbd\x43\xb8"
+ "\x8c\xb8\x90\x35\x8d\xc6\x0d\x52\x0d\x11\x9f\xb0\xdc\x24\x0b\x61"
+ }
+ },
+ /* 2 */
+ {
+ NULL, /* AlgorithmIdentifier */
+ { /* Z */
+ 256,
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ },
+ "lha@SU.SE", /* client, partyUInfo */
+ "krbtgt/SU.SE@SU.SE", /* server, partyVInfo */
+ ETYPE_AES256_CTS_HMAC_SHA1_96, /* enctype */
+ { /* as_req */
+ 10,
+ "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"
+ },
+ { /* pk_as_rep */
+ 9,
+ "\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB\xBB"
+ },
+ { /* ticket */
+ 55,
+ "\x61\x35\x30\x33\xa0\x03\x02\x01\x05\xa1\x07\x1b\x05\x53\x55\x2e"
+ "\x53\x45\xa2\x10\x30\x0e\xa0\x03\x02\x01\x01\xa1\x07\x30\x05\x1b"
+ "\x03\x6c\x68\x61\xa3\x11\x30\x0f\xa0\x03\x02\x01\x12\xa2\x08\x04"
+ "\x06\x68\x65\x6a\x68\x65\x6a"
+ },
+ { /* key */
+ 32,
+ "\x8a\x9a\xc5\x5f\x45\xda\x1a\x73\xd9\x1e\xe9\x88\x1f\xa9\x48\x81"
+ "\xce\xac\x66\x2d\xb1\xd3\xb9\x0a\x9d\x0e\x52\x83\xdf\xe1\x84\x3d"
+ }
+ }
+};
+
+#ifdef MAKETICKET
+static void
+fooTicket(void)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+ Ticket t;
+
+ t.tkt_vno = 5;
+ t.realm = "SU.SE";
+ t.sname.name_type = KRB5_NT_PRINCIPAL;
+ t.sname.name_string.len = 1;
+ t.sname.name_string.val = ecalloc(1, sizeof(t.sname.name_string.val[0]));
+ t.sname.name_string.val[0] = estrdup("lha");
+ t.enc_part.etype = ETYPE_AES256_CTS_HMAC_SHA1_96;
+ t.enc_part.kvno = NULL;
+ t.enc_part.cipher.length = 6;
+ t.enc_part.cipher.data = "hejhej";
+
+ ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, &t, &size, ret);
+ if (ret)
+ errx(1, "ASN1_MALLOC_ENCODE(Ticket)");
+
+ rk_dumpdata("foo", data.data, data.length);
+ free(data.data);
+}
+#endif
+
+static void
+test_dh2key(krb5_context context, int i, struct testcase *c)
+{
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_principal client, server;
+ Ticket ticket;
+ AlgorithmIdentifier ai;
+ size_t size;
+
+ memset(&ticket, 0, sizeof(ticket));
+
+ ai.algorithm = *c->oid;
+ ai.parameters = NULL;
+
+ ret = decode_Ticket(c->ticket.data, c->ticket.length, &ticket, &size);
+ if (ret)
+ krb5_errx(context, 1, "decode ticket: %d", ret);
+
+ ret = krb5_parse_name(context, c->client, &client);
+ if (ret)
+ krb5_err(context, 1, ret, "parse_name: %s", c->client);
+ ret = krb5_parse_name(context, c->server, &server);
+ if (ret)
+ krb5_err(context, 1, ret, "parse_name: %s", c->server);
+ /*
+ * Making krb5_build_principal*() set a reasonable default principal
+ * name type broke the test vectors here. Rather than regenerate
+ * the vectors, and to prove that this was the issue, we coerce the
+ * name types back to their original.
+ */
+ krb5_principal_set_type(context, client, KRB5_NT_PRINCIPAL);
+ krb5_principal_set_type(context, server, KRB5_NT_PRINCIPAL);
+
+ if (verbose_flag) {
+ char *str;
+ hex_encode(c->Z.data, c->Z.length, &str);
+ printf("Z: %s\n", str);
+ free(str);
+ printf("client: %s\n", c->client);
+ printf("server: %s\n", c->server);
+ printf("enctype: %d\n", (int)c->enctype);
+ hex_encode(c->as_req.data, c->as_req.length, &str);
+ printf("as-req: %s\n", str);
+ free(str);
+ hex_encode(c->pk_as_rep.data, c->pk_as_rep.length, &str);
+ printf("pk-as-rep: %s\n", str);
+ free(str);
+ hex_encode(c->ticket.data, c->ticket.length, &str);
+ printf("ticket: %s\n", str);
+ free(str);
+ }
+
+ ret = _krb5_pk_kdf(context,
+ &ai,
+ c->Z.data,
+ c->Z.length,
+ client,
+ server,
+ c->enctype,
+ &c->as_req,
+ &c->pk_as_rep,
+ &ticket,
+ &key);
+ krb5_free_principal(context, client);
+ krb5_free_principal(context, server);
+ if (ret)
+ krb5_err(context, 1, ret, "_krb5_pk_kdf: %d", i);
+
+ if (verbose_flag) {
+ char *str;
+ hex_encode(key.keyvalue.data, key.keyvalue.length, &str);
+ printf("key: %s\n", str);
+ free(str);
+ }
+
+ if (key.keyvalue.length != c->key.length ||
+ memcmp(key.keyvalue.data, c->key.data, c->key.length) != 0)
+ krb5_errx(context, 1, "resulting key wrong: %d", i);
+
+ krb5_free_keyblock_contents(context, &key);
+ free_Ticket(&ticket);
+}
+
+
+
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"verbose", 0, arg_flag, &verbose_flag,
+ "verbose output", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+#ifdef MAKETICKET
+ fooTicket();
+#endif
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ tests[0].oid = &asn1_oid_id_pkinit_kdf_ah_sha1;
+ tests[1].oid = &asn1_oid_id_pkinit_kdf_ah_sha256;
+ tests[2].oid = &asn1_oid_id_pkinit_kdf_ah_sha512;
+
+ for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++)
+ test_dh2key(context, i, &tests[i]);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_plugin.c b/third_party/heimdal/lib/krb5/test_plugin.c
new file mode 100644
index 0000000..6aedfa2
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_plugin.c
@@ -0,0 +1,131 @@
+/*
+ * 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 "locate_plugin.h"
+
+static krb5_error_code KRB5_CALLCONV
+resolve_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ return 0;
+}
+
+static void KRB5_CALLCONV
+resolve_fini(void *ctx)
+{
+}
+
+static krb5_error_code KRB5_CALLCONV
+resolve_lookup(void *ctx,
+ enum locate_service_type service,
+ const char *realm,
+ int domain,
+ int type,
+ int (*add)(void *,int,struct sockaddr *),
+ void *addctx)
+{
+ struct sockaddr_in s;
+
+ memset(&s, 0, sizeof(s));
+
+#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
+ s.sin_len = sizeof(s);
+#endif
+ s.sin_family = AF_INET;
+ s.sin_port = htons(88);
+ s.sin_addr.s_addr = htonl(0x7f000002);
+
+ if (strcmp(realm, "NOTHERE.H5L.SE") == 0) {
+ (*add)(addctx, type, (struct sockaddr *)&s);
+ return 0;
+ }
+
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+
+krb5plugin_service_locate_ftable resolve = {
+ 0,
+ resolve_init,
+ resolve_fini,
+ resolve_lookup,
+ NULL
+};
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_krbhst_handle handle;
+ char host[MAXHOSTNAMELEN];
+ int found = 0;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_contex");
+
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_LOCATE, &resolve);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_plugin_register");
+
+
+ ret = krb5_krbhst_init_flags(context,
+ "NOTHERE.H5L.SE",
+ KRB5_KRBHST_KDC,
+ 0,
+ &handle);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_krbhst_init_flags");
+
+
+ while(krb5_krbhst_next_as_string(context, handle, host, sizeof(host)) == 0){
+ found++;
+ if (!found && strcmp(host, "127.0.0.2") != 0 && strcmp(host, "tcp/127.0.0.2") != 0)
+ krb5_errx(context, 1, "wrong address: %s", host);
+ }
+ if (!found)
+ krb5_errx(context, 1, "failed to find host");
+ if (found < 2)
+ krb5_errx(context, 1, "did not get the two expected results");
+
+ krb5_krbhst_free(context, handle);
+
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_prf.c b/third_party/heimdal/lib/krb5/test_prf.c
new file mode 100644
index 0000000..26ba2a6
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_prf.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 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 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 <hex.h>
+#include <err.h>
+
+/*
+ * key: string2key(aes256, "testkey", "testkey", default_params)
+ * input: unhex(1122334455667788)
+ * output: 58b594b8a61df6e9439b7baa991ff5c1
+ *
+ * key: string2key(aes128, "testkey", "testkey", default_params)
+ * input: unhex(1122334455667788)
+ * output: ffa2f823aa7f83a8ce3c5fb730587129
+ */
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_crypto crypto;
+ size_t length;
+ krb5_data input, output, output2;
+ krb5_enctype etype = ETYPE_AES256_CTS_HMAC_SHA1_96;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context %d", ret);
+
+ ret = krb5_generate_random_keyblock(context, etype, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ ret = krb5_crypto_prf_length(context, etype, &length);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_prf_length");
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+ input.data = rk_UNCONST("foo");
+ input.length = 3;
+
+ ret = krb5_crypto_prf(context, crypto, &input, &output);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_prf");
+
+ ret = krb5_crypto_prf(context, crypto, &input, &output2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_prf");
+
+ if (krb5_data_cmp(&output, &output2) != 0)
+ krb5_errx(context, 1, "krb5_data_cmp");
+
+ krb5_data_free(&output);
+ krb5_data_free(&output2);
+
+ krb5_crypto_destroy(context, crypto);
+
+ krb5_free_keyblock_contents(context, &key);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_princ.c b/third_party/heimdal/lib/krb5/test_princ.c
new file mode 100644
index 0000000..e61a603
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_princ.c
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+
+/*
+ * Check that a closed cc still keeps it data and that it's no longer
+ * there when it's destroyed.
+ */
+
+static void
+test_princ(krb5_context context)
+{
+ const char *princ = "lha@SU.SE";
+ const char *princ_short = "lha";
+ const char *noquote;
+ krb5_error_code ret;
+ char *princ_unparsed;
+ char *princ_reformed = NULL;
+ const char *realm;
+
+ krb5_principal p, p2;
+
+ ret = krb5_parse_name(context, princ, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_unparse_name(context, p, &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (strcmp(princ, princ_unparsed) != 0) {
+ krb5_errx(context, 1, "%s != %s", princ, princ_unparsed);
+ }
+
+ free(princ_unparsed);
+
+ ret = krb5_unparse_name_flags(context, p,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (strcmp(princ_short, princ_unparsed) != 0)
+ krb5_errx(context, 1, "%s != %s", princ_short, princ_unparsed);
+ free(princ_unparsed);
+
+ realm = krb5_principal_get_realm(context, p);
+
+ if (asprintf(&princ_reformed, "%s@%s", princ_short, realm) < 0 || princ_reformed == NULL)
+ errx(1, "malloc");
+
+ ret = krb5_parse_name(context, princ_reformed, &p2);
+ free(princ_reformed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (!krb5_principal_compare(context, p, p2)) {
+ krb5_errx(context, 1, "p != p2");
+ }
+
+ krb5_free_principal(context, p2);
+
+ ret = krb5_set_default_realm(context, "SU.SE");
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_unparse_name_flags(context, p,
+ KRB5_PRINCIPAL_UNPARSE_SHORT,
+ &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (strcmp(princ_short, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ_short, princ_unparsed);
+ free(princ_unparsed);
+
+ ret = krb5_parse_name(context, princ_short, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (!krb5_principal_compare(context, p, p2))
+ krb5_errx(context, 1, "p != p2");
+ krb5_free_principal(context, p2);
+
+ ret = krb5_unparse_name(context, p, &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (strcmp(princ, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed);
+ free(princ_unparsed);
+
+ ret = krb5_set_default_realm(context, "SAMBA.ORG");
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_parse_name(context, princ_short, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (krb5_principal_compare(context, p, p2))
+ krb5_errx(context, 1, "p == p2");
+
+ if (!krb5_principal_compare_any_realm(context, p, p2))
+ krb5_errx(context, 1, "(ignoring realms) p != p2");
+
+ ret = krb5_unparse_name(context, p2, &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (strcmp(princ, princ_unparsed) == 0)
+ krb5_errx(context, 1, "%s == %s", princ, princ_unparsed);
+ free(princ_unparsed);
+
+ krb5_free_principal(context, p2);
+
+ ret = krb5_parse_name(context, princ, &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (!krb5_principal_compare(context, p, p2))
+ krb5_errx(context, 1, "p != p2");
+
+ ret = krb5_unparse_name(context, p2, &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (strcmp(princ, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed);
+ free(princ_unparsed);
+
+ krb5_free_principal(context, p2);
+
+ ret = krb5_unparse_name_flags(context, p,
+ KRB5_PRINCIPAL_UNPARSE_SHORT,
+ &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name_short");
+
+ if (strcmp(princ, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed);
+ free(princ_unparsed);
+
+ ret = krb5_unparse_name(context, p, &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name_short");
+
+ if (strcmp(princ, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed);
+ free(princ_unparsed);
+
+ ret = krb5_parse_name_flags(context, princ,
+ KRB5_PRINCIPAL_PARSE_NO_REALM,
+ &p2);
+ if (!ret)
+ krb5_err(context, 1, ret, "Should have failed to parse %s a "
+ "short name", princ);
+
+ ret = krb5_parse_name_flags(context, princ_short,
+ KRB5_PRINCIPAL_PARSE_NO_REALM,
+ &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_unparse_name_flags(context, p2,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &princ_unparsed);
+ krb5_free_principal(context, p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name_norealm");
+
+ if (strcmp(princ_short, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ_short, princ_unparsed);
+ free(princ_unparsed);
+
+ ret = krb5_parse_name_flags(context, princ_short,
+ KRB5_PRINCIPAL_PARSE_REQUIRE_REALM,
+ &p2);
+ if (!ret)
+ krb5_err(context, 1, ret, "Should have failed to parse %s "
+ "because it lacked a realm", princ_short);
+
+ ret = krb5_parse_name_flags(context, princ,
+ KRB5_PRINCIPAL_PARSE_REQUIRE_REALM,
+ &p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ if (!krb5_principal_compare(context, p, p2))
+ krb5_errx(context, 1, "p != p2");
+
+ ret = krb5_unparse_name_flags(context, p2,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &princ_unparsed);
+ krb5_free_principal(context, p2);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name_norealm");
+
+ if (strcmp(princ_short, princ_unparsed) != 0)
+ krb5_errx(context, 1, "'%s' != '%s'", princ_short, princ_unparsed);
+ free(princ_unparsed);
+
+ krb5_free_principal(context, p);
+
+ /* test quoting */
+
+ princ = "test\\ principal@SU.SE";
+ noquote = "test principal@SU.SE";
+
+ ret = krb5_parse_name_flags(context, princ, 0, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_unparse_name_flags(context, p, 0, &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name_flags");
+
+ if (strcmp(princ, princ_unparsed) != 0)
+ krb5_errx(context, 1, "q '%s' != '%s'", princ, princ_unparsed);
+ free(princ_unparsed);
+
+ ret = krb5_unparse_name_flags(context, p, KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+ &princ_unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name_flags");
+
+ if (strcmp(noquote, princ_unparsed) != 0)
+ krb5_errx(context, 1, "nq '%s' != '%s'", noquote, princ_unparsed);
+ free(princ_unparsed);
+
+ krb5_free_principal(context, p);
+}
+
+static void
+test_enterprise(krb5_context context)
+{
+ krb5_error_code ret;
+ char *unparsed;
+ krb5_principal p;
+
+ ret = krb5_set_default_realm(context, "SAMBA.ORG");
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name");
+
+ ret = krb5_parse_name_flags(context, "lha@su.se@WIN.SU.SE",
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name_flags");
+
+ ret = krb5_unparse_name(context, p, &unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ krb5_free_principal(context, p);
+
+ if (strcmp(unparsed, "lha\\@su.se@WIN.SU.SE") != 0)
+ krb5_errx(context, 1, "enterprise name failed 1");
+ free(unparsed);
+
+ /*
+ *
+ */
+
+ ret = krb5_parse_name_flags(context, "lha\\@su.se@WIN.SU.SE",
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name_flags");
+
+ ret = krb5_unparse_name(context, p, &unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ krb5_free_principal(context, p);
+ if (strcmp(unparsed, "lha\\@su.se\\@WIN.SU.SE@SAMBA.ORG") != 0)
+ krb5_errx(context, 1, "enterprise name failed 2: %s", unparsed);
+ free(unparsed);
+
+ /*
+ *
+ */
+
+ ret = krb5_parse_name_flags(context, "lha\\@su.se@WIN.SU.SE", 0, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name_flags");
+
+ ret = krb5_unparse_name(context, p, &unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ krb5_free_principal(context, p);
+ if (strcmp(unparsed, "lha\\@su.se@WIN.SU.SE") != 0)
+ krb5_errx(context, 1, "enterprise name failed 3");
+ free(unparsed);
+
+ /*
+ *
+ */
+
+ ret = krb5_parse_name_flags(context, "lha@su.se",
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_parse_name_flags");
+
+ ret = krb5_unparse_name(context, p, &unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ krb5_free_principal(context, p);
+ if (strcmp(unparsed, "lha\\@su.se@SAMBA.ORG") != 0)
+ krb5_errx(context, 1, "enterprise name failed 2: %s", unparsed);
+ free(unparsed);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ test_princ(context);
+
+ test_enterprise(context);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_renew.c b/third_party/heimdal/lib/krb5/test_renew.c
new file mode 100644
index 0000000..40d3739
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_renew.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 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 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 <err.h>
+#include <getarg.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "[principal]");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_principal client;
+ krb5_context context;
+ const char *in_tkt_service = NULL;
+ krb5_ccache id;
+ krb5_error_code ret;
+ krb5_creds out;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc > 0)
+ in_tkt_service = argv[0];
+
+ memset(&out, 0, sizeof(out));
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_init_context");
+
+ ret = krb5_cc_default(context, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default");
+
+ ret = krb5_cc_get_principal(context, id, &client);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_default");
+
+ ret = krb5_get_renewed_creds(context,
+ &out,
+ client,
+ id,
+ in_tkt_service);
+
+ if(ret)
+ krb5_err(context, 1, ret, "krb5_get_renewed_creds");
+
+ if (krb5_principal_compare(context, out.client, client) != TRUE)
+ krb5_errx(context, 1, "return principal is not as expected");
+
+ krb5_free_cred_contents(context, &out);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_rfc3961.c b/third_party/heimdal/lib/krb5/test_rfc3961.c
new file mode 100644
index 0000000..ed8ee9b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_rfc3961.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (c) 2003-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+#include <getarg.h>
+
+static void
+time_hmac(krb5_context context, size_t size, int iterations)
+{
+ struct timeval tv1, tv2;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ char sha1_data[20];
+ Checksum result;
+ char *buf;
+ int i;
+
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ buf = calloc(1, size);
+ if (buf == NULL)
+ krb5_errx(context, 1, "out of memory");
+
+ gettimeofday(&tv1, NULL);
+
+ result.checksum.data = &sha1_data;
+ result.checksum.length = sizeof(sha1_data);
+ for (i = 0; i < iterations; i++) {
+ ret = krb5_hmac(context, CKSUMTYPE_SHA1, buf, size, 0, &key, &result);
+ if (ret)
+ krb5_err(context, 1, ret, "hmac: %d", i);
+ }
+
+ gettimeofday(&tv2, NULL);
+
+ timevalsub(&tv2, &tv1);
+
+ printf("HMAC-SHA1 size: %7lu iterations: %d time: %3ld.%06ld\n",
+ (unsigned long)size, iterations,
+ (long)tv2.tv_sec, (long)tv2.tv_usec);
+
+ free(buf);
+ krb5_free_keyblock_contents(context, &key);
+}
+
+static void
+time_hmac_evp(krb5_context context, size_t size, int iterations)
+{
+ struct timeval tv1, tv2;
+ struct krb5_crypto_iov iov;
+ struct _krb5_key_data kd;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_crypto crypto;
+ char sha1_data[20];
+ Checksum result;
+ char *buf;
+ int i;
+
+ ret = krb5_generate_random_keyblock(context,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ buf = calloc(1, size);
+ if (buf == NULL)
+ krb5_errx(context, 1, "out of memory");
+
+ gettimeofday(&tv1, NULL);
+
+ result.checksum.data = &sha1_data;
+ result.checksum.length = sizeof(sha1_data);
+ iov.data.data = buf;
+ iov.data.length = size;
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+ kd.key = &key;
+ kd.schedule = NULL;
+
+ ret = krb5_crypto_init(context, &key, ETYPE_AES128_CTS_HMAC_SHA1_96,
+ &crypto);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+ for (i = 0; i < iterations; i++) {
+ ret = _krb5_SP_HMAC_SHA1_checksum(context, crypto, &kd, 0,
+ &iov, 1, &result);
+ if (ret)
+ krb5_err(context, 1, ret, "hmac: %d", i);
+ }
+
+ gettimeofday(&tv2, NULL);
+
+ timevalsub(&tv2, &tv1);
+
+ printf("HMAC-SHA1 (evp) size: %7lu iterations: %d time: %3ld.%06ld\n",
+ (unsigned long)size, iterations,
+ (long)tv2.tv_sec, (long)tv2.tv_usec);
+
+ free(buf);
+ krb5_free_keyblock_contents(context, &key);
+ krb5_crypto_destroy(context, crypto);
+}
+
+static void
+time_encryption(krb5_context context, size_t size,
+ krb5_enctype etype, int iterations)
+{
+ struct timeval tv1, tv2;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_crypto crypto;
+ krb5_data data;
+ char *etype_name;
+ void *buf;
+ int i;
+
+ ret = krb5_generate_random_keyblock(context, etype, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
+
+ ret = krb5_enctype_to_string(context, etype, &etype_name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_enctype_to_string");
+
+ buf = malloc(size);
+ if (buf == NULL)
+ krb5_errx(context, 1, "out of memory");
+ memset(buf, 0, size);
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_crypto_init");
+
+ gettimeofday(&tv1, NULL);
+
+ for (i = 0; i < iterations; i++) {
+ ret = krb5_encrypt(context, crypto, 0, buf, size, &data);
+ if (ret)
+ krb5_err(context, 1, ret, "encrypt: %d", i);
+ krb5_data_free(&data);
+ }
+
+ gettimeofday(&tv2, NULL);
+
+ timevalsub(&tv2, &tv1);
+
+ printf("%s size: %7lu iterations: %d time: %3ld.%06ld\n",
+ etype_name, (unsigned long)size, iterations,
+ (long)tv2.tv_sec, (long)tv2.tv_usec);
+
+ free(buf);
+ free(etype_name);
+ krb5_crypto_destroy(context, crypto);
+ krb5_free_keyblock_contents(context, &key);
+}
+
+static void
+time_s2k(krb5_context context,
+ krb5_enctype etype,
+ const char *password,
+ krb5_salt salt,
+ int iterations)
+{
+ struct timeval tv1, tv2;
+ krb5_error_code ret;
+ krb5_keyblock key;
+ krb5_data opaque;
+ char *etype_name;
+ int i;
+
+ ret = krb5_enctype_to_string(context, etype, &etype_name);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_enctype_to_string");
+
+ opaque.data = NULL;
+ opaque.length = 0;
+
+ gettimeofday(&tv1, NULL);
+
+ for (i = 0; i < iterations; i++) {
+ ret = krb5_string_to_key_salt_opaque(context, etype, password, salt,
+ opaque, &key);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_string_to_key_data_salt_opaque");
+ krb5_free_keyblock_contents(context, &key);
+ }
+
+ gettimeofday(&tv2, NULL);
+
+ timevalsub(&tv2, &tv1);
+
+ printf("%s string2key %d iterations time: %3ld.%06ld\n",
+ etype_name, iterations, (long)tv2.tv_sec, (long)tv2.tv_usec);
+ free(etype_name);
+
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+/* SHA1 test vectors from RFC2202 */
+
+struct rfc2202 {
+ char key[80];
+ int keylen;
+ char data[80];
+ int datalen;
+ char digest[20];
+ int digestlen;
+};
+
+static struct rfc2202 rfc2202_vectors[] =
+{
+ {
+ {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b},
+ 20,
+ "Hi There",
+ 8,
+ {0xb6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64,
+ 0xe2, 0x8b, 0xc0, 0xb6, 0xfb, 0x37, 0x8c, 0x8e,
+ 0xf1, 0x46, 0xbe, 0x00},
+ 20
+ },
+ {
+ "Jefe",
+ 4,
+ "what do ya want for nothing?",
+ 28,
+ {0xef, 0xfc, 0xdf, 0x6a, 0xe5, 0xeb, 0x2f, 0xa2,
+ 0xd2, 0x74, 0x16, 0xd5, 0xf1, 0x84, 0xdf, 0x9c,
+ 0x25, 0x9a, 0x7c, 0x79},
+ 20
+ },
+ {
+ {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa},
+ 20,
+ {0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xdd, 0xdd},
+ 50,
+ {0x12, 0x5d, 0x73, 0x42, 0xb9, 0xac, 0x11, 0xcd,
+ 0x91, 0xa3, 0x9a, 0xf4, 0x8a, 0xa1, 0x7b, 0x4f,
+ 0x63, 0xf1, 0x75, 0xd3},
+ 20
+ },
+ {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19},
+ 25,
+ {0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+ 0xcd, 0xcd},
+ 50,
+ {0x4c, 0x90, 0x07, 0xf4, 0x02, 0x62, 0x50, 0xc6,
+ 0xbc, 0x84, 0x14, 0xf9, 0xbf, 0x50, 0xc8, 0x6c,
+ 0x2d, 0x72, 0x35, 0xda},
+ 20
+ },
+ {
+ {0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c},
+ 20,
+ "Test With Truncation",
+ 20,
+ {0x4c, 0x1a, 0x03, 0x42, 0x4b, 0x55, 0xe0, 0x7f,
+ 0xe7, 0xf2, 0x7b, 0xe1, 0xd5, 0x8b, 0xb9, 0x32,
+ 0x4a, 0x9a, 0x5a, 0x04},
+ 20
+ },
+ {
+ {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa},
+ 80,
+ "Test Using Larger Than Block-Size Key - Hash Key First",
+ 54,
+ {0xaa, 0x4a, 0xe5, 0xe1, 0x52, 0x72, 0xd0, 0x0e,
+ 0x95, 0x70, 0x56, 0x37, 0xce, 0x8a, 0x3b, 0x55,
+ 0xed, 0x40, 0x21, 0x12},
+ 20
+ },
+ {
+ {0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa},
+ 80,
+ "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data",
+ 73,
+ {0xe8, 0xe9, 0x9d, 0x0f, 0x45, 0x23, 0x7d, 0x78,
+ 0x6d, 0x6b, 0xba, 0xa7, 0x96, 0x5c, 0x78, 0x08,
+ 0xbb, 0xff, 0x1a, 0x91},
+ 20
+ }
+};
+
+/* RFC 2202 test vectors for HMAC-SHA1 */
+static void
+test_rfc2202(krb5_context context)
+{
+ int num_tests;
+ int i;
+
+ num_tests = sizeof(rfc2202_vectors) / sizeof(struct rfc2202);
+
+ printf("Running %d RFC2202 HMAC-SHA1 tests\n", num_tests);
+ for (i = 0; i < num_tests; i++) {
+ krb5_keyblock keyblock;
+ Checksum result;
+ struct krb5_crypto_iov iov;
+ struct _krb5_key_data kd;
+ char sha1_data[20];
+ int code;
+
+ memset(&keyblock, 0, sizeof(keyblock));
+ memset(&result, 0, sizeof(result));
+
+ keyblock.keyvalue.length = rfc2202_vectors[i].keylen;
+ keyblock.keyvalue.data = &rfc2202_vectors[i].key;
+
+ result.checksum.data = &sha1_data;
+ result.checksum.length = sizeof(sha1_data);
+
+ code = krb5_hmac(context, CKSUMTYPE_SHA1,
+ &rfc2202_vectors[i].data, rfc2202_vectors[i].datalen,
+ 0, &keyblock, &result);
+
+ if (code != 0)
+ errx(1, "HMAC-SHA1 failed with %d on test %d", code, i + 1);
+
+ if (memcmp(&sha1_data, rfc2202_vectors[i].digest, sizeof(sha1_data)) !=0)
+ errx(1, "Digests don't match on test %d", i);
+
+ printf("Test %d okay\n", (i * 2) + 1);
+
+ /* Now check the same using the internal HMAC function */
+
+ iov.data.data = rfc2202_vectors[i].data;
+ iov.data.length = rfc2202_vectors[i].datalen;
+ iov.flags = KRB5_CRYPTO_TYPE_DATA;
+ kd.key = &keyblock;
+ kd.schedule = NULL;
+ code = _krb5_SP_HMAC_SHA1_checksum(context, NULL, &kd, 0,
+ &iov, 1, &result);
+
+ if (code != 0)
+ errx(1, "HMAC-SHA1 failed with %d on test %d", code, i + 1);
+
+ if (memcmp(&sha1_data, rfc2202_vectors[i].digest, sizeof(sha1_data)) !=0)
+ errx(1, "Digests don't match on test %d", i);
+
+ printf("Test %d okay\n", (i * 2) + 2);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int i, enciter, s2kiter, hmaciter;
+ int optidx = 0;
+ krb5_salt salt;
+
+ krb5_enctype enctypes[] = {
+ ETYPE_DES_CBC_CRC,
+ ETYPE_DES3_CBC_SHA1,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
+ ETYPE_AES256_CTS_HMAC_SHA384_192
+ };
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ salt.salttype = KRB5_PW_SALT;
+ salt.saltvalue.data = NULL;
+ salt.saltvalue.length = 0;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ test_rfc2202(context);
+
+ enciter = 1000;
+ hmaciter = 10000;
+ s2kiter = 100;
+
+ time_hmac(context, 16, hmaciter);
+ time_hmac(context, 32, hmaciter);
+ time_hmac(context, 512, hmaciter);
+ time_hmac(context, 1024, hmaciter);
+ time_hmac(context, 2048, hmaciter);
+ time_hmac(context, 4096, hmaciter);
+ time_hmac(context, 8192, hmaciter);
+ time_hmac(context, 16384, hmaciter);
+ time_hmac(context, 32768, hmaciter);
+
+ time_hmac_evp(context, 16, hmaciter);
+ time_hmac_evp(context, 32, hmaciter);
+ time_hmac_evp(context, 512, hmaciter);
+ time_hmac_evp(context, 1024, hmaciter);
+ time_hmac_evp(context, 2048, hmaciter);
+ time_hmac_evp(context, 4096, hmaciter);
+ time_hmac_evp(context, 8192, hmaciter);
+ time_hmac_evp(context, 16384, hmaciter);
+ time_hmac_evp(context, 32768, hmaciter);
+
+ for (i = 0; i < sizeof(enctypes)/sizeof(enctypes[0]); i++) {
+
+ krb5_enctype_enable(context, enctypes[i]);
+
+ time_encryption(context, 16, enctypes[i], enciter);
+ time_encryption(context, 32, enctypes[i], enciter);
+ time_encryption(context, 512, enctypes[i], enciter);
+ time_encryption(context, 1024, enctypes[i], enciter);
+ time_encryption(context, 2048, enctypes[i], enciter);
+ time_encryption(context, 4096, enctypes[i], enciter);
+ time_encryption(context, 8192, enctypes[i], enciter);
+ time_encryption(context, 16384, enctypes[i], enciter);
+ time_encryption(context, 32768, enctypes[i], enciter);
+
+ time_s2k(context, enctypes[i], "mYsecreitPassword", salt, s2kiter);
+ }
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_set_kvno0.c b/third_party/heimdal/lib/krb5/test_set_kvno0.c
new file mode 100644
index 0000000..0c7e6b4
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_set_kvno0.c
@@ -0,0 +1,182 @@
+/*
+ * 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.
+ *
+ */
+
+#include "krb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+
+#if 0
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <krb5.h>
+#endif
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache src_cc = NULL;
+ krb5_ccache dst_cc = NULL;
+ krb5_cc_cursor cursor;
+ krb5_principal me = NULL;
+ krb5_creds cred;
+ const char *during;
+ Ticket t;
+ size_t len;
+ int make_kvno_absent = 0;
+ int opt;
+
+ memset(&cred, 0, sizeof (cred));
+ during = "init_context";
+ ret = krb5_init_context(&context);
+ if (ret) goto err;
+
+ while ((opt = getopt(argc, argv, "c:n")) != -1) {
+ switch (opt) {
+ case 'c':
+ during = "cc_resolve of source ccache";
+ ret = krb5_cc_resolve(context, optarg, &src_cc);
+ if (ret) goto err;
+ break;
+ case 'n':
+ make_kvno_absent++;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, "Usage: %s [-n] [-c ccache]\n"
+ "\tThis utility edits a ccache, setting all ticket\n"
+ "\tenc_part kvnos to zero or absent (if -n is set).\n",
+ argv[0]);
+ return 1;
+ }
+ }
+
+ if (!src_cc) {
+ during = "cc_default";
+ ret = krb5_cc_default(context, &src_cc);
+ if (ret) goto err;
+ }
+
+ during = "cc_get_principal";
+ ret = krb5_cc_get_principal(context, src_cc, &me);
+ if (ret) goto err;
+
+ if (optind != argc) {
+ fprintf(stderr, "Usage: %s [-n] [-c ccache]\n"
+ "\tThis utility edits a ccache, setting all ticket\n"
+ "\tenc_part kvnos to zero or absent (if -n is set).\n",
+ argv[0]);
+ return 1;
+ }
+
+ during = "cc_new_unique of temporary ccache";
+ ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, src_cc),
+ NULL, &dst_cc);
+
+ during = "cc_initialize of temporary ccache";
+ ret = krb5_cc_initialize(context, dst_cc, me);
+ if (ret) goto err;
+
+ during = "cc_start_seq_get";
+ ret = krb5_cc_start_seq_get(context, src_cc, &cursor);
+ if (ret) goto err;
+
+ while ((ret = krb5_cc_next_cred(context, src_cc, &cursor, &cred)) == 0) {
+ krb5_data data;
+
+ during = "decode_Ticket";
+ memset(&t, 0, sizeof (t));
+ ret = decode_Ticket(cred.ticket.data, cred.ticket.length, &t, &len);
+ if (ret == ASN1_MISSING_FIELD) {
+ krb5_free_cred_contents(context, &cred);
+ memset(&cred, 0, sizeof (cred));
+ continue;
+ }
+ if (ret) goto err;
+ if (t.enc_part.kvno) {
+ *t.enc_part.kvno = 0;
+ if (make_kvno_absent) {
+ free(t.enc_part.kvno);
+ t.enc_part.kvno = NULL;
+ }
+ /*
+ * The new Ticket has to need less or same space as before, so
+ * we reuse cred->icket.data.
+ */
+ during = "encode_Ticket";
+ ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, &t, &len, ret);
+ if (ret) {
+ free_Ticket(&t);
+ goto err;
+ }
+ krb5_data_free(&cred.ticket);
+ cred.ticket = data;
+ }
+ free_Ticket(&t);
+ during = "cc_store_cred";
+ ret = krb5_cc_store_cred(context, dst_cc, &cred);
+ if (ret) goto err;
+ krb5_free_cred_contents(context, &cred);
+ memset(&cred, 0, sizeof (cred));
+ }
+ during = "cc_next_cred";
+ if (ret != KRB5_CC_END) goto err;
+
+ during = "cc_end_seq_get";
+ ret = krb5_cc_end_seq_get(context, src_cc, &cursor);
+ if (ret) goto err;
+
+ during = "cc_move";
+ ret = krb5_cc_move(context, dst_cc, src_cc);
+ if (ret) goto err;
+ dst_cc = NULL;
+
+ during = "cc_switch";
+ ret = krb5_cc_switch(context, src_cc);
+ if (ret) goto err;
+
+err:
+ (void) krb5_free_principal(context, me);
+ if (src_cc)
+ (void) krb5_cc_close(context, src_cc);
+ if (dst_cc)
+ (void) krb5_cc_destroy(context, dst_cc);
+ if (ret) {
+ fprintf(stderr, "Failed while doing %s (%d)\n", during, ret);
+ ret = 1;
+ }
+ return (ret);
+}
+
diff --git a/third_party/heimdal/lib/krb5/test_store.c b/third_party/heimdal/lib/krb5/test_store.c
new file mode 100644
index 0000000..163022c
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_store.c
@@ -0,0 +1,364 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "krb5_locl.h"
+#include <getarg.h>
+
+static void
+test_int8(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ int i;
+ int8_t val[] = {
+ 0, 1, -1, 128, -127
+ }, v;
+
+ krb5_storage_truncate(sp, 0);
+
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
+
+ ret = krb5_store_int8(sp, val[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_store_int8");
+ krb5_storage_seek(sp, i, SEEK_SET);
+ ret = krb5_ret_int8(sp, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ret_int8");
+ if (v != val[i])
+ krb5_errx(context, 1, "store and ret mismatch");
+ }
+}
+
+static void
+test_int16(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ int i;
+ int16_t val[] = {
+ 0, 1, -1, 32767, -32768
+ }, v;
+
+ krb5_storage_truncate(sp, 0);
+
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
+
+ ret = krb5_store_int16(sp, val[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_store_int16");
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
+ ret = krb5_ret_int16(sp, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ret_int16");
+ if (v != val[i])
+ krb5_errx(context, 1, "store and ret mismatch");
+ }
+}
+
+static void
+test_int32(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ int i;
+ int32_t val[] = {
+ 0, 1, -1, 2147483647, -2147483646
+ }, v;
+
+ krb5_storage_truncate(sp, 0);
+
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
+
+ ret = krb5_store_int32(sp, val[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_store_int32");
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
+ ret = krb5_ret_int32(sp, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ret_int32");
+ if (v != val[i])
+ krb5_errx(context, 1, "store and ret mismatch");
+ }
+}
+
+static void
+test_uint8(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ int i;
+ uint8_t val[] = {
+ 0, 1, 255
+ }, v;
+
+ krb5_storage_truncate(sp, 0);
+
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
+
+ ret = krb5_store_uint8(sp, val[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_store_uint8");
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
+ ret = krb5_ret_uint8(sp, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ret_uint8");
+ if (v != val[i])
+ krb5_errx(context, 1, "store and ret mismatch");
+ }
+}
+
+static void
+test_uint16(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ int i;
+ uint16_t val[] = {
+ 0, 1, 65535
+ }, v;
+
+ krb5_storage_truncate(sp, 0);
+
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
+
+ ret = krb5_store_uint16(sp, val[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_store_uint16");
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
+ ret = krb5_ret_uint16(sp, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ret_uint16");
+ if (v != val[i])
+ krb5_errx(context, 1, "store and ret mismatch");
+ }
+}
+
+static void
+test_uint32(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ int i;
+ uint32_t val[] = {
+ 0, 1, 4294967295UL
+ }, v;
+
+ krb5_storage_truncate(sp, 0);
+
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
+
+ ret = krb5_store_uint32(sp, val[i]);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_store_uint32");
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
+ ret = krb5_ret_uint32(sp, &v);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_ret_uint32");
+ if (v != val[i])
+ krb5_errx(context, 1, "store and ret mismatch");
+ }
+}
+
+
+static void
+test_storage(krb5_context context, krb5_storage *sp)
+{
+ test_int8(context, sp);
+ test_int16(context, sp);
+ test_int32(context, sp);
+ test_uint8(context, sp);
+ test_uint16(context, sp);
+ test_uint32(context, sp);
+}
+
+
+static void
+test_truncate(krb5_context context, krb5_storage *sp, int fd)
+{
+ struct stat sb;
+
+ krb5_storage_truncate(sp, 0);
+ krb5_store_string(sp, "hej");
+ krb5_storage_truncate(sp, 2);
+
+ if (fstat(fd, &sb) != 0)
+ krb5_err(context, 1, errno, "fstat");
+ if (sb.st_size != 2)
+ krb5_errx(context, 1, "length not 2");
+
+ krb5_storage_truncate(sp, 1024);
+
+ if (fstat(fd, &sb) != 0)
+ krb5_err(context, 1, errno, "fstat");
+ if (sb.st_size != 1024)
+ krb5_errx(context, 1, "length not 1024");
+}
+
+static void
+test_buffer_issues(krb5_context context, krb5_storage *sp)
+{
+ krb5_error_code ret;
+ size_t i;
+ uint32_t v;
+
+ krb5_storage_set_eof_code(sp, -1);
+ krb5_storage_truncate(sp, 0);
+ for (i=0; i < 4096; i++) {
+ krb5_store_uint32(sp, i);
+ }
+
+ krb5_storage_truncate(sp, 1024);
+ ret = krb5_ret_uint32(sp, &v);
+ if (ret != -1)
+ krb5_errx(context, 1, "Should have received EOF");
+
+ krb5_storage_seek(sp, 8, SEEK_SET);
+ ret = krb5_ret_uint32(sp, &v);
+ if (ret == -1)
+ krb5_errx(context, 1, "Should not have received EOF");
+ if (v != 2)
+ krb5_errx(context, 1, "uint32 should have been 2");
+}
+
+static void
+check_too_large(krb5_context context, krb5_storage *sp)
+{
+ uint32_t too_big_sizes[] = { UINT_MAX, UINT_MAX / 2, UINT_MAX / 4, UINT_MAX / 8 + 1};
+ krb5_error_code ret;
+ krb5_data data;
+ size_t n;
+
+ for (n = 0; n < sizeof(too_big_sizes) / sizeof(too_big_sizes[0]); n++) {
+ krb5_storage_truncate(sp, 0);
+ krb5_store_uint32(sp, too_big_sizes[n]);
+ krb5_storage_seek(sp, 0, SEEK_SET);
+ ret = krb5_ret_data(sp, &data);
+ if (ret != HEIM_ERR_TOO_BIG)
+ errx(1, "not too big: %lu", (unsigned long)n);
+ }
+}
+
+/*
+ *
+ */
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int fd, optidx = 0;
+ krb5_storage *sp;
+ const char *fn = "test-store-data";
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context (&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ /*
+ * Test encoding/decoding of primotive types on diffrent backends
+ */
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ krb5_errx(context, 1, "krb5_storage_emem: no mem");
+
+ test_storage(context, sp);
+ check_too_large(context, sp);
+ krb5_storage_free(sp);
+
+
+ fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ if (fd < 0)
+ krb5_err(context, 1, errno, "open(%s)", fn);
+
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ krb5_errx(context, 1, "krb5_storage_from_fd: %s no mem", fn);
+
+ test_storage(context, sp);
+ test_truncate(context, sp, fd);
+ test_buffer_issues(context, sp);
+ krb5_storage_free(sp);
+ close(fd);
+ unlink(fn);
+
+ fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, 0600);
+ if (fd < 0)
+ krb5_err(context, 1, errno, "open(%s)", fn);
+
+ sp = krb5_storage_stdio_from_fd(fd, "r+");
+ if (sp == NULL)
+ krb5_errx(context, 1, "krb5_storage_stdio_from_fd: %s no mem", fn);
+
+ test_storage(context, sp);
+ test_truncate(context, sp, fd);
+ test_buffer_issues(context, sp);
+ krb5_storage_free(sp);
+ close(fd);
+ unlink(fn);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_time.c b/third_party/heimdal/lib/krb5/test_time.c
new file mode 100644
index 0000000..9c6d1cc
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_time.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of 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 <err.h>
+
+static void
+check_set_time(krb5_context context)
+{
+ krb5_error_code ret;
+ krb5_timestamp sec;
+ int32_t usec;
+ struct timeval tv;
+ int diff = 10;
+ int diff2;
+
+ gettimeofday(&tv, NULL);
+
+ ret = krb5_set_real_time(context, tv.tv_sec + diff, tv.tv_usec);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_us_timeofday");
+
+ ret = krb5_us_timeofday(context, &sec, &usec);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_us_timeofday");
+
+ diff2 = krb5_time_abs(sec, tv.tv_sec);
+
+ if (diff2 < 9 || diff > 11)
+ krb5_errx(context, 1, "set time error: diff: %ld",
+ krb5_time_abs(sec, tv.tv_sec));
+}
+
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context %d", ret);
+
+ check_set_time(context);
+ check_set_time(context);
+ check_set_time(context);
+ check_set_time(context);
+ check_set_time(context);
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/test_x500.c b/third_party/heimdal/lib/krb5/test_x500.c
new file mode 100644
index 0000000..ecad779
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/test_x500.c
@@ -0,0 +1,110 @@
+/*
+ * 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 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 <err.h>
+
+/*
+ *
+ */
+
+static void
+check_linear(krb5_context context,
+ const char *client_realm,
+ const char *server_realm,
+ const char *realm,
+ ...)
+{
+ unsigned int num_inrealms = 0, num_realms = 0, n;
+ char **inrealms = NULL;
+ char **realms = NULL;
+ krb5_error_code ret;
+ krb5_data tr;
+ va_list va;
+
+ krb5_data_zero(&tr);
+
+ va_start(va, realm);
+
+ while (realm) {
+ inrealms = erealloc(inrealms, (num_inrealms + 2) * sizeof(inrealms[0]));
+ inrealms[num_inrealms] = rk_UNCONST(realm);
+ num_inrealms++;
+ realm = va_arg(va, const char *);
+ }
+ if (inrealms)
+ inrealms[num_inrealms] = NULL;
+
+ ret = krb5_domain_x500_encode(inrealms, num_inrealms, &tr);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_domain_x500_encode");
+
+ ret = krb5_domain_x500_decode(context, tr,
+ &realms, &num_realms,
+ client_realm, server_realm);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_domain_x500_decode");
+
+ krb5_data_free(&tr);
+
+ if (num_inrealms != num_realms)
+ errx(1, "num_inrealms != num_realms");
+
+ for(n = 0; n < num_realms; n++)
+ free(realms[n]);
+ free(realms);
+
+ free(inrealms);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context");
+
+
+ check_linear(context, "KTH1.SE", "KTH1.SE", NULL);
+ check_linear(context, "KTH1.SE", "KTH2.SE", NULL);
+ check_linear(context, "KTH1.SE", "KTH3.SE", "KTH2.SE", NULL);
+ check_linear(context, "KTH1.SE", "KTH4.SE", "KTH3.SE", "KTH2.SE", NULL);
+ check_linear(context, "KTH1.SE", "KTH5.SE", "KTH4.SE", "KTH3.SE", "KTH2.SE", NULL);
+
+ return 0;
+}
diff --git a/third_party/heimdal/lib/krb5/ticket.c b/third_party/heimdal/lib/krb5/ticket.c
new file mode 100644
index 0000000..d19fcc6
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/ticket.c
@@ -0,0 +1,967 @@
+/*
+ * Copyright (c) 1997 - 2001 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"
+
+/**
+ * Free ticket and content
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket 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_ticket(krb5_context context,
+ krb5_ticket *ticket)
+{
+ free_EncTicketPart(&ticket->ticket);
+ krb5_free_principal(context, ticket->client);
+ krb5_free_principal(context, ticket->server);
+ free(ticket);
+ return 0;
+}
+
+/**
+ * Copy ticket and content
+ *
+ * @param context a Kerberos 5 context
+ * @param from ticket to copy
+ * @param to new copy of ticket, free with krb5_free_ticket()
+ *
+ * @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_ticket(krb5_context context,
+ const krb5_ticket *from,
+ krb5_ticket **to)
+{
+ krb5_error_code ret;
+ krb5_ticket *tmp;
+
+ *to = NULL;
+ tmp = malloc(sizeof(*tmp));
+ if (tmp == NULL)
+ return krb5_enomem(context);
+ if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
+ free(tmp);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, from->client, &tmp->client);
+ if(ret){
+ free_EncTicketPart(&tmp->ticket);
+ free(tmp);
+ return ret;
+ }
+ ret = krb5_copy_principal(context, from->server, &tmp->server);
+ if(ret){
+ krb5_free_principal(context, tmp->client);
+ free_EncTicketPart(&tmp->ticket);
+ free(tmp);
+ return ret;
+ }
+ *to = tmp;
+ return 0;
+}
+
+/**
+ * Return client principal in ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ * @param client client principal, free with krb5_free_principal()
+ *
+ * @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_ticket_get_client(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_principal *client)
+{
+ return krb5_copy_principal(context, ticket->client, client);
+}
+
+/**
+ * Return server principal in ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ * @param server server principal, free with krb5_free_principal()
+ *
+ * @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_ticket_get_server(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_principal *server)
+{
+ return krb5_copy_principal(context, ticket->server, server);
+}
+
+/**
+ * Return end time of a ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ *
+ * @return end time of ticket
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
+krb5_ticket_get_endtime(krb5_context context,
+ const krb5_ticket *ticket)
+{
+ return ticket->ticket.endtime;
+}
+
+/**
+ * Return authentication, start, end, and renew limit times of a ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ * @param t pointer to krb5_times structure
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_ticket_get_times(krb5_context context,
+ const krb5_ticket *ticket,
+ krb5_times *t)
+{
+ t->authtime = ticket->ticket.authtime;
+ t->starttime = ticket->ticket.starttime ? *ticket->ticket.starttime :
+ t->authtime;
+ t->endtime = ticket->ticket.endtime;
+ t->renew_till = ticket->ticket.renew_till ? *ticket->ticket.renew_till :
+ t->endtime;
+}
+
+/**
+ * Get the flags from the Kerberos ticket
+ *
+ * @param context Kerberos context
+ * @param ticket Kerberos ticket
+ *
+ * @return ticket flags
+ *
+ * @ingroup krb5_ticket
+ */
+KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
+krb5_ticket_get_flags(krb5_context context,
+ const krb5_ticket *ticket)
+{
+ return TicketFlags2int(ticket->ticket.flags);
+}
+
+/*
+ * Find an authz-data element in the given `ad'. If `failp', then validate any
+ * containing AD-KDC-ISSUED's keyed checksum with the `sessionkey' (if given).
+ *
+ * All AD-KDC-ISSUED will be validated (if requested) even when `type' is
+ * `KRB5_AUTHDATA_KDC_ISSUED'.
+ *
+ * Only the first matching element will be output (via `data').
+ *
+ * Note that all AD-KDC-ISSUEDs found while traversing the authz-data will be
+ * validated, though only the first one will be returned.
+ *
+ * XXX We really need a better interface though. First, forget AD-AND-OR --
+ * just remove it. Second, probably forget AD-KDC-ISSUED, but still, between
+ * that, the PAC, and the CAMMAC, we need an interface that can:
+ *
+ * a) take the derived keys instead of the service key or the session key,
+ * b) can indicate whether the element was marked critical,
+ * c) can indicate whether the element was authenticated to the KDC,
+ * d) can iterate over all the instances found (if more than one is found).
+ *
+ * Also, we need to know here if the authz-data is from a Ticket or from an
+ * Authenticator -- if the latter then we must refuse to find AD-KDC-ISSUED /
+ * PAC / CAMMAC or anything of the sort, ever.
+ */
+static int
+find_type_in_ad(krb5_context context,
+ int type,
+ krb5_data *data, /* optional */
+ krb5_boolean *found,
+ krb5_boolean failp, /* validate AD-KDC-ISSUED */
+ krb5_keyblock *sessionkey, /* ticket session key */
+ const AuthorizationData *ad,
+ int level)
+{
+ krb5_error_code ret = 0;
+ size_t i;
+
+ if (level > 9) {
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("Authorization data nested deeper "
+ "then %d levels, stop searching", ""),
+ level);
+ goto out;
+ }
+
+ /*
+ * Only copy out the element the first time we get to it, we need
+ * to run over the whole authorization data fields to check if
+ * there are any container clases we need to care about.
+ */
+ for (i = 0; i < ad->len; i++) {
+ if (!*found && ad->val[i].ad_type == type) {
+ if (data) {
+ ret = der_copy_octet_string(&ad->val[i].ad_data, data);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("malloc: out of memory", ""));
+ goto out;
+ }
+ }
+ *found = TRUE;
+ if (type != KRB5_AUTHDATA_KDC_ISSUED ||
+ !failp || !sessionkey || !sessionkey->keyvalue.length)
+ continue;
+ /* else go on to validate the AD-KDC-ISSUED's keyed checksum */
+ }
+ switch (ad->val[i].ad_type) {
+ case KRB5_AUTHDATA_IF_RELEVANT: {
+ AuthorizationData child;
+ ret = decode_AuthorizationData(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode "
+ "IF_RELEVANT with %d", ""),
+ (int)ret);
+ goto out;
+ }
+ ret = find_type_in_ad(context, type, data, found, FALSE,
+ sessionkey, &child, level + 1);
+ free_AuthorizationData(&child);
+ if (ret)
+ goto out;
+ break;
+ }
+ case KRB5_AUTHDATA_KDC_ISSUED: {
+ AD_KDCIssued child;
+
+ ret = decode_AD_KDCIssued(ad->val[i].ad_data.data,
+ ad->val[i].ad_data.length,
+ &child,
+ NULL);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode "
+ "AD_KDCIssued with %d", ""),
+ ret);
+ goto out;
+ }
+ if (failp && sessionkey && sessionkey->keyvalue.length) {
+ krb5_boolean valid;
+ krb5_data buf;
+ size_t len;
+
+ ASN1_MALLOC_ENCODE(AuthorizationData, buf.data, buf.length,
+ &child.elements, &len, ret);
+ if (ret) {
+ free_AD_KDCIssued(&child);
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ if(buf.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = krb5_c_verify_checksum(context, sessionkey, 19, &buf,
+ &child.ad_checksum, &valid);
+ krb5_data_free(&buf);
+ if (ret) {
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ if (!valid) {
+ krb5_clear_error_message(context);
+ ret = ENOENT;
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ } else if (failp) {
+ krb5_clear_error_message(context);
+ ret = ENOENT;
+ free_AD_KDCIssued(&child);
+ goto out;
+ }
+ ret = find_type_in_ad(context, type, data, found, failp, sessionkey,
+ &child.elements, level + 1);
+ free_AD_KDCIssued(&child);
+ if (ret)
+ goto out;
+ break;
+ }
+ case KRB5_AUTHDATA_AND_OR:
+ if (!failp)
+ break;
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("Authorization data contains "
+ "AND-OR element that is unknown to the "
+ "application", ""));
+ goto out;
+ default:
+ if (!failp)
+ break;
+ ret = ENOENT; /* XXX */
+ krb5_set_error_message(context, ret,
+ N_("Authorization data contains "
+ "unknown type (%d) ", ""),
+ ad->val[i].ad_type);
+ goto out;
+ }
+ }
+out:
+ if (ret) {
+ if (*found) {
+ if (data)
+ krb5_data_free(data);
+ *found = 0;
+ }
+ }
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_get_ad(krb5_context context,
+ const AuthorizationData *ad,
+ krb5_keyblock *sessionkey,
+ int type,
+ krb5_data *data)
+{
+ krb5_boolean found = FALSE;
+ krb5_error_code ret;
+
+ if (data)
+ krb5_data_zero(data);
+
+ if (ad == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ N_("No authorization data", ""));
+ return ENOENT; /* XXX */
+ }
+
+ ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
+ if (ret)
+ return ret;
+ if (!found) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Have no authorization data of type %d", ""),
+ type);
+ return ENOENT; /* XXX */
+ }
+ return 0;
+}
+
+
+/**
+ * Extract the authorization data type of type from the ticket. Store
+ * the field in data. This function is to use for kerberos
+ * applications.
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket Kerberos ticket
+ * @param type type to fetch
+ * @param data returned data, free with krb5_data_free()
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ticket_get_authorization_data_type(krb5_context context,
+ krb5_ticket *ticket,
+ int type,
+ krb5_data *data)
+{
+ AuthorizationData *ad;
+ krb5_error_code ret;
+ krb5_boolean found = FALSE;
+
+ if (data)
+ krb5_data_zero(data);
+
+ ad = ticket->ticket.authorization_data;
+ if (ticket->ticket.authorization_data == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Ticket has no authorization data", ""));
+ return ENOENT; /* XXX */
+ }
+
+ ret = find_type_in_ad(context, type, data, &found, TRUE,
+ &ticket->ticket.key, ad, 0);
+ if (ret)
+ return ret;
+ if (!found) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Ticket has no "
+ "authorization data of type %d", ""),
+ type);
+ return ENOENT; /* XXX */
+ }
+ return 0;
+}
+
+static krb5_error_code
+check_server_referral(krb5_context context,
+ krb5_kdc_rep *rep,
+ unsigned flags,
+ krb5_const_principal requested,
+ krb5_const_principal returned,
+ krb5_keyblock * key)
+{
+ krb5_error_code ret;
+ PA_ServerReferralData ref;
+ krb5_crypto session;
+ EncryptedData ed;
+ size_t len;
+ krb5_data data;
+ PA_DATA *pa;
+ int i = 0, cmp;
+
+ if (rep->kdc_rep.padata == NULL)
+ goto noreferral;
+
+ pa = krb5_find_padata(rep->kdc_rep.padata->val,
+ rep->kdc_rep.padata->len,
+ KRB5_PADATA_SERVER_REFERRAL, &i);
+ if (pa == NULL)
+ goto noreferral;
+
+ memset(&ed, 0, sizeof(ed));
+ memset(&ref, 0, sizeof(ref));
+
+ ret = decode_EncryptedData(pa->padata_value.data,
+ pa->padata_value.length,
+ &ed, &len);
+ if (ret)
+ return ret;
+ if (len != pa->padata_value.length) {
+ free_EncryptedData(&ed);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Referral EncryptedData wrong for realm %s",
+ "realm"), requested->realm);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &session);
+ if (ret) {
+ free_EncryptedData(&ed);
+ return ret;
+ }
+
+ ret = krb5_decrypt_EncryptedData(context, session,
+ KRB5_KU_PA_SERVER_REFERRAL,
+ &ed, &data);
+ free_EncryptedData(&ed);
+ krb5_crypto_destroy(context, session);
+ if (ret)
+ return ret;
+
+ ret = decode_PA_ServerReferralData(data.data, data.length, &ref, &len);
+ if (ret) {
+ krb5_data_free(&data);
+ return ret;
+ }
+ krb5_data_free(&data);
+
+ if (strcmp(requested->realm, returned->realm) != 0) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("server ref realm mismatch, "
+ "requested realm %s got back %s", ""),
+ requested->realm, returned->realm);
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (krb5_principal_is_krbtgt(context, returned)) {
+ const char *realm = returned->name.name_string.val[1];
+
+ if (ref.referred_realm == NULL
+ || strcmp(*ref.referred_realm, realm) != 0)
+ {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("tgt returned with wrong ref", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else if (krb5_principal_compare(context, returned, requested) == 0) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("req princ no same as returned", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ if (ref.requested_principal_name) {
+ cmp = _krb5_principal_compare_PrincipalName(context,
+ requested,
+ ref.requested_principal_name);
+ if (!cmp) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("referred principal not same "
+ "as requested", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else if (flags & EXTRACT_TICKET_AS_REQ) {
+ free_PA_ServerReferralData(&ref);
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Requested principal missing on AS-REQ", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+
+ free_PA_ServerReferralData(&ref);
+
+ return ret;
+noreferral:
+ /*
+ * Expect excact match or that we got a krbtgt
+ */
+ if (krb5_principal_compare(context, requested, returned) != TRUE &&
+ (krb5_realm_compare(context, requested, returned) != TRUE &&
+ krb5_principal_is_krbtgt(context, returned) != TRUE))
+ {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Not same server principal returned "
+ "as requested", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ return 0;
+}
+
+/*
+ * Verify KDC supported anonymous if requested
+ */
+static krb5_error_code
+check_client_anonymous(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_const_principal requested,
+ krb5_const_principal mapped,
+ krb5_boolean is_tgs_rep)
+{
+ int flags;
+
+ if (!rep->enc_part.flags.anonymous)
+ return KRB5KDC_ERR_BADOPTION;
+
+ /*
+ * Here we must validate that the AS returned a ticket of the expected type
+ * for either a fully anonymous request, or authenticated request for an
+ * anonymous ticket. If this is a TGS request, we're done. Then if the
+ * 'requested' principal was anonymous, we'll check the 'mapped' principal
+ * accordingly (without enforcing the name type and perhaps the realm).
+ * Finally, if the 'requested' principal was not anonymous, well check
+ * that the 'mapped' principal has an anonymous name and type, in a
+ * non-anonymous realm. (Should we also be checking for a realm match
+ * between the request and the mapped name in this case?)
+ */
+ if (is_tgs_rep)
+ flags = KRB5_ANON_MATCH_ANY_NONT;
+ else if (krb5_principal_is_anonymous(context, requested,
+ KRB5_ANON_MATCH_ANY_NONT))
+ flags = KRB5_ANON_MATCH_UNAUTHENTICATED | KRB5_ANON_IGNORE_NAME_TYPE;
+ else
+ flags = KRB5_ANON_MATCH_AUTHENTICATED;
+
+ if (!krb5_principal_is_anonymous(context, mapped, flags))
+ return KRB5KRB_AP_ERR_MODIFIED;
+
+ return 0;
+}
+
+/*
+ * Verify returned client principal name in anonymous/referral case
+ */
+
+static krb5_error_code
+check_client_mismatch(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_const_principal requested,
+ krb5_const_principal mapped,
+ krb5_keyblock const * key)
+{
+ if (rep->enc_part.flags.anonymous) {
+ if (!krb5_principal_is_anonymous(context, mapped,
+ KRB5_ANON_MATCH_ANY_NONT)) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Anonymous ticket does not contain anonymous "
+ "principal", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ } else {
+ if (krb5_principal_compare(context, requested, mapped) == FALSE &&
+ !rep->enc_part.flags.enc_pa_rep) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ N_("Not same client principal returned "
+ "as requested", ""));
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ }
+
+ return 0;
+}
+
+
+static krb5_error_code KRB5_CALLCONV
+decrypt_tkt (krb5_context context,
+ krb5_keyblock *key,
+ krb5_key_usage usage,
+ krb5_const_pointer decrypt_arg,
+ krb5_kdc_rep *dec_rep)
+{
+ krb5_error_code ret;
+ krb5_data data;
+ size_t size;
+ krb5_crypto crypto;
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ return ret;
+
+ ret = krb5_decrypt_EncryptedData (context,
+ crypto,
+ usage,
+ &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);
+ krb5_data_free (&data);
+ if (ret) {
+ krb5_set_error_message(context, ret,
+ N_("Failed to decode encpart in ticket", ""));
+ return ret;
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+_krb5_extract_ticket(krb5_context context,
+ krb5_kdc_rep *rep,
+ krb5_creds *creds,
+ krb5_keyblock *key,
+ krb5_const_pointer keyseed,
+ krb5_key_usage key_usage,
+ krb5_addresses *addrs,
+ unsigned nonce,
+ unsigned flags,
+ krb5_data *request,
+ krb5_decrypt_proc decrypt_proc,
+ krb5_const_pointer decryptarg)
+{
+ krb5_error_code ret;
+ krb5_principal tmp_principal;
+ size_t len = 0;
+ time_t tmp_time;
+ krb5_timestamp sec_now;
+
+ /* decrypt */
+
+ if (decrypt_proc == NULL)
+ decrypt_proc = decrypt_tkt;
+
+ ret = (*decrypt_proc)(context, key, key_usage, decryptarg, rep);
+ if (ret)
+ goto out;
+
+ if (rep->enc_part.flags.enc_pa_rep && request) {
+ krb5_crypto crypto = NULL;
+ Checksum cksum;
+ PA_DATA *pa = NULL;
+ int idx = 0;
+
+ _krb5_debug(context, 5, "processing enc-ap-rep");
+
+ if (rep->enc_part.encrypted_pa_data == NULL ||
+ (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
+ rep->enc_part.encrypted_pa_data->len,
+ KRB5_PADATA_REQ_ENC_PA_REP,
+ &idx)) == NULL)
+ {
+ _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = decode_Checksum(pa->padata_value.data,
+ pa->padata_value.length,
+ &cksum, NULL);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_AS_REQ,
+ request->data, request->length,
+ &cksum);
+ krb5_crypto_destroy(context, crypto);
+ free_Checksum(&cksum);
+ _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
+ if (ret)
+ goto out;
+ }
+
+ /* save session key */
+
+ creds->session.keyvalue.length = 0;
+ creds->session.keyvalue.data = NULL;
+ creds->session.keytype = rep->enc_part.key.keytype;
+ ret = krb5_data_copy (&creds->session.keyvalue,
+ rep->enc_part.key.keyvalue.data,
+ rep->enc_part.key.keyvalue.length);
+ if (ret) {
+ krb5_clear_error_message(context);
+ goto out;
+ }
+
+ /* compare client and save */
+ ret = _krb5_principalname2krb5_principal(context,
+ &tmp_principal,
+ rep->kdc_rep.cname,
+ rep->kdc_rep.crealm);
+ if (ret)
+ goto out;
+
+ /* check KDC supported anonymous if it was requested */
+ if (flags & EXTRACT_TICKET_MATCH_ANON) {
+ ret = check_client_anonymous(context,rep,
+ creds->client,
+ tmp_principal,
+ request == NULL); /* is TGS */
+ if (ret) {
+ krb5_free_principal(context, tmp_principal);
+ goto out;
+ }
+ }
+
+ /* check client referral and save principal */
+ if((flags & EXTRACT_TICKET_ALLOW_CNAME_MISMATCH) == 0) {
+ ret = check_client_mismatch(context, rep,
+ creds->client,
+ tmp_principal,
+ &creds->session);
+ if (ret) {
+ krb5_free_principal (context, tmp_principal);
+ goto out;
+ }
+ }
+ krb5_free_principal (context, creds->client);
+ creds->client = tmp_principal;
+
+ /* check server referral and save principal */
+ ret = _krb5_kdcrep2krb5_principal(context, &tmp_principal, &rep->enc_part);
+ if (ret)
+ goto out;
+
+ tmp_principal->nameattrs->peer_realm =
+ calloc(1, sizeof(tmp_principal->nameattrs->peer_realm[0]));
+ if (tmp_principal->nameattrs->peer_realm == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ret = copy_Realm(&creds->client->realm, tmp_principal->nameattrs->peer_realm);
+ if (ret) goto out;
+
+ if((flags & EXTRACT_TICKET_ALLOW_SERVER_MISMATCH) == 0){
+ ret = check_server_referral(context,
+ rep,
+ flags,
+ creds->server,
+ tmp_principal,
+ &creds->session);
+ if (ret) {
+ krb5_free_principal (context, tmp_principal);
+ goto out;
+ }
+ }
+ krb5_free_principal(context, creds->server);
+ creds->server = tmp_principal;
+
+ /* verify names */
+ if(flags & EXTRACT_TICKET_MATCH_REALM){
+ const char *srealm = krb5_principal_get_realm(context, creds->server);
+ const char *crealm = krb5_principal_get_realm(context, creds->client);
+
+ if (strcmp(rep->enc_part.srealm, srealm) != 0 ||
+ strcmp(rep->enc_part.srealm, crealm) != 0)
+ {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_clear_error_message(context);
+ goto out;
+ }
+ }
+
+ /* compare nonces */
+
+ if (nonce != (unsigned)rep->enc_part.nonce) {
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ goto out;
+ }
+
+ /* set kdc-offset */
+
+ krb5_timeofday (context, &sec_now);
+ if (rep->enc_part.flags.initial
+ && (flags & EXTRACT_TICKET_TIMESYNC)
+ && context->kdc_sec_offset == 0
+ && krb5_config_get_bool (context, NULL,
+ "libdefaults",
+ "kdc_timesync",
+ NULL)) {
+ context->kdc_sec_offset = rep->enc_part.authtime - sec_now;
+ krb5_timeofday (context, &sec_now);
+ }
+
+ /* check all times */
+
+ if (rep->enc_part.starttime) {
+ tmp_time = *rep->enc_part.starttime;
+ } else
+ tmp_time = rep->enc_part.authtime;
+
+ if (creds->times.starttime == 0
+ && krb5_time_abs(tmp_time, sec_now) > context->max_skew) {
+ ret = KRB5KRB_AP_ERR_SKEW;
+ krb5_set_error_message (context, ret,
+ N_("time skew (%ld) larger than max (%ld)", ""),
+ (long)krb5_time_abs(tmp_time, sec_now),
+ (long)context->max_skew);
+ goto out;
+ }
+
+ if (creds->times.starttime != 0
+ && tmp_time != creds->times.starttime) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.starttime = tmp_time;
+
+ if (rep->enc_part.renew_till) {
+ tmp_time = *rep->enc_part.renew_till;
+ } else
+ tmp_time = 0;
+
+ if (creds->times.renew_till != 0
+ && tmp_time > creds->times.renew_till) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.renew_till = tmp_time;
+
+ creds->times.authtime = rep->enc_part.authtime;
+
+ if (creds->times.endtime != 0
+ && rep->enc_part.endtime > creds->times.endtime) {
+ krb5_clear_error_message (context);
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ creds->times.endtime = rep->enc_part.endtime;
+
+ if(rep->enc_part.caddr)
+ krb5_copy_addresses (context, rep->enc_part.caddr, &creds->addresses);
+ else if(addrs)
+ krb5_copy_addresses (context, addrs, &creds->addresses);
+ else {
+ creds->addresses.len = 0;
+ creds->addresses.val = NULL;
+ }
+ creds->flags.b = rep->enc_part.flags;
+
+ creds->authdata.len = 0;
+ creds->authdata.val = NULL;
+
+ /* extract ticket */
+ ASN1_MALLOC_ENCODE(Ticket, creds->ticket.data, creds->ticket.length,
+ &rep->kdc_rep.ticket, &len, ret);
+ if(ret)
+ goto out;
+ if (creds->ticket.length != len)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+ creds->second_ticket.length = 0;
+ creds->second_ticket.data = NULL;
+
+
+out:
+ memset (rep->enc_part.key.keyvalue.data, 0,
+ rep->enc_part.key.keyvalue.length);
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/time.c b/third_party/heimdal/lib/krb5/time.c
new file mode 100644
index 0000000..d0d4af1
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/time.c
@@ -0,0 +1,138 @@
+/*
+ * 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"
+
+/**
+ * Set the absolute time that the caller knows the kdc has so the
+ * kerberos library can calculate the relative diffrence beteen the
+ * KDC time and local system time.
+ *
+ * @param context Keberos 5 context.
+ * @param sec The applications new of "now" in seconds
+ * @param usec The applications new of "now" in micro seconds
+
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_real_time (krb5_context context,
+ krb5_timestamp sec,
+ int32_t usec)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ context->kdc_sec_offset = sec - tv.tv_sec;
+
+ /**
+ * If the caller passes in a negative usec, its assumed to be
+ * unknown and the function will use the current time usec.
+ */
+ if (usec >= 0) {
+ context->kdc_usec_offset = usec - tv.tv_usec;
+
+ if (context->kdc_usec_offset < 0) {
+ context->kdc_sec_offset--;
+ context->kdc_usec_offset += 1000000;
+ }
+ } else
+ context->kdc_usec_offset = tv.tv_usec;
+
+ return 0;
+}
+
+/*
+ * return ``corrected'' time in `timeret'.
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_timeofday (krb5_context context,
+ krb5_timestamp *timeret)
+{
+ *timeret = time(NULL) + context->kdc_sec_offset;
+ return 0;
+}
+
+/*
+ * like gettimeofday but with time correction to the KDC
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_us_timeofday (krb5_context context,
+ krb5_timestamp *sec,
+ int32_t *usec)
+{
+ struct timeval tv;
+
+ gettimeofday (&tv, NULL);
+
+ *sec = tv.tv_sec + context->kdc_sec_offset;
+ *usec = tv.tv_usec; /* XXX */
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_format_time(krb5_context context, time_t t,
+ char *s, size_t len, krb5_boolean include_time)
+{
+ struct tm *tm;
+ if(context->log_utc)
+ tm = gmtime (&t);
+ else
+ tm = localtime(&t);
+ if(tm == NULL ||
+ strftime(s, len, include_time ? context->time_fmt : context->date_fmt, tm) == 0)
+ snprintf(s, len, "%ld", (long)t);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_string_to_deltat(const char *string, krb5_deltat *deltat)
+{
+ if((*deltat = parse_time(string, "s")) == -1)
+ return KRB5_DELTAT_BADFORMAT;
+ return 0;
+}
+
+krb5_deltat
+krb5_time_abs(krb5_deltat t1, krb5_deltat t2)
+{
+ krb5_deltat t = t1 - t2;
+ if (t < 0)
+ return -t;
+ return t;
+}
diff --git a/third_party/heimdal/lib/krb5/transited.c b/third_party/heimdal/lib/krb5/transited.c
new file mode 100644
index 0000000..484fd39
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/transited.c
@@ -0,0 +1,699 @@
+/*
+ * Copyright (c) 1997 - 2001, 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+/* this is an attempt at one of the most horrible `compression'
+ schemes that has ever been invented; it's so amazingly brain-dead
+ that words can not describe it, and all this just to save a few
+ silly bytes */
+
+struct tr_realm {
+ char *realm;
+ unsigned leading_space:1;
+ unsigned leading_slash:1;
+ unsigned trailing_dot:1;
+ struct tr_realm *next;
+};
+
+static void
+free_realms(struct tr_realm *r)
+{
+ struct tr_realm *p;
+ while(r){
+ p = r;
+ r = r->next;
+ free(p->realm);
+ free(p);
+ }
+}
+
+static int
+make_path(krb5_context context, struct tr_realm *r,
+ const char *from, const char *to)
+{
+ struct tr_realm *tmp;
+ const char *p;
+
+ if(strlen(from) < strlen(to)){
+ const char *str;
+ str = from;
+ from = to;
+ to = str;
+ }
+
+ if(strcmp(from + strlen(from) - strlen(to), to) == 0){
+ p = from;
+ while(1){
+ p = strchr(p, '.');
+ if(p == NULL) {
+ krb5_clear_error_message (context);
+ return KRB5KDC_ERR_POLICY;
+ }
+ p++;
+ if(strcmp(p, to) == 0)
+ break;
+ tmp = calloc(1, sizeof(*tmp));
+ if(tmp == NULL)
+ return krb5_enomem(context);
+ tmp->next = r->next;
+ r->next = tmp;
+ tmp->realm = strdup(p);
+ if(tmp->realm == NULL){
+ r->next = tmp->next;
+ free(tmp);
+ return krb5_enomem(context);
+ }
+ }
+ }else if(strncmp(from, to, strlen(to)) == 0){
+ p = from + strlen(from);
+ while(1){
+ while(p >= from && *p != '/') p--;
+ if(p == from)
+ return KRB5KDC_ERR_POLICY;
+
+ if(strncmp(to, from, p - from) == 0)
+ break;
+ tmp = calloc(1, sizeof(*tmp));
+ if(tmp == NULL)
+ return krb5_enomem(context);
+ tmp->next = r->next;
+ r->next = tmp;
+ tmp->realm = malloc(p - from + 1);
+ if(tmp->realm == NULL){
+ r->next = tmp->next;
+ free(tmp);
+ return krb5_enomem(context);
+ }
+ memcpy(tmp->realm, from, p - from);
+ tmp->realm[p - from] = '\0';
+ p--;
+ }
+ } else {
+ krb5_clear_error_message (context);
+ return KRB5KDC_ERR_POLICY;
+ }
+
+ return 0;
+}
+
+static int
+make_paths(krb5_context context,
+ struct tr_realm *realms, const char *client_realm,
+ const char *server_realm)
+{
+ struct tr_realm *r;
+ int ret;
+ const char *prev_realm = client_realm;
+ const char *next_realm = NULL;
+ for(r = realms; r; r = r->next){
+ /* it *might* be that you can have more than one empty
+ component in a row, at least that's how I interpret the
+ "," exception in 1510 */
+ if(r->realm[0] == '\0'){
+ while(r->next && r->next->realm[0] == '\0')
+ r = r->next;
+ if(r->next)
+ next_realm = r->next->realm;
+ else
+ next_realm = server_realm;
+ ret = make_path(context, r, prev_realm, next_realm);
+ if(ret){
+ free_realms(realms);
+ return ret;
+ }
+ }
+ prev_realm = r->realm;
+ }
+ return 0;
+}
+
+static int
+expand_realms(krb5_context context,
+ struct tr_realm *realms, const char *client_realm)
+{
+ struct tr_realm *r;
+ const char *prev_realm = NULL;
+ for(r = realms; r; r = r->next){
+ if(r->trailing_dot){
+ char *tmp;
+ size_t len;
+
+ if(prev_realm == NULL)
+ prev_realm = client_realm;
+
+ len = strlen(r->realm) + strlen(prev_realm) + 1;
+
+ tmp = realloc(r->realm, len);
+ if(tmp == NULL){
+ free_realms(realms);
+ return krb5_enomem(context);
+ }
+ r->realm = tmp;
+ strlcat(r->realm, prev_realm, len);
+ }else if(r->leading_slash && !r->leading_space && prev_realm){
+ /* yet another exception: if you use x500-names, the
+ leading realm doesn't have to be "quoted" with a space */
+ char *tmp;
+ size_t len = strlen(r->realm) + strlen(prev_realm) + 1;
+
+ tmp = malloc(len);
+ if(tmp == NULL){
+ free_realms(realms);
+ return krb5_enomem(context);
+ }
+ strlcpy(tmp, prev_realm, len);
+ strlcat(tmp, r->realm, len);
+ free(r->realm);
+ r->realm = tmp;
+ }
+ prev_realm = r->realm;
+ }
+ return 0;
+}
+
+static struct tr_realm *
+make_realm(char *realm)
+{
+ struct tr_realm *r;
+ char *p, *q;
+ int quote = 0;
+ r = calloc(1, sizeof(*r));
+ if(r == NULL){
+ free(realm);
+ return NULL;
+ }
+ r->realm = realm;
+ for(p = q = r->realm; *p; p++){
+ if(p == r->realm && *p == ' '){
+ r->leading_space = 1;
+ continue;
+ }
+ if(q == r->realm && *p == '/')
+ r->leading_slash = 1;
+ if(quote){
+ *q++ = *p;
+ quote = 0;
+ continue;
+ }
+ if(*p == '\\'){
+ quote = 1;
+ continue;
+ }
+ if(p[0] == '.' && p[1] == '\0')
+ r->trailing_dot = 1;
+ *q++ = *p;
+ }
+ *q = '\0';
+ return r;
+}
+
+static struct tr_realm*
+append_realm(struct tr_realm *head, struct tr_realm *r)
+{
+ struct tr_realm *p;
+ if(head == NULL){
+ r->next = NULL;
+ return r;
+ }
+ p = head;
+ while(p->next) p = p->next;
+ p->next = r;
+ return head;
+}
+
+static int
+decode_realms(krb5_context context,
+ const char *tr, int length, struct tr_realm **realms)
+{
+ struct tr_realm *r = NULL;
+
+ char *tmp;
+ int quote = 0;
+ const char *start = tr;
+ int i;
+
+ for(i = 0; i < length; i++){
+ if(quote){
+ quote = 0;
+ continue;
+ }
+ if(tr[i] == '\\'){
+ quote = 1;
+ continue;
+ }
+ if(tr[i] == ','){
+ tmp = malloc(tr + i - start + 1);
+ if(tmp == NULL) {
+ free_realms(*realms);
+ *realms = NULL;
+ return krb5_enomem(context);
+ }
+ memcpy(tmp, start, tr + i - start);
+ tmp[tr + i - start] = '\0';
+ r = make_realm(tmp);
+ if(r == NULL){
+ free_realms(*realms);
+ *realms = NULL;
+ return krb5_enomem(context);
+ }
+ *realms = append_realm(*realms, r);
+ start = tr + i + 1;
+ }
+ }
+ tmp = malloc(tr + i - start + 1);
+ if(tmp == NULL){
+ free_realms(*realms);
+ *realms = NULL;
+ return krb5_enomem(context);
+ }
+ memcpy(tmp, start, tr + i - start);
+ tmp[tr + i - start] = '\0';
+ r = make_realm(tmp);
+ if(r == NULL){
+ free_realms(*realms);
+ *realms = NULL;
+ return krb5_enomem(context);
+ }
+ *realms = append_realm(*realms, r);
+
+ return 0;
+}
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_domain_x500_decode(krb5_context context,
+ krb5_data tr, char ***realms, unsigned int *num_realms,
+ const char *client_realm, const char *server_realm)
+{
+ struct tr_realm *r = NULL;
+ struct tr_realm *p, **q;
+ int ret;
+
+ if(tr.length == 0) {
+ *realms = NULL;
+ *num_realms = 0;
+ return 0;
+ }
+
+ /* split string in components */
+ ret = decode_realms(context, tr.data, tr.length, &r);
+ if(ret)
+ return ret;
+
+ /* apply prefix rule */
+ ret = expand_realms(context, r, client_realm);
+ if(ret)
+ return ret;
+
+ ret = make_paths(context, r, client_realm, server_realm);
+ if(ret)
+ return ret;
+
+ /* remove empty components and count realms */
+ *num_realms = 0;
+ for(q = &r; *q; ){
+ if((*q)->realm[0] == '\0'){
+ p = *q;
+ *q = (*q)->next;
+ free(p->realm);
+ free(p);
+ }else{
+ q = &(*q)->next;
+ (*num_realms)++;
+ }
+ }
+ if (*num_realms + 1 > UINT_MAX/sizeof(**realms))
+ return ERANGE;
+
+ {
+ char **R;
+ R = malloc((*num_realms + 1) * sizeof(*R));
+ *realms = R;
+ while(r){
+ *R++ = r->realm;
+ p = r->next;
+ free(r);
+ r = p;
+ }
+ if (*realms == NULL)
+ return krb5_enomem(context);
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_domain_x500_encode(char **realms, unsigned int num_realms,
+ krb5_data *encoding)
+{
+ char *s = NULL;
+ int len = 0;
+ unsigned int i;
+ krb5_data_zero(encoding);
+ if (num_realms == 0)
+ return 0;
+ for(i = 0; i < num_realms; i++){
+ len += strlen(realms[i]);
+ if(realms[i][0] == '/')
+ len++;
+ }
+ len += num_realms - 1;
+ s = malloc(len + 1);
+ if (s == NULL)
+ return ENOMEM;
+ *s = '\0';
+ for(i = 0; i < num_realms; i++){
+ if(i)
+ strlcat(s, ",", len + 1);
+ if(realms[i][0] == '/')
+ strlcat(s, " ", len + 1);
+ strlcat(s, realms[i], len + 1);
+ }
+ encoding->data = s;
+ encoding->length = strlen(s);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_free_capath(krb5_context context, char **capath)
+{
+ char **s;
+
+ for (s = capath; s && *s; ++s)
+ free(*s);
+ free(capath);
+}
+
+struct hier_iter {
+ const char *local_realm;
+ const char *server_realm;
+ const char *lr; /* Pointer into tail of local realm */
+ const char *sr; /* Pointer into tail of server realm */
+ size_t llen; /* Length of local_realm */
+ size_t slen; /* Length of server_realm */
+ size_t len; /* Length of common suffix */
+ size_t num; /* Path element count */
+};
+
+/*
+ * Step up from local_realm to common suffix, or else down to server_realm.
+ */
+static const char *
+hier_next(struct hier_iter *state)
+{
+ const char *lr = state->lr;
+ const char *sr = state->sr;
+ const char *lsuffix = state->local_realm + state->llen - state->len;
+ const char *server_realm = state->server_realm;
+
+ if (lr != NULL) {
+ while (lr < lsuffix)
+ if (*lr++ == '.')
+ return state->lr = lr;
+ state->lr = NULL;
+ }
+ if (sr != NULL) {
+ while (--sr >= server_realm)
+ if (sr == server_realm || sr[-1] == '.')
+ return state->sr = sr;
+ state->sr = NULL;
+ }
+ return NULL;
+}
+
+static void
+hier_init(struct hier_iter *state, const char *local_realm, const char *server_realm)
+{
+ size_t llen;
+ size_t slen;
+ size_t len = 0;
+ const char *lr;
+ const char *sr;
+
+ state->local_realm = local_realm;
+ state->server_realm = server_realm;
+ state->llen = llen = strlen(local_realm);
+ state->slen = slen = strlen(server_realm);
+ state->len = 0;
+ state->num = 0;
+
+ if (slen == 0 || llen == 0)
+ return;
+
+ /* Find first difference from the back */
+ for (lr = local_realm + llen, sr = server_realm + slen;
+ lr != local_realm && sr != server_realm;
+ --lr, --sr) {
+ if (lr[-1] != sr[-1])
+ break;
+ if (lr[-1] == '.')
+ len = llen - (lr - local_realm);
+ }
+
+ /* Nothing in common? */
+ if (*lr == '\0')
+ return;
+
+ /* Everything in common? */
+ if (llen == slen && lr == local_realm)
+ return;
+
+ /* Is one realm is a suffix of the other? */
+ if ((llen < slen && lr == local_realm && sr[-1] == '.') ||
+ (llen > slen && sr == server_realm && lr[-1] == '.'))
+ len = llen - (lr - local_realm);
+
+ state->len = len;
+ /* `lr` starts at local realm and walks up the tree to common suffix */
+ state->lr = local_realm;
+ /* `sr` starts at common suffix in server realm and walks down the tree */
+ state->sr = server_realm + slen - len;
+
+ /* Count elements and reset */
+ while (hier_next(state) != NULL)
+ ++state->num;
+ state->lr = local_realm;
+ state->sr = server_realm + slen - len;
+}
+
+/*
+ * Find a referral path from client_realm to server_realm via local_realm.
+ * Either via [capaths] or hierarchicaly.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_find_capath(krb5_context context,
+ const char *client_realm,
+ const char *local_realm,
+ const char *server_realm,
+ krb5_boolean use_hierarchical,
+ char ***rpath,
+ size_t *npath)
+{
+ char **confpath;
+ char **capath;
+ struct hier_iter hier_state;
+ char **rp;
+ const char *r;
+
+ *rpath = NULL;
+ *npath = 0;
+
+ confpath = krb5_config_get_strings(context, NULL, "capaths",
+ client_realm, server_realm, NULL);
+ if (confpath == NULL)
+ confpath = krb5_config_get_strings(context, NULL, "capaths",
+ local_realm, server_realm, NULL);
+ /*
+ * With a [capaths] setting from the client to the server we look for our
+ * own realm in the list. If our own realm is not present, we return the
+ * full list. Otherwise, we return our realm's successors, or possibly
+ * NULL. Ignoring a [capaths] settings risks loops plus would violate
+ * explicit policy and the principle of least surpise.
+ */
+ if (confpath != NULL) {
+ char **start = confpath;
+ size_t i;
+ size_t n;
+
+ for (rp = start; *rp; rp++)
+ if (strcmp(*rp, local_realm) == 0)
+ start = rp+1;
+ n = rp - start;
+
+ if (n == 0) {
+ krb5_config_free_strings(confpath);
+ return 0;
+ }
+
+ capath = calloc(n + 1, sizeof(*capath));
+ if (capath == NULL) {
+ krb5_config_free_strings(confpath);
+ return krb5_enomem(context);
+ }
+
+ for (i = 0, rp = start; *rp; rp++) {
+ if ((capath[i++] = strdup(*rp)) == NULL) {
+ _krb5_free_capath(context, capath);
+ krb5_config_free_strings(confpath);
+ return krb5_enomem(context);
+ }
+ }
+ krb5_config_free_strings(confpath);
+ capath[i] = NULL;
+ *rpath = capath;
+ *npath = n;
+ return 0;
+ }
+
+ /* The use_hierarchical flag makes hierarchical path lookup unconditional */
+ if (! use_hierarchical &&
+ ! krb5_config_get_bool_default(context, NULL, TRUE, "libdefaults",
+ "allow_hierarchical_capaths", NULL))
+ return 0;
+
+ /*
+ * When validating transit paths, local_realm == client_realm. Otherwise,
+ * with hierarchical referrals, they may differ, and we may be building a
+ * path forward from our own realm!
+ */
+ hier_init(&hier_state, local_realm, server_realm);
+ if (hier_state.num == 0)
+ return 0;
+
+ rp = capath = calloc(hier_state.num + 1, sizeof(*capath));
+ if (capath == NULL)
+ return krb5_enomem(context);
+ while ((r = hier_next(&hier_state)) != NULL) {
+ if ((*rp++ = strdup(r)) == NULL) {
+ _krb5_free_capath(context, capath);
+ return krb5_enomem(context);
+ }
+ }
+
+ *rp = NULL;
+ *rpath = capath;
+ *npath = hier_state.num;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_check_transited(krb5_context context,
+ krb5_const_realm client_realm,
+ krb5_const_realm server_realm,
+ krb5_realm *realms,
+ unsigned int num_realms,
+ int *bad_realm)
+{
+ krb5_error_code ret = 0;
+ char **capath = NULL;
+ size_t num_capath = 0;
+ size_t i = 0;
+ size_t j = 0;
+
+ /* In transit checks hierarchical capaths are optional */
+ ret = _krb5_find_capath(context, client_realm, client_realm, server_realm,
+ FALSE, &capath, &num_capath);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_realms; i++) {
+ for (j = 0; j < num_capath && capath[j]; ++j) {
+ /* `capath[j]' can't be NULL, but compilers be dumb */
+ if (strcmp(realms[i], capath[j]) == 0)
+ break;
+ }
+ if (j == num_capath || !capath[j]) {
+ _krb5_free_capath(context, capath);
+ krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
+ N_("no transit allowed "
+ "through realm %s from %s to %s", ""),
+ realms[i], client_realm, server_realm);
+ if (bad_realm)
+ *bad_realm = i;
+ return KRB5KRB_AP_ERR_ILL_CR_TKT;
+ }
+ }
+
+ _krb5_free_capath(context, capath);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_check_transited_realms(krb5_context context,
+ const char *const *realms,
+ unsigned int num_realms,
+ int *bad_realm)
+{
+ size_t i;
+ int ret = 0;
+ char **bad_realms = krb5_config_get_strings(context, NULL,
+ "libdefaults",
+ "transited_realms_reject",
+ NULL);
+ if(bad_realms == NULL)
+ return 0;
+
+ for(i = 0; i < num_realms; i++) {
+ char **p;
+ for(p = bad_realms; *p; p++)
+ if(strcmp(*p, realms[i]) == 0) {
+ ret = KRB5KRB_AP_ERR_ILL_CR_TKT;
+ krb5_set_error_message (context, ret,
+ N_("no transit allowed "
+ "through realm %s", ""),
+ *p);
+ if(bad_realm)
+ *bad_realm = i;
+ break;
+ }
+ }
+ krb5_config_free_strings(bad_realms);
+ return ret;
+}
+
+#if 0
+int
+main(int argc, char **argv)
+{
+ krb5_data x;
+ char **r;
+ int num, i;
+ x.data = argv[1];
+ x.length = strlen(x.data);
+ if(domain_expand(x, &r, &num, argv[2], argv[3]))
+ exit(1);
+ for(i = 0; i < num; i++)
+ printf("%s\n", r[i]);
+ return 0;
+}
+#endif
diff --git a/third_party/heimdal/lib/krb5/verify_init.c b/third_party/heimdal/lib/krb5/verify_init.c
new file mode 100644
index 0000000..128502f
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/verify_init.c
@@ -0,0 +1,246 @@
+/*
+ * 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"
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_init_creds_opt_init(krb5_verify_init_creds_opt *options)
+{
+ memset (options, 0, sizeof(*options));
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_init_creds_opt_set_ap_req_nofail(krb5_verify_init_creds_opt *options,
+ int ap_req_nofail)
+{
+ options->flags |= KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL;
+ options->ap_req_nofail = ap_req_nofail;
+}
+
+/*
+ *
+ */
+
+static krb5_boolean
+fail_verify_is_ok (krb5_context context,
+ krb5_verify_init_creds_opt *options)
+{
+
+ if (options && (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)
+ && options->ap_req_nofail != 0)
+ return FALSE;
+
+ if (krb5_config_get_bool(context,
+ NULL,
+ "libdefaults",
+ "verify_ap_req_nofail",
+ NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_init_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal ap_req_server,
+ krb5_keytab ap_req_keytab,
+ krb5_ccache *ccache,
+ krb5_verify_init_creds_opt *options)
+{
+ krb5_error_code ret;
+ krb5_data req;
+ krb5_ccache local_ccache = NULL;
+ krb5_creds *new_creds = NULL;
+ krb5_auth_context auth_context = NULL;
+ krb5_principal server = NULL;
+ krb5_keytab keytab = NULL;
+
+ krb5_data_zero (&req);
+
+ if (ap_req_server == NULL) {
+ char local_hostname[MAXHOSTNAMELEN];
+
+ if (gethostname (local_hostname, sizeof(local_hostname)) < 0) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "gethostname: %s",
+ strerror(ret));
+ return ret;
+ }
+
+ ret = krb5_sname_to_principal (context,
+ local_hostname,
+ "host",
+ KRB5_NT_SRV_HST,
+ &server);
+ if (ret)
+ goto cleanup;
+ } else
+ server = ap_req_server;
+
+ if (ap_req_keytab == NULL) {
+ ret = krb5_kt_default (context, &keytab);
+ if (ret)
+ goto cleanup;
+ } else
+ keytab = ap_req_keytab;
+
+ if (ccache && *ccache)
+ local_ccache = *ccache;
+ else {
+ ret = krb5_cc_new_unique(context, krb5_cc_type_memory,
+ NULL, &local_ccache);
+ if (ret)
+ goto cleanup;
+ ret = krb5_cc_initialize (context,
+ local_ccache,
+ creds->client);
+ if (ret)
+ goto cleanup;
+ ret = krb5_cc_store_cred (context,
+ local_ccache,
+ creds);
+ if (ret)
+ goto cleanup;
+ }
+
+ if (!krb5_principal_compare (context, server, creds->server)) {
+ krb5_creds match_cred;
+
+ memset (&match_cred, 0, sizeof(match_cred));
+
+ match_cred.client = creds->client;
+ match_cred.server = server;
+
+ ret = krb5_get_credentials (context,
+ 0,
+ local_ccache,
+ &match_cred,
+ &new_creds);
+ if (ret) {
+ if (fail_verify_is_ok (context, options))
+ ret = 0;
+ goto cleanup;
+ }
+ creds = new_creds;
+ }
+
+ ret = krb5_mk_req_extended (context,
+ &auth_context,
+ 0,
+ NULL,
+ creds,
+ &req);
+
+ krb5_auth_con_free (context, auth_context);
+ auth_context = NULL;
+
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_rd_req (context,
+ &auth_context,
+ &req,
+ server,
+ keytab,
+ 0,
+ NULL);
+
+ if (ret == KRB5_KT_NOTFOUND && fail_verify_is_ok (context, options))
+ ret = 0;
+cleanup:
+ if (auth_context)
+ krb5_auth_con_free (context, auth_context);
+ krb5_data_free (&req);
+ if (new_creds != NULL)
+ krb5_free_creds (context, new_creds);
+ if (ap_req_server == NULL && server)
+ krb5_free_principal (context, server);
+ if (ap_req_keytab == NULL && keytab)
+ krb5_kt_close (context, keytab);
+ if (local_ccache != NULL
+ &&
+ (ccache == NULL
+ || (ret != 0 && *ccache == NULL)))
+ krb5_cc_destroy (context, local_ccache);
+
+ if (ret == 0 && ccache != NULL && *ccache == NULL)
+ *ccache = local_ccache;
+
+ return ret;
+}
+
+/**
+ * Validate the newly fetch credential, see also krb5_verify_init_creds().
+ *
+ * @param context a Kerberos 5 context
+ * @param creds the credentials to verify
+ * @param client the client name to match up
+ * @param ccache the credential cache to use
+ * @param service a service name to use, used with
+ * krb5_sname_to_principal() to build a hostname to use to
+ * verify.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_get_validated_creds(krb5_context context,
+ krb5_creds *creds,
+ krb5_principal client,
+ krb5_ccache ccache,
+ char *service)
+{
+ krb5_verify_init_creds_opt vopt;
+ krb5_principal server;
+ krb5_error_code ret;
+
+ if (krb5_principal_compare(context, creds->client, client) != TRUE) {
+ krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
+ N_("Validation credentials and client "
+ "doesn't match", ""));
+ return KRB5_PRINC_NOMATCH;
+ }
+
+ ret = krb5_sname_to_principal (context, NULL, service,
+ KRB5_NT_SRV_HST, &server);
+ if(ret)
+ return ret;
+
+ krb5_verify_init_creds_opt_init(&vopt);
+
+ ret = krb5_verify_init_creds(context, creds, server, NULL, NULL, &vopt);
+ krb5_free_principal(context, server);
+
+ return ret;
+}
diff --git a/third_party/heimdal/lib/krb5/verify_krb5_conf-version.rc b/third_party/heimdal/lib/krb5/verify_krb5_conf-version.rc
new file mode 100644
index 0000000..f7d90df
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/verify_krb5_conf-version.rc
@@ -0,0 +1,36 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define RC_FILE_TYPE VFT_APP
+#define RC_FILE_DESC_0409 "Krb5.conf Verification Tool"
+#define RC_FILE_ORIG_0409 "verify_krb5_conf.exe"
+
+#include "../../windows/version.rc"
diff --git a/third_party/heimdal/lib/krb5/verify_krb5_conf.8 b/third_party/heimdal/lib/krb5/verify_krb5_conf.8
new file mode 100644
index 0000000..fc580fe
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/verify_krb5_conf.8
@@ -0,0 +1,95 @@
+.\" Copyright (c) 2000 - 2004 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd December 8, 2004
+.Dt VERIFY_KRB5_CONF 8
+.Os HEIMDAL
+.Sh NAME
+.Nm verify_krb5_conf
+.Nd checks krb5.conf for obvious errors
+.Sh SYNOPSIS
+.Nm
+.Ar [config-file]
+.Sh DESCRIPTION
+.Nm
+reads the configuration file
+.Pa krb5.conf ,
+or the file given on the command line,
+parses it, checking verifying that the syntax is not correctly wrong.
+.Pp
+If the file is syntactically correct,
+.Nm
+tries to verify that the contents of the file is of relevant nature.
+.Sh ENVIRONMENT
+.Ev KRB5_CONFIG
+points to the configuration file to read.
+.Sh FILES
+.Bl -tag -width /etc/krb5.conf -compact
+.It Pa /etc/krb5.conf
+Kerberos 5 configuration file
+.El
+.Sh DIAGNOSTICS
+Possible output from
+.Nm
+include:
+.Bl -tag -width "FpathF"
+.It "<path>: failed to parse <something> as size/time/number/boolean"
+Usually means that <something> is misspelled, or that it contains
+weird characters. The parsing done by
+.Nm
+is more strict than the one performed by libkrb5, so strings that
+work in real life might be reported as bad.
+.It "<path>: host not found (<hostname>)"
+Means that <path> is supposed to point to a host, but it can't be
+recognised as one.
+.It <path>: unknown or wrong type
+Means that <path> is either a string when it should be a list, vice
+versa, or just that
+.Nm
+is confused.
+.It <path>: unknown entry
+Means that <string> is not known by
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr krb5.conf 5
+.Sh BUGS
+Since each application can put almost anything in the config file,
+it's hard to come up with a watertight verification process. Most of
+the default settings are sanity checked, but this does not mean that
+every problem is discovered, or that everything that is reported as a
+possible problem actually is one. This tool should thus be used with
+some care.
+.Pp
+It should warn about obsolete data, or bad practice, but currently
+doesn't.
diff --git a/third_party/heimdal/lib/krb5/verify_krb5_conf.c b/third_party/heimdal/lib/krb5/verify_krb5_conf.c
new file mode 100644
index 0000000..c258a2b
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/verify_krb5_conf.c
@@ -0,0 +1,795 @@
+/*
+ * 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.
+ */
+
+#include "krb5_locl.h"
+#include <getarg.h>
+#include <parse_bytes.h>
+#include <err.h>
+
+/* verify krb5.conf */
+static int dumpconfig_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+static int warn_mit_syntax_flag = 0;
+
+static struct getargs args[] = {
+ {"dumpconfig", 0, arg_flag, &dumpconfig_flag,
+ "show the parsed config files", NULL },
+ {"warn-mit-syntax", 0, arg_flag, &warn_mit_syntax_flag,
+ "show the parsed config files", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "[config-file]");
+ exit (ret);
+}
+
+static int
+check_bytes(krb5_context context, const char *path, char *data)
+{
+ if(parse_bytes(data, NULL) == -1) {
+ krb5_warnx(context, "%s: failed to parse \"%s\" as size", path, data);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_time(krb5_context context, const char *path, char *data)
+{
+ if(parse_time(data, NULL) == -1) {
+ krb5_warnx(context, "%s: failed to parse \"%s\" as time", path, data);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_numeric(krb5_context context, const char *path, char *data)
+{
+ long v;
+ char *end;
+ v = strtol(data, &end, 0);
+
+ if ((v == LONG_MIN || v == LONG_MAX) && errno != 0) {
+ krb5_warnx(context, "%s: over/under flow for \"%s\"",
+ path, data);
+ return 1;
+ }
+ if(*end != '\0') {
+ krb5_warnx(context, "%s: failed to parse \"%s\" as a number",
+ path, data);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+check_boolean(krb5_context context, const char *path, char *data)
+{
+ long int v;
+ char *end;
+ if(strcasecmp(data, "yes") == 0 ||
+ strcasecmp(data, "true") == 0 ||
+ strcasecmp(data, "no") == 0 ||
+ strcasecmp(data, "false") == 0)
+ return 0;
+ v = strtol(data, &end, 0);
+ if(*end != '\0') {
+ krb5_warnx(context, "%s: failed to parse \"%s\" as a boolean",
+ path, data);
+ return 1;
+ }
+ if(v != 0 && v != 1)
+ krb5_warnx(context, "%s: numeric value \"%s\" is treated as \"true\"",
+ path, data);
+ return 0;
+}
+
+static int
+check_524(krb5_context context, const char *path, char *data)
+{
+ if(strcasecmp(data, "yes") == 0 ||
+ strcasecmp(data, "no") == 0 ||
+ strcasecmp(data, "2b") == 0 ||
+ strcasecmp(data, "local") == 0)
+ return 0;
+
+ krb5_warnx(context, "%s: didn't contain a valid option `%s'",
+ path, data);
+ return 1;
+}
+
+static int
+check_host(krb5_context context, const char *path, char *data)
+{
+ int ret;
+ char hostname[128];
+ const char *p = data;
+ struct addrinfo hints;
+ char service[32];
+ int defport;
+ struct addrinfo *ai;
+
+ hints.ai_flags = 0;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = 0;
+ hints.ai_protocol = 0;
+
+ hints.ai_addrlen = 0;
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ /* XXX data could be a list of hosts that this code can't handle */
+ /* XXX copied from krbhst.c */
+ if (strncmp(p, "http://", 7) == 0){
+ p += 7;
+ hints.ai_socktype = SOCK_STREAM;
+ strlcpy(service, "http", sizeof(service));
+ defport = 80;
+ } else if (strncmp(p, "http/", 5) == 0) {
+ p += 5;
+ hints.ai_socktype = SOCK_STREAM;
+ strlcpy(service, "http", sizeof(service));
+ defport = 80;
+ } else if (strncmp(p, "tcp/", 4) == 0){
+ p += 4;
+ hints.ai_socktype = SOCK_STREAM;
+ strlcpy(service, "kerberos", sizeof(service));
+ defport = 88;
+ } else if (strncmp(p, "udp/", 4) == 0) {
+ p += 4;
+ hints.ai_socktype = SOCK_DGRAM;
+ strlcpy(service, "kerberos", sizeof(service));
+ defport = 88;
+ } else {
+ hints.ai_socktype = SOCK_DGRAM;
+ strlcpy(service, "kerberos", sizeof(service));
+ defport = 88;
+ }
+ if (strsep_copy(&p, ":", hostname, sizeof(hostname)) < 0) {
+ return 1;
+ }
+ hostname[strcspn(hostname, "/")] = '\0';
+ if (p != NULL) {
+ char *end;
+ int tmp = strtol(p, &end, 0);
+ if (end == p) {
+ krb5_warnx(context, "%s: failed to parse port number in %s",
+ path, data);
+ return 1;
+ }
+ defport = tmp;
+ snprintf(service, sizeof(service), "%u", defport);
+ }
+ ret = getaddrinfo(hostname, service, &hints, &ai);
+ if (ret == EAI_SERVICE && !isdigit((unsigned char)service[0])) {
+ snprintf(service, sizeof(service), "%u", defport);
+ ret = getaddrinfo(hostname, service, &hints, &ai);
+ }
+ if (ret != 0) {
+ krb5_warnx(context, "%s: %s (%s)", path, gai_strerror(ret), hostname);
+ return 1;
+ }
+ freeaddrinfo(ai);
+ return 0;
+}
+
+static int
+check_directory(krb5_context context, const char *path, char *data)
+{
+ DIR *d = opendir(data);
+ if (d == NULL) {
+ krb5_warn(context, errno, "%s: could not open directory `%s'",
+ path, data);
+ return 1;
+ }
+
+ closedir(d);
+ return 0;
+}
+
+static int
+mit_entry(krb5_context context, const char *path, char *data)
+{
+ if (warn_mit_syntax_flag)
+ krb5_warnx(context, "%s is only used by MIT Kerberos", path);
+ return 0;
+}
+
+struct s2i {
+ const char *s;
+ int val;
+};
+
+#define L(X) { #X, LOG_ ## X }
+
+static struct s2i syslogvals[] = {
+ /* severity */
+ L(EMERG),
+ L(ALERT),
+ L(CRIT),
+ L(ERR),
+ L(WARNING),
+ L(NOTICE),
+ L(INFO),
+ L(DEBUG),
+ /* facility */
+ L(AUTH),
+#ifdef LOG_AUTHPRIV
+ L(AUTHPRIV),
+#endif
+#ifdef LOG_CRON
+ L(CRON),
+#endif
+ L(DAEMON),
+#ifdef LOG_FTP
+ L(FTP),
+#endif
+ L(KERN),
+ L(LPR),
+ L(MAIL),
+#ifdef LOG_NEWS
+ L(NEWS),
+#endif
+ L(SYSLOG),
+ L(USER),
+#ifdef LOG_UUCP
+ L(UUCP),
+#endif
+ L(LOCAL0),
+ L(LOCAL1),
+ L(LOCAL2),
+ L(LOCAL3),
+ L(LOCAL4),
+ L(LOCAL5),
+ L(LOCAL6),
+ L(LOCAL7),
+ { NULL, -1 }
+};
+
+static int
+find_value(const char *s, struct s2i *table)
+{
+ while (table->s && strcasecmp(table->s, s) != 0)
+ table++;
+ return table->val;
+}
+
+static int
+check_log(krb5_context context, const char *path, char *data)
+{
+ /* XXX sync with log.c */
+ int min = 0, max = -1, n;
+ char c;
+ const char *p = data;
+#ifdef _WIN32
+ const char *q;
+#endif
+
+ n = sscanf(p, "%d%c%d/", &min, &c, &max);
+ if(n == 2){
+ if(ISPATHSEP(c)) {
+ if(min < 0){
+ max = -min;
+ min = 0;
+ }else{
+ max = min;
+ }
+ }
+ }
+ if(n){
+#ifdef _WIN32
+ q = strrchr(p, '\\');
+ if (q != NULL)
+ p = q;
+ else
+#endif
+ p = strchr(p, '/');
+ if(p == NULL) {
+ krb5_warnx(context, "%s: failed to parse \"%s\"", path, data);
+ return 1;
+ }
+ p++;
+ }
+ if(strcmp(p, "STDERR") == 0 ||
+ strcmp(p, "CONSOLE") == 0 ||
+ (strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')) ||
+ (strncmp(p, "DEVICE", 6) == 0 && p[6] == '='))
+ return 0;
+ if(strncmp(p, "SYSLOG", 6) == 0){
+ int ret = 0;
+ char severity[128] = "";
+ char facility[128] = "";
+ p += 6;
+ if(*p != '\0')
+ p++;
+ if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
+ strsep_copy(&p, ":", facility, sizeof(facility));
+ if(*severity == '\0')
+ strlcpy(severity, "ERR", sizeof(severity));
+ if(*facility == '\0')
+ strlcpy(facility, "AUTH", sizeof(facility));
+ if(find_value(facility, syslogvals) == -1) {
+ krb5_warnx(context, "%s: unknown syslog facility \"%s\"",
+ path, facility);
+ ret++;
+ }
+ if(find_value(severity, syslogvals) == -1) {
+ krb5_warnx(context, "%s: unknown syslog severity \"%s\"",
+ path, severity);
+ ret++;
+ }
+ return ret;
+ }else{
+ krb5_warnx(context, "%s: unknown log type: \"%s\"", path, data);
+ return 1;
+ }
+}
+
+typedef int (*check_func_t)(krb5_context, const char*, char*);
+struct entry {
+ const char *name;
+ int type;
+ void *check_data;
+ int deprecated;
+};
+
+struct entry all_strings[] = {
+ { "", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry all_boolean[] = {
+ { "", krb5_config_string, check_boolean, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+
+struct entry v4_name_convert_entries[] = {
+ { "host", krb5_config_list, all_strings, 0 },
+ { "plain", krb5_config_list, all_strings, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry libdefaults_entries[] = {
+ { "accept_null_addresses", krb5_config_string, check_boolean, 0 },
+ { "allow_weak_crypto", krb5_config_string, check_boolean, 0 },
+ { "capath", krb5_config_list, all_strings, 1 },
+ { "ccapi_library", krb5_config_string, NULL, 0 },
+ { "check_pac", krb5_config_string, check_boolean, 0 },
+ { "check-rd-req-server", krb5_config_string, check_boolean, 0 },
+ { "clockskew", krb5_config_string, check_time, 0 },
+ { "date_format", krb5_config_string, NULL, 0 },
+ { "default_as_etypes", krb5_config_string, NULL, 0 },
+ { "default_ccache_name", krb5_config_string, NULL, 0 },
+ { "default_client_keytab_name", krb5_config_string, NULL, 0 },
+ { "default_cc_name", krb5_config_string, NULL, 0 },
+ { "default_cc_type", krb5_config_string, NULL, 0 },
+ { "default_cc_collection", krb5_config_string, NULL, 0 },
+ { "default_etypes", krb5_config_string, NULL, 0 },
+ { "default_etypes_des", krb5_config_string, NULL, 0 },
+ { "default_keytab_modify_name", krb5_config_string, NULL, 0 },
+ { "default_keytab_name", krb5_config_string, NULL, 0 },
+ { "default_keytab_modify_name", krb5_config_string, NULL, 0 },
+ { "default_realm", krb5_config_string, NULL, 0 },
+ { "default_tgs_etypes", krb5_config_string, NULL, 0 },
+ { "dns_canonicalize_hostname", krb5_config_string, check_boolean, 0 },
+ { "dns_proxy", krb5_config_string, NULL, 0 },
+ { "dns_lookup_kdc", krb5_config_string, check_boolean, 0 },
+ { "dns_lookup_realm", krb5_config_string, check_boolean, 0 },
+ { "dns_lookup_realm_labels", krb5_config_string, NULL, 0 },
+ { "egd_socket", krb5_config_string, NULL, 0 },
+ { "encrypt", krb5_config_string, check_boolean, 0 },
+ { "extra_addresses", krb5_config_string, NULL, 0 },
+ { "fcache_version", krb5_config_string, check_numeric, 0 },
+ { "fcache_strict_checking", krb5_config_string, check_boolean, 0 },
+ { "fcc-mit-ticketflags", krb5_config_string, check_boolean, 0 },
+ { "forward", krb5_config_string, check_boolean, 0 },
+ { "forwardable", krb5_config_string, check_boolean, 0 },
+ { "allow_hierarchical_capaths", krb5_config_string, check_boolean, 0 },
+ { "host_timeout", krb5_config_string, check_time, 0 },
+ { "http_proxy", krb5_config_string, check_host /* XXX */, 0 },
+ { "ignore_addresses", krb5_config_string, NULL, 0 },
+ { "k5login_authoritative", krb5_config_string, check_boolean, 0 },
+ { "k5login_directory", krb5_config_string, NULL, 0 },
+ { "kdc_timeout", krb5_config_string, check_time, 0 },
+ { "kdc_timesync", krb5_config_string, check_boolean, 0 },
+ { "kuserok", krb5_config_string, NULL, 0 },
+ { "large_message_size", krb5_config_string, check_numeric, 0 },
+ { "log_utc", krb5_config_string, check_boolean, 0 },
+ { "max_retries", krb5_config_string, check_numeric, 0 },
+ { "maximum_message_size", krb5_config_string, check_numeric, 0 },
+ { "moduli", krb5_config_string, NULL, 0 },
+ { "name_canon_rules", krb5_config_string, NULL, 0 },
+ { "no-addresses", krb5_config_string, check_boolean, 0 },
+ { "pkinit_dh_min_bits", krb5_config_string, NULL, 0 },
+ { "plugin_dir", krb5_config_string, check_directory, 0 },
+ { "proxiable", krb5_config_string, check_boolean, 0 },
+ { "renew_lifetime", krb5_config_string, check_time, 0 },
+ { "scan_interfaces", krb5_config_string, check_boolean, 0 },
+ { "srv_lookup", krb5_config_string, check_boolean, 0 },
+ { "srv_try_txt", krb5_config_string, check_boolean, 0 },
+ { "ticket_lifetime", krb5_config_string, check_time, 0 },
+ { "time_format", krb5_config_string, NULL, 0 },
+ { "transited_realms_reject", krb5_config_string, NULL, 0 },
+ { "use_fallback", krb5_config_string, check_boolean, 0 },
+ { "v4_instance_resolve", krb5_config_string, check_boolean, 0 },
+ { "v4_name_convert", krb5_config_list, v4_name_convert_entries, 0 },
+ { "verify_ap_req_nofail", krb5_config_string, check_boolean, 0 },
+ { "warn_pwexpire", krb5_config_string, check_time, 0 },
+
+ /* MIT stuff */
+ { "permitted_enctypes", krb5_config_string, mit_entry, 0 },
+ { "default_tgs_enctypes", krb5_config_string, mit_entry, 0 },
+ { "default_tkt_enctypes", krb5_config_string, mit_entry, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry appdefaults_entries[] = {
+ { "afslog", krb5_config_string, check_boolean, 0 },
+ { "afs-use-524", krb5_config_string, check_524, 0 },
+#if 0
+ { "anonymous", krb5_config_string, check_boolean, 0 },
+#endif
+ { "encrypt", krb5_config_string, check_boolean, 0 },
+ { "forward", krb5_config_string, check_boolean, 0 },
+ { "forwardable", krb5_config_string, check_boolean, 0 },
+ { "krb4_get_tickets", krb5_config_string, check_boolean, 0 },
+ { "proxiable", krb5_config_string, check_boolean, 0 },
+ { "renew_lifetime", krb5_config_string, check_time, 0 },
+ { "no-addresses", krb5_config_string, check_boolean, 0 },
+ { "pkinit_anchors", krb5_config_string, NULL, 0 },
+ { "pkinit_pool", krb5_config_string, NULL, 0 },
+ { "pkinit_require_eku", krb5_config_string, NULL, 0 },
+ { "pkinit_require_hostname_match", krb5_config_string, NULL, 0 },
+ { "pkinit_require_krbtgt_otherName", krb5_config_string, NULL, 0 },
+ { "pkinit_revoke", krb5_config_string, NULL, 0 },
+ { "pkinit_trustedCertifiers", krb5_config_string, check_boolean, 0 },
+ { "pkinit_win2k", krb5_config_string, NULL, 0 },
+ { "pkinit_win2k_require_binding", krb5_config_string, NULL, 0 },
+ { "ticket_lifetime", krb5_config_string, check_time, 0 },
+ { "", krb5_config_list, appdefaults_entries, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry realms_entries[] = {
+ { "admin_server", krb5_config_string, check_host, 0 },
+ { "auth_to_local", krb5_config_string, NULL, 0 },
+ { "auth_to_local_names", krb5_config_string, NULL, 0 },
+ { "default_domain", krb5_config_string, NULL, 0 },
+ { "forwardable", krb5_config_string, check_boolean, 0 },
+ { "allow_hierarchical_capaths", krb5_config_string, check_boolean, 0 },
+ { "kdc", krb5_config_string, check_host, 0 },
+ { "kpasswd_server", krb5_config_string, check_host, 0 },
+ { "krb524_server", krb5_config_string, check_host, 0 },
+ { "kx509_ca", krb5_config_string, NULL, 0 },
+ { "kx509_include_pkinit_san", krb5_config_string, check_boolean, 0 },
+ { "name_canon_rules", krb5_config_string, NULL, 0 },
+ { "no-addresses", krb5_config_string, check_boolean, 0 },
+ { "pkinit_anchors", krb5_config_string, NULL, 0 },
+ { "pkinit_require_eku", krb5_config_string, NULL, 0 },
+ { "pkinit_require_hostname_match", krb5_config_string, NULL, 0 },
+ { "pkinit_require_krbtgt_otherName", krb5_config_string, NULL, 0 },
+ { "pkinit_trustedCertifiers", krb5_config_string, check_boolean, 0 },
+ { "pkinit_win2k", krb5_config_string, NULL, 0 },
+ { "pkinit_win2k_require_binding", krb5_config_string, NULL, 0 },
+ { "proxiable", krb5_config_string, check_boolean, 0 },
+ { "renew_lifetime", krb5_config_string, check_time, 0 },
+ { "require_initial_kca_tickets", krb5_config_string, check_boolean, 0 },
+ { "ticket_lifetime", krb5_config_string, check_time, 0 },
+ { "v4_domains", krb5_config_string, NULL, 0 },
+ { "v4_instance_convert", krb5_config_list, all_strings, 0 },
+ { "v4_name_convert", krb5_config_list, v4_name_convert_entries, 0 },
+ { "warn_pwexpire", krb5_config_string, check_time, 0 },
+ { "win2k_pkinit", krb5_config_string, NULL, 0 },
+
+ /* MIT stuff */
+ { "admin_keytab", krb5_config_string, mit_entry, 0 },
+ { "acl_file", krb5_config_string, mit_entry, 0 },
+ { "database_name", krb5_config_string, mit_entry, 0 },
+ { "default_principal_expiration", krb5_config_string, mit_entry, 0 },
+ { "default_principal_flags", krb5_config_string, mit_entry, 0 },
+ { "dict_file", krb5_config_string, mit_entry, 0 },
+ { "kadmind_port", krb5_config_string, mit_entry, 0 },
+ { "kpasswd_port", krb5_config_string, mit_entry, 0 },
+ { "master_kdc", krb5_config_string, mit_entry, 0 },
+ { "master_key_name", krb5_config_string, mit_entry, 0 },
+ { "master_key_type", krb5_config_string, mit_entry, 0 },
+ { "key_stash_file", krb5_config_string, mit_entry, 0 },
+ { "max_life", krb5_config_string, mit_entry, 0 },
+ { "max_renewable_life", krb5_config_string, mit_entry, 0 },
+ { "supported_enctypes", krb5_config_string, mit_entry, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry realms_foobar[] = {
+ { "", krb5_config_list, realms_entries, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+
+struct entry kdc_database_entries[] = {
+ { "acl_file", krb5_config_string, NULL, 0 },
+ { "dbname", krb5_config_string, NULL, 0 },
+ { "log_file", krb5_config_string, NULL, 0 },
+ { "mkey_file", krb5_config_string, NULL, 0 },
+ { "realm", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry kdc_entries[] = {
+ { "addresses", krb5_config_string, NULL, 0 },
+ { "allow-anonymous", krb5_config_string, check_boolean, 0 },
+ { "allow-null-ticket-addresses", krb5_config_string, check_boolean, 0 },
+ { "check-ticket-addresses", krb5_config_string, check_boolean, 0 },
+ { "database", krb5_config_list, kdc_database_entries, 0 },
+ { "detach", krb5_config_string, check_boolean, 0 },
+ { "digests_allowed", krb5_config_string, NULL, 0 },
+ { "disable-des", krb5_config_string, check_boolean, 0 },
+ { "enable-524", krb5_config_string, check_boolean, 0 },
+ { "enable-digest", krb5_config_string, check_boolean, 0 },
+ { "enable-kaserver", krb5_config_string, check_boolean, 1 },
+ { "enable-kerberos4", krb5_config_string, check_boolean, 1 },
+ { "enable-kx509", krb5_config_string, check_boolean, 0 },
+ { "enable-http", krb5_config_string, check_boolean, 0 },
+ { "enable-pkinit", krb5_config_string, check_boolean, 0 },
+ { "encode_as_rep_as_tgs_rep", krb5_config_string, check_boolean, 0 },
+ { "enforce-transited-policy", krb5_config_string, NULL, 1 },
+ { "hdb-ldap-create-base", krb5_config_string, NULL, 0 },
+ { "iprop-acl", krb5_config_string, NULL, 0 },
+ { "iprop-stats", krb5_config_string, NULL, 0 },
+ { "kdc-request-log", krb5_config_string, NULL, 0 },
+ { "kdc_warn_pwexpire", krb5_config_string, check_time, 0 },
+ { "key-file", krb5_config_string, NULL, 0 },
+ { "kx509_ca", krb5_config_string, NULL, 0 },
+ { "kx509_include_pkinit_san", krb5_config_string, check_boolean, 0 },
+ { "kx509_template", krb5_config_string, NULL, 0 },
+ { "logging", krb5_config_string, check_log, 0 },
+ { "max-kdc-datagram-reply-length", krb5_config_string, check_bytes, 0 },
+ { "max-request", krb5_config_string, check_bytes, 0 },
+ { "num-kdc-processes", krb5_config_string, check_numeric, 0 },
+ { "pkinit_allow_proxy_certificate", krb5_config_string, check_boolean, 0 },
+ { "pkinit_anchors", krb5_config_string, NULL, 0 },
+ { "pkinit_dh_min_bits", krb5_config_string, check_numeric, 0 },
+ { "pkinit_identity", krb5_config_string, NULL, 0 },
+ { "pkinit_kdc_friendly_name", krb5_config_string, NULL, 0 },
+ { "pkinit_kdc_ocsp", krb5_config_string, NULL, 0 },
+ { "pkinit_mappings_file", krb5_config_string, NULL, 0 },
+ { "pkinit_pool", krb5_config_string, NULL, 0 },
+ { "pkinit_principal_in_certificate", krb5_config_string, check_boolean, 0 },
+ { "pkinit_revoke", krb5_config_string, NULL, 0 },
+ { "pkinit_win2k_require_binding", krb5_config_string, check_boolean, 0 },
+ { "ports", krb5_config_string, NULL, 0 },
+ { "preauth-use-strongest-session-key", krb5_config_string, check_boolean, 0 },
+ { "require_initial_kca_tickets", krb5_config_string, check_boolean, 0 },
+ { "require-preauth", krb5_config_string, check_boolean, 0 },
+ { "strict-nametypes", krb5_config_string, check_boolean, 0 },
+ { "svc-use-strongest-session-key", krb5_config_string, check_boolean, 0 },
+ { "tgt-use-strongest-session-key", krb5_config_string, check_boolean, 0 },
+ { "transited-policy", krb5_config_string, NULL, 0 },
+ { "use_2b", krb5_config_list, NULL, 0 },
+ { "use-strongest-server-key", krb5_config_string, check_boolean, 0 },
+ { "v4_realm", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry kadmin_entries[] = {
+ { "allow_self_change_password", krb5_config_string, check_boolean, 0 },
+ { "default_keys", krb5_config_string, NULL, 0 },
+ { "password_lifetime", krb5_config_string, check_time, 0 },
+ { "plugin_dir", krb5_config_string, check_directory, 0 },
+ { "require-preauth", krb5_config_string, check_boolean, 0 },
+ { "save-password", krb5_config_string, check_boolean, 0 },
+ { "use_v4_salt", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+};
+struct entry log_strings[] = {
+ { "", krb5_config_string, check_log, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+
+/* MIT stuff */
+struct entry kdcdefaults_entries[] = {
+ { "kdc_ports", krb5_config_string, mit_entry, 0 },
+ { "v4_mode", krb5_config_string, mit_entry, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry capaths_entries[] = {
+ { "", krb5_config_list, all_strings, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry kcm_entries[] = {
+ { "detach", krb5_config_string, check_boolean, 0 },
+ { "disallow-getting-krbtgt", krb5_config_string, check_boolean, 0 },
+ { "logging", krb5_config_string, NULL, 0 },
+ { "max-request", krb5_config_string, NULL, 0 },
+ { "system_ccache", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry password_quality_entries[] = {
+ { "enforce_on_admin_set", krb5_config_string, check_boolean, 0 },
+ { "check_function", krb5_config_string, NULL, 0 },
+ { "check_library", krb5_config_string, NULL, 0 },
+ { "external_program", krb5_config_string, NULL, 0 },
+ { "min_classes", krb5_config_string, check_numeric, 0 },
+ { "min_length", krb5_config_string, check_numeric, 0 },
+ { "policies", krb5_config_string, NULL, 0 },
+ { "policy_libraries", krb5_config_string, NULL, 0 },
+ { "", krb5_config_list, all_strings, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry toplevel_sections[] = {
+ { "appdefaults", krb5_config_list, appdefaults_entries, 0 },
+ { "capaths", krb5_config_list, capaths_entries, 0 },
+ { "domain_realm", krb5_config_list, all_strings, 0 },
+ { "gssapi", krb5_config_list, NULL, 0 },
+ { "kadmin", krb5_config_list, kadmin_entries, 0 },
+ { "kcm", krb5_config_list, kcm_entries, 0 },
+ { "kdc", krb5_config_list, kdc_entries, 0 },
+ { "libdefaults" , krb5_config_list, libdefaults_entries, 0 },
+ { "logging", krb5_config_list, log_strings, 0 },
+ { "password_quality", krb5_config_list, password_quality_entries, 0 },
+ { "realms", krb5_config_list, realms_foobar, 0 },
+
+ /* MIT stuff */
+ { "kdcdefaults", krb5_config_list, kdcdefaults_entries, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+
+static int
+check_section(krb5_context context, const char *path, krb5_config_section *cf,
+ struct entry *entries)
+{
+ int error = 0;
+ krb5_config_section *p;
+ struct entry *e;
+
+ char *local;
+
+ for(p = cf; p != NULL; p = p->next) {
+ local = NULL;
+ if (asprintf(&local, "%s/%s", path, p->name) < 0 || local == NULL)
+ errx(1, "out of memory");
+ for(e = entries; e->name != NULL; e++) {
+ if(*e->name == '\0' || strcmp(e->name, p->name) == 0) {
+ if(e->type != p->type) {
+ krb5_warnx(context, "%s: unknown or wrong type", local);
+ error |= 1;
+ } else if(p->type == krb5_config_string && e->check_data != NULL) {
+ error |= (*(check_func_t)e->check_data)(context, local, p->u.string);
+ } else if(p->type == krb5_config_list && e->check_data != NULL) {
+ error |= check_section(context, local, p->u.list, e->check_data);
+ }
+ if(e->deprecated) {
+ krb5_warnx(context, "%s: is a deprecated entry", local);
+ error |= 1;
+ }
+ break;
+ }
+ }
+ if(e->name == NULL) {
+ krb5_warnx(context, "%s: unknown entry", local);
+ error |= 1;
+ }
+ free(local);
+ }
+ return error;
+}
+
+
+static void
+dumpconfig(int level, krb5_config_section *top)
+{
+ krb5_config_section *x;
+ for(x = top; x; x = x->next) {
+ switch(x->type) {
+ case krb5_config_list:
+ if(level == 0) {
+ printf("[%s]\n", x->name);
+ } else {
+ printf("%*s%s = {\n", 4 * level, " ", x->name);
+ }
+ dumpconfig(level + 1, x->u.list);
+ if(level > 0)
+ printf("%*s}\n", 4 * level, " ");
+ break;
+ case krb5_config_string:
+ printf("%*s%s = %s\n", 4 * level, " ", x->name, x->u.string);
+ break;
+ }
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_config_section *tmp_cf;
+ int optidx = 0;
+
+ setprogname (argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret == KRB5_CONFIG_BADFORMAT)
+ errx (1, "krb5_init_context failed to parse configuration file");
+ else if (ret)
+ errx (1, "krb5_init_context failed with %d", ret);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ tmp_cf = NULL;
+ if(argc == 0)
+ krb5_get_default_config_files(&argv);
+
+ while(*argv) {
+ ret = krb5_config_parse_file_multi(context, *argv, &tmp_cf);
+ if (ret != 0)
+ krb5_warn (context, ret, "krb5_config_parse_file");
+ argv++;
+ }
+
+ if(dumpconfig_flag)
+ dumpconfig(0, tmp_cf);
+
+ return check_section(context, "", tmp_cf, toplevel_sections);
+}
diff --git a/third_party/heimdal/lib/krb5/verify_user.c b/third_party/heimdal/lib/krb5/verify_user.c
new file mode 100644
index 0000000..c6ead8e
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/verify_user.c
@@ -0,0 +1,258 @@
+/*
+ * 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
+verify_common (krb5_context context,
+ krb5_principal principal,
+ krb5_ccache ccache,
+ krb5_keytab keytab,
+ krb5_boolean secure,
+ const char *service,
+ krb5_creds *cred)
+{
+ krb5_error_code ret;
+ krb5_principal server;
+ krb5_verify_init_creds_opt vopt;
+ krb5_ccache id;
+
+ ret = krb5_sname_to_principal (context, NULL, service, KRB5_NT_SRV_HST,
+ &server);
+ if(ret)
+ return ret;
+
+ krb5_verify_init_creds_opt_init(&vopt);
+ krb5_verify_init_creds_opt_set_ap_req_nofail(&vopt, secure);
+
+ ret = krb5_verify_init_creds(context,
+ cred,
+ server,
+ keytab,
+ NULL,
+ &vopt);
+ krb5_free_principal(context, server);
+ if(ret)
+ return ret;
+ if(ccache == NULL)
+ ret = krb5_cc_default (context, &id);
+ else
+ id = ccache;
+ if(ret == 0){
+ ret = krb5_cc_initialize(context, id, principal);
+ if(ret == 0){
+ ret = krb5_cc_store_cred(context, id, cred);
+ }
+ if(ccache == NULL)
+ krb5_cc_close(context, id);
+ }
+ return ret;
+}
+
+/*
+ * Verify user `principal' with `password'.
+ *
+ * If `secure', also verify against local service key for `service'.
+ *
+ * As a side effect, fresh tickets are obtained and stored in `ccache'.
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_init(krb5_verify_opt *opt)
+{
+ memset(opt, 0, sizeof(*opt));
+ opt->secure = TRUE;
+ opt->service = "host";
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_verify_opt_alloc(krb5_context context, krb5_verify_opt **opt)
+{
+ *opt = calloc(1, sizeof(**opt));
+ if ((*opt) == NULL)
+ return krb5_enomem(context);
+ krb5_verify_opt_init(*opt);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_free(krb5_verify_opt *opt)
+{
+ free(opt);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_set_ccache(krb5_verify_opt *opt, krb5_ccache ccache)
+{
+ opt->ccache = ccache;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_set_keytab(krb5_verify_opt *opt, krb5_keytab keytab)
+{
+ opt->keytab = keytab;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_set_secure(krb5_verify_opt *opt, krb5_boolean secure)
+{
+ opt->secure = secure;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_set_service(krb5_verify_opt *opt, const char *service)
+{
+ opt->service = service;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_verify_opt_set_flags(krb5_verify_opt *opt, unsigned int flags)
+{
+ opt->flags |= flags;
+}
+
+static krb5_error_code
+verify_user_opt_int(krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ krb5_verify_opt *vopt)
+
+{
+ krb5_error_code ret;
+ krb5_get_init_creds_opt *opt;
+ krb5_creds cred;
+
+ ret = krb5_get_init_creds_opt_alloc (context, &opt);
+ if (ret)
+ return ret;
+ krb5_get_init_creds_opt_set_default_flags(context, NULL,
+ krb5_principal_get_realm(context, principal),
+ opt);
+ ret = krb5_get_init_creds_password (context,
+ &cred,
+ principal,
+ password,
+ krb5_prompter_posix,
+ NULL,
+ 0,
+ NULL,
+ opt);
+ krb5_get_init_creds_opt_free(context, opt);
+ if(ret)
+ return ret;
+#define OPT(V, D) ((vopt && (vopt->V)) ? (vopt->V) : (D))
+ ret = verify_common (context, principal, OPT(ccache, NULL),
+ OPT(keytab, NULL), vopt ? vopt->secure : TRUE,
+ OPT(service, "host"), &cred);
+#undef OPT
+ krb5_free_cred_contents(context, &cred);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_user_opt(krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ krb5_verify_opt *opt)
+{
+ krb5_error_code ret;
+
+ if(opt && (opt->flags & KRB5_VERIFY_LREALMS)) {
+ krb5_realm *realms, *r;
+ ret = krb5_get_default_realms (context, &realms);
+ if (ret)
+ return ret;
+ ret = KRB5_CONFIG_NODEFREALM;
+
+ for (r = realms; *r != NULL && ret != 0; ++r) {
+ ret = krb5_principal_set_realm(context, principal, *r);
+ if (ret) {
+ krb5_free_host_realm (context, realms);
+ return ret;
+ }
+
+ ret = verify_user_opt_int(context, principal, password, opt);
+ }
+ krb5_free_host_realm (context, realms);
+ if(ret)
+ return ret;
+ } else
+ ret = verify_user_opt_int(context, principal, password, opt);
+ return ret;
+}
+
+/* compat function that calls above */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_user(krb5_context context,
+ krb5_principal principal,
+ krb5_ccache ccache,
+ const char *password,
+ krb5_boolean secure,
+ const char *service)
+{
+ krb5_verify_opt opt;
+
+ krb5_verify_opt_init(&opt);
+
+ krb5_verify_opt_set_ccache(&opt, ccache);
+ krb5_verify_opt_set_secure(&opt, secure);
+ krb5_verify_opt_set_service(&opt, service);
+
+ return krb5_verify_user_opt(context, principal, password, &opt);
+}
+
+/*
+ * A variant of `krb5_verify_user'. The realm of `principal' is
+ * ignored and all the local realms are tried.
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verify_user_lrealm(krb5_context context,
+ krb5_principal principal,
+ krb5_ccache ccache,
+ const char *password,
+ krb5_boolean secure,
+ const char *service)
+{
+ krb5_verify_opt opt;
+
+ krb5_verify_opt_init(&opt);
+
+ krb5_verify_opt_set_ccache(&opt, ccache);
+ krb5_verify_opt_set_secure(&opt, secure);
+ krb5_verify_opt_set_service(&opt, service);
+ krb5_verify_opt_set_flags(&opt, KRB5_VERIFY_LREALMS);
+
+ return krb5_verify_user_opt(context, principal, password, &opt);
+}
diff --git a/third_party/heimdal/lib/krb5/version-script.map b/third_party/heimdal/lib/krb5/version-script.map
new file mode 100644
index 0000000..6c1df0c
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/version-script.map
@@ -0,0 +1,892 @@
+HEIMDAL_KRB5_2.0 {
+ global:
+ krb524_convert_creds_kdc;
+ krb524_convert_creds_kdc_ccache;
+ krb5_abort;
+ krb5_abortx;
+ krb5_acl_match_file;
+ krb5_acl_match_string;
+ krb5_add_et_list;
+ krb5_add_extra_addresses;
+ krb5_add_ignore_addresses;
+ krb5_addlog_dest;
+ krb5_addlog_func;
+ krb5_addr2sockaddr;
+ krb5_address_compare;
+ krb5_address_order;
+ krb5_address_prefixlen_boundary;
+ krb5_address_search;
+ krb5_allow_weak_crypto;
+ krb5_aname_to_localname;
+ krb5_anyaddr;
+ krb5_appdefault_boolean;
+ krb5_appdefault_string;
+ krb5_appdefault_time;
+ krb5_append_addresses;
+ krb5_auth_con_addflags;
+ krb5_auth_con_add_AuthorizationData;
+ krb5_auth_con_add_AuthorizationDataIfRelevant;
+ krb5_auth_con_free;
+ krb5_auth_con_genaddrs;
+ krb5_auth_con_generatelocalsubkey;
+ krb5_auth_con_getaddrs;
+ krb5_auth_con_getauthenticator;
+ krb5_auth_con_getcksumtype;
+ krb5_auth_con_getflags;
+ krb5_auth_con_getkey;
+ krb5_auth_con_getkeytype;
+ krb5_auth_con_getlocalseqnumber;
+ krb5_auth_con_getlocalsubkey;
+ krb5_auth_con_getrcache;
+ krb5_auth_con_getremoteseqnumber;
+ krb5_auth_con_getremotesubkey;
+ krb5_auth_con_getsendsubkey;
+ krb5_auth_con_init;
+ krb5_auth_con_removeflags;
+ krb5_auth_con_setaddrs;
+ krb5_auth_con_setaddrs_from_fd;
+ krb5_auth_con_setcksumtype;
+ krb5_auth_con_setflags;
+ krb5_auth_con_setkey;
+ krb5_auth_con_setkeytype;
+ krb5_auth_con_setlocalseqnumber;
+ krb5_auth_con_setlocalsubkey;
+ krb5_auth_con_setrcache;
+ krb5_auth_con_setremoteseqnumber;
+ krb5_auth_con_setremotesubkey;
+ krb5_auth_con_setuserkey;
+ krb5_auth_getremoteseqnumber;
+ krb5_build_ap_req;
+ krb5_build_principal;
+ krb5_build_principal_ext;
+ krb5_build_principal_va;
+ krb5_build_principal_va_ext;
+ krb5_c_block_size;
+ krb5_c_checksum_length;
+ krb5_c_decrypt;
+ krb5_c_encrypt;
+ krb5_c_encrypt_length;
+ krb5_c_enctype_compare;
+ krb5_c_get_checksum;
+ krb5_c_is_coll_proof_cksum;
+ krb5_c_is_keyed_cksum;
+ krb5_c_keylengths;
+ krb5_c_make_checksum;
+ krb5_c_make_random_key;
+ krb5_c_prf;
+ krb5_c_prf_length;
+ krb5_c_set_checksum;
+ krb5_c_valid_cksumtype;
+ krb5_c_valid_enctype;
+ krb5_c_verify_checksum;
+ krb5_cc_cache_end_seq_get;
+ krb5_cc_cache_get_first;
+ krb5_cc_cache_match;
+ krb5_cc_cache_next;
+ krb5_cc_clear_mcred;
+ krb5_cc_close;
+ krb5_cc_configured_default_name;
+ krb5_cc_copy_cache;
+ krb5_cc_copy_match_f;
+ krb5_cc_default;
+ krb5_cc_default_for;
+ krb5_cc_default_sub;
+ krb5_cc_default_name;
+ krb5_cc_destroy;
+ krb5_cc_end_seq_get;
+ krb5_cc_gen_new;
+ krb5_cc_get_config;
+ krb5_cc_get_friendly_name;
+ krb5_cc_get_full_name;
+ krb5_cc_get_kdc_offset;
+ krb5_cc_get_lifetime;
+ krb5_cc_get_name;
+ krb5_cc_get_ops;
+ krb5_cc_get_prefix_ops;
+ krb5_cc_get_principal;
+ krb5_cc_get_type;
+ krb5_cc_get_version;
+ krb5_cc_initialize;
+ krb5_cc_last_change_time;
+ krb5_cc_move;
+ krb5_cc_new_unique;
+ krb5_cc_next_cred;
+ krb5_cc_next_cred_match;
+ krb5_cc_register;
+ krb5_cc_remove_cred;
+ krb5_cc_resolve;
+ krb5_cc_resolve_for;
+ krb5_cc_resolve_sub;
+ krb5_cc_retrieve_cred;
+ krb5_cc_set_config;
+ krb5_cc_set_default_name;
+ krb5_cc_set_flags;
+ krb5_cc_set_kdc_offset;
+ krb5_cc_start_seq_get;
+ krb5_cc_store_cred;
+ krb5_cc_support_switch;
+ krb5_cc_switch;
+ krb5_cc_set_friendly_name;
+ krb5_change_password;
+ krb5_check_transited;
+ krb5_check_transited_realms;
+ krb5_checksum_disable;
+ krb5_checksum_free;
+ krb5_checksum_is_collision_proof;
+ krb5_checksum_is_keyed;
+ krb5_checksumsize;
+ krb5_cksumtype_to_enctype;
+ krb5_cksumtype_valid;
+ krb5_clear_error_string;
+ krb5_clear_error_message;
+ krb5_closelog;
+ krb5_compare_creds;
+ krb5_config_file_free;
+ krb5_config_free_strings;
+ krb5_config_get_bool;
+ krb5_config_get_bool_default;
+ krb5_config_get_int;
+ krb5_config_get_int_default;
+ krb5_config_get_list;
+ krb5_config_get_string;
+ krb5_config_get_string_default;
+ krb5_config_get_strings;
+ krb5_config_get_time;
+ krb5_config_get_time_default;
+ krb5_config_parse_file;
+ krb5_config_parse_file_multi;
+ krb5_config_parse_string_multi;
+ krb5_config_vget_bool;
+ krb5_config_vget_bool_default;
+ krb5_config_vget_int;
+ krb5_config_vget_int_default;
+ krb5_config_vget_list;
+ krb5_config_vget_string;
+ krb5_config_vget_string_default;
+ krb5_config_vget_strings;
+ krb5_config_vget_time;
+ krb5_config_vget_time_default;
+ krb5_copy_address;
+ krb5_copy_addresses;
+ krb5_copy_checksum;
+ krb5_copy_creds;
+ krb5_copy_creds_contents;
+ krb5_copy_context;
+ krb5_copy_data;
+ krb5_copy_host_realm;
+ krb5_copy_keyblock;
+ krb5_copy_keyblock_contents;
+ krb5_copy_principal;
+ krb5_copy_ticket;
+ krb5_create_checksum;
+ krb5_create_checksum_iov;
+ krb5_crypto_destroy;
+ krb5_crypto_fx_cf2;
+ krb5_crypto_get_checksum_type;
+ krb5_crypto_getblocksize;
+ krb5_crypto_getconfoundersize;
+ krb5_crypto_getenctype;
+ krb5_crypto_getpadsize;
+ krb5_crypto_init;
+ krb5_crypto_overhead;
+ krb5_crypto_prf;
+ krb5_crypto_prfplus;
+ krb5_crypto_prf_length;
+ krb5_crypto_length;
+ krb5_crypto_length_iov;
+ krb5_decrypt_iov_ivec;
+ krb5_encrypt_iov_ivec;
+ krb5_enomem;
+ krb5_data_alloc;
+ krb5_data_ct_cmp;
+ krb5_data_cmp;
+ krb5_data_copy;
+ krb5_data_free;
+ krb5_data_realloc;
+ krb5_data_zero;
+ krb5_debug;
+ krb5_decode_Authenticator;
+ krb5_decode_ETYPE_INFO2;
+ krb5_decode_ETYPE_INFO;
+ krb5_decode_EncAPRepPart;
+ krb5_decode_EncASRepPart;
+ krb5_decode_EncKrbCredPart;
+ krb5_decode_EncTGSRepPart;
+ krb5_decode_EncTicketPart;
+ krb5_decode_ap_req;
+ krb5_decrypt;
+ krb5_decrypt_EncryptedData;
+ krb5_decrypt_ivec;
+ krb5_decrypt_ticket;
+ krb5_derive_key;
+ krb5_digest_alloc;
+ krb5_digest_free;
+ krb5_digest_get_client_binding;
+ krb5_digest_get_identifier;
+ krb5_digest_get_opaque;
+ krb5_digest_get_rsp;
+ krb5_digest_get_server_nonce;
+ krb5_digest_get_session_key;
+ krb5_digest_get_tickets;
+ krb5_digest_init_request;
+ krb5_digest_probe;
+ krb5_digest_rep_get_status;
+ krb5_digest_request;
+ krb5_digest_set_authentication_user;
+ krb5_digest_set_authid;
+ krb5_digest_set_client_nonce;
+ krb5_digest_set_digest;
+ krb5_digest_set_hostname;
+ krb5_digest_set_identifier;
+ krb5_digest_set_method;
+ krb5_digest_set_nonceCount;
+ krb5_digest_set_opaque;
+ krb5_digest_set_qop;
+ krb5_digest_set_realm;
+ krb5_digest_set_responseData;
+ krb5_digest_set_server_cb;
+ krb5_digest_set_server_nonce;
+ krb5_digest_set_type;
+ krb5_digest_set_uri;
+ krb5_digest_set_username;
+ krb5_domain_x500_decode;
+ krb5_domain_x500_encode;
+ krb5_eai_to_heim_errno;
+ krb5_encode_Authenticator;
+ krb5_encode_ETYPE_INFO2;
+ krb5_encode_ETYPE_INFO;
+ krb5_encode_EncAPRepPart;
+ krb5_encode_EncASRepPart;
+ krb5_encode_EncKrbCredPart;
+ krb5_encode_EncTGSRepPart;
+ krb5_encode_EncTicketPart;
+ krb5_encrypt;
+ krb5_encrypt_EncryptedData;
+ krb5_encrypt_ivec;
+ krb5_enctype_enable;
+ krb5_enctype_disable;
+ krb5_enctype_keybits;
+ krb5_enctype_keysize;
+ krb5_enctype_to_keytype;
+ krb5_enctype_to_string;
+ krb5_enctype_valid;
+ krb5_enctypes_compatible_keys;
+ krb5_err;
+ krb5_error_from_rd_error;
+ krb5_errx;
+ krb5_expand_hostname;
+ krb5_expand_hostname_realms;
+ krb5_find_padata;
+ krb5_format_time;
+ krb5_free_address;
+ krb5_free_addresses;
+ krb5_free_ap_rep_enc_part;
+ krb5_free_authenticator;
+ krb5_free_checksum;
+ krb5_free_checksum_contents;
+ krb5_free_config_files;
+ krb5_free_context;
+ krb5_free_cred_contents;
+ krb5_free_creds;
+ krb5_free_creds_contents;
+ krb5_free_data;
+ krb5_free_data_contents;
+ krb5_free_default_realm;
+ krb5_free_error;
+ krb5_free_error_contents;
+ krb5_free_error_string;
+ krb5_free_error_message;
+ krb5_free_host_realm;
+ krb5_free_kdc_rep;
+ krb5_free_keyblock;
+ krb5_free_keyblock_contents;
+ krb5_free_krbhst;
+ krb5_free_principal;
+ krb5_free_salt;
+ krb5_free_ticket;
+ krb5_free_unparsed_name;
+ krb5_fwd_tgt_creds;
+ krb5_generate_random_block;
+ krb5_generate_random_keyblock;
+ krb5_generate_seq_number;
+ krb5_generate_subkey;
+ krb5_generate_subkey_extended;
+ krb5_get_all_client_addrs;
+ krb5_get_all_server_addrs;
+ krb5_get_cred_from_kdc;
+ krb5_get_cred_from_kdc_opt;
+ krb5_get_credentials;
+ krb5_get_credentials_with_flags;
+ krb5_get_creds;
+ krb5_get_creds_opt_add_options;
+ krb5_get_creds_opt_alloc;
+ krb5_get_creds_opt_free;
+ krb5_get_creds_opt_set_enctype;
+ krb5_get_creds_opt_set_impersonate;
+ krb5_get_creds_opt_set_options;
+ krb5_get_creds_opt_set_ticket;
+ krb5_get_default_config_files;
+ krb5_get_default_in_tkt_etypes;
+ krb5_get_default_principal;
+ krb5_get_default_realm;
+ krb5_get_default_realms;
+ krb5_get_dns_canonicalize_hostname;
+ krb5_get_err_text;
+ krb5_get_error_message;
+ krb5_get_error_string;
+ krb5_get_extra_addresses;
+ krb5_get_fcache_version;
+ krb5_get_forwarded_creds;
+ krb5_get_host_realm;
+ krb5_get_ignore_addresses;
+ krb5_get_in_cred;
+ krb5_cccol_last_change_time;
+ krb5_get_in_tkt;
+ krb5_get_in_tkt_with_keytab;
+ krb5_get_in_tkt_with_password;
+ krb5_get_in_tkt_with_skey;
+ krb5_get_init_creds;
+ krb5_get_init_creds_keyblock;
+ krb5_get_init_creds_keytab;
+ krb5_get_init_creds_opt_alloc;
+ krb5_get_init_creds_opt_free;
+ krb5_get_init_creds_opt_get_error;
+ krb5_get_init_creds_opt_init;
+ krb5_get_init_creds_opt_set_address_list;
+ krb5_get_init_creds_opt_set_addressless;
+ krb5_get_init_creds_opt_set_anonymous;
+ krb5_get_init_creds_opt_set_change_password_prompt;
+ krb5_get_init_creds_opt_set_canonicalize;
+ krb5_get_init_creds_opt_set_default_flags;
+ krb5_get_init_creds_opt_set_etype_list;
+ krb5_get_init_creds_opt_set_forwardable;
+ krb5_get_init_creds_opt_set_pa_password;
+ krb5_get_init_creds_opt_set_pac_request;
+ krb5_get_init_creds_opt_set_pkinit;
+ krb5_get_init_creds_opt_set_preauth_list;
+ krb5_get_init_creds_opt_set_process_last_req;
+ krb5_get_init_creds_opt_set_proxiable;
+ krb5_get_init_creds_opt_set_renew_life;
+ krb5_get_init_creds_opt_set_salt;
+ krb5_get_init_creds_opt_set_tkt_life;
+ krb5_get_init_creds_opt_set_win2k;
+ krb5_get_init_creds_opt_set_fast_ccache;
+ krb5_get_init_creds_opt_set_fast_ccache_name;
+ krb5_get_init_creds_opt_set_fast_flags;
+ krb5_get_init_creds_password;
+ krb5_get_instance;
+ krb5_get_kdc_cred;
+ krb5_get_kdc_sec_offset;
+ krb5_get_krb524hst;
+ krb5_get_krb_admin_hst;
+ krb5_get_krb_changepw_hst;
+ krb5_get_krb_readonly_admin_hst;
+ krb5_get_krbhst;
+ krb5_get_max_time_skew;
+ krb5_get_pw_salt;
+ krb5_get_renewed_creds;
+ krb5_get_server_rcache;
+ krb5_get_use_admin_kdc;
+ krb5_get_warn_dest;
+ krb5_get_wrapped_length;
+ krb5_getportbyname;
+ krb5_h_addr2addr;
+ krb5_h_addr2sockaddr;
+ krb5_h_errno_to_heim_errno;
+ krb5_have_debug;
+ krb5_have_error_string;
+ krb5_hmac;
+ krb5_init_context;
+ krb5_init_ets;
+ krb5_initlog;
+ krb5_is_config_principal;
+ krb5_is_enctype_old;
+ krb5_is_enctype_weak;
+ krb5_is_thread_safe;
+ krb5_kcm_call;
+ krb5_kcm_storage_request;
+ krb5_kerberos_enctypes;
+ krb5_keyblock_get_enctype;
+ krb5_keyblock_init;
+ krb5_keyblock_key_proc;
+ krb5_keyblock_zero;
+ krb5_keytab_key_proc;
+ krb5_keytype_to_enctypes;
+ krb5_keytype_to_enctypes_default;
+ krb5_keytype_to_string;
+ krb5_krbhst_format_string;
+ krb5_krbhst_free;
+ krb5_krbhst_get_addrinfo;
+ krb5_krbhst_init;
+ krb5_krbhst_init_flags;
+ krb5_krbhst_next;
+ krb5_krbhst_next_as_string;
+ krb5_krbhst_reset;
+ krb5_kt_add_entry;
+ krb5_kt_close;
+ krb5_kt_compare;
+ krb5_kt_copy_entry_contents;
+ krb5_kt_default;
+ krb5_kt_default_modify_name;
+ krb5_kt_default_name;
+ krb5_kt_destroy;
+ krb5_kt_end_seq_get;
+ krb5_kt_free_entry;
+ krb5_kt_get_entry;
+ krb5_kt_get_full_name;
+ krb5_kt_get_name;
+ krb5_kt_get_type;
+ krb5_kt_have_content;
+ krb5_kt_next_entry;
+ krb5_kt_read_service_key;
+ krb5_kt_register;
+ krb5_kt_remove_entry;
+ krb5_kt_resolve;
+ krb5_kt_start_seq_get;
+ krb5_kuserok;
+ krb5_kx509;
+ krb5_kx509_ctx_add_eku;
+ krb5_kx509_ctx_add_san_dns_name;
+ krb5_kx509_ctx_add_san_ms_upn;
+ krb5_kx509_ctx_add_san_pkinit;
+ krb5_kx509_ctx_add_san_registeredID;
+ krb5_kx509_ctx_add_san_rfc822Name;
+ krb5_kx509_ctx_add_san_xmpp;
+ krb5_kx509_ctx_free;
+ krb5_kx509_ctx_init;
+ krb5_kx509_ctx_set_csr_der;
+ krb5_kx509_ctx_set_key;
+ krb5_kx509_ctx_set_realm;
+ krb5_kx509_ext;
+ krb5_log;
+ krb5_log_msg;
+ krb5_make_addrport;
+ krb5_make_principal;
+ krb5_max_sockaddr_size;
+ krb5_mk_1cred;
+ krb5_mk_error;
+ krb5_mk_error_ext;
+ krb5_mk_ncred;
+ krb5_mk_priv;
+ krb5_mk_rep;
+ krb5_mk_req;
+ krb5_mk_req_exact;
+ krb5_mk_req_extended;
+ krb5_mk_safe;
+ krb5_net_read;
+ krb5_net_write;
+ krb5_net_write_block;
+ krb5_ntlm_alloc;
+ krb5_ntlm_free;
+ krb5_ntlm_init_get_challenge;
+ krb5_ntlm_init_get_flags;
+ krb5_ntlm_init_get_opaque;
+ krb5_ntlm_init_get_targetinfo;
+ krb5_ntlm_init_get_targetname;
+ krb5_ntlm_init_request;
+ krb5_ntlm_rep_get_sessionkey;
+ krb5_ntlm_rep_get_status;
+ krb5_ntlm_req_set_flags;
+ krb5_ntlm_req_set_lm;
+ krb5_ntlm_req_set_ntlm;
+ krb5_ntlm_req_set_opaque;
+ krb5_ntlm_req_set_session;
+ krb5_ntlm_req_set_targetname;
+ krb5_ntlm_req_set_username;
+ krb5_ntlm_request;
+ krb5_openlog;
+ krb5_pac_add_buffer;
+ krb5_pac_free;
+ krb5_pac_get_buffer;
+ _krb5_pac_get_buffer_by_name;
+ krb5_pac_get_kdc_checksum_info;
+ krb5_pac_get_types;
+ krb5_pac_init;
+ krb5_pac_is_trusted;
+ krb5_pac_parse;
+ krb5_pac_set_trusted;
+ krb5_pac_verify;
+ krb5_padata_add;
+ _krb5_parse_address_no_lookup;
+ krb5_parse_address;
+ krb5_parse_name;
+ krb5_parse_name_flags;
+ krb5_parse_nametype;
+ krb5_passwd_result_to_string;
+ krb5_password_key_proc;
+ krb5_get_permitted_enctypes;
+ krb5_plugin_register;
+ krb5_prepend_config_files;
+ krb5_prepend_config_files_default;
+ krb5_prepend_error_message;
+ krb5_princ_realm;
+ krb5_princ_set_realm;
+ krb5_principal_compare;
+ krb5_principal_compare_any_realm;
+ krb5_principal_get_comp_string;
+ krb5_principal_get_num_comp;
+ krb5_principal_get_realm;
+ krb5_principal_get_type;
+ krb5_principal_match;
+ krb5_principal_set_comp_string;
+ krb5_principal_set_realm;
+ krb5_principal_set_type;
+ krb5_principal_is_anonymous;
+ krb5_principal_is_federated;
+ krb5_principal_is_krbtgt;
+ krb5_principal_is_root_krbtgt;
+ krb5_principalname_is_krbtgt;
+ krb5_print_address;
+ krb5_program_setup;
+ krb5_prompter_posix;
+ krb5_random_to_key;
+ krb5_rc_close;
+ krb5_rc_default;
+ krb5_rc_default_name;
+ krb5_rc_default_type;
+ krb5_rc_destroy;
+ krb5_rc_expunge;
+ krb5_rc_get_lifespan;
+ krb5_rc_get_name;
+ krb5_rc_get_type;
+ krb5_rc_initialize;
+ krb5_rc_recover;
+ krb5_rc_resolve;
+ krb5_rc_resolve_full;
+ krb5_rc_resolve_type;
+ krb5_rc_store;
+ krb5_rd_cred2;
+ krb5_rd_cred;
+ krb5_rd_error;
+ krb5_rd_priv;
+ krb5_rd_rep;
+ krb5_rd_req;
+ krb5_rd_req_ctx;
+ krb5_rd_req_in_ctx_alloc;
+ krb5_rd_req_in_ctx_free;
+ krb5_rd_req_in_set_keyblock;
+ krb5_rd_req_in_set_keytab;
+ krb5_rd_req_in_set_pac_check;
+ krb5_rd_req_out_ctx_free;
+ krb5_rd_req_out_get_ap_req_options;
+ krb5_rd_req_out_get_keyblock;
+ krb5_rd_req_out_get_ticket;
+ krb5_rd_req_with_keyblock;
+ krb5_rd_safe;
+ krb5_read_message;
+ krb5_read_priv_message;
+ krb5_read_safe_message;
+ krb5_realm_compare;
+ krb5_recvauth;
+ krb5_recvauth_match_version;
+ krb5_ret_address;
+ krb5_ret_addrs;
+ krb5_ret_authdata;
+ krb5_ret_creds;
+ krb5_ret_creds_tag;
+ krb5_ret_data;
+ krb5_ret_int16;
+ krb5_ret_int32;
+ krb5_ret_int64;
+ krb5_ret_int8;
+ krb5_ret_keyblock;
+ krb5_ret_principal;
+ krb5_ret_string;
+ krb5_ret_stringnl;
+ krb5_ret_stringz;
+ krb5_ret_times;
+ krb5_ret_uint16;
+ krb5_ret_uint32;
+ krb5_ret_uint64;
+ krb5_ret_uint8;
+ krb5_salttype_to_string;
+ krb5_sendauth;
+ krb5_sendto;
+ krb5_sendto_context;
+ krb5_sendto_ctx_add_flags;
+ krb5_sendto_ctx_alloc;
+ krb5_sendto_ctx_free;
+ krb5_sendto_ctx_get_flags;
+ krb5_sendto_ctx_set_func;
+ krb5_sendto_ctx_set_type;
+ krb5_sendto_kdc;
+ krb5_sendto_kdc_flags;
+ krb5_sendto_set_hostname;
+ krb5_sendto_set_sitename;
+ krb5_set_config;
+ krb5_set_config_files;
+ krb5_set_debug_dest;
+ krb5_set_default_in_tkt_etypes;
+ krb5_set_default_realm;
+ krb5_set_dns_canonicalize_hostname;
+ krb5_set_error_message;
+ krb5_set_error_string;
+ krb5_set_extra_addresses;
+ krb5_set_fcache_version;
+ krb5_set_home_dir_access;
+ krb5_set_ignore_addresses;
+ krb5_set_kdc_sec_offset;
+ krb5_set_log_dest;
+ krb5_set_max_time_skew;
+ krb5_set_password;
+ krb5_set_password_using_ccache;
+ krb5_set_real_time;
+ krb5_set_send_to_kdc_func;
+ krb5_set_use_admin_kdc;
+ krb5_set_warn_dest;
+ krb5_sname_to_principal;
+ krb5_sock_to_principal;
+ krb5_sockaddr2address;
+ krb5_sockaddr2port;
+ krb5_sockaddr_uninteresting;
+ krb5_std_usage;
+ krb5_storage_clear_flags;
+ krb5_storage_emem;
+ krb5_storage_free;
+ krb5_storage_from_data;
+ krb5_storage_from_fd;
+ krb5_storage_from_mem;
+ krb5_storage_from_readonly_mem;
+ krb5_storage_from_socket;
+ krb5_storage_fsync;
+ krb5_storage_get_byteorder;
+ krb5_storage_get_eof_code;
+ krb5_storage_is_flags;
+ krb5_storage_read;
+ krb5_storage_stdio_from_fd;
+ krb5_storage_seek;
+ krb5_storage_set_byteorder;
+ krb5_storage_set_eof_code;
+ krb5_storage_set_flags;
+ krb5_storage_set_max_alloc;
+ krb5_storage_to_data;
+ krb5_storage_truncate;
+ krb5_storage_write;
+ krb5_store_address;
+ krb5_store_addrs;
+ krb5_store_authdata;
+ krb5_store_bytes;
+ krb5_store_creds;
+ krb5_store_creds_tag;
+ krb5_store_data;
+ krb5_store_datalen;
+ krb5_store_int16;
+ krb5_store_int32;
+ krb5_store_int64;
+ krb5_store_int8;
+ krb5_store_keyblock;
+ krb5_store_principal;
+ krb5_store_string;
+ krb5_store_stringnl;
+ krb5_store_stringz;
+ krb5_store_times;
+ krb5_store_uint16;
+ krb5_store_uint32;
+ krb5_store_uint64;
+ krb5_store_uint8;
+ krb5_string_to_deltat;
+ krb5_string_to_enctype;
+ krb5_string_to_key;
+ krb5_string_to_key_data;
+ krb5_string_to_key_data_salt;
+ krb5_string_to_key_data_salt_opaque;
+ krb5_string_to_key_derived;
+ krb5_string_to_key_salt;
+ krb5_string_to_key_salt_opaque;
+ krb5_string_to_keysalts2;
+ krb5_string_to_keytype;
+ krb5_string_to_salttype;
+ krb5_ticket_get_authorization_data_type;
+ krb5_ticket_get_client;
+ krb5_ticket_get_endtime;
+ krb5_ticket_get_times;
+ krb5_ticket_get_server;
+ krb5_timeofday;
+ krb5_unparse_name;
+ krb5_unparse_name_fixed;
+ krb5_unparse_name_fixed_flags;
+ krb5_unparse_name_fixed_short;
+ krb5_unparse_name_flags;
+ krb5_unparse_name_short;
+ krb5_us_timeofday;
+ krb5_vabort;
+ krb5_vabortx;
+ krb5_verify_ap_req2;
+ krb5_verify_ap_req;
+ krb5_verify_authenticator_checksum;
+ krb5_verify_checksum;
+ krb5_verify_checksum_iov;
+ krb5_verify_init_creds;
+ krb5_verify_init_creds_opt_init;
+ krb5_verify_init_creds_opt_set_ap_req_nofail;
+ krb5_verify_opt_alloc;
+ krb5_verify_opt_free;
+ krb5_verify_opt_init;
+ krb5_verify_opt_set_ccache;
+ krb5_verify_opt_set_flags;
+ krb5_verify_opt_set_keytab;
+ krb5_verify_opt_set_secure;
+ krb5_verify_opt_set_service;
+ krb5_verify_user;
+ krb5_verify_user_lrealm;
+ krb5_verify_user_opt;
+ krb5_verr;
+ krb5_verrx;
+ krb5_vlog;
+ krb5_vlog_msg;
+ krb5_vprepend_error_message;
+ krb5_vset_error_message;
+ krb5_vset_error_string;
+ krb5_vwarn;
+ krb5_vwarnx;
+ krb5_warn;
+ krb5_warnx;
+ krb5_write_message;
+ krb5_write_priv_message;
+ krb5_write_safe_message;
+ krb5_xfree;
+ krb5_cccol_cursor_new;
+ krb5_cccol_cursor_next;
+ krb5_cccol_cursor_free;
+ krb5_cccol_get_default_ccname;
+
+ # com_err error tables
+ initialize_krb5_error_table_r;
+ initialize_krb5_error_table;
+ initialize_krb_error_table_r;
+ initialize_krb_error_table;
+ initialize_heim_error_table_r;
+ initialize_heim_error_table;
+ initialize_k524_error_table_r;
+ initialize_k524_error_table;
+ initialize_k5e1_error_table_r;
+ initialize_k5e1_error_table;
+
+ # variables
+ krb5_dcc_ops;
+ krb5_mcc_ops;
+ krb5_acc_ops;
+ krb5_fcc_ops;
+ krb5_krcc_ops;
+ krb5_scc_ops;
+ krb5_kcm_ops;
+ krb5_wrfkt_ops;
+ krb5_mkt_ops;
+ krb5_akf_ops;
+ krb5_any_ops;
+ heimdal_version;
+ heimdal_long_version;
+ krb5_config_file;
+ krb5_defkeyname;
+ krb5_cc_type_api;
+ krb5_cc_type_dcc;
+ krb5_cc_type_file;
+ krb5_cc_type_memory;
+ krb5_cc_type_kcm;
+ krb5_cc_type_keyring;
+ krb5_cc_type_scc;
+
+ # shared with HDB
+ _krb5_plugin_run_f;
+ _krb5_enctype_requires_random_salt;
+
+ # Shared with GSSAPI krb5
+ _krb5_crc_init_table;
+ _krb5_crc_update;
+ _krb5_get_krbtgt;
+ _krb5_build_authenticator;
+ _krb5_kt_client_default_name;
+ _krb5_have_debug;
+ _krb5_SP800_108_HMAC_KDF;
+ _krb5_get_ad;
+
+ # Shared with GSSAPI preauth wrapper
+ _krb5_init_creds_set_gss_mechanism;
+ _krb5_init_creds_get_gss_mechanism;
+ _krb5_init_creds_set_gss_cred;
+ _krb5_init_creds_get_gss_cred;
+ _krb5_init_creds_init_gss;
+
+ # Private init_creds API
+ _krb5_init_creds_get_cred_starttime;
+ _krb5_init_creds_get_cred_endtime;
+ _krb5_init_creds_get_cred_client;
+
+ # Shared with libkadm5
+ _krb5_load_plugins;
+ _krb5_unload_plugins;
+
+ # Shared with libkdc
+ _krb5_AES_SHA1_string_to_default_iterator;
+ _krb5_AES_SHA2_string_to_default_iterator;
+ _krb5_dh_group_ok;
+ _krb5_get_host_realm_int;
+ _krb5_get_int;
+ _krb5_get_int64;
+ _krb5_pac_sign;
+ _krb5_pac_get_attributes_info;
+ _krb5_pac_get_canon_principal;
+ _krb5_kdc_pac_sign_ticket;
+ _krb5_kdc_pac_ticket_parse;
+ _kdc_tkt_insert_pac;
+ _kdc_tkt_add_if_relevant_ad;
+ _krb5_parse_moduli;
+ _krb5_pk_kdf;
+ _krb5_pk_load_id;
+ _krb5_pk_mk_ContentInfo;
+ _krb5_pk_octetstring2key;
+ _krb5_plugin_run_f;
+ _krb5_principal2principalname;
+ _krb5_principalname2krb5_principal;
+ _krb5_kdcrep2krb5_principal;
+ _krb5_ticket2krb5_principal;
+ _krb5_put_int;
+ _krb5_s4u2self_to_checksumdata;
+ _krb5_HMAC_MD5_checksum;
+ _krb5_crypto_set_flags;
+ _krb5_make_pa_enc_challenge;
+ _krb5_validate_pa_enc_challenge;
+
+ # kinit helper
+ krb5_get_init_creds_opt_set_pkinit_user_certs;
+ krb5_pk_enterprise_cert;
+ krb5_process_last_request;
+ krb5_init_creds_init;
+ krb5_init_creds_set_service;
+ krb5_init_creds_set_fast_anon_pkinit;
+ _krb5_init_creds_set_fast_anon_pkinit_optimistic;
+ krb5_init_creds_set_fast_ccache;
+ krb5_init_creds_set_keytab;
+ krb5_init_creds_set_kdc_hostname;
+ krb5_init_creds_get;
+ krb5_init_creds_get_as_reply_key;
+ krb5_init_creds_get_creds;
+ krb5_init_creds_get_error;
+ krb5_init_creds_set_password;
+ krb5_init_creds_set_sitename;
+ krb5_init_creds_step;
+ krb5_init_creds_store;
+ krb5_init_creds_store_config;
+ krb5_init_creds_free;
+ krb5_init_creds_warn_user;
+
+ # testing
+ krb5_time_abs;
+ _krb5_aes_cts_encrypt;
+ _krb5_n_fold;
+ _krb5_expand_default_cc_name;
+ _krb5_expand_path_tokensv;
+ _krb5_expand_path_tokens;
+
+ # FAST
+ _krb5_fast_cf2;
+ _krb5_fast_armor_key;
+ _krb5_fast_explicit_armor_key;
+
+ # TGS
+ _krb5_find_capath;
+ _krb5_free_capath;
+
+ local:
+ *;
+};
diff --git a/third_party/heimdal/lib/krb5/version.c b/third_party/heimdal/lib/krb5/version.c
new file mode 100644
index 0000000..302854d
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/version.c
@@ -0,0 +1,39 @@
+/*
+ * 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"
+
+/* this is just to get a version stamp in the library file */
+
+#include "version.h"
+
diff --git a/third_party/heimdal/lib/krb5/warn.c b/third_party/heimdal/lib/krb5/warn.c
new file mode 100644
index 0000000..65a7db6
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/warn.c
@@ -0,0 +1,330 @@
+/*
+ * 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.
+ */
+
+#if defined(_MSC_VER)
+# pragma warning(disable: 4646)
+# pragma warning(disable: 4716)
+#endif
+
+#include "krb5_locl.h"
+#include <err.h>
+
+static krb5_error_code _warnerr(krb5_context context, int do_errtext,
+ krb5_error_code code, int level, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 5, 0)));
+
+static krb5_error_code
+_warnerr(krb5_context context, int do_errtext,
+ krb5_error_code code, int level, const char *fmt, va_list ap)
+{
+ if (do_errtext)
+ return heim_vwarn(context ? context->hcontext : NULL, code, fmt, ap);
+ else
+ return heim_vwarnx(context ? context->hcontext : NULL, fmt, ap);
+}
+
+#define FUNC_NORET(ETEXT, CODE, LEVEL) \
+ va_list ap; \
+ va_start(ap, fmt); \
+ (void) _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
+ va_end(ap);
+
+#undef __attribute__
+#define __attribute__(X)
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_vwarn(krb5_context context, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 3, 0)))
+{
+ return heim_vwarn(context ? context->hcontext : NULL, code, fmt, ap);
+}
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_warn(krb5_context context, krb5_error_code code, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)))
+{
+ krb5_error_code ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = krb5_vwarn(context, code, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_vwarnx(krb5_context context, const char *fmt, va_list ap)
+ __attribute__ ((__format__ (__printf__, 2, 0)))
+{
+ return heim_vwarnx(context ? context->hcontext : NULL, fmt, ap);
+}
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_warnx(krb5_context context, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 2, 3)))
+{
+ krb5_error_code ret;
+ va_list ap;
+
+ va_start(ap, fmt);
+ ret = krb5_vwarnx(context, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Log a warning to the log, default stderr, include bthe error from
+ * the last failure and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verr(krb5_context context, int eval, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 0)))
+{
+ _warnerr(context, 1, code, 0, fmt, ap);
+ exit(eval);
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Log a warning to the log, default stderr, include bthe error from
+ * the last failure and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param code error code of the last error
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_err(krb5_context context, int eval, krb5_error_code code,
+ const char *fmt, ...)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 5)))
+{
+ FUNC_NORET(1, code, 0);
+ exit(eval);
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Log a warning to the log, default stderr, and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_verrx(krb5_context context, int eval, const char *fmt, va_list ap)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)))
+{
+ _warnerr(context, 0, 0, 0, fmt, ap);
+ exit(eval);
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Log a warning to the log, default stderr, and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_errx(krb5_context context, int eval, const char *fmt, ...)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)))
+{
+ FUNC_NORET(0, 0, 0);
+ exit(eval);
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Log a warning to the log, default stderr, include bthe error from
+ * the last failure and then abort.
+ *
+ * @param context A Kerberos 5 context
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_vabort(krb5_context context, krb5_error_code code,
+ const char *fmt, va_list ap)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)))
+{
+ _warnerr(context, 1, code, 0, fmt, ap);
+ abort();
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure and then abort.
+ *
+ * @param context A Kerberos 5 context
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ... arguments for format string
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_abort(krb5_context context, krb5_error_code code, const char *fmt, ...)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)))
+{
+ FUNC_NORET(1, code, 0);
+ abort();
+ UNREACHABLE(return 0);
+}
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_vabortx(krb5_context context, const char *fmt, va_list ap)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 0)))
+{
+ _warnerr(context, 0, 0, 0, fmt, ap);
+ abort();
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Log a warning to the log, default stderr, and then abort.
+ *
+ * @param context A Kerberos 5 context
+ * @param fmt printf format string of message to print
+ * @param ... arguments for format string
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_NORETURN_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_abortx(krb5_context context, const char *fmt, ...)
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)))
+{
+ FUNC_NORET(0, 0, 0);
+ abort();
+ UNREACHABLE(return 0);
+}
+
+/**
+ * Set the default logging facility.
+ *
+ * @param context A Kerberos 5 context
+ * @param fac Facility to use for logging.
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_warn_dest(krb5_context context, krb5_log_facility *fac)
+{
+ return heim_set_warn_dest(context->hcontext, fac);
+}
+
+/**
+ * Get the default logging facility.
+ *
+ * @param context A Kerberos 5 context
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION krb5_log_facility * KRB5_LIB_CALL
+krb5_get_warn_dest(krb5_context context)
+{
+ return heim_get_warn_dest(context->hcontext);
+}
diff --git a/third_party/heimdal/lib/krb5/write_message.c b/third_party/heimdal/lib/krb5/write_message.c
new file mode 100644
index 0000000..8410046
--- /dev/null
+++ b/third_party/heimdal/lib/krb5/write_message.c
@@ -0,0 +1,87 @@
+/*
+ * 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_write_message (krb5_context context,
+ krb5_pointer p_fd,
+ krb5_data *data)
+{
+ uint32_t len;
+ uint8_t buf[4];
+ int ret;
+
+ len = data->length;
+ _krb5_put_int(buf, len, 4);
+ if (krb5_net_write (context, p_fd, buf, 4) != 4
+ || krb5_net_write (context, p_fd, data->data, len) != len) {
+ ret = errno;
+ krb5_set_error_message (context, ret, "write: %s", strerror(ret));
+ return ret;
+ }
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_write_priv_message(krb5_context context,
+ krb5_auth_context ac,
+ krb5_pointer p_fd,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_data packet;
+
+ ret = krb5_mk_priv (context, ac, data, &packet, NULL);
+ if(ret)
+ return ret;
+ ret = krb5_write_message(context, p_fd, &packet);
+ krb5_data_free(&packet);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_write_safe_message(krb5_context context,
+ krb5_auth_context ac,
+ krb5_pointer p_fd,
+ krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_data packet;
+ ret = krb5_mk_safe (context, ac, data, &packet, NULL);
+ if(ret)
+ return ret;
+ ret = krb5_write_message(context, p_fd, &packet);
+ krb5_data_free(&packet);
+ return ret;
+}