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