+
+/* wrapper around gss_krb5_import_cred() that prefers to use gss_acquire_cred_from()
+ * if this GSSAPI extension is available. gss_acquire_cred_from() is properly
+ * interposed by GSSPROXY while gss_krb5_import_cred() is not.
+ *
+ * This wrapper requires a proper krb5_context to resolve ccache name.
+ * All gss_krb5_import_cred() callers in Samba already have krb5_context available. */
+uint32_t smb_gss_krb5_import_cred(uint32_t *minor_status, krb5_context ctx,
+ krb5_ccache id, krb5_principal keytab_principal,
+ krb5_keytab keytab, gss_cred_id_t *cred)
+{
+ uint32_t major_status = 0;
+
+#if HAVE_GSS_ACQUIRE_CRED_FROM
+ uint32_t minor = 0;
+ gss_key_value_element_desc ccache_element = {
+ .key = "ccache",
+ .value = NULL,
+ };
+
+ gss_key_value_element_desc keytab_element = {
+ .key = "keytab",
+ .value = NULL,
+ };
+
+ gss_key_value_element_desc elements[2];
+
+ gss_key_value_set_desc cred_store = {
+ .elements = &ccache_element,
+ .count = 1,
+ };
+
+ gss_OID_set mech_set = GSS_C_NO_OID_SET;
+ gss_cred_usage_t cred_usage = GSS_C_INITIATE;
+ gss_name_t name = NULL;
+ gss_buffer_desc pr_name = {
+ .value = NULL,
+ .length = 0,
+ };
+
+ if (id != NULL) {
+ major_status = krb5_cc_get_full_name(ctx,
+ id,
+ discard_const(&ccache_element.value));
+ if (major_status != 0) {
+ return major_status;
+ }
+ }
+
+ if (keytab != NULL) {
+ keytab_element.value = malloc(4096);
+ if (!keytab_element.value) {
+ return ENOMEM;
+ }
+ major_status = krb5_kt_get_name(ctx,
+ keytab,
+ discard_const(keytab_element.value), 4096);
+ if (major_status != 0) {
+ free(discard_const(keytab_element.value));
+ return major_status;
+ }
+ cred_usage = GSS_C_ACCEPT;
+ cred_store.elements = &keytab_element;
+
+ if (keytab_principal != NULL) {
+ major_status = krb5_unparse_name(ctx, keytab_principal, (char**)&pr_name.value);
+ if (major_status != 0) {
+ free(discard_const(keytab_element.value));
+ return major_status;
+ }
+ pr_name.length = strlen(pr_name.value);
+
+ major_status = gss_import_name(minor_status,
+ &pr_name,
+ discard_const(GSS_KRB5_NT_PRINCIPAL_NAME),
+ &name);
+ if (major_status != 0) {
+ krb5_free_unparsed_name(ctx, pr_name.value);
+ free(discard_const(keytab_element.value));
+ return major_status;
+ }
+ }
+ }
+
+ if (id != NULL && keytab != NULL) {
+ elements[0] = ccache_element;
+ elements[1] = keytab_element;
+
+ cred_store.elements = elements;
+ cred_store.count = 2;
+ cred_usage = GSS_C_BOTH;
+ }
+
+ major_status = gss_acquire_cred_from(minor_status,
+ name,
+ 0,
+ mech_set,
+ cred_usage,
+ &cred_store,
+ cred,
+ NULL,
+ NULL);
+
+ if (pr_name.value != NULL) {
+ (void)gss_release_name(&minor, &name);
+ krb5_free_unparsed_name(ctx, pr_name.value);
+ }
+ if (keytab_element.value != NULL) {
+ free(discard_const(keytab_element.value));
+ }
+ krb5_free_string(ctx, discard_const(ccache_element.value));
+#else
+ major_status = gss_krb5_import_cred(minor_status,
+ id,
+ keytab_principal,
+ keytab, cred);
+#endif
+ return major_status;
+}
+
+