/*
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Strsafe.h>
#define GSS_DLL_FILE
#include <gssapi.h>
#define SECURITY_WIN32
#include <sspi.h>
#pragma comment(lib, "secur32.lib")
#define DEBUG
#ifdef DEBUG
TCHAR _bb[256];
#define SEC_SUCCESS(Status) \
((Status) >= 0 ? TRUE: \
(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, \
0, ss, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), _bb, 256, 0), \
printf("SECURITY_STATUS: (%lx) %ls\n", ss, _bb), \
FALSE))
#define PP(fmt, ...) \
fprintf(stdout, "SSPI (%ld): ", __LINE__); \
fprintf(stdout, fmt, ##__VA_ARGS__); \
fprintf(stdout, "\n"); \
fflush(stdout)
#else
#define SEC_SUCCESS(Status) ((Status) >= 0)
#define PP(dmt, ...)
#endif
gss_OID_desc KRB5_OID = {9, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"};
gss_OID_desc SPNEGO_OID = {6, "\x2b\x06\x01\x05\x05\x02"};
gss_OID_desc USER_NAME_OID = {10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"};
gss_OID_desc HOST_SERVICE_NAME_OID = {10, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"};
gss_OID_desc EXPORT_NAME_OID = {6, "\x2b\x06\x01\x05\x06\x04"};
// gss_name_t is Name*
// gss_cred_id_t is Credentials*. One CredHandle for each mech.
// gss_ctx_id_t is Context*
typedef struct {
TCHAR PackageName[20];
SEC_WCHAR* name;
} Name;
typedef struct {
TCHAR PackageName[20];
CredHandle* phCred;
CtxtHandle hCtxt;
DWORD cbMaxMessage;
SecPkgContext_Sizes SecPkgContextSizes;
SecPkgContext_NativeNames nnames;
BOOLEAN established;
} Context;
typedef struct {
TCHAR PackageName[20];
CredHandle* phCred;
} OneCred;
typedef struct {
int count;
OneCred* creds;
long time;
} Credential;
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* This section holds supporting functions that are not exported */
void showTime(TimeStamp* ts)
{
SYSTEMTIME stLocal;
FileTimeToSystemTime((FILETIME*)ts, &stLocal);
// Build a string showing the date and time.
PP("---------------");
PP("TS low high %ld %ld", ts->LowPart, ts->HighPart);
PP("Local: %02d/%02d/%d %02d:%02d",
stLocal.wMonth, stLocal.wDay, stLocal.wYear,
stLocal.wHour, stLocal.wMinute);
}
long
SecondsUntil(TimeStamp *time)
{
// time is local time
ULARGE_INTEGER uiLocal;
FILETIME nowUTC, nowLocal;
GetSystemTimeAsFileTime(&nowUTC);
if (FileTimeToLocalFileTime(&nowUTC, &nowLocal) == 0) {
return -1;
}
uiLocal.HighPart = nowLocal.dwHighDateTime;
uiLocal.LowPart = nowLocal.dwLowDateTime;
long diff = (long)((time->QuadPart - uiLocal.QuadPart) / 10000000);
if (diff < 0 || diff > 8640000) {
// AcquireCredentialsHandle returns a strange TimeStamp.
PP("SecondsUntil is %ld. Change to 1 day", diff);
diff = 86400;
}
return diff;
}
Context*
NewContext(TCHAR* PackageName)
{
SECURITY_STATUS ss;
PSecPkgInfo pkgInfo;
Context* out = new Context;
if (out == NULL) {
return NULL;
}
ss = QuerySecurityPackageInfo(PackageName, &pkgInfo);
if (!SEC_SUCCESS(ss)) {
delete out;
return NULL;
}
out->phCred = NULL;
out->cbMaxMessage = pkgInfo->cbMaxToken;
wcscpy_s(out->PackageName, 20, PackageName);
FreeContextBuffer(pkgInfo);
return out;
}
int
flagSspi2Gss(int fin)
{
int fout = 0;
if (fin & ISC_REQ_MUTUAL_AUTH) fout |= GSS_C_MUTUAL_FLAG;
if (fin & ISC_REQ_CONFIDENTIALITY) fout |= GSS_C_CONF_FLAG;
if (fin & ISC_REQ_DELEGATE) fout |= GSS_C_DELEG_FLAG;
if (fin & ISC_REQ_INTEGRITY) fout |= GSS_C_INTEG_FLAG;
if (fin & ISC_REQ_REPLAY_DETECT) fout |= GSS_C_REPLAY_FLAG;
if (fin & ISC_REQ_SEQUENCE_DETECT) fout |= GSS_C_SEQUENCE_FLAG;
return fout;
}
int
flagGss2Sspi(int fin)
{
int fout = 0;
if (fin & GSS_C_MUTUAL_FLAG) fout |= ISC_RET_MUTUAL_AUTH;
if (fin & GSS_C_CONF_FLAG) fout |= ISC_RET_CONFIDENTIALITY;
if (fin & GSS_C_DELEG_FLAG) fout |= ISC_RET_DELEGATE;
if (fin & GSS_C_INTEG_FLAG) fout |= ISC_RET_INTEGRITY;
if (fin & GSS_C_REPLAY_FLAG) fout |= ISC_RET_REPLAY_DETECT;
if (fin & GSS_C_SEQUENCE_FLAG) fout |= ISC_RET_SEQUENCE_DETECT;
return fout;
}
BOOLEAN
isKerberosOID(gss_OID mech)
{
return mech->length == KRB5_OID.length
&& !memcmp(mech->elements, KRB5_OID.elements, KRB5_OID.length);
}
BOOLEAN
isNegotiateOID(gss_OID mech)
{
return mech->length == SPNEGO_OID.length
&& !memcmp(mech->elements, SPNEGO_OID.elements, SPNEGO_OID.length);
}
void
displayOID(gss_OID mech)
{
if (isKerberosOID(mech)) {
PP("Kerberos OID");
} else if (isNegotiateOID(mech)) {
PP("SPNEGO OID");
} else {
PP("UNKNOWN %d", mech->length);
}
}
void
displayOidSet(gss_OID_set mechs)
{
if (mechs == NULL) {
PP("OID set is NULL");
return;
}
PP("set.count is %d", (int)mechs->count);
for (int i = 0; i < mechs->count; i++) {
displayOID(&mechs->elements[i]);
}
}
/* End support section */
/* This section holds exported functions that currently have no implementation */
__declspec(dllexport) OM_uint32
gss_release_name(OM_uint32 *minor_status,
gss_name_t *name)
{
PP(">>>> Calling gss_release_name %p...", *name);
if (name != NULL && *name != GSS_C_NO_NAME) {
Name* name1 = (Name*)*name;
if (name1->name != NULL) {
delete[] name1->name;
}
delete name1;
*name = GSS_C_NO_NAME;
}
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_import_name(OM_uint32 *minor_status,
gss_buffer_t input_name_buffer,
gss_OID input_name_type,
gss_name_t *output_name)
{
PP(">>>> Calling gss_import_name...");
Name* name = new Name;
if (input_name_buffer == NULL || input_name_buffer->value == NULL
|| input_name_buffer->length == 0) {
return GSS_S_CALL_INACCESSIBLE_READ;
}
int len = (int)input_name_buffer->length;
LPSTR input = (LPSTR)input_name_buffer->value;
BOOLEAN isNegotiate = true;
if (input_name_type != NULL
&& input_name_type->length == EXPORT_NAME_OID.length
&& !memcmp(input_name_type->elements, EXPORT_NAME_OID.elements,
EXPORT_NAME_OID.length)) {
len -= (int)input[3] + 8;
isNegotiate = (int)input[3] == 6;
input = input + (int)input[3] + 8;
}
SEC_WCHAR* value = new SEC_WCHAR[len + 1];
if (value == NULL) {
goto err;
}
if (MultiByteToWideChar(CP_ACP, 0, input, len, value, len) == 0) {
goto err;
}
value[len] = 0;
if (input_name_type != NULL
&& input_name_type->length == HOST_SERVICE_NAME_OID.length
&& !memcmp(input_name_type->elements, HOST_SERVICE_NAME_OID.elements,
HOST_SERVICE_NAME_OID.length)) {
for (int i = 0; i < len; i++) {
if (value[i] == '@') {
value[i] = '/';
break;
}
}
}
name->name = value;
wcscpy_s(name->PackageName, 20, isNegotiate ? L"Negotiate" : L"Kerberos"); // TODO
*output_name = (gss_name_t) name;
return GSS_S_COMPLETE;
err:
if (value != NULL) {
delete[] value;
}
delete name;
return GSS_S_FAILURE;
}
__declspec(dllexport) OM_uint32
gss_compare_name(OM_uint32 *minor_status,
gss_name_t name1,
gss_name_t name2,
int *name_equal)
{
PP(">>>> Calling gss_compare_name...");
if (name1 == NULL || name2 == NULL) {
*name_equal = 0;
return GSS_S_CALL_INACCESSIBLE_READ;
}
SEC_WCHAR* names1 = ((Name*)name1)->name;
SEC_WCHAR* names2 = ((Name*)name2)->name;
if (lstrcmp(names1, names2)) {
*name_equal = 0;
} else {
*name_equal = 1;
}
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_canonicalize_name(OM_uint32 *minor_status,
gss_name_t input_name,
gss_OID mech_type,
gss_name_t *output_name)
{
PP(">>>> Calling gss_canonicalize_name...");
Name* names1 = (Name*)input_name;
Name* names2 = new Name;
if (names2 == NULL) {
return GSS_S_FAILURE;
}
PP("new name at %p", names2);
names2->name = new SEC_WCHAR[lstrlen(names1->name) + 1];
if (names2->name == NULL) {
delete names2;
return GSS_S_FAILURE;
}
wcscpy_s(names2->PackageName, 20, isNegotiateOID(mech_type)
? L"Negotiate" : L"Kerberos");
StringCchCopy(names2->name, lstrlen(names1->name) + 1, names1->name);
*output_name = (gss_name_t)names2;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_export_name(OM_uint32 *minor_status,
gss_name_t input_name,
gss_buffer_t exported_name)
{
PP(">>>> Calling gss_export_name...");
Name* name1 = (Name*)input_name;
SEC_WCHAR* names = name1->name;
TCHAR mech = name1->PackageName[0];
PP("name is %ls %ls", name1->PackageName, name1->name);
int len = (int)wcslen(names);
if (len < 256) {
// 04 01 00 ** 06 ** OID len:int32 name
int mechLen = mech == 'K' ? KRB5_OID.length : SPNEGO_OID.length;
char* buffer = new char[10 + mechLen + len];
if (buffer == NULL) {
return GSS_S_FAILURE;
}
buffer[0] = 4;
buffer[1] = 1;
buffer[2] = 0;
buffer[3] = 2 + mechLen;
buffer[4] = 6;
buffer[5] = mechLen;
memcpy_s(buffer + 6, mechLen, mech == 'K' ? KRB5_OID.elements : SPNEGO_OID.elements, mechLen);
buffer[6 + mechLen] = buffer[7 + mechLen] = buffer[8 + mechLen] = 0;
buffer[9 + mechLen] = (char)len;
if (WideCharToMultiByte(CP_ACP, 0, names, len,
buffer+10+mechLen, len, NULL, NULL) == 0) {
delete buffer;
return GSS_S_FAILURE;
}
exported_name->length = 10 + mechLen + len;
exported_name->value = buffer;
return GSS_S_COMPLETE;
} else {
return GSS_S_FAILURE;
}
}
__declspec(dllexport) OM_uint32
gss_display_name(OM_uint32 *minor_status,
gss_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID *output_name_type)
{
PP(">>>> Calling gss_display_name...");
SEC_WCHAR* names = ((Name*)input_name)->name;
int len = (int)wcslen(names);
char* buffer = new char[len+1];
if (WideCharToMultiByte(CP_ACP, 0, names, len, buffer, len, NULL, NULL) == 0) {
return GSS_S_FAILURE;
}
buffer[len] = 0;
output_name_buffer->length = len;
output_name_buffer->value = buffer;
PP("Name found: %ls", names);
PP("%d [%s]", len, buffer);
if (output_name_type != NULL) {
*output_name_type = &USER_NAME_OID;
}
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_acquire_cred(OM_uint32 *minor_status,
gss_name_t desired_name,
OM_uint32 time_req,
gss_OID_set desired_mech,
gss_cred_usage_t cred_usage,
gss_cred_id_t *output_cred_handle,
gss_OID_set *actual_mechs,
OM_uint32 *time_rec)
{
PP(">>>> Calling gss_acquire_cred...");
SECURITY_STATUS ss;
TimeStamp ts;
ts.QuadPart = 0;
cred_usage = 0;
PP("AcquireCredentialsHandle with %d %p", cred_usage, desired_mech);
displayOidSet(desired_mech);
Credential* cred = new Credential();
cred->count = (int)desired_mech->count;
cred->creds = new OneCred[cred->count];
for (int i = 0; i < cred->count; i++) {
TCHAR* name = isKerberosOID(&desired_mech->elements[i])
? L"Kerberos" : L"Negotiate";
wcscpy_s(cred->creds[i].PackageName, 20, name);
cred->creds[i].phCred = new CredHandle();
ts.QuadPart = 0;
ss = AcquireCredentialsHandle(
NULL,
name,
cred_usage == 0 ? SECPKG_CRED_BOTH :
(cred_usage == 1 ? SECPKG_CRED_OUTBOUND : SECPKG_CRED_INBOUND),
NULL,
NULL,
NULL,
NULL,
cred->creds[i].phCred,
&ts);
}
actual_mechs = &desired_mech; // dup?
*output_cred_handle = (void*)cred;
showTime(&ts);
cred->time = SecondsUntil(&ts);
if (time_rec != NULL) {
*time_rec = cred->time;
}
if (desired_name != NULL) {
gss_name_t realname;
gss_inquire_cred(minor_status, *output_cred_handle, &realname,
NULL, NULL, NULL);
SEC_WCHAR* dnames = ((Name*)desired_name)->name;
SEC_WCHAR* rnames = ((Name*)realname)->name;
PP("comp name %ls %ls", dnames, rnames);
int cmp = lstrcmp(dnames, rnames);
gss_release_name(minor_status, &realname);
return cmp ? GSS_S_FAILURE : GSS_S_COMPLETE; // Only support default cred
}
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_release_cred(OM_uint32 *minor_status,
gss_cred_id_t *cred_handle)
{
PP(">>>> Calling gss_release_cred...");
if (cred_handle && *cred_handle) {
Credential* cred = (Credential*)*cred_handle;
for (int i = 0; i < cred->count; i++) {
FreeCredentialsHandle(cred->creds[i].phCred);
delete cred->creds[i].phCred;
}
delete cred;
*cred_handle = GSS_C_NO_CREDENTIAL;
}
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_inquire_cred(OM_uint32 *minor_status,
gss_cred_id_t cred_handle,
gss_name_t *name,
OM_uint32 *lifetime,
gss_cred_usage_t *cred_usage,
gss_OID_set *mechanisms)
{
PP(">>>> Calling gss_inquire_cred...");
CredHandle* cred = ((Credential*)cred_handle)->creds[0].phCred;
SECURITY_STATUS ss;
if (name) {
SecPkgCredentials_Names snames;
ss = QueryCredentialsAttributes(cred, SECPKG_CRED_ATTR_NAMES, &snames);
if (!SEC_SUCCESS(ss)) {
return GSS_S_FAILURE;
}
SEC_WCHAR* names = new SEC_WCHAR[lstrlen(snames.sUserName) + 1];
if (names == NULL) {
return GSS_S_FAILURE;
}
StringCchCopy(names, lstrlen(snames.sUserName) + 1, snames.sUserName);
FreeContextBuffer(&snames);
PP("new name at %p", names);
Name* name1 = new Name;
if (name1 == NULL) {
delete[] names;
return GSS_S_FAILURE;
}
name1->name = names;
wcscpy_s(name1->PackageName, 20, ((Credential*)cred_handle)->creds[0].PackageName);
*name = (gss_name_t) name1;
}
if (lifetime) {
*lifetime = ((Credential*)cred_handle)->time;
}
if (cred_usage) {
*cred_usage = 1; // We only support INITIATE_ONLY now
}
if (mechanisms) {
// Useless for Java
}
// Others inquiries not supported yet
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_import_sec_context(OM_uint32 *minor_status,
gss_buffer_t interprocess_token,
gss_ctx_id_t *context_handle)
{
// Not transferable, return FAILURE
PP(">>>> Calling UNIMPLEMENTED gss_import_sec_context...");
return GSS_S_FAILURE;
}
__declspec(dllexport) OM_uint32
gss_init_sec_context(OM_uint32 *minor_status,
gss_cred_id_t initiator_cred_handle,
gss_ctx_id_t *context_handle,
gss_name_t target_name,
gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
gss_channel_bindings_t input_chan_bindings,
gss_buffer_t input_token,
gss_OID *actual_mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec)
{
PP(">>>> Calling gss_init_sec_context...");
SECURITY_STATUS ss;
TimeStamp Lifetime;
SecBufferDesc InBuffDesc;
SecBuffer InSecBuff;
SecBufferDesc OutBuffDesc;
SecBuffer OutSecBuff;
Context* pc;
if (input_token->length == 0) {
TCHAR* name = isKerberosOID(mech_type) ? L"Kerberos" : L"Negotiate";
pc = NewContext(name);
if (pc == NULL) {
return GSS_S_FAILURE;
}
Credential* cred = (Credential*)initiator_cred_handle;
if (cred != NULL) {
for (int i = 0; i < cred->count; i++) {
if (!lstrcmp(cred->creds[i].PackageName, name)) {
pc->phCred = cred->creds[i].phCred;
}
}
}
*context_handle = (gss_ctx_id_t) pc;
} else {
pc = (Context*)*context_handle;
}
output_token->length = pc->cbMaxMessage;
output_token->value = new char[pc->cbMaxMessage];
if (output_token->value == NULL) {
return GSS_S_FAILURE;
}
DWORD outFlag;
TCHAR outName[100];
OM_uint32 minor;
gss_buffer_desc tn;
gss_display_name(&minor, target_name, &tn, NULL);
if (MultiByteToWideChar(CP_ACP, 0, (LPCCH)tn.value, (int)tn.length,
outName, (int)tn.length) == 0) {
return GSS_S_FAILURE;
}
outName[tn.length] = 0;
BOOL pfDone;
int flag = flagGss2Sspi(req_flags);
OutBuffDesc.ulVersion = SECBUFFER_VERSION;
OutBuffDesc.cBuffers = 1;
OutBuffDesc.pBuffers = &OutSecBuff;
OutSecBuff.cbBuffer = (ULONG)output_token->length;
OutSecBuff.BufferType = SECBUFFER_TOKEN;
OutSecBuff.pvBuffer = output_token->value;
if (input_token->value) {
InBuffDesc.ulVersion = SECBUFFER_VERSION;
InBuffDesc.cBuffers = 1;
InBuffDesc.pBuffers = &InSecBuff;
InSecBuff.BufferType = SECBUFFER_TOKEN;
InSecBuff.cbBuffer = (ULONG)input_token->length;
InSecBuff.pvBuffer = input_token->value;
} else {
if (!pc->phCred) {
PP("No credentials %p provided, acquire %ls automatically",
pc->phCred, pc->PackageName);
CredHandle* newCred = new CredHandle();
ss = AcquireCredentialsHandle(
NULL,
pc->PackageName,
SECPKG_CRED_OUTBOUND,
NULL,
NULL,
NULL,
NULL,
newCred,
&Lifetime);
pc->phCred = newCred;
PP("end");
if (!(SEC_SUCCESS(ss))) {
PP("Failed");
return GSS_S_FAILURE;
}
} else {
PP("Credentials OK");
}
}
ss = InitializeSecurityContext(
pc->phCred,
input_token->value ? &pc->hCtxt : NULL,
outName,
flag,
0,
SECURITY_NATIVE_DREP,
input_token->value ? &InBuffDesc : NULL,
0,
&pc->hCtxt,
&OutBuffDesc,
&outFlag,
&Lifetime);
if (!SEC_SUCCESS(ss)) {
return GSS_S_FAILURE;
}
if ((SEC_I_COMPLETE_NEEDED == ss)
|| (SEC_I_COMPLETE_AND_CONTINUE == ss)) {
ss = CompleteAuthToken(&pc->hCtxt, &OutBuffDesc);
if (!SEC_SUCCESS(ss)) {
return GSS_S_FAILURE;
}
}
output_token->length = OutSecBuff.cbBuffer;
pfDone = !((SEC_I_CONTINUE_NEEDED == ss) ||
(SEC_I_COMPLETE_AND_CONTINUE == ss));
outFlag = flagSspi2Gss(outFlag);
PP("Done? %d outFlag: %d", pfDone, outFlag);
*ret_flags = (OM_uint32)outFlag;
if (ss == SEC_I_CONTINUE_NEEDED) {
return GSS_S_CONTINUE_NEEDED;
} else {
ss = QueryContextAttributes(
&pc->hCtxt, SECPKG_ATTR_SIZES, &pc->SecPkgContextSizes);
if (!SEC_SUCCESS(ss)) {
return GSS_S_FAILURE;
}
pc->established = true;
ss = QueryContextAttributes(&pc->hCtxt, SECPKG_ATTR_NATIVE_NAMES, &pc->nnames);
if (!SEC_SUCCESS(ss)) {
return GSS_S_FAILURE;
}
*ret_flags |= GSS_C_PROT_READY_FLAG;
return GSS_S_COMPLETE;
}
}
__declspec(dllexport) OM_uint32
gss_accept_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_cred_id_t acceptor_cred_handle,
gss_buffer_t input_token,
gss_channel_bindings_t input_chan_bindings,
gss_name_t *src_name,
gss_OID *mech_type,
gss_buffer_t output_token,
OM_uint32 *ret_flags,
OM_uint32 *time_rec,
gss_cred_id_t *delegated_cred_handle)
{
PP(">>>> Calling UNIMPLEMENTED gss_accept_sec_context...");
return GSS_S_FAILURE;
}
__declspec(dllexport) OM_uint32
gss_inquire_context(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
gss_name_t *src_name,
gss_name_t *targ_name,
OM_uint32 *lifetime_rec,
gss_OID *mech_type,
OM_uint32 *ctx_flags,
int *locally_initiated,
int *open)
{
PP(">>>> Calling UNIMPLEMENTED gss_inquire_context...");
Context* pc = (Context*) context_handle;
Name* n1 = NULL;
Name* n2 = NULL;
if (!pc->established) {
return GSS_S_NO_CONTEXT;
}
if (src_name != NULL) {
n1 = new Name;
if (n1 == NULL) {
goto err;
}
n1->name = new SEC_WCHAR[lstrlen(pc->nnames.sClientName) + 1];
if (n1->name == NULL) {
goto err;
}
PP("new name at %p", n1->name);
StringCchCopy(n1->name, lstrlen(pc->nnames.sClientName) + 1, pc->nnames.sClientName);
wcscpy_s(n1->PackageName, 20, pc->PackageName);
*src_name = (gss_name_t) n1;
}
if (targ_name != NULL) {
n2 = new Name;
if (n2 == NULL) {
goto err;
}
n2->name = new SEC_WCHAR[lstrlen(pc->nnames.sServerName) + 1];
if (n2->name == NULL) {
goto err;
}
PP("new name at %p", n2->name);
StringCchCopy(n2->name, lstrlen(pc->nnames.sServerName) + 1, pc->nnames.sServerName);
wcscpy_s(n2->PackageName, 20, pc->PackageName);
*targ_name = (gss_name_t) n2;
}
if (lifetime_rec != NULL) {
SecPkgContext_Lifespan ls;
SECURITY_STATUS ss;
ss = QueryContextAttributes(&pc->hCtxt, SECPKG_ATTR_LIFESPAN, &ls);
if (!SEC_SUCCESS(ss)) {
goto err;
}
*lifetime_rec = SecondsUntil(&ls.tsExpiry);
}
if (mech_type != NULL) {
// No need for Java
}
// TODO: other inquiries
return GSS_S_COMPLETE;
err:
if (n1 != NULL) {
if (n1->name != NULL) {
delete[] n1->name;
}
delete n1;
n1 = NULL;
}
if (n2 != NULL) {
if (n2->name != NULL) {
delete[] n2->name;
}
delete n2;
n2 = NULL;
}
return GSS_S_FAILURE;
}
__declspec(dllexport) OM_uint32
gss_delete_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t output_token)
{
PP(">>>> Calling gss_delete_sec_context...");
Context* pc = (Context*) *context_handle;
DeleteSecurityContext(&pc->hCtxt);
if (pc->phCred != NULL) {
FreeCredentialsHandle(pc->phCred);
pc->phCred = NULL;
}
FreeContextBuffer(&pc->nnames);
delete pc;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_context_time(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
OM_uint32 *time_rec)
{
PP(">>>> Calling IMPLEMENTED gss_context_time...");
SECURITY_STATUS ss;
Context* pc = (Context*) context_handle;
SecPkgContext_Lifespan ls;
ss = QueryContextAttributes(&pc->hCtxt, SECPKG_ATTR_LIFESPAN, &ls);
if (ss == SEC_E_OK) {
*time_rec = SecondsUntil(&ls.tsExpiry);
showTime(&ls.tsStart);
showTime(&ls.tsExpiry);
TimeStamp ts;
GetSystemTimeAsFileTime((FILETIME*)&ts);
showTime(&ts);
return GSS_S_COMPLETE;
} else {
PP("QueryContextAttributes failed");
return GSS_S_FAILURE;
}
}
__declspec(dllexport) OM_uint32
gss_wrap_size_limit(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
OM_uint32 *max_input_size)
{
PP(">>>> Calling gss_wrap_size_limit...");
Context* pc = (Context*) context_handle;
*max_input_size = pc->cbMaxMessage;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_export_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t interprocess_token)
{
PP(">>>> Calling UNIMPLEMENTED gss_export_sec_context...");
return GSS_S_FAILURE;
}
__declspec(dllexport) OM_uint32
gss_get_mic(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
gss_qop_t qop_req,
gss_buffer_t message_buffer,
gss_buffer_t msg_token)
{
PP(">>>> Calling gss_get_mic...");
Context* pc = (Context*) context_handle;
SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[2];
BuffDesc.cBuffers = 2;
BuffDesc.pBuffers = SecBuff;
BuffDesc.ulVersion = SECBUFFER_VERSION;
SecBuff[0].BufferType = SECBUFFER_DATA;
SecBuff[0].cbBuffer = (ULONG)message_buffer->length;
SecBuff[0].pvBuffer = message_buffer->value;
SecBuff[1].BufferType = SECBUFFER_TOKEN;
SecBuff[1].cbBuffer = pc->SecPkgContextSizes.cbMaxSignature;
SecBuff[1].pvBuffer = msg_token->value = malloc(SecBuff[1].cbBuffer);
ss = MakeSignature(&pc->hCtxt, 0, &BuffDesc, 0);
if (!SEC_SUCCESS(ss)) {
free(SecBuff[1].pvBuffer);
return GSS_S_FAILURE;
}
msg_token->length = SecBuff[1].cbBuffer;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_verify_mic(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
gss_buffer_t message_buffer,
gss_buffer_t token_buffer,
gss_qop_t *qop_state)
{
PP(">>>> Calling gss_verify_mic...");
Context* pc = (Context*) context_handle;
SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[2];
ULONG qop;
BuffDesc.ulVersion = 0;
BuffDesc.cBuffers = 2;
BuffDesc.pBuffers = SecBuff;
SecBuff[0].BufferType = SECBUFFER_TOKEN;
SecBuff[0].cbBuffer = (ULONG)token_buffer->length;
SecBuff[0].pvBuffer = token_buffer->value;
SecBuff[1].BufferType = SECBUFFER_DATA;
SecBuff[1].cbBuffer = (ULONG)message_buffer->length;
SecBuff[1].pvBuffer = message_buffer->value;
ss = VerifySignature(&pc->hCtxt, &BuffDesc, 0, &qop);
*qop_state = qop;
if (ss == SEC_E_OK) {
return GSS_S_COMPLETE;
} else if (ss == SEC_E_OUT_OF_SEQUENCE) {
return GSS_S_UNSEQ_TOKEN;
} else {
return GSS_S_BAD_SIG;
}
}
__declspec(dllexport) OM_uint32
gss_wrap(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
gss_buffer_t input_message_buffer,
int *conf_state,
gss_buffer_t output_message_buffer)
{
PP(">>>> Calling gss_wrap...");
Context* pc = (Context*) context_handle;
SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[3];
BuffDesc.ulVersion = SECBUFFER_VERSION;
BuffDesc.cBuffers = 3;
BuffDesc.pBuffers = SecBuff;
SecBuff[0].BufferType = SECBUFFER_TOKEN;
SecBuff[0].cbBuffer = pc->SecPkgContextSizes.cbSecurityTrailer;
output_message_buffer->value = SecBuff[0].pvBuffer = malloc(
pc->SecPkgContextSizes.cbSecurityTrailer
+ input_message_buffer->length
+ pc->SecPkgContextSizes.cbBlockSize);;
SecBuff[1].BufferType = SECBUFFER_DATA;
SecBuff[1].cbBuffer = (ULONG)input_message_buffer->length;
SecBuff[1].pvBuffer = malloc(SecBuff[1].cbBuffer);
memcpy_s(SecBuff[1].pvBuffer, SecBuff[1].cbBuffer,
input_message_buffer->value, input_message_buffer->length);
SecBuff[2].BufferType = SECBUFFER_PADDING;
SecBuff[2].cbBuffer = pc->SecPkgContextSizes.cbBlockSize;
SecBuff[2].pvBuffer = malloc(SecBuff[2].cbBuffer);
ss = EncryptMessage(&pc->hCtxt, conf_req_flag ? 0 : SECQOP_WRAP_NO_ENCRYPT,
&BuffDesc, 0);
*conf_state = conf_req_flag;
if (!SEC_SUCCESS(ss)) {
free(SecBuff[0].pvBuffer);
free(SecBuff[1].pvBuffer);
free(SecBuff[2].pvBuffer);
return GSS_S_FAILURE;
}
memcpy_s((PBYTE)SecBuff[0].pvBuffer + SecBuff[0].cbBuffer,
input_message_buffer->length + pc->SecPkgContextSizes.cbBlockSize,
SecBuff[1].pvBuffer,
SecBuff[1].cbBuffer);
memcpy_s((PBYTE)SecBuff[0].pvBuffer + SecBuff[0].cbBuffer + SecBuff[1].cbBuffer,
pc->SecPkgContextSizes.cbBlockSize,
SecBuff[2].pvBuffer,
SecBuff[2].cbBuffer);
output_message_buffer->length = SecBuff[1].cbBuffer + SecBuff[0].cbBuffer
+ SecBuff[2].cbBuffer;
free(SecBuff[1].pvBuffer);
free(SecBuff[2].pvBuffer);
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_unwrap(OM_uint32 *minor_status,
gss_ctx_id_t context_handle,
gss_buffer_t input_message_buffer,
gss_buffer_t output_message_buffer,
int *conf_state,
gss_qop_t *qop_state)
{
PP(">>>> Calling gss_unwrap...");
Context* pc = (Context*) context_handle;
SECURITY_STATUS ss;
SecBufferDesc BuffDesc;
SecBuffer SecBuff[2];
ULONG ulQop = 0;
BuffDesc.cBuffers = 2;
BuffDesc.pBuffers = SecBuff;
BuffDesc.ulVersion = SECBUFFER_VERSION;
SecBuff[0].BufferType = SECBUFFER_STREAM;
SecBuff[0].cbBuffer = (ULONG)input_message_buffer->length;
output_message_buffer->value = SecBuff[0].pvBuffer
= malloc(input_message_buffer->length);
memcpy_s(SecBuff[0].pvBuffer, input_message_buffer->length,
input_message_buffer->value, input_message_buffer->length);
SecBuff[1].BufferType = SECBUFFER_DATA;
SecBuff[1].cbBuffer = 0;
SecBuff[1].pvBuffer = NULL;
ss = DecryptMessage(&pc->hCtxt, &BuffDesc, 0, &ulQop);
if (!SEC_SUCCESS(ss)) {
free(SecBuff[0].pvBuffer);
return GSS_S_FAILURE;
}
output_message_buffer->length = SecBuff[1].cbBuffer;
output_message_buffer->value = SecBuff[1].pvBuffer;
*conf_state = ulQop == SECQOP_WRAP_NO_ENCRYPT ? 0 : 1;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_indicate_mechs(OM_uint32 *minor_status,
gss_OID_set *mech_set)
{
PP(">>>> Calling gss_indicate_mechs...");
OM_uint32 minor = 0;
OM_uint32 major = GSS_S_COMPLETE;
BOOLEAN hasSpnego = false, hasKerberos = false;
ULONG ccPackages;
PSecPkgInfo packages;
EnumerateSecurityPackages(&ccPackages, &packages);
PP("EnumerateSecurityPackages returns %ld", ccPackages);
PSecPkgInfo pkgInfo;
SECURITY_STATUS ss = QuerySecurityPackageInfo(L"Negotiate", &pkgInfo);
if (ss == SEC_E_OK) {
hasSpnego = true;
}
ss = QuerySecurityPackageInfo(L"Kerberos", &pkgInfo);
if (ss == SEC_E_OK) {
hasKerberos = true;
}
if (gss_create_empty_oid_set(minor_status, mech_set)) {
major = GSS_S_FAILURE;
goto done;
}
if (hasKerberos) {
gss_add_oid_set_member(minor_status, &KRB5_OID, mech_set);
}
if (hasSpnego) {
gss_add_oid_set_member(minor_status, &SPNEGO_OID, mech_set);
}
done:
if (major != GSS_S_COMPLETE) {
// (void) generic_gss_release_oid_set(&minor, ©);
}
return (major);
}
__declspec(dllexport) OM_uint32
gss_inquire_names_for_mech(OM_uint32 *minor_status,
const gss_OID mechanism,
gss_OID_set *name_types)
{
PP(">>>> Calling IMPLEMENTED gss_inquire_names_for_mech...");
gss_create_empty_oid_set(minor_status, name_types);
gss_add_oid_set_member(minor_status, &USER_NAME_OID, name_types);
gss_add_oid_set_member(minor_status, &HOST_SERVICE_NAME_OID, name_types);
gss_add_oid_set_member(minor_status, &EXPORT_NAME_OID, name_types);
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_add_oid_set_member(OM_uint32 *minor_status,
gss_OID member_oid,
gss_OID_set *oid_set)
{
PP(">>>> Calling gss_add_oid_set_member...");
if (member_oid == NULL || member_oid->length == 0
|| member_oid->elements == NULL) {
return GSS_S_CALL_INACCESSIBLE_READ;
}
if (oid_set == NULL) {
return GSS_S_CALL_INACCESSIBLE_WRITE;
}
int count = (int)(*oid_set)->count;
for (int i = 0; i < count; i++) {
if ((*oid_set)->elements[i].length == member_oid->length
&& !memcmp((*oid_set)->elements[i].elements, member_oid->elements, member_oid->length)) {
// already there
return GSS_S_COMPLETE;
}
}
gss_OID existing = (*oid_set)->elements;
gss_OID newcopy = new gss_OID_desc[count + 1];
if (newcopy == NULL) {
return GSS_S_FAILURE;
}
if (existing) {
memcpy_s(newcopy, (count + 1) * sizeof(gss_OID_desc),
existing, count * sizeof(gss_OID_desc));
}
newcopy[count].length = member_oid->length;
newcopy[count].elements = new char[member_oid->length];
if (newcopy[count].elements == NULL) {
delete[] newcopy;
return GSS_S_FAILURE;
}
memcpy_s(newcopy[count].elements, member_oid->length,
member_oid->elements, member_oid->length);
(*oid_set)->elements = newcopy;
(*oid_set)->count++;
if (existing) {
delete[] existing;
}
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_display_status(OM_uint32 *minor_status,
OM_uint32 status_value,
int status_type,
gss_OID mech_type,
OM_uint32 *message_context,
gss_buffer_t status_string)
{
PP(">>>> Calling UNIMPLEMENTED gss_display_status...");
status_string->value = new char[7];
memcpy_s(status_string->value, 7, "Nothing", 7);
status_string->length = 7;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_create_empty_oid_set(OM_uint32 *minor_status,
gss_OID_set *oid_set)
{
PP(">>>> Calling gss_create_empty_oid_set...");
if (oid_set == NULL) {
return GSS_S_CALL_INACCESSIBLE_WRITE;
}
if (*oid_set = new gss_OID_set_desc) {
memset(*oid_set, 0, sizeof(gss_OID_set_desc));
return GSS_S_COMPLETE;
}
return GSS_S_FAILURE;
}
__declspec(dllexport) OM_uint32
gss_release_oid_set(OM_uint32 *minor_status,
gss_OID_set *set)
{
PP(">>>> Calling gss_release_oid_set...");
if (set == NULL || *set == GSS_C_NO_OID_SET) {
return GSS_S_COMPLETE;
}
for (int i = 0; i < (*set)->count; i++) {
delete[] (*set)->elements[i].elements;
}
delete[] (*set)->elements;
delete *set;
*set = GSS_C_NO_OID_SET;
return GSS_S_COMPLETE;
}
__declspec(dllexport) OM_uint32
gss_release_buffer(OM_uint32 *minor_status,
gss_buffer_t buffer)
{
PP(">>>> Calling gss_release_buffer...");
if (buffer == NULL) {
return GSS_S_COMPLETE;
}
if (buffer->value) {
delete[] buffer->value;
buffer->value = NULL;
buffer->length = 0;
}
return GSS_S_COMPLETE;
}
/* End implemented section */
#ifdef __cplusplus
}
#endif