int main(int argc, char **argv) { int optind = 0; OM_uint32 min_stat, maj_stat; gss_ctx_id_t cctx, sctx; void *ctx; gss_OID nameoid, mechoid, actual_mech, actual_mech2; gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL; gss_name_t cname = GSS_C_NO_NAME; gss_buffer_desc credential_data = GSS_C_EMPTY_BUFFER; setprogname(argv[0]); init_o2n(); if (krb5_init_context(&context)) errx(1, "krb5_init_context"); cctx = sctx = GSS_C_NO_CONTEXT; if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind)) usage(1); if (help_flag) usage (0); if(version_flag){ print_version(NULL); exit(0); } argc -= optind; argv += optind; if (argc != 1) usage(1); if (dns_canon_flag != -1) gsskrb5_set_dns_canonicalize(dns_canon_flag); if (type_string == NULL) nameoid = GSS_C_NT_HOSTBASED_SERVICE; else if (strcmp(type_string, "hostbased-service") == 0) nameoid = GSS_C_NT_HOSTBASED_SERVICE; else if (strcmp(type_string, "krb5-principal-name") == 0) nameoid = GSS_KRB5_NT_PRINCIPAL_NAME; else errx(1, "%s not suppported", type_string); if (mech_string == NULL) mechoid = GSS_KRB5_MECHANISM; else mechoid = string_to_oid(mech_string); if (gsskrb5_acceptor_identity) { maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity); if (maj_stat) errx(1, "gsskrb5_acceptor_identity: %s", gssapi_err(maj_stat, 0, GSS_C_NO_OID)); } if (client_password) { credential_data.value = client_password; credential_data.length = strlen(client_password); } if (client_name) { gss_buffer_desc cn; cn.value = client_name; cn.length = strlen(client_name); maj_stat = gss_import_name(&min_stat, &cn, GSS_C_NT_USER_NAME, &cname); if (maj_stat) errx(1, "gss_import_name: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } if (client_password) { maj_stat = gss_acquire_cred_with_password(&min_stat, cname, &credential_data, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred, NULL, NULL); if (GSS_ERROR(maj_stat)) errx(1, "gss_acquire_cred_with_password: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } else { maj_stat = gss_acquire_cred(&min_stat, cname, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &client_cred, NULL, NULL); if (GSS_ERROR(maj_stat)) errx(1, "gss_acquire_cred: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } if (limit_enctype_string) { krb5_error_code ret; ret = krb5_string_to_enctype(context, limit_enctype_string, &limit_enctype); if (ret) krb5_err(context, 1, ret, "krb5_string_to_enctype"); } if (limit_enctype) { if (client_cred == NULL) errx(1, "client_cred missing"); maj_stat = gss_krb5_set_allowable_enctypes(&min_stat, client_cred, 1, &limit_enctype); if (maj_stat) errx(1, "gss_krb5_set_allowable_enctypes: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); } loop(mechoid, nameoid, argv[0], client_cred, &sctx, &cctx, &actual_mech, &deleg_cred); if (verbose_flag) printf("resulting mech: %s\n", oid_to_string(actual_mech)); if (ret_mech_string) { gss_OID retoid; retoid = string_to_oid(ret_mech_string); if (gss_oid_equal(retoid, actual_mech) == 0) errx(1, "actual_mech mech is not the expected type %s", ret_mech_string); } /* XXX should be actual_mech */ if (gss_oid_equal(mechoid, GSS_KRB5_MECHANISM)) { time_t time; gss_buffer_desc authz_data; gss_buffer_desc in, out1, out2; krb5_keyblock *keyblock, *keyblock2; krb5_timestamp now; krb5_error_code ret; ret = krb5_timeofday(context, &now); if (ret) errx(1, "krb5_timeofday failed"); /* client */ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &cctx, 1, /* version */ &ctx); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_krb5_export_lucid_sec_context failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); maj_stat = gss_krb5_free_lucid_sec_context(&maj_stat, ctx); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_krb5_free_lucid_sec_context failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); /* server */ maj_stat = gss_krb5_export_lucid_sec_context(&min_stat, &sctx, 1, /* version */ &ctx); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_krb5_export_lucid_sec_context failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); maj_stat = gss_krb5_free_lucid_sec_context(&min_stat, ctx); if (maj_stat != GSS_S_COMPLETE) errx(1, "gss_krb5_free_lucid_sec_context failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat, sctx, &time); if (maj_stat != GSS_S_COMPLETE) errx(1, "gsskrb5_extract_authtime_from_sec_context failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); if (time > now) errx(1, "gsskrb5_extract_authtime_from_sec_context failed: " "time authtime is before now: %ld %ld", (long)time, (long)now); maj_stat = gsskrb5_extract_service_keyblock(&min_stat, sctx, &keyblock); if (maj_stat != GSS_S_COMPLETE) errx(1, "gsskrb5_export_service_keyblock failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); krb5_free_keyblock(context, keyblock); maj_stat = gsskrb5_get_subkey(&min_stat, sctx, &keyblock); if (maj_stat != GSS_S_COMPLETE && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY))) errx(1, "gsskrb5_get_subkey server failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); if (maj_stat != GSS_S_COMPLETE) keyblock = NULL; else if (limit_enctype && keyblock->keytype != limit_enctype) errx(1, "gsskrb5_get_subkey wrong enctype"); maj_stat = gsskrb5_get_subkey(&min_stat, cctx, &keyblock2); if (maj_stat != GSS_S_COMPLETE && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY))) errx(1, "gsskrb5_get_subkey client failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); if (maj_stat != GSS_S_COMPLETE) keyblock2 = NULL; else if (limit_enctype && keyblock->keytype != limit_enctype) errx(1, "gsskrb5_get_subkey wrong enctype"); if (keyblock || keyblock2) { if (keyblock == NULL) errx(1, "server missing token keyblock"); if (keyblock2 == NULL) errx(1, "client missing token keyblock"); if (keyblock->keytype != keyblock2->keytype) errx(1, "enctype mismatch"); if (keyblock->keyvalue.length != keyblock2->keyvalue.length) errx(1, "key length mismatch"); if (memcmp(keyblock->keyvalue.data, keyblock2->keyvalue.data, keyblock2->keyvalue.length) != 0) errx(1, "key data mismatch"); } if (session_enctype_string) { krb5_enctype enctype; ret = krb5_string_to_enctype(context, session_enctype_string, &enctype); if (ret) krb5_err(context, 1, ret, "krb5_string_to_enctype"); if (enctype != keyblock->keytype) errx(1, "keytype is not the expected %d != %d", (int)enctype, (int)keyblock2->keytype); } if (keyblock) krb5_free_keyblock(context, keyblock); if (keyblock2) krb5_free_keyblock(context, keyblock2); maj_stat = gsskrb5_get_initiator_subkey(&min_stat, sctx, &keyblock); if (maj_stat != GSS_S_COMPLETE && (!(maj_stat == GSS_S_FAILURE && min_stat == GSS_KRB5_S_KG_NO_SUBKEY))) errx(1, "gsskrb5_get_initiator_subkey failed: %s", gssapi_err(maj_stat, min_stat, actual_mech)); if (maj_stat == GSS_S_COMPLETE) { if (limit_enctype && keyblock->keytype != limit_enctype) errx(1, "gsskrb5_get_initiator_subkey wrong enctype"); krb5_free_keyblock(context, keyblock); } maj_stat = gsskrb5_extract_authz_data_from_sec_context(&min_stat, sctx, 128, &authz_data); if (maj_stat == GSS_S_COMPLETE) gss_release_buffer(&min_stat, &authz_data); memset(&out1, 0, sizeof(out1)); memset(&out2, 0, sizeof(out2)); in.value = "foo"; in.length = 3; gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in, 100, &out1); gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_FULL, &in, 100, &out2); if (out1.length != out2.length) errx(1, "prf len mismatch"); if (memcmp(out1.value, out2.value, out1.length) != 0) errx(1, "prf data mismatch"); gss_release_buffer(&min_stat, &out1); gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_FULL, &in, 100, &out1); if (out1.length != out2.length) errx(1, "prf len mismatch"); if (memcmp(out1.value, out2.value, out1.length) != 0) errx(1, "prf data mismatch"); gss_release_buffer(&min_stat, &out1); gss_release_buffer(&min_stat, &out2); in.value = "bar"; in.length = 3; gss_pseudo_random(&min_stat, sctx, GSS_C_PRF_KEY_PARTIAL, &in, 100, &out1); gss_pseudo_random(&min_stat, cctx, GSS_C_PRF_KEY_PARTIAL, &in, 100, &out2); if (out1.length != out2.length) errx(1, "prf len mismatch"); if (memcmp(out1.value, out2.value, out1.length) != 0) errx(1, "prf data mismatch"); gss_release_buffer(&min_stat, &out1); gss_release_buffer(&min_stat, &out2); wrapunwrap_flag = 1; getverifymic_flag = 1; } if (wrapunwrap_flag) { wrapunwrap(cctx, sctx, 0, actual_mech); wrapunwrap(cctx, sctx, 1, actual_mech); wrapunwrap(sctx, cctx, 0, actual_mech); wrapunwrap(sctx, cctx, 1, actual_mech); } if (iov_flag) { wrapunwrap_iov(cctx, sctx, 0, actual_mech); wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech); wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|USE_SIGN_ONLY|FORCE_IOV, actual_mech); /* works */ wrapunwrap_iov(cctx, sctx, 0, actual_mech); wrapunwrap_iov(cctx, sctx, FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY, actual_mech); wrapunwrap_iov(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY, actual_mech); wrapunwrap_iov(cctx, sctx, USE_HEADER_ONLY|FORCE_IOV, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY, actual_mech); wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech); } if (getverifymic_flag) { getverifymic(cctx, sctx, actual_mech); getverifymic(cctx, sctx, actual_mech); getverifymic(sctx, cctx, actual_mech); getverifymic(sctx, cctx, actual_mech); } gss_delete_sec_context(&min_stat, &cctx, NULL); gss_delete_sec_context(&min_stat, &sctx, NULL); if (deleg_cred != GSS_C_NO_CREDENTIAL) { gss_cred_id_t cred2 = GSS_C_NO_CREDENTIAL; gss_buffer_desc cb; if (verbose_flag) printf("checking actual mech (%s) on delegated cred\n", oid_to_string(actual_mech)); loop(actual_mech, nameoid, argv[0], deleg_cred, &sctx, &cctx, &actual_mech2, &cred2); gss_delete_sec_context(&min_stat, &cctx, NULL); gss_delete_sec_context(&min_stat, &sctx, NULL); gss_release_cred(&min_stat, &cred2); /* try again using SPNEGO */ if (verbose_flag) printf("checking spnego on delegated cred\n"); loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], deleg_cred, &sctx, &cctx, &actual_mech2, &cred2); gss_delete_sec_context(&min_stat, &cctx, NULL); gss_delete_sec_context(&min_stat, &sctx, NULL); gss_release_cred(&min_stat, &cred2); /* check export/import */ if (ei_flag) { maj_stat = gss_export_cred(&min_stat, deleg_cred, &cb); if (maj_stat != GSS_S_COMPLETE) errx(1, "export failed: %s", gssapi_err(maj_stat, min_stat, NULL)); maj_stat = gss_import_cred(&min_stat, &cb, &cred2); if (maj_stat != GSS_S_COMPLETE) errx(1, "import failed: %s", gssapi_err(maj_stat, min_stat, NULL)); gss_release_buffer(&min_stat, &cb); gss_release_cred(&min_stat, &deleg_cred); if (verbose_flag) printf("checking actual mech (%s) on export/imported cred\n", oid_to_string(actual_mech)); loop(actual_mech, nameoid, argv[0], cred2, &sctx, &cctx, &actual_mech2, &deleg_cred); gss_release_cred(&min_stat, &deleg_cred); gss_delete_sec_context(&min_stat, &cctx, NULL); gss_delete_sec_context(&min_stat, &sctx, NULL); /* try again using SPNEGO */ if (verbose_flag) printf("checking SPNEGO on export/imported cred\n"); loop(GSS_SPNEGO_MECHANISM, nameoid, argv[0], cred2, &sctx, &cctx, &actual_mech2, &deleg_cred); gss_release_cred(&min_stat, &deleg_cred); gss_delete_sec_context(&min_stat, &cctx, NULL); gss_delete_sec_context(&min_stat, &sctx, NULL); gss_release_cred(&min_stat, &cred2); } else { gss_release_cred(&min_stat, &deleg_cred); } } empty_release(); krb5_free_context(context); return 0; }
NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx, gss_ctx_id_t gssapi_context, gss_name_t gss_client_name, DATA_BLOB *pac_blob) { NTSTATUS status; OM_uint32 gss_maj, gss_min; #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE /* * gss_get_name_attribute() in MIT krb5 1.10.0 can return unintialized pac_display_buffer * and later gss_release_buffer() will crash on attempting to release it. * * So always initialize the buffer descriptors. * * See following links for more details: * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=658514 * http://krbdev.mit.edu/rt/Ticket/Display.html?user=guest&pass=guest&id=7087 */ gss_buffer_desc pac_buffer = { .value = NULL, .length = 0 }; gss_buffer_desc pac_display_buffer = { .value = NULL, .length = 0 }; gss_buffer_desc pac_name = { .value = discard_const("urn:mspac:"), .length = sizeof("urn:mspac:")-1 }; int more = -1; int authenticated = false; int complete = false; gss_maj = gss_get_name_attribute( &gss_min, gss_client_name, &pac_name, &authenticated, &complete, &pac_buffer, &pac_display_buffer, &more); if (gss_maj != 0) { gss_OID oid = discard_const(gss_mech_krb5); DBG_NOTICE("obtaining PAC via GSSAPI gss_get_name_attribute " "failed: %s\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, oid)); return NT_STATUS_ACCESS_DENIED; } else if (authenticated && complete) { /* The PAC blob is returned directly */ *pac_blob = data_blob_talloc(mem_ctx, pac_buffer.value, pac_buffer.length); if (!pac_blob->data) { status = NT_STATUS_NO_MEMORY; } else { status = NT_STATUS_OK; } gss_maj = gss_release_buffer(&gss_min, &pac_buffer); gss_maj = gss_release_buffer(&gss_min, &pac_display_buffer); return status; } else { DEBUG(0, ("obtaining PAC via GSSAPI failed: authenticated: %s, complete: %s, more: %s\n", authenticated ? "true" : "false", complete ? "true" : "false", more ? "true" : "false")); return NT_STATUS_ACCESS_DENIED; } #elif defined(HAVE_GSS_INQUIRE_SEC_CONTEXT_BY_OID) gss_OID_desc pac_data_oid = { .elements = discard_const(EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID), .length = EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH }; gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; /* If we didn't have the routine to get a verified, validated * PAC (supplied only by MIT at the time of writing), then try * with the Heimdal OID (fetches the PAC directly and always * validates) */ gss_maj = gss_inquire_sec_context_by_oid( &gss_min, gssapi_context, &pac_data_oid, &set); /* First check for the error MIT gives for an unknown OID */ if (gss_maj == GSS_S_UNAVAILABLE) { DEBUG(1, ("unable to obtain a PAC against this GSSAPI library. " "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n")); } else if (gss_maj != 0) { DEBUG(2, ("obtaining PAC via GSSAPI gss_inqiure_sec_context_by_oid (Heimdal OID) failed: %s\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5))); } else { if (set == GSS_C_NO_BUFFER_SET) { DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown " "data in results.\n")); return NT_STATUS_INTERNAL_ERROR; } /* The PAC blob is returned directly */ *pac_blob = data_blob_talloc(mem_ctx, set->elements[0].value, set->elements[0].length); if (!pac_blob->data) { status = NT_STATUS_NO_MEMORY; } else { status = NT_STATUS_OK; } gss_maj = gss_release_buffer_set(&gss_min, &set); return status; } #else DEBUG(1, ("unable to obtain a PAC against this GSSAPI library. " "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n")); #endif return NT_STATUS_ACCESS_DENIED; } NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx, gss_ctx_id_t gssapi_context, DATA_BLOB *session_key, uint32_t *keytype) { OM_uint32 gss_min, gss_maj; gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; gss_maj = gss_inquire_sec_context_by_oid( &gss_min, gssapi_context, &gse_sesskey_inq_oid, &set); if (gss_maj) { DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, discard_const_p(struct gss_OID_desc_struct, gss_mech_krb5)))); return NT_STATUS_NO_USER_SESSION_KEY; } if ((set == GSS_C_NO_BUFFER_SET) || (set->count == 0)) { #ifdef HAVE_GSSKRB5_GET_SUBKEY krb5_keyblock *subkey; gss_maj = gsskrb5_get_subkey(&gss_min, gssapi_context, &subkey); if (gss_maj != 0) { DEBUG(1, ("NO session key for this mech\n")); return NT_STATUS_NO_USER_SESSION_KEY; } if (session_key) { *session_key = data_blob_talloc(mem_ctx, KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey)); } if (keytype) { *keytype = KRB5_KEY_TYPE(subkey); } krb5_free_keyblock(NULL /* should be krb5_context */, subkey); return NT_STATUS_OK; #else DEBUG(0, ("gss_inquire_sec_context_by_oid didn't return any session key (and no alternative method available)\n")); return NT_STATUS_NO_USER_SESSION_KEY; #endif } if (session_key) { *session_key = data_blob_talloc(mem_ctx, set->elements[0].value, set->elements[0].length); } if (keytype) { int diflen, i; const uint8_t *p; *keytype = 0; if (set->count < 2) { #ifdef HAVE_GSSKRB5_GET_SUBKEY krb5_keyblock *subkey; gss_maj = gsskrb5_get_subkey(&gss_min, gssapi_context, &subkey); if (gss_maj == 0) { *keytype = KRB5_KEY_TYPE(subkey); krb5_free_keyblock(NULL /* should be krb5_context */, subkey); } #endif gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_OK; } else if (memcmp(set->elements[1].value, gse_sesskeytype_oid.elements, gse_sesskeytype_oid.length) != 0) { /* Perhaps a non-krb5 session key */ gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_OK; } p = (const uint8_t *)set->elements[1].value + gse_sesskeytype_oid.length; diflen = set->elements[1].length - gse_sesskeytype_oid.length; if (diflen <= 0) { gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_INVALID_PARAMETER; } for (i = 0; i < diflen; i++) { *keytype = (*keytype << 7) | (p[i] & 0x7f); if (i + 1 != diflen && (p[i] & 0x80) == 0) { gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_INVALID_PARAMETER; } } } gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_OK; } char *gssapi_error_string(TALLOC_CTX *mem_ctx, OM_uint32 maj_stat, OM_uint32 min_stat, const gss_OID mech) { OM_uint32 disp_min_stat, disp_maj_stat; gss_buffer_desc maj_error_message; gss_buffer_desc min_error_message; char *maj_error_string, *min_error_string; OM_uint32 msg_ctx = 0; char *ret; maj_error_message.value = NULL; min_error_message.value = NULL; maj_error_message.length = 0; min_error_message.length = 0; disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE, mech, &msg_ctx, &maj_error_message); if (disp_maj_stat != 0) { maj_error_message.value = NULL; maj_error_message.length = 0; } disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE, mech, &msg_ctx, &min_error_message); if (disp_maj_stat != 0) { min_error_message.value = NULL; min_error_message.length = 0; } maj_error_string = talloc_strndup(mem_ctx, (char *)maj_error_message.value, maj_error_message.length); min_error_string = talloc_strndup(mem_ctx, (char *)min_error_message.value, min_error_message.length); ret = talloc_asprintf(mem_ctx, "%s: %s", maj_error_string, min_error_string); talloc_free(maj_error_string); talloc_free(min_error_string); gss_release_buffer(&disp_min_stat, &maj_error_message); gss_release_buffer(&disp_min_stat, &min_error_message); return ret; }