I would like to ask you a question about implementing mutual authentication with Kerberos, using SSPI and LDAP API.
I am using the guidelines described in: ldap_sasl_bind_s(GSSAPI) - What should be provided in the credentials BERVAL structure.
Here is the algorithm I am using:
//--------------------------------------------------------------------------------------------
// client side
AcquireCredentialsHandle(NULL, "Kerberos", SECPKG_CRED_BOTH, NULL, &secIdent, NULL, NULL, &kClientCredential, &kClientTimeOut);
// AcquireCredentialsHandle returns SEC_E_OK
// begin validation
unsigned long ulClientFlags = ISC_REQ_CONNECTION | ISC_REQ_MUTUAL_AUTH | ISC_REQ_DELEGATE;
int iCliStatus = InitializeSecurityContext(&kClientCredential, isContextNull(kClientContext) ? NULL : &kClientContext,
pacTargetName, ulClientFlags, 0, SECURITY_NATIVE_DREP, pkServerToken,
0, &kClientContext, &kClientToken, &ulContextAttr, NULL);
// InitializeSecurityContext returns SEC_I_CONTINUE_NEEDED
//--------------------------------------------------------------------------------------------
// server side
// ldap_init returns ok
ldap_set_option(ld, LDAP_OPT_SIGN, LDAP_OPT_OFF);
ldap_set_option(ld, LDAP_OPT_ENCRYPT, LDAP_OPT_OFF);
unsigned long ulVersion = LDAP_VERSION3;
ldap_set_option(ld, LDAP_OPT_VERSION, &ulVersion);
// ldap_connect returns LDAP_SUCCESS
// build the credentials based on what InitializeSecurityContext returned
BERVAL creds;
creds.bv_len = kClientToken.pBuffers[0].cbBuffer;
creds.bv_val = reinterpret_cast(kClientToken.pBuffers[0].pvBuffer);
BERVAL* pServerCreds = NULL;
int iError = ldap_sasl_bind_s(ld, "", "GSSAPI", &creds, NULL, NULL, &pServerCreds);
// ldap_sasl_bind_s returns LDAP_SUCCESS
unsigned long ulError = 0;
ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &ulError);
// ulError is equal to LDAP_SASL_BIND_IN_PROGRESS
And here is the problem: both LDAP error codes are ok, but pServerCreds points to an empty BERVAL structure (not NULL, but bv_len equals to 0), and it should contain the server credential I have to pass to the next InitializeSecurityContext call. If I use that data to build the SecBufferDesc structure for the following call, it returns SEC_E_INVALID_TOKEN.
Is ldap_sasl_bind_s supposed to return an empty BERVAL or am I doing something wrong?
I have tested the authentication using full SSPI calls (AcceptSecurityContext for the server) and it works just as expected. The problem is that I need the server to be cross-platform, so I cannot use SSPI.
Thanks for taking the time to answer!
Juan
I found the problem.
According to this thread there is a bug with ldap_sasl_bind_s returning empty server credentials in Windows XP. I have tested my application under Windows 2008 Server and the credentials are properly returned.
Related
I'm facing with a really strange issue. I interfaced a SAML authentication with OTRS which is an ITSM written in Perl and the Identity Provider sends the attributes as follow :
LoginName : dev-znuny02
mail : test2#company.dev
Profile : company.autre.idp.v2()
Profile : company.autre.mcf.sp(dev)
givenName : MyName
sn : Test2
I handle these with a module called Mod_Auth_Mellon and as you can see the attribute Profile is multivaluated. In short I retrieve all of these values with the following snippet :
sub new {
my ( $Type, %Param ) = #_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ConfigObject} = $Kernel::OM->Get('Kernel::Config');
$Self->{UserObject} = Kernel::System::User->new( %{$Self} );
# Handle header's attributes
$Self->{loginName} = 'MELLON_LoginName';
$Self->{eMail} = 'MELLON_mail';
$Self->{Profile_0} = 'MELLON_Profile_0';
$Self->{Profile_1} = 'MELLON_Profile_1';
$Self->{gName} = 'MELLON_givenName';
$Self->{sName} = 'MELLON_sn';
return $Self;
}
sub Auth {
my ( $Self, %Param ) = #_;
# get params
my $lname = $ENV{$Self->{loginName}};
my $email = $ENV{$Self->{eMail}};
my $profile0 = $ENV{$Self->{Profile_0}};
my $profile1 = $ENV{$Self->{Profile_1}};
my $gname = $ENV{$Self->{gName}};
my $sname = $ENV{$Self->{sName}};
...
}
I can handle all the values of the attributes except the attribute Profile. When I take a look to the documentation, they said :
If an attribute has multiple values, then they will be stored as MELLON_<name>_0, MELLON_<name>_1, MELLON_<name>_2
To be sure, I activated the diagnostics of the Mellon module and indeed I receive the information correctly :
...
MELLON_LoginName : dev_znuny02
MELLON_LoginName_0 : dev_znuny02
MELLON_mail : test2#company.dev
MELLON_mail_0 : test2#company.dev
MELLON_Profile : company.autre.idp.v2()
MELLON_Profile_0 : company.autre.idp.v2()
MELLON_Profile_1 : company.autre.mcf.sp(dev)
...
When I try to manipulate the MELLON_Profile_0 or MELLON_Profile_1 attributes in the Perl script, the variable assigned to it seems empty. Do you have any idea on what can be the issue here ?
Any help is welcome ! Thanks a lot guys
PS : I have no control on the Identity Provider so I can't edit the attributes sent
I didn't managed to make it work but I found a workaround to prevent users who don't have the Profile attribute value from logging into the application:
MellonCond Profile company.autre.mcf.sp(dev)
according the documentation :
You can also utilize SAML attributes to control whether Mellon authentication succeeds (a form of authorization). So even though the IdP may have successfully authenticated the user you can apply additional constraints via the MellonCond directive. The basic idea is that each MellonCond directive specifies one condition that either evaluates to True or False.
I have a certificate that I need to send in the header of an http request. This is how I acquired the cert:
PCCERT_CONTEXT cert = nullptr;
wstring store = // store name
wstring subjectName = // subject name
HCERTSTORE hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
store.c_str());
cert = CertFindCertificateInStore(
hStoreHandle,
X509_ASN_ENCODING,
0,
CERT_FIND_SUBJECT_STR,
subjectName.c_str(),
NULL);
I need to send it as a custom header, as the load balancer that sits in front of my service strips off the certificate header ["X-ARR-CLIENTCERT"] before forwarding the request. I believe I need to send the cert->pbCertEncoded, but on the server, I can't decode it and convert it back to an X509Certificate2.
This is what I tried on the client:
request.headers().add("client-cert", cert->pbCertEncoded);
On the server:
var headerCert = Request.Headers["client-cert"];
byte[] certdata = Convert.FromBase64String(headerCert);
X509Certificate2 cert = new X509Certificate2(certdata);
The request header on the server is non-null. But it cannot parse it back to an X509Certificate2.
I tried another thing on the client. After getting the cert, I converted it to a string
DWORD size = 0;
CryptBinaryToString(cert->pbCertEncoded, cert->cbCertEncoded, CRYPT_STRING_BASE64, NULL, &size);
LPWSTR outstring = new TCHAR[size];
CryptBinaryToString(cert->pbCertEncoded, cert->cbCertEncoded, CRYPT_STRING_BASE64, outstring, &size);
If I try to send outstring in the header, it complains:
WinHttpAddRequestHeaders: 87: The parameter is incorrect.
But when I take the contents of outstring and try to parse it on the server, it decodes back to the right certificate. This tells me that I'm not doing something right when passing cert->pbCertEncoded in the header. Maybe I need to re-encode it or transform it somehow so the server can correctly parse it? I'd appreciate any help. Thanks!
My client is in c++ and server in .NET. I'm using cpprestsdk to send the certificate in the http request.
The pbCertEncoded is the ASN.1 encoded representation of the certificate. Look for instance here.
So you must encode the bytes to base64 for instance like this:
#include <Wincrypt.h>
#pragma comment (lib, "Crypt32.lib")
int ToBase64Crypto(const BYTE* pSrc, int nLenSrc, char* pDst, int nLenDst )
{
DWORD nLenOut = nLenDst;
BOOL fRet = CryptBinaryToString(
(const BYTE*)pSrc,
nLenSrc,
CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF,
pDst, &nLenOut
);
if (!fRet) {
nLenOut=0; // failed
}
return (nLenOut);
}
How do I create a local group using Core Services.? Documentation for Core Services says "The Core Services Identity Reference allows developers to support user and group creation.." but there are no examples on how to do it.
Update. This is the code I have so far but It doesn't work and ErrorCode return -2, error description is null. Really struggling to find any documentation that explains how to do it. 0 information on error codes as well.
CFStringRef realName = CFStringCreateWithCString(NULL, "newGroupTest",
kCFStringEncodingMacRoman);
CFStringRef posixName = CFStringCreateWithCString(NULL, "newgrptst1",
kCFStringEncodingMacRoman);
AuthorizationRef auth;
OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment,
kAuthorizationFlagDefaults,
&auth);
CSIdentityAuthorityRef authority = CSGetDefaultIdentityAuthority();
CSIdentityRef identity = CSIdentityCreate(NULL, kCSIdentityClassGroup, realName,
posixName, kCSIdentityFlagNone, authority);
CFErrorRef error;
BOOL success = CSIdentityCommit(Identity, auth, &error);
if(!success)
{
CFIndex index = CFErrorGetCode(error);
CFStringRef desc = CFErrorCopyDescription(error);
const char* cDesc = CFStringGetCStringPtr(desc, CFStringGetSystemEncoding());
}
Found what was the problem. I wasn't using correct identity authority. To create a local group you need to use CSGetLocaldentityAuthority() that get a local identity authority that stores the identities local to the system, instead of
CSGetDefaultIdentityAuthority() that represents the network-bound authorities.
I have working code where two peers are connecting over a relay server (coturn) and everything seems to be fine over pseudo-tcp. I've tested message exchange successfully with nice_agent_attach_recv() and nice_agent_get_io_stream().
But when I try to create a GTlsClientConnection I get back: 0:TLS support is not available
Here is some partial code:
if(!nice_agent_set_relay_info(agent, stream_id,
NICE_COMPONENT_TYPE_RTP,
"my.coturn.server",
5349, //tls-listener-port (I also tried the non tls port: 3478)
username.c_str(),
password.c_str(),
NICE_RELAY_TYPE_TURN_TCP))
{
printf("error setting up relay info\n");
}
...
//after state has changed to NICE_COMPONENT_STATE_READY
...
io_stream = nice_agent_get_io_stream (agent, stream_id, component_id);
input = g_io_stream_get_input_stream (G_IO_STREAM (io_stream));
output = g_io_stream_get_output_stream (G_IO_STREAM (io_stream));
GIOStream* tlsConnection = g_tls_client_connection_new
(G_IO_STREAM (io_stream), NULL, &error);
/////////////////////////
/// error == 0 (TLS support is not available)
I am new to libnice and glib. So, I may be missing something basic.
Probably need the glib-networking package installed.
Days of troubleshooting on this one, googling solutions & re-reading Microsoft documentation on the needed functions. Changing variables, retrying again and again. Help is very thoroughly appreciated, I'm sure it's not just me running into this.
I am working to implement networked client & server apps which communicate with a SSL/TLS layer using SChannel (and later will also get this working with OpenSSL for cross-compatibility). I have a client which is known to work, so we can focus on server-side.
For now the goal is to have it work without providing a certificate on either side (they should just generate on the fly as needed). I perform AcquireCredentialsHandle, load up the initial token from the client (running on the same host) and call AcceptSecurityContext.
It seems that no matter what variable I change I always end up with the same 0x80090331 error on the first call to AcceptSecurityContext. Tested on windows 7 & Windows Server 2012.
It seems to me there must be something outside my code, an OS setting that I need to fix. I'm finding contradictory information on the web. TLS 1.1 & TLS 1.2 have been added to the registry under SecurityProviders\SCHANNEL\Protocols* and set with a data DisabledByDefault=0. Also added ", schannel.dll" to 'SecurityProviders'.
Code is as follows:
... snip ... (Code to call AcquireCredentialsHandle)
PCCERT_CONTEXT serverCerts[1] = {0};
SCHANNEL_CRED sc = {0};
sc.dwVersion = SCHANNEL_CRED_VERSION;
//sc.grbitEnabledProtocols = SP_PROT_SSL3_SERVER | SP_PROT_TLS1_SERVER | SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER;
sc.grbitEnabledProtocols = SP_PROT_TLS1_1_SERVER | SP_PROT_TLS1_2_SERVER;
sc.dwFlags = 0;
sc.cCreds = 0; // Let Crypto API find the appropriate certificate for us
sc.paCred = serverCerts;
TimeStamp tsExpiry;
SECURITY_STATUS status = AcquireCredentialsHandle(
NULL,
UNISP_NAME,
SECPKG_CRED_INBOUND,
NULL,
&sc,
NULL,
NULL,
&hCredential,
&tsExpiry);
std::cout << "AcquireCredentialsHandle result = 0x" << std::hex << status << std::endl;
... snip ... (Code to call AcceptSecurityContext)
// TOKEN is received from client into m_receivedData
//std::vector<char> m_receivedData;
m_ctxtFlags = ASC_REQ_ALLOCATE_MEMORY | ASC_REQ_STREAM;
SecBuffer inBuffers[2];
// Provide Schannel with the remote host's handshake data
inBuffers[0].pvBuffer = (char*)(&m_receivedData[0]);
inBuffers[0].cbBuffer = (unsigned long)m_receivedData.size();
inBuffers[0].BufferType = SECBUFFER_TOKEN;
inBuffers[1].pvBuffer = NULL;
inBuffers[1].cbBuffer = 0;
inBuffers[1].BufferType = SECBUFFER_EMPTY;
SecBufferDesc inBufferDesc = {0};
inBufferDesc.cBuffers = 2;
inBufferDesc.pBuffers = inBuffers;
inBufferDesc.ulVersion = SECBUFFER_VERSION;
SecBuffer outBuffers[2];
// We let Schannel allocate the output buffer for us
outBuffers[0].pvBuffer = NULL;
outBuffers[0].cbBuffer = 0;
outBuffers[0].BufferType = SECBUFFER_TOKEN;
// Contains alert data if an alert is generated
outBuffers[1].pvBuffer = NULL;
outBuffers[1].cbBuffer = 0;
outBuffers[1].BufferType = SECBUFFER_ALERT;
SecBufferDesc outBufferDesc = {0};
outBufferDesc.cBuffers = 2;
outBufferDesc.pBuffers = outBuffers;
outBufferDesc.ulVersion = SECBUFFER_VERSION;
unsigned long fContextAttr;
TimeStamp tsTimeStamp;
SECURITY_STATUS status = NULL;
if (isFirstCall) {
status = AcceptSecurityContext(
&hCredential,
NULL,
&inBufferDesc,
m_ctxtFlags,
0,
&hNewContext,
&outBufferDesc,
&fContextAttr,
NULL);
std::cout << "AcceptSecurityContext (isFirstCall) result = 0x" << std::hex << status << std::endl;
}
SSL/TLS requires a server-side certificate. A missing server-side certificate generates the 0x80090331 error. The next step is to create a self-signed certificate as needed. See CertCreateSelfSignCertificate. A C example using CryptoAPI can be found here.