d738b9
From 0bcadcebe22566a3bebd95974603b6b6593a4119 Mon Sep 17 00:00:00 2001
d738b9
From: Simo Sorce <simo@redhat.com>
d738b9
Date: Thu, 30 Mar 2017 11:27:09 -0400
d738b9
Subject: [PATCH] Add support to query the SSF of a GSS context
d738b9
d738b9
Cyrus SASL provides a Security Strength Factor number to assess the
d738b9
relative "strength" of the negotiated mechanism, and applications
d738b9
sometimes make access control decisions based on it.
d738b9
d738b9
Add a call that allows us to query the mechanism that established the
d738b9
GSS security context to ask what is the current SSF, based on the
d738b9
enctype of the session key.
d738b9
d738b9
ticket: 8569 (new)
d738b9
(cherry picked from commit 7feb7da54c0321b5a3eeb6c3797846a3cf7eda28)
d738b9
[rharwood@redhat.com: stub out GSS_KRB5_GET_CRED_IMPERSONATOR]
d738b9
---
d738b9
 src/include/k5-int.h                    |  1 +
d738b9
 src/lib/crypto/krb/crypto_int.h         |  1 +
d738b9
 src/lib/crypto/krb/enctype_util.c       | 16 ++++++++++++
d738b9
 src/lib/crypto/krb/etypes.c             | 33 ++++++++++++++-----------
d738b9
 src/lib/crypto/libk5crypto.exports      |  1 +
d738b9
 src/lib/gssapi/generic/gssapi_ext.h     | 11 +++++++++
d738b9
 src/lib/gssapi/generic/gssapi_generic.c |  9 +++++++
d738b9
 src/lib/gssapi/krb5/gssapiP_krb5.h      |  6 +++++
d738b9
 src/lib/gssapi/krb5/gssapi_krb5.c       |  4 +++
d738b9
 src/lib/gssapi/krb5/inq_context.c       | 27 ++++++++++++++++++++
d738b9
 src/lib/gssapi/libgssapi_krb5.exports   |  1 +
d738b9
 src/lib/gssapi32.def                    |  3 +++
d738b9
 src/lib/krb5_32.def                     |  3 +++
d738b9
 src/tests/gssapi/t_enctypes.c           | 14 +++++++++++
d738b9
 14 files changed, 115 insertions(+), 15 deletions(-)
d738b9
d738b9
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
d738b9
index cea644d0a..06ca2b66d 100644
d738b9
--- a/src/include/k5-int.h
d738b9
+++ b/src/include/k5-int.h
d738b9
@@ -2114,6 +2114,7 @@ krb5_get_tgs_ktypes(krb5_context, krb5_const_principal, krb5_enctype **);
d738b9
 krb5_boolean krb5_is_permitted_enctype(krb5_context, krb5_enctype);
d738b9
 
d738b9
 krb5_boolean KRB5_CALLCONV krb5int_c_weak_enctype(krb5_enctype);
d738b9
+krb5_error_code k5_enctype_to_ssf(krb5_enctype enctype, unsigned int *ssf_out);
d738b9
 
d738b9
 krb5_error_code krb5_kdc_rep_decrypt_proc(krb5_context, const krb5_keyblock *,
d738b9
                                           krb5_const_pointer, krb5_kdc_rep *);
d738b9
diff --git a/src/lib/crypto/krb/crypto_int.h b/src/lib/crypto/krb/crypto_int.h
d738b9
index d75b49c69..e5099291e 100644
d738b9
--- a/src/lib/crypto/krb/crypto_int.h
d738b9
+++ b/src/lib/crypto/krb/crypto_int.h
d738b9
@@ -111,6 +111,7 @@ struct krb5_keytypes {
d738b9
     prf_func prf;
d738b9
     krb5_cksumtype required_ctype;
d738b9
     krb5_flags flags;
d738b9
+    unsigned int ssf;
d738b9
 };
d738b9
 
d738b9
 #define ETYPE_WEAK 1
d738b9
diff --git a/src/lib/crypto/krb/enctype_util.c b/src/lib/crypto/krb/enctype_util.c
d738b9
index 0ed74bd6e..b1b40e7ec 100644
d738b9
--- a/src/lib/crypto/krb/enctype_util.c
d738b9
+++ b/src/lib/crypto/krb/enctype_util.c
d738b9
@@ -131,3 +131,19 @@ krb5_enctype_to_name(krb5_enctype enctype, krb5_boolean shortest,
d738b9
         return ENOMEM;
d738b9
     return 0;
d738b9
 }
d738b9
+
d738b9
+/* The security of a mechanism cannot be summarized with a simple integer
d738b9
+ * value, but we provide a per-enctype value for Cyrus SASL's SSF. */
d738b9
+krb5_error_code
d738b9
+k5_enctype_to_ssf(krb5_enctype enctype, unsigned int *ssf_out)
d738b9
+{
d738b9
+    const struct krb5_keytypes *ktp;
d738b9
+
d738b9
+    *ssf_out = 0;
d738b9
+
d738b9
+    ktp = find_enctype(enctype);
d738b9
+    if (ktp == NULL)
d738b9
+        return EINVAL;
d738b9
+    *ssf_out = ktp->ssf;
d738b9
+    return 0;
d738b9
+}
d738b9
diff --git a/src/lib/crypto/krb/etypes.c b/src/lib/crypto/krb/etypes.c
d738b9
index 0e5e977d4..53d4a5c79 100644
d738b9
--- a/src/lib/crypto/krb/etypes.c
d738b9
+++ b/src/lib/crypto/krb/etypes.c
d738b9
@@ -42,7 +42,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_des_string_to_key, k5_rand2key_des,
d738b9
       krb5int_des_prf,
d738b9
       CKSUMTYPE_RSA_MD5_DES,
d738b9
-      ETYPE_WEAK },
d738b9
+      ETYPE_WEAK, 56 },
d738b9
     { ENCTYPE_DES_CBC_MD4,
d738b9
       "des-cbc-md4", { 0 }, "DES cbc mode with RSA-MD4",
d738b9
       &krb5int_enc_des, &krb5int_hash_md4,
d738b9
@@ -51,7 +51,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_des_string_to_key, k5_rand2key_des,
d738b9
       krb5int_des_prf,
d738b9
       CKSUMTYPE_RSA_MD4_DES,
d738b9
-      ETYPE_WEAK },
d738b9
+      ETYPE_WEAK, 56 },
d738b9
     { ENCTYPE_DES_CBC_MD5,
d738b9
       "des-cbc-md5", { "des" }, "DES cbc mode with RSA-MD5",
d738b9
       &krb5int_enc_des, &krb5int_hash_md5,
d738b9
@@ -60,7 +60,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_des_string_to_key, k5_rand2key_des,
d738b9
       krb5int_des_prf,
d738b9
       CKSUMTYPE_RSA_MD5_DES,
d738b9
-      ETYPE_WEAK },
d738b9
+      ETYPE_WEAK, 56 },
d738b9
     { ENCTYPE_DES_CBC_RAW,
d738b9
       "des-cbc-raw", { 0 }, "DES cbc mode raw",
d738b9
       &krb5int_enc_des, NULL,
d738b9
@@ -69,7 +69,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_des_string_to_key, k5_rand2key_des,
d738b9
       krb5int_des_prf,
d738b9
       0,
d738b9
-      ETYPE_WEAK },
d738b9
+      ETYPE_WEAK, 56 },
d738b9
     { ENCTYPE_DES3_CBC_RAW,
d738b9
       "des3-cbc-raw", { 0 }, "Triple DES cbc mode raw",
d738b9
       &krb5int_enc_des3, NULL,
d738b9
@@ -78,7 +78,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_dk_string_to_key, k5_rand2key_des3,
d738b9
       NULL, /*PRF*/
d738b9
       0,
d738b9
-      ETYPE_WEAK },
d738b9
+      ETYPE_WEAK, 112 },
d738b9
 
d738b9
     { ENCTYPE_DES3_CBC_SHA1,
d738b9
       "des3-cbc-sha1", { "des3-hmac-sha1", "des3-cbc-sha1-kd" },
d738b9
@@ -89,7 +89,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_dk_string_to_key, k5_rand2key_des3,
d738b9
       krb5int_dk_prf,
d738b9
       CKSUMTYPE_HMAC_SHA1_DES3,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 112 },
d738b9
 
d738b9
     { ENCTYPE_DES_HMAC_SHA1,
d738b9
       "des-hmac-sha1", { 0 }, "DES with HMAC/sha1",
d738b9
@@ -99,7 +99,10 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_dk_string_to_key, k5_rand2key_des,
d738b9
       NULL, /*PRF*/
d738b9
       0,
d738b9
-      ETYPE_WEAK },
d738b9
+      ETYPE_WEAK, 56 },
d738b9
+
d738b9
+    /* rc4-hmac uses a 128-bit key, but due to weaknesses in the RC4 cipher, we
d738b9
+     * consider its strength degraded and assign it an SSF value of 64. */
d738b9
     { ENCTYPE_ARCFOUR_HMAC,
d738b9
       "arcfour-hmac", { "rc4-hmac", "arcfour-hmac-md5" },
d738b9
       "ArcFour with HMAC/md5",
d738b9
@@ -110,7 +113,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_arcfour_decrypt, krb5int_arcfour_string_to_key,
d738b9
       k5_rand2key_direct, krb5int_arcfour_prf,
d738b9
       CKSUMTYPE_HMAC_MD5_ARCFOUR,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 64 },
d738b9
     { ENCTYPE_ARCFOUR_HMAC_EXP,
d738b9
       "arcfour-hmac-exp", { "rc4-hmac-exp", "arcfour-hmac-md5-exp" },
d738b9
       "Exportable ArcFour with HMAC/md5",
d738b9
@@ -121,7 +124,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_arcfour_decrypt, krb5int_arcfour_string_to_key,
d738b9
       k5_rand2key_direct, krb5int_arcfour_prf,
d738b9
       CKSUMTYPE_HMAC_MD5_ARCFOUR,
d738b9
-      ETYPE_WEAK
d738b9
+      ETYPE_WEAK, 40
d738b9
     },
d738b9
 
d738b9
     { ENCTYPE_AES128_CTS_HMAC_SHA1_96,
d738b9
@@ -133,7 +136,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_aes_string_to_key, k5_rand2key_direct,
d738b9
       krb5int_dk_prf,
d738b9
       CKSUMTYPE_HMAC_SHA1_96_AES128,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 128 },
d738b9
     { ENCTYPE_AES256_CTS_HMAC_SHA1_96,
d738b9
       "aes256-cts-hmac-sha1-96", { "aes256-cts", "aes256-sha1" },
d738b9
       "AES-256 CTS mode with 96-bit SHA-1 HMAC",
d738b9
@@ -143,7 +146,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_aes_string_to_key, k5_rand2key_direct,
d738b9
       krb5int_dk_prf,
d738b9
       CKSUMTYPE_HMAC_SHA1_96_AES256,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 256 },
d738b9
 
d738b9
     { ENCTYPE_CAMELLIA128_CTS_CMAC,
d738b9
       "camellia128-cts-cmac", { "camellia128-cts" },
d738b9
@@ -155,7 +158,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_camellia_string_to_key, k5_rand2key_direct,
d738b9
       krb5int_dk_cmac_prf,
d738b9
       CKSUMTYPE_CMAC_CAMELLIA128,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 128 },
d738b9
     { ENCTYPE_CAMELLIA256_CTS_CMAC,
d738b9
       "camellia256-cts-cmac", { "camellia256-cts" },
d738b9
       "Camellia-256 CTS mode with CMAC",
d738b9
@@ -166,7 +169,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_camellia_string_to_key, k5_rand2key_direct,
d738b9
       krb5int_dk_cmac_prf,
d738b9
       CKSUMTYPE_CMAC_CAMELLIA256,
d738b9
-      0 /*flags */ },
d738b9
+      0 /*flags */, 256 },
d738b9
 
d738b9
     { ENCTYPE_AES128_CTS_HMAC_SHA256_128,
d738b9
       "aes128-cts-hmac-sha256-128", { "aes128-sha2" },
d738b9
@@ -177,7 +180,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_aes2_string_to_key, k5_rand2key_direct,
d738b9
       krb5int_aes2_prf,
d738b9
       CKSUMTYPE_HMAC_SHA256_128_AES128,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 128 },
d738b9
     { ENCTYPE_AES256_CTS_HMAC_SHA384_192,
d738b9
       "aes256-cts-hmac-sha384-192", { "aes256-sha2" },
d738b9
       "AES-256 CTS mode with 192-bit SHA-384 HMAC",
d738b9
@@ -187,7 +190,7 @@ const struct krb5_keytypes krb5int_enctypes_list[] = {
d738b9
       krb5int_aes2_string_to_key, k5_rand2key_direct,
d738b9
       krb5int_aes2_prf,
d738b9
       CKSUMTYPE_HMAC_SHA384_192_AES256,
d738b9
-      0 /*flags*/ },
d738b9
+      0 /*flags*/, 256 },
d738b9
 };
d738b9
 
d738b9
 const int krb5int_enctypes_length =
d738b9
diff --git a/src/lib/crypto/libk5crypto.exports b/src/lib/crypto/libk5crypto.exports
d738b9
index 447e45644..82eb5f30c 100644
d738b9
--- a/src/lib/crypto/libk5crypto.exports
d738b9
+++ b/src/lib/crypto/libk5crypto.exports
d738b9
@@ -108,3 +108,4 @@ krb5int_nfold
d738b9
 k5_allow_weak_pbkdf2iter
d738b9
 krb5_c_prfplus
d738b9
 krb5_c_derive_prfplus
d738b9
+k5_enctype_to_ssf
d738b9
diff --git a/src/lib/gssapi/generic/gssapi_ext.h b/src/lib/gssapi/generic/gssapi_ext.h
d738b9
index 9ad44216d..9d3a7e736 100644
d738b9
--- a/src/lib/gssapi/generic/gssapi_ext.h
d738b9
+++ b/src/lib/gssapi/generic/gssapi_ext.h
d738b9
@@ -575,4 +575,15 @@ gss_import_cred(
d738b9
 }
d738b9
 #endif
d738b9
 
d738b9
+/*
d738b9
+ * When used with gss_inquire_sec_context_by_oid(), return a buffer set with
d738b9
+ * the first member containing an unsigned 32-bit integer in network byte
d738b9
+ * order.  This is the Security Strength Factor (SSF) associated with the
d738b9
+ * secure channel established by the security context.  NOTE: This value is
d738b9
+ * made available solely as an indication for use by APIs like Cyrus SASL that
d738b9
+ * classify the strength of a secure channel via this number.  The strength of
d738b9
+ * a channel cannot necessarily be represented by a simple number.
d738b9
+ */
d738b9
+GSS_DLLIMP extern gss_OID GSS_C_SEC_CONTEXT_SASL_SSF;
d738b9
+
d738b9
 #endif /* GSSAPI_EXT_H_ */
d738b9
diff --git a/src/lib/gssapi/generic/gssapi_generic.c b/src/lib/gssapi/generic/gssapi_generic.c
d738b9
index 5496aa335..fa144c2bf 100644
d738b9
--- a/src/lib/gssapi/generic/gssapi_generic.c
d738b9
+++ b/src/lib/gssapi/generic/gssapi_generic.c
d738b9
@@ -157,6 +157,13 @@ static const gss_OID_desc const_oids[] = {
d738b9
     {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x19"},
d738b9
     {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x1a"},
d738b9
     {7, (void *)"\x2b\x06\x01\x05\x05\x0d\x1b"},
d738b9
+
d738b9
+    /*
d738b9
+     * GSS_SEC_CONTEXT_SASL_SSF_OID 1.2.840.113554.1.2.2.5.15
d738b9
+     * iso(1) member-body(2) United States(840) mit(113554)
d738b9
+     * infosys(1) gssapi(2) krb5(2) krb5-gssapi-ext(5) sasl-ssf(15)
d738b9
+     */
d738b9
+    {11, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0f"},
d738b9
 };
d738b9
 
d738b9
 /* Here are the constants which point to the static structure above.
d738b9
@@ -218,6 +225,8 @@ GSS_DLLIMP gss_const_OID GSS_C_MA_PFS               = oids+33;
d738b9
 GSS_DLLIMP gss_const_OID GSS_C_MA_COMPRESS          = oids+34;
d738b9
 GSS_DLLIMP gss_const_OID GSS_C_MA_CTX_TRANS         = oids+35;
d738b9
 
d738b9
+GSS_DLLIMP gss_OID GSS_C_SEC_CONTEXT_SASL_SSF = oids+36;
d738b9
+
d738b9
 static gss_OID_set_desc gss_ma_known_attrs_desc = { 27, oids+9 };
d738b9
 gss_OID_set gss_ma_known_attrs = &gss_ma_known_attrs_desc;
d738b9
 
d738b9
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
d738b9
index d7bdef7e2..ef030707e 100644
d738b9
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
d738b9
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
d738b9
@@ -1144,6 +1144,12 @@ gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *,
d738b9
                                               const gss_OID,
d738b9
                                               gss_buffer_set_t *);
d738b9
 
d738b9
+#define GET_SEC_CONTEXT_SASL_SSF_OID_LENGTH 11
d738b9
+#define GET_SEC_CONTEXT_SASL_SSF_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0f"
d738b9
+OM_uint32
d738b9
+gss_krb5int_sec_context_sasl_ssf(OM_uint32 *, const gss_ctx_id_t,
d738b9
+                                 const gss_OID, gss_buffer_set_t *);
d738b9
+
d738b9
 #define GSS_KRB5_IMPORT_CRED_OID_LENGTH 11
d738b9
 #define GSS_KRB5_IMPORT_CRED_OID "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x0d"
d738b9
 
d738b9
diff --git a/src/lib/gssapi/krb5/gssapi_krb5.c b/src/lib/gssapi/krb5/gssapi_krb5.c
d738b9
index 99092ccab..de4131980 100644
d738b9
--- a/src/lib/gssapi/krb5/gssapi_krb5.c
d738b9
+++ b/src/lib/gssapi/krb5/gssapi_krb5.c
d738b9
@@ -352,6 +352,10 @@ static struct {
d738b9
     {
d738b9
         {GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID},
d738b9
         gss_krb5int_extract_authtime_from_sec_context
d738b9
+    },
d738b9
+    {
d738b9
+        {GET_SEC_CONTEXT_SASL_SSF_OID_LENGTH, GET_SEC_CONTEXT_SASL_SSF_OID},
d738b9
+        gss_krb5int_sec_context_sasl_ssf
d738b9
     }
d738b9
 };
d738b9
 
d738b9
diff --git a/src/lib/gssapi/krb5/inq_context.c b/src/lib/gssapi/krb5/inq_context.c
d738b9
index 9024b3c7e..d2e466e60 100644
d738b9
--- a/src/lib/gssapi/krb5/inq_context.c
d738b9
+++ b/src/lib/gssapi/krb5/inq_context.c
d738b9
@@ -310,3 +310,30 @@ gss_krb5int_extract_authtime_from_sec_context(OM_uint32 *minor_status,
d738b9
 
d738b9
     return generic_gss_add_buffer_set_member(minor_status, &rep, data_set);
d738b9
 }
d738b9
+
d738b9
+OM_uint32
d738b9
+gss_krb5int_sec_context_sasl_ssf(OM_uint32 *minor_status,
d738b9
+                                 const gss_ctx_id_t context_handle,
d738b9
+                                 const gss_OID desired_object,
d738b9
+                                 gss_buffer_set_t *data_set)
d738b9
+{
d738b9
+    krb5_gss_ctx_id_rec *ctx;
d738b9
+    krb5_key key;
d738b9
+    krb5_error_code code;
d738b9
+    gss_buffer_desc ssfbuf;
d738b9
+    unsigned int ssf;
d738b9
+    uint8_t buf[4];
d738b9
+
d738b9
+    ctx = (krb5_gss_ctx_id_rec *)context_handle;
d738b9
+    key = ctx->have_acceptor_subkey ? ctx->acceptor_subkey : ctx->subkey;
d738b9
+
d738b9
+    code = k5_enctype_to_ssf(key->keyblock.enctype, &ssf;;
d738b9
+    if (code)
d738b9
+        return GSS_S_FAILURE;
d738b9
+
d738b9
+    store_32_be(ssf, buf);
d738b9
+    ssfbuf.value = buf;
d738b9
+    ssfbuf.length = sizeof(buf);
d738b9
+
d738b9
+    return generic_gss_add_buffer_set_member(minor_status, &ssfbuf, data_set);
d738b9
+}
d738b9
diff --git a/src/lib/gssapi/libgssapi_krb5.exports b/src/lib/gssapi/libgssapi_krb5.exports
d738b9
index 9facb3f42..936540e41 100644
d738b9
--- a/src/lib/gssapi/libgssapi_krb5.exports
d738b9
+++ b/src/lib/gssapi/libgssapi_krb5.exports
d738b9
@@ -37,6 +37,7 @@ GSS_C_MA_CBINDINGS
d738b9
 GSS_C_MA_PFS
d738b9
 GSS_C_MA_COMPRESS
d738b9
 GSS_C_MA_CTX_TRANS
d738b9
+GSS_C_SEC_CONTEXT_SASL_SSF
d738b9
 gss_accept_sec_context
d738b9
 gss_acquire_cred
d738b9
 gss_acquire_cred_with_password
d738b9
diff --git a/src/lib/gssapi32.def b/src/lib/gssapi32.def
d738b9
index 362b9bce8..dff057754 100644
d738b9
--- a/src/lib/gssapi32.def
d738b9
+++ b/src/lib/gssapi32.def
d738b9
@@ -182,3 +182,6 @@ EXPORTS
d738b9
 	gss_verify_mic_iov				@146
d738b9
 ; Added in 1.14
d738b9
 	GSS_KRB5_CRED_NO_CI_FLAGS_X			@147	DATA
d738b9
+; Added in 1.16
d738b9
+;	GSS_KRB5_GET_CRED_IMPERSONATOR			@148	DATA
d738b9
+	GSS_C_SEC_CONTEXT_SASL_SSF			@149	DATA
d738b9
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
d738b9
index e5b560dfc..f7b428e16 100644
d738b9
--- a/src/lib/krb5_32.def
d738b9
+++ b/src/lib/krb5_32.def
d738b9
@@ -470,3 +470,6 @@ EXPORTS
d738b9
 	krb5_get_init_creds_opt_set_pac_request		@435
d738b9
 	krb5int_trace					@436 ; PRIVATE GSSAPI
d738b9
 	krb5_expand_hostname				@437
d738b9
+
d738b9
+; new in 1.16
d738b9
+	k5_enctype_to_ssf				@438 ; PRIVATE GSSAPI
d738b9
diff --git a/src/tests/gssapi/t_enctypes.c b/src/tests/gssapi/t_enctypes.c
d738b9
index a2ad18f47..3fd31e2f8 100644
d738b9
--- a/src/tests/gssapi/t_enctypes.c
d738b9
+++ b/src/tests/gssapi/t_enctypes.c
d738b9
@@ -32,6 +32,7 @@
d738b9
 
d738b9
 #include "k5-int.h"
d738b9
 #include "common.h"
d738b9
+#include "gssapi_ext.h"
d738b9
 
d738b9
 /*
d738b9
  * This test program establishes contexts with the krb5 mech, the default
d738b9
@@ -86,6 +87,9 @@ main(int argc, char *argv[])
d738b9
     gss_krb5_lucid_context_v1_t *ilucid, *alucid;
d738b9
     gss_krb5_rfc1964_keydata_t *i1964, *a1964;
d738b9
     gss_krb5_cfx_keydata_t *icfx, *acfx;
d738b9
+    gss_buffer_set_t bufset = GSS_C_NO_BUFFER_SET;
d738b9
+    gss_OID ssf_oid = GSS_C_SEC_CONTEXT_SASL_SSF;
d738b9
+    unsigned int ssf;
d738b9
     size_t count;
d738b9
     void *lptr;
d738b9
     int c;
d738b9
@@ -139,6 +143,16 @@ main(int argc, char *argv[])
d738b9
     establish_contexts(&mech_krb5, icred, acred, tname, flags, &ictx, &actx,
d738b9
                        NULL, NULL, NULL);
d738b9
 
d738b9
+    /* Query the SSF value and range-check the result. */
d738b9
+    major = gss_inquire_sec_context_by_oid(&minor, ictx, ssf_oid, &bufset);
d738b9
+    check_gsserr("gss_inquire_sec_context_by_oid(ssf)", major, minor);
d738b9
+    if (bufset->elements[0].length != 4)
d738b9
+        errout("SSF buffer has unexpected length");
d738b9
+    ssf = load_32_be(bufset->elements[0].value);
d738b9
+    if (ssf < 56 || ssf > 256)
d738b9
+        errout("SSF value not within acceptable range (56-256)");
d738b9
+    (void)gss_release_buffer_set(&minor, &bufset);
d738b9
+
d738b9
     /* Export to lucid contexts. */
d738b9
     major = gss_krb5_export_lucid_sec_context(&minor, &ictx, 1, &lptr);
d738b9
     check_gsserr("gss_export_lucid_sec_context(initiator)", major, minor);