2025-04-27 07:49:33 -04:00

1420 lines
30 KiB
C++

//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: db2.cpp
//
// Contents: Cert Server Data Base interface implementation
//
// History: 15-nov-96 hanshu created
//
//---------------------------------------------------------------------------
// NOTE: this is a TEMPORARY implementation of the old property-based
// api onto the new table-based database. it is not a happy fit,
// and this will be replaced by an update/query api that is
// more appropriate to the relational model and scales better.
//
// FURTHER NOTE: there's now a caching layer that improves the performance
// of the one-field-per-call design of the api. Set-operations are
// not written to the data base immediately, but are queued up and
// lazy-written in a single bulk-update sql operation. Get-operations
// read from the queue or go to the database if needed. A further
// optimization would be to do a bulk-read to prime the queue data,
// but the present scheme is sufficient for the current pattern of
// use where the cert is built up field-by-field.
#include <pch.cpp>
#pragma hdrstop
#include "certdb2.h"
#include "csprop2.h"
#include "db2.h"
#include "dbcore.h"
#include "odbc.h"
#define __myFILE__ "db2.cpp"
// NOTE: the only data base operation that needs concurency-protection is
// the allocation of new RequestIDs and NameIDs in the Miscellaneous
// table.
//
// For now I assume that the ODBC drivers are thread-safe enough to
// allow concurrent activity on separate HSTMTs; if this turns out to
// be false, then every entry-point in this module must be serialized
// globally.
DWORD rgdwActualTable[] = // maps the above values to their actual host table
{
TABLE_NAMES,
TABLE_REQUESTS,
TABLE_CERTIFICATES,
TABLE_NAMES,
TABLE_NAMES,
TABLE_REQUEST_ATTRIBS,
TABLE_EXTENSIONS
};
UCHAR dsn[64] = "CertSrv"; // default values
UCHAR user[64] = "Admin";
UCHAR pwd[64] = "";
/////
DBTABLE_RED const db_adtRequests[] =
{
{ // DWORD
g_wszPropRequestRequestID,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RequestID",
SQL_C_ULONG,
SQL_INTEGER
},
{ // BLOB
g_wszPropRequestRawRequest,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RawRequest",
SQL_C_BINARY,
SQL_LONGVARBINARY
},
{ // DWORD
g_wszPropRequestAttributes,
NULL,
0, // dwFlags
cchATTRIBUTESMAX, // dwcbMax
TABLE_REQUESTS,
L"RequestAttributes",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // DWORD
g_wszPropRequestType,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RequestType",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequestFlags,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RequestFlags",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequestStatus,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RequestStatus",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequestStatusCode,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"StatusCode",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequestDisposition,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"Disposition",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequestDispositionMessage,
NULL,
0, // dwFlags
cchREQUESTDISPOSITIONMESSAGE,// dwcbMax
TABLE_REQUESTS,
L"DispositionMessage",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // FILETIME
g_wszPropRequestSubmittedWhen,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"SubmittedWhen",
SQL_C_TIMESTAMP,
SQL_TIMESTAMP
},
{ // FILETIME
g_wszPropRequestResolvedWhen,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"ResolvedWhen",
SQL_C_TIMESTAMP,
SQL_TIMESTAMP
},
{ // FILETIME
g_wszPropRequestRevokedWhen,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RevokedWhen",
SQL_C_TIMESTAMP,
SQL_TIMESTAMP
},
{ // FILETIME
g_wszPropRequestRevokedEffectiveWhen,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RevokedEffectiveWhen",
SQL_C_TIMESTAMP,
SQL_TIMESTAMP
},
{ // DWORD
g_wszPropRequestRevokedReason,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,
L"RevokedReason",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequestSubjectNameID,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_REQUESTS,//TABLE_CERTIFICATES,
L"SubjectNameID",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropRequesterName,
NULL,
0, // dwFlags
cchREQUESTERNAMEMAX, // dwcbMax
TABLE_REQUESTS,
L"RequesterName",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // DWORD
g_wszPropRequesterAddress,
NULL,
0, // dwFlags
cchREQUESTERADDRESSMAX, // dwcbMax
TABLE_REQUESTS,
L"RequesterAddress",
SQL_C_CHAR,
SQL_VARCHAR
},
{ NULL, NULL, 0, 0, NULL, 0, 0 } // Termination marker
};
DBTABLE_RED const db_adtCertificates[] =
{
{ // DWORD
g_wszPropCertificateRequestID,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"RequestID",
SQL_C_ULONG,
SQL_INTEGER
},
{ // BLOB
g_wszPropRawCertificate,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"RawCertificate",
SQL_C_BINARY,
SQL_LONGVARBINARY
},
{ // DWORD
wszPROPCERTIFICATEHASH,
NULL,
0, // dwFlags
cchHASHMAX, // dwcbMax
TABLE_CERTIFICATES,
L"CertificateHash",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // DWORD
g_wszPropCertificateType,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"CertificateType",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropCertificateSerialNumber,
NULL,
0, // dwFlags
cchSERIALNUMBERMAX, // dwcbMax
TABLE_CERTIFICATES,
L"SerialNumber",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // DWORD
g_wszPropCertificateIssuerNameID,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"IssuerNameID",
SQL_C_ULONG,
SQL_INTEGER
},
{ // DWORD
g_wszPropCertificateSubjectNameID,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"SubjectNameID",
SQL_C_ULONG,
SQL_INTEGER
},
{ // FILETIME
g_wszPropCertificateNotBeforeDate,
NULL,
DBTF_POLICYWRITEABLE, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"NotBefore",
SQL_C_TIMESTAMP,
SQL_TIMESTAMP
},
{ // FILETIME
g_wszPropCertificateNotAfterDate,
NULL,
DBTF_POLICYWRITEABLE, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"NotAfter",
SQL_C_TIMESTAMP,
SQL_TIMESTAMP
},
{ // BLOB
g_wszPropCertificateRawPublicKey,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"PublicKey",
SQL_C_BINARY,
SQL_LONGVARBINARY
},
{ // STRING
g_wszPropCertificatePublicKeyAlgorithm,
NULL,
0, // dwFlags
cchOBJECTIDMAX, // dwcbMax
TABLE_CERTIFICATES,
L"PublicKeyAlgorithm",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // BLOB
g_wszPropCertificateRawPublicKeyAlgorithmParameters,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_CERTIFICATES,
L"PublicKeyParams",
SQL_C_BINARY,
SQL_VARBINARY
},
{ NULL, NULL, 0, 0, NULL, 0, 0 } // Termination marker
};
DBTABLE_RED const db_adtNames[] =
{
{ // STRING
g_wszPropDistinguishedName,
NULL,
0, // dwFlags
cchDISTINGUISHEDNAMEMAX,// dwcbMax
TABLE_SUBJECT_NAME,
L"DistinguishedName",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // BLOB
g_wszPropRawName,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_SUBJECT_NAME,
L"RawName",
SQL_C_BINARY,
SQL_LONGVARBINARY
},
{ // DWORD
g_wszPropNameType,
NULL,
0, // dwFlags
0, // dwcbMax
TABLE_SUBJECT_NAME,
L"NameType",
SQL_C_ULONG,
SQL_INTEGER
},
{ // STRING
g_wszPropCountry,
TEXT(szOID_COUNTRY_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchCOUNTRYNAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"Country",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropOrganization,
TEXT(szOID_ORGANIZATION_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchORGANIZATIONNAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"Organization",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropOrgUnit,
TEXT(szOID_ORGANIZATIONAL_UNIT_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchORGANIZATIONALUNITNAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"OrganizationalUnit",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropCommonName,
TEXT(szOID_COMMON_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchCOMMONNAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"CommonName",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropLocality,
TEXT(szOID_LOCALITY_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchLOCALITYMANAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"Locality",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropState,
TEXT(szOID_STATE_OR_PROVINCE_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchSTATEORPROVINCENAMEMAX,// dwcbMax
TABLE_SUBJECT_NAME,
L"StateOrProvince",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropTitle,
TEXT(szOID_TITLE),
DBTF_POLICYWRITEABLE, // dwFlags
cchTITLEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"Title",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropGivenName,
TEXT(szOID_GIVEN_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchGIVENNAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"GivenName",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropInitials,
TEXT(szOID_INITIALS),
DBTF_POLICYWRITEABLE, // dwFlags
cchINITIALSMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"Initials",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropSurName,
TEXT(szOID_SUR_NAME),
DBTF_POLICYWRITEABLE, // dwFlags
cchSURNAMEMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"SurName",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropDomainComponent,
TEXT(szOID_DOMAIN_COMPONENT),
DBTF_POLICYWRITEABLE, // dwFlags
cchDOMAINCOMPONENTMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"DomainComponent",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropEMail,
TEXT(szOID_RSA_emailAddr),
DBTF_POLICYWRITEABLE, // dwFlags
cchEMAILMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"EMail",
SQL_C_CHAR,
SQL_VARCHAR
},
{ // STRING
g_wszPropStreetAddress,
TEXT(szOID_STREET_ADDRESS),
DBTF_POLICYWRITEABLE, // dwFlags
cchSTREETADDRESSMAX, // dwcbMax
TABLE_SUBJECT_NAME,
L"StreetAddress",
SQL_C_CHAR,
SQL_VARCHAR
},
{ NULL, NULL, 0, 0, NULL, 0, 0 } // Termination marker
};
DBTABLE_RED const db_dtExtensionFlags =
{ // DWORD
wszPROPCERTIFICATEEXTENSIONFLAGS,
NULL,
DBTF_POLICYWRITEABLE, // dwFlags
0, // dwcbMax
TABLE_EXTENSIONS,
L"ExtensionFlags",
SQL_C_ULONG,
SQL_INTEGER
};
DBTABLE_RED const db_dtExtensionValue =
{ // BLOB
wszPROPCERTIFICATEEXTENSIONVALUE,
NULL,
DBTF_POLICYWRITEABLE, // dwFlags
0, // dwcbMax
TABLE_EXTENSIONS,
L"ExtensionRawValue",
SQL_C_BINARY,
SQL_VARBINARY
};
DBTABLE_RED const db_attrib =
{
// STRING
NULL,
NULL,
0, // dwFlags
cchATTRIBUTESMAX, // dwcbMax
TABLE_REQUEST_ATTRIBS,
NULL, // filled in dynamically
SQL_C_CHAR,
SQL_VARCHAR
};
// Note: Ordered DUPTABLE must match Names Table columns 1 to 1.
DUPTABLE const db_dntr[] =
{
{
"NameID",
FALSE,
SQL_C_ULONG,
SQL_INTEGER,
NULL,
},
{
"DistinguishedName",
FALSE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectDistinguishedName,
},
{
"RawName",
FALSE,
SQL_C_BINARY,
SQL_LONGVARBINARY,
g_wszPropSubjectRawName,
},
{
"NameType",
FALSE,
SQL_C_ULONG,
SQL_INTEGER,
g_wszPropSubjectNameType,
},
{
"Country",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectCountry,
},
{
"Organization",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectOrganization,
},
{
"OrganizationalUnit",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectOrgUnit,
},
{
"CommonName",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectCommonName,
},
{
"Locality",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectLocality,
},
{
"StateOrProvince",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectState,
},
{
"Title",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectTitle,
},
{
"GivenName",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectGivenName,
},
{
"Initials",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectInitials,
},
{
"SurName",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectSurName,
},
{
"DomainComponent",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectDomainComponent,
},
{
"EMail",
TRUE,
SQL_C_CHAR,
SQL_VARCHAR,
g_wszPropSubjectEMail,
},
};
/////
void Error ( CHAR* msg )
{
wprintf(L"DBError: %hs\n", msg);
}
///// initialize database access
HRESULT
RedDBOpen(
WCHAR const *pwszAuthority)
{
STATUS rc = SQL_SUCCESS;
STATUS rt;
// get info from registry
HKEY hkey;
HKEY hkeyCA;
int s;
s = RegOpenKey(HKEY_LOCAL_MACHINE, wszREGKEYCONFIGPATH, &hkey);
if (ERROR_SUCCESS == s)
{
s = RegOpenKey(hkey, pwszAuthority, &hkeyCA);
if (ERROR_SUCCESS == s)
{
DWORD size;
// Ignore errors -- just use the previously initialized values.
size = sizeof(dsn);
s = RegQueryValueExA(hkeyCA, szREGDBDSN, NULL, NULL, dsn, &size);
size = sizeof(user);
s = RegQueryValueExA(hkeyCA, szREGDBUSER, NULL, NULL, user, &size);
size = sizeof(pwd);
s = RegQueryValueExA(hkeyCA, szREGDBPASSWORD, NULL, NULL, pwd, &size);
RegCloseKey(hkeyCA);
}
RegCloseKey(hkey);
}
wprintf(myLoadResourceString(IDS_RED_CONNECTING), dsn, user);
wprintf(wszNewLine);
rc = odbcInitRequestQueue(dsn, user, pwd);
if (ERROR_SUCCESS != rc)
{
goto error;
}
error:
return(rc);
}
/////
HRESULT
RedDBShutDown(VOID) // finalise database access
{
odbcFinishRequestQueue();
return ERROR_SUCCESS;
}
/////
BOOL
dbVerifyPropertyLength(
IN DWORD dwFlags,
IN DWORD cbProp,
IN BYTE const *pbProp)
{
BOOL fOk = FALSE;
switch (dwFlags & PROPTYPE_MASK)
{
case PROPTYPE_LONG:
fOk = sizeof(LONG) == cbProp;
break;
case PROPTYPE_DATE:
fOk = sizeof(FILETIME) == cbProp;
break;
case PROPTYPE_BINARY:
fOk = TRUE; // nothing to check
break;
case PROPTYPE_STRING:
if (MAXDWORD == cbProp)
{
cbProp = wcslen((WCHAR const *) pbProp) * sizeof(WCHAR);
}
fOk =
0 == cbProp ||
NULL == pbProp ||
wcslen((WCHAR const *) pbProp) * sizeof(WCHAR) == cbProp;
break;
case PROPTYPE_ANSI:
if (MAXDWORD == cbProp)
{
cbProp = strlen((char const *) pbProp);
}
fOk =
0 == cbProp ||
NULL == pbProp ||
strlen((char const *) pbProp) == cbProp;
break;
}
return(fOk);
}
BOOL
dbVerifyPropertyValue(
IN DWORD dwFlags,
IN DWORD cbProp,
IN DBTABLE_RED const *pdt)
{
SWORD wType;
DWORD err = ERROR_INVALID_PARAMETER;
switch (dwFlags & PROPTYPE_MASK)
{
case PROPTYPE_LONG:
wType = SQL_C_ULONG;
break;
case PROPTYPE_DATE:
wType = SQL_C_TIMESTAMP;
break;
case PROPTYPE_BINARY:
wType = SQL_C_BINARY;
break;
case PROPTYPE_STRING:
CSASSERT(!"dbVerifyPropertyValue: unexpected PROPTYPE_STRING");
cbProp /= sizeof(WCHAR);
wType = SQL_C_CHAR;
break;
case PROPTYPE_ANSI:
wType = SQL_C_CHAR;
break;
default:
DBGERRORPRINTLINE("Property value type unknown", err);
goto error;
}
if (pdt->wCType != wType)
{
DBGERRORPRINTLINE("Property value type mismatch", err);
goto error;
}
// Note: cbProp and dwcbMax do not include the trailing '\0'.
if (SQL_C_CHAR == wType && pdt->dwcbMax < cbProp)
{
err = ERROR_BUFFER_OVERFLOW;
DBGERRORPRINTLINE("Property value string too long", err);
DBGCODE(wprintf(
L"dbVerifyPropertyValue: len = %u, max = %u\n",
cbProp,
pdt->dwcbMax));
goto error;
}
err = ERROR_SUCCESS;
error:
return(err);
}
DWORD // ERROR_*
RedDBGetPropertyW(
IN DWORD ReqId,
IN WCHAR const *pwszPropName,
IN DWORD dwFlags,
IN OUT DWORD *pcbProp,
OPTIONAL OUT BYTE *pbProp)
{
DWORD err;
DWORD cchProp;
char szProp[4 * MAX_PATH];
DWORD *pcbPropT = pcbProp;
BYTE *pbPropT = pbProp;
DWORD FlagsT = dwFlags;
if (PROPTYPE_STRING == (dwFlags & PROPTYPE_MASK))
{
FlagsT = (dwFlags & ~PROPTYPE_MASK) | PROPTYPE_ANSI;
cchProp = sizeof(szProp);
pcbPropT = &cchProp;
pbPropT = (BYTE *) szProp;
}
err = RedDBGetProperty(ReqId, pwszPropName, FlagsT, pcbPropT, pbPropT);
if (ERROR_SUCCESS != err)
{
goto error;
}
if (0 == *pcbPropT)
{
err = CERTSRV_E_PROPERTY_EMPTY;
//DBGERRORPRINTLINE("Empty property", err);
goto error;
}
CSASSERT(dbVerifyPropertyLength(FlagsT, *pcbPropT, pbPropT));
if (PROPTYPE_STRING == (dwFlags & PROPTYPE_MASK))
{
DWORD cwc;
CSASSERT(strlen(szProp) == cchProp);
// do size calc
cwc = MultiByteToWideChar(
GetACP(),
0, // dwFlags
szProp, // lpMultiByteStr
-1, // cchMultiByte
(WCHAR *) pbProp, // lpWideCharStr
0); // cchWideChar
if (*pcbProp < (cwc * sizeof(WCHAR)) )
{
*pcbProp = cwc * sizeof(WCHAR);
err = ERROR_MORE_DATA;
goto error;
}
cwc = MultiByteToWideChar(
GetACP(),
0, // dwFlags
szProp, // lpMultiByteStr
-1, // cchMultiByte
(WCHAR *) pbProp, // lpWideCharStr
cwc); // cchWideChar
if (0 >= cwc)
{
err = GetLastError();
DBGERRORPRINTLINE("MultiByteToWideChar", err);
goto error;
}
// report real size, but remove wchar on returned blob
*pcbProp = cwc * sizeof(WCHAR);
*pcbProp -= sizeof(WCHAR);
}
CSASSERT(dbVerifyPropertyLength(dwFlags, *pcbProp, pbProp));
error:
return(err);
}
/////
// get a field value
STATUS
RedDBGetProperty(
IN REQID ReqId,
IN WCHAR const *pwszPropName,
IN DWORD dwFlags,
IN OUT DWORD *pcbProp,
OPTIONAL OUT BYTE *pbProp)
{
RETCODE rc = SQL_SUCCESS;
DWORD err;
HSTMT hstmt = SQL_NULL_HSTMT;
UCHAR query[256], *whichquery;
DBTABLE_RED dtOut;
BYTE* pbData = pbProp;
DWORD cbData = *pcbProp;
SQLLEN outlen;
DWORD dummy = 0;
NAMEID nameid = 0;
DWORD PropertyType;
static UCHAR getreq[] = "SELECT %ws FROM Requests WHERE RequestID = ?;";
static UCHAR getcert[] = "SELECT %ws FROM Certificates WHERE RequestID = ?;";
static UCHAR getsubject[] = "SELECT Names.%ws, Names.NameId FROM Certificates INNER JOIN "
"Names ON Certificates.SubjectNameID = Names.NameId WHERE RequestID = ?;";
static UCHAR getsubjectrequest[] = "SELECT Names.%ws, Names.NameId FROM Requests INNER JOIN "
"Names ON Requests.SubjectNameID = Names.NameId WHERE RequestID = ?;";
static UCHAR getissuer[] = "SELECT Names.%ws, Names.NameId FROM Certificates INNER JOIN "
"Names ON Certificates.IssuerNameID = Names.NameId WHERE RequestID = ?;";
static UCHAR getattribs[] = "SELECT AttributeValue FROM RequestAttributes WHERE AttributeName = \'%ws\' AND RequestID = ?;";
static UCHAR getextensions[] = "SELECT %ws FROM CertificateExtensions WHERE ExtensionName = \'%ws\' AND RequestID = ?;";
err = MapPropID(pwszPropName, dwFlags, &dtOut);
if (ERROR_SUCCESS != err)
{
DBGERRORPRINTLINE("RedDBGetProperty: MapPropID", err);
goto error;
}
err = dbVerifyPropertyValue(dwFlags, 0, &dtOut);
if (ERROR_SUCCESS != err)
{
DBGERRORPRINTLINE("Property value type mismatch", err);
goto error;
}
// answer field size queries for fixed-size types
if (pbProp == NULL)
{
switch ( dtOut.wCType )
{
case SQL_C_TIMESTAMP:
*pcbProp = sizeof(FILETIME);
goto done;
case SQL_C_ULONG:
*pcbProp = sizeof(ULONG);
goto done;
default: // fall through and do dynamic determination for other types
break;
}
}
// build query string, filling in appropriate field name
switch ( dtOut.dwTable )
{
case TABLE_REQUESTS:
whichquery = getreq;
break;
case TABLE_CERTIFICATES:
whichquery = getcert;
break;
case TABLE_SUBJECT_NAME:
whichquery = getsubject;
break;
case TABLE_REQUESTSUBJECT_NAME:
whichquery = getsubjectrequest;
break;
case TABLE_ISSUER_NAME:
whichquery = getissuer;
break;
case TABLE_REQUEST_ATTRIBS:
whichquery = getattribs;
break;
case TABLE_EXTENSIONS:
whichquery = getextensions;
break;
default:
CSASSERT(!"Unknown table(2)");
}
if (dtOut.dwTable == TABLE_EXTENSIONS)
{
sprintf(
(char *) query,
(char *) whichquery,
dtOut.pwszPropNameObjId,
dtOut.pwszFieldName);
}
else
{
sprintf(
(char *) query,
(char *) whichquery,
dtOut.pwszFieldName);
}
// prepare for output parameter conversion
TIMESTAMP_STRUCT ts;
if ( dtOut.wCType == SQL_C_TIMESTAMP )
{
// need to copy time format on the way out
if (sizeof(FILETIME) > cbData)
{
err = ERROR_BUFFER_OVERFLOW;
DBGERRORPRINTLINE("*pcbProp too small", err);
goto error;
}
pbData = (BYTE*)&ts;
cbData = sizeof(ts);
}
// first see if this property is in the cache
{
if ( pbData == NULL )
{
// no data available - prepare to receive length
pbData = (BYTE*)&dummy; // need non-NULL buffer address
cbData = 0;
}
// not in cache - get data from data base
rc = odbcGPDataFromDB(
ReqId,
dtOut.dwTable,
dtOut.wCType,
pbData,
cbData,
query,
&nameid,
&outlen);
// Check for the case where caller was trying to find out
// size of buffer needed to get a SQL_C_CHAR property.
// Return size of string plus null terminator because of
// ODBC always will null terminates strings that are returned
// even if buffer is too small
if ((SQL_SUCCESS_WITH_INFO == rc || SQL_SUCCESS == rc) &&
NULL == pbProp &&
SQL_C_CHAR == dtOut.wCType &&
SQL_NULL_DATA != outlen)
{
outlen++;
}
if (SQL_SUCCESS_WITH_INFO == rc)
{
// not really interested in the extra info for now
// natural truncation because cbData == 0.
// disagreement on how to count terminating null.
if (NULL != pbProp && SQL_C_CHAR != dtOut.wCType)
{
DBCHECKLINE(rc, hstmt);
}
rc = SQL_SUCCESS;
}
}
if (SQL_SUCCESS == rc)
{
// return proper data size
if (SQL_NULL_DATA == outlen)
{
*pcbProp = 0;
goto done;
}
else if (dtOut.wCType == SQL_C_TIMESTAMP)
{
// convert from ODBC timestamp to FILETIME
SYSTEMTIME st;
st.wYear = ts.year;
st.wMonth = ts.month;
st.wDay = ts.day;
st.wHour = ts.hour;
st.wMinute = ts.minute;
st.wSecond = ts.second;
st.wMilliseconds = 0;
BOOL b = SystemTimeToFileTime ( &st, (FILETIME*)pbProp );
outlen = sizeof(FILETIME);
}
*pcbProp = (DWORD)outlen;
}
// done
if ((rc == SQL_SUCCESS) && (pbProp != NULL) && ((DWORD) outlen > cbData))
{
err = ERROR_MORE_DATA;
//DBGERRORPRINTLINE("RedDBGetProperty: Buffer too small", err);
goto error;
}
CSASSERT(
SQL_SUCCESS != rc ||
dbVerifyPropertyLength(dwFlags, *pcbProp, pbProp));
// handle this special case
if (SQL_NO_DATA_FOUND == rc)
err = ERROR_NO_MORE_ITEMS;
else
err = DBStatus(rc);
error:
done:
if (ERROR_MORE_DATA != err)
{
_PrintIfErrorStr3(
err,
"RedDBGetProperty",
pwszPropName,
CERTSRV_E_PROPERTY_EMPTY,
ERROR_NO_MORE_ITEMS);
}
return(err);
}
// Search the passed DBTABLE_RED matching on property name or objectid string.
DBTABLE_RED const *
dbMapTable(
WCHAR const *pwszPropName,
DBTABLE_RED const *pdt)
{
while (NULL != pdt->pwszPropName)
{
if (0 == lstrcmpi(pwszPropName, pdt->pwszPropName) ||
(NULL != pdt->pwszPropNameObjId &&
0 == lstrcmpi(pwszPropName, pdt->pwszPropNameObjId)))
{
return(pdt);
}
pdt++;
}
return(NULL);
}
/////
// map property-id to sql field name and odbc types
RETCODE
MapPropID(
IN WCHAR const *pwszPropName,
IN DWORD dwFlags,
OUT DBTABLE_RED *pdtOut)
{
DBTABLE_RED const *pdt = NULL;
WCHAR wszPrefix[2 * (sizeof(wszPROPSUBJECTDOT) / sizeof(WCHAR))];
DWORD dwTable;
WCHAR *pwszExtensionName;
RETCODE rc = SQL_SUCCESS;
DBTABLE_RED const *pdbTable;
CSASSERT(NULL != pwszPropName);
dwTable = PROPTABLE_MASK & dwFlags;
CSASSERT(PROPTABLE_REQUEST == dwTable ||
PROPTABLE_CERTIFICATE == dwTable ||
PROPTABLE_EXTENSION == dwTable ||
PROPTABLE_ATTRIBUTE == dwTable);
// Check to see if the request is for L"Subject.", L"RequestAttributes.",
// etc. If it matches one of the known prefixes, parse off the prefix,
// and look up the rest of the property name in the appropriate table.
// If L"RequestAttributes.", return the rest of the property name as the
// AttributeName to retrieve the value field from the database.
if (PROPTABLE_EXTENSION == dwTable)
{
CSASSERT(
((PROPTABLE_EXTENSIONFLAGS | PROPTABLE_EXTENSIONVALUE) & dwFlags) ==
PROPTABLE_EXTENSIONFLAGS ||
((PROPTABLE_EXTENSIONFLAGS | PROPTABLE_EXTENSIONVALUE) & dwFlags) ==
PROPTABLE_EXTENSIONVALUE);
if (PROPTABLE_EXTENSIONVALUE & dwFlags)
{
pdt = &db_dtExtensionValue;
}
else
{
pdt = &db_dtExtensionFlags;
}
*pdtOut = *pdt; // structure copy
pdtOut->pwszPropNameObjId = pdtOut->pwszFieldName;
pdtOut->pwszFieldName = pwszPropName;
}
else
if (PROPTABLE_ATTRIBUTE == dwTable)
{
pdt = &db_attrib;
*pdtOut = *pdt; // structure copy
pdtOut->pwszPropName = pwszPropName;
pdtOut->pwszFieldName = pwszPropName;
}
else
{
WCHAR *pwsz;
CSASSERT(
!((PROPTABLE_EXTENSIONFLAGS | PROPTABLE_EXTENSIONVALUE) & dwFlags));
pwsz = wcschr(pwszPropName, L'.');
if (NULL != pwsz &&
pwsz - pwszPropName + 2 <= sizeof(wszPrefix)/sizeof(WCHAR))
{
BOOL fSubject, fRequestTable;
pwsz++; // skip past L'.'
CopyMemory(
wszPrefix,
pwszPropName,
SAFE_SUBTRACT_POINTERS(pwsz, pwszPropName) * sizeof(WCHAR));
wszPrefix[pwsz - pwszPropName] = L'\0';
fSubject = (0 == lstrcmpi(wszPrefix, g_wszPropSubjectDot));
fRequestTable = (PROPTABLE_REQUEST == dwTable); // SubjectNameID in Certificates or Requests?
if (fSubject)
{
pdt = dbMapTable(pwsz, db_adtNames);
if (NULL != pdt)
{
*pdtOut = *pdt; // structure copy
pdtOut->dwTable =
fRequestTable? TABLE_REQUESTSUBJECT_NAME : TABLE_SUBJECT_NAME;
}
}
}
}
if (NULL == pdt)
{
pdbTable = NULL;
// Otherwise just search the Requests or Certificates table for a
// matching property name or property objectid string.
switch (dwTable)
{
case PROPTABLE_REQUEST:
pdbTable = db_adtRequests;
break;
case PROPTABLE_CERTIFICATE:
pdbTable = db_adtCertificates;
break;
}
if (NULL != pdbTable)
{
pdt = dbMapTable(pwszPropName, pdbTable);
}
if (NULL != pdt)
{
*pdtOut = *pdt; // structure copy
}
else
{
wprintf(
L"DB: unknown \"%ws\" property: %ws\n",
PROPTABLE_REQUEST == dwTable? L"Request" : L"Certificate",
pwszPropName);
rc = SQL_NO_DATA_FOUND;
}
}
return(rc);
}
STATUS
RedDBEnumerateSetup(
IN DWORD ReqId,
IN DWORD fExtOrAttr,
OUT HANDLE *phEnum)
{
DWORD err = ERROR_SUCCESS;
err = odbcDBEnumSetup(ReqId, fExtOrAttr, phEnum);
return(err);
}
STATUS
RedDBEnumerate(
IN HANDLE hEnum,
DWORD *pcb,
WCHAR *pb)
{
DWORD err = ERROR_SUCCESS;
err = odbcDBEnum(hEnum, pcb, pb);
return(err);
}
STATUS
RedDBEnumerateClose(
IN HANDLE hEnum)
{
DWORD err = ERROR_SUCCESS;
err = odbcDBEnumClose(hEnum);
return(err);
}