int connectViaSocksProxy( INOUT STREAM *stream ) { MESSAGE_DATA msgData; BYTE socksBuffer[ 64 + CRYPT_MAX_TEXTSIZE + 8 ], *bufPtr = socksBuffer; char userName[ CRYPT_MAX_TEXTSIZE + 8 ]; int length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); REQUIRES_S( stream->type == STREAM_TYPE_NETWORK ); /* Get the SOCKS user name, defaulting to "cryptlib" if there's none set */ setMessageData( &msgData, userName, CRYPT_MAX_TEXTSIZE ); status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_OPTION_NET_SOCKS_USERNAME ); if( cryptStatusOK( status ) ) userName[ msgData.length ] = '\0'; else strlcpy_s( userName, CRYPT_MAX_TEXTSIZE, "cryptlib" ); /* Build up the SOCKSv4 request string: BYTE: version = 4 BYTE: command = 1 (connect) WORD: port LONG: IP address STRING: userName + '\0' Note that this has a potential problem in that it requires a DNS lookup by the client, which can lead to problems if the client can't get DNS requests out because only SOCKSified access is allowed. A related problem occurs when SOCKS is being used as a tunnelling interface because the DNS lookup will communicate data about the client to an observer outside the tunnel. To work around this there's a so-called SOCKSv4a protocol that has the SOCKS proxy perform the lookup: BYTE: version = 4 BYTE: command = 1 (connect) WORD: port LONG: IP address = 0x00 0x00 0x00 0xFF STRING: userName + '\0' STRING: FQDN + '\0' Unfortunately there's no way to tell whether a SOCKS server supports 4a or only 4, but in any case since SOCKS support is currently disabled we leave the poke-and-hope 4a detection until such time as someone actually requests it */ *bufPtr++ = 4; *bufPtr++ = 1; mputWord( bufPtr, netStream->port ); status = getIPAddress( stream, bufPtr, netStream->host ); strlcpy_s( bufPtr + 4, CRYPT_MAX_TEXTSIZE, userName ); length = 1 + 1 + 2 + 4 + strlen( userName ) + 1; if( cryptStatusError( status ) ) { netStream->transportDisconnectFunction( stream, TRUE ); return( status ); } /* Send the data to the server and read back the reply */ status = netStream->transportWriteFunction( stream, socksBuffer, length, TRANSPORT_FLAG_FLUSH ); if( cryptStatusOK( status ) ) status = netStream->transportReadFunction( stream, socksBuffer, 8, TRANSPORT_FLAG_BLOCKING ); if( cryptStatusError( status ) ) { /* The involvement of a proxy complicates matters somewhat because we can usually connect to the proxy OK but may run into problems going from the proxy to the remote server, so if we get an error at this stage (which will typically show up as a read error from the proxy) we report it as an open error instead */ if( status == CRYPT_ERROR_READ || status == CRYPT_ERROR_COMPLETE ) status = CRYPT_ERROR_OPEN; netStream->transportDisconnectFunction( stream, TRUE ); return( status ); } /* Make sure that everything is OK: BYTE: null = 0 BYTE: status = 90 (OK) WORD: port LONG: IP address */ if( socksBuffer[ 1 ] != 90 ) { int i; netStream->transportDisconnectFunction( stream, TRUE ); strlcpy_s( netStream->errorInfo->errorString, MAX_ERRMSG_SIZE, "Socks proxy returned" ); for( i = 0; i < 8; i++ ) { sprintf_s( netStream->errorInfo->errorString + 20 + ( i * 3 ), MAX_ERRMSG_SIZE - ( 20 + ( i * 3 ) ), " %02X", socksBuffer[ i ] ); } strlcat_s( netStream->errorInfo->errorString, MAX_ERRMSG_SIZE, "." ); netStream->errorCode = socksBuffer[ 1 ]; return( CRYPT_ERROR_OPEN ); } return( CRYPT_OK ); }
static int getItemFunction( DEVICE_INFO *deviceInfo, CRYPT_CONTEXT *iCryptContext, const KEYMGMT_ITEM_TYPE itemType, const CRYPT_KEYID_TYPE keyIDtype, const void *keyID, const int keyIDlength, void *auxInfo, int *auxInfoLength, const int flags ) { #if 0 const CRYPT_DEVICE cryptDevice = deviceInfo->objectHandle; CRYPT_CERTIFICATE iCryptCert = CRYPT_UNUSED; const CAPABILITY_INFO *capabilityInfoPtr; HW_KEYINFO keyInfo; MESSAGE_DATA msgData; const int extraFlags = ( itemType == KEYMGMT_ITEM_PRIVATEKEY ) ? \ KEYMGMT_FLAG_DATAONLY_CERT : 0; int keyHandle, p15status, status; #endif CRYPT_CONTEXT iLocalContext; HARDWARE_INFO *hardwareInfo = deviceInfo->deviceHardware; MESSAGE_KEYMGMT_INFO getkeyInfo; int keyHandle, status; assert( isWritePtr( deviceInfo, sizeof( DEVICE_INFO ) ) ); assert( isWritePtr( iCryptContext, sizeof( CRYPT_CONTEXT ) ) ); assert( isReadPtr( keyID, keyIDlength ) ); REQUIRES( itemType == KEYMGMT_ITEM_PUBLICKEY || \ itemType == KEYMGMT_ITEM_PRIVATEKEY ); REQUIRES( keyIDtype == CRYPT_KEYID_NAME || \ keyIDtype == CRYPT_KEYID_URI || \ keyIDtype == CRYPT_IKEYID_KEYID || \ keyIDtype == CRYPT_IKEYID_PGPKEYID || \ keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER ); REQUIRES( auxInfo == NULL && *auxInfoLength == 0 ); REQUIRES( flags >= KEYMGMT_FLAG_NONE && flags < KEYMGMT_FLAG_MAX ); #if 1 /* Redirect the fetch down to the PKCS #15 storage object, which will create either a dummy context that we have to connect to the actual hardware for a private-key object or a native public-key/certificate object if it's a non-private-key item */ if( hardwareInfo->iCryptKeyset == CRYPT_ERROR ) return( CRYPT_ERROR_NOTINITED ); setMessageKeymgmtInfo( &getkeyInfo, keyIDtype, keyID, keyIDlength, NULL, 0, flags ); status = krnlSendMessage( hardwareInfo->iCryptKeyset, IMESSAGE_KEY_GETKEY, &getkeyInfo, itemType ); if( cryptStatusError( status ) ) return( status ); iLocalContext = getkeyInfo.cryptHandle; /* If it's a public-key fetch, we've created a native object and we're done */ if( itemType != KEYMGMT_ITEM_PRIVATEKEY ) { *iCryptContext = iLocalContext; return( CRYPT_OK ); } /* It was a private-key fetch, we need to connect the dummy context that was created with the underlying hardware. When this final step has been completed we can move the context to the initialised state */ status = getHardwareReference( iLocalContext, &keyHandle ); if( cryptStatusError( status ) ) { krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT ); return( status ); } status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE, &keyHandle, CRYPT_IATTRIBUTE_DEVICEOBJECT ); if( cryptStatusOK( status ) ) { status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_UNUSED, CRYPT_IATTRIBUTE_INITIALISED ); } if( cryptStatusError( status ) ) { krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT ); return( status ); } #else /* As a first step we redirect the fetch down to the PKCS #15 storage object to get any certificate that may be associated with the item. We always do this because if we're fetching a public key then this is all that we need and if we're fetching a private key then we'll associate the certificate with it if it's present */ if( hardwareInfo->iCryptKeyset == CRYPT_ERROR ) return( CRYPT_ERROR_NOTINITED ); setMessageKeymgmtInfo( &getkeyInfo, keyIDtype, keyID, keyIDlength, NULL, 0, flags | extraFlags ); p15status = krnlSendMessage( hardwareInfo->iCryptKeyset, IMESSAGE_KEY_GETKEY, &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY ); if( cryptStatusOK( p15status ) ) { iCryptCert = getkeyInfo.cryptHandle; /* If we were after a public key, we're done */ if( itemType == KEYMGMT_ITEM_PUBLICKEY ) { *iCryptContext = iCryptCert; return( CRYPT_OK ); } /* If we were after a private key and the keyID is one that isn't stored locally, extract it from the returned private key */ // This won't work, there's no CRYPT_IKEYID_KEYID or // CRYPT_IKEYID_PGPKEYID with the cert, only an // CRYPT_IKEYID_ISSUERANDSERIALNUMBER } /* Look up the private key, which we may use directly or simply extract the public-key components from. If there's an error then we preferentially return the error code from the storage object, which is likely to be more informative than a generic CRYPT_ERROR_NOTFOUND from the hardware lookup */ status = hwLookupItemInfo( keyIDtype, keyID, keyIDlength, &keyHandle, &keyInfo ); if( cryptStatusError( status ) ) return( cryptStatusError( p15status ) ? p15status : status ); if( itemType == KEYMGMT_ITEM_PUBLICKEY ) { MESSAGE_CREATEOBJECT_INFO createInfo; /* There's no certificate present but we do have private-key components from which we can extract the public-key portion to create a native context instead of a device one. This solves a variety of problems including the fact that performing public-key operations natively is often much faster than the time it takes to marshall the data and get it to the crypto hardware, and that if we do it ourselves we can defend against a variety of RSA padding and timing attacks that have come up since the device firmware was done */ setMessageCreateObjectInfo( &createInfo, keyInfo.cryptAlgo ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT, &createInfo, OBJECT_TYPE_CONTEXT ); if( cryptStatusError( status ) ) return( status ); iLocalContext = createInfo.cryptHandle; /* Send the keying information to the context. We don't set the action flags because there are none recorded at the hardware level */ status = setPublicComponents( createInfo.cryptHandle, keyInfo.cryptAlgo, &keyInfo.publicKeyInfo ); if( cryptStatusError( status ) ) { krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT ); return( status ); } *iCryptContext = iLocalContext; return( CRYPT_OK ); } /* It's a private key, create a dummy context for the device object, remember the device that it's contained in, and record the handle for the device-internal key */ capabilityInfoPtr = findCapabilityInfo( deviceInfo->capabilityInfoList, keyInfo.cryptAlgo ); if( capabilityInfoPtr == NULL ) return( CRYPT_ERROR_NOTAVAIL ); status = createContextFromCapability( &iLocalContext, cryptDevice, capabilityInfoPtr, CREATEOBJECT_FLAG_DUMMY | \ CREATEOBJECT_FLAG_PERSISTENT ); if( cryptStatusError( status ) ) { if( iCryptCert != CRYPT_UNUSED ) krnlSendNotifier( iCryptCert, IMESSAGE_DECREFCOUNT ); return( status ); } status = krnlSendMessage( iLocalContext, IMESSAGE_SETDEPENDENT, ( MESSAGE_CAST ) &cryptDevice, SETDEP_OPTION_INCREF ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE, ( MESSAGE_CAST ) &keyHandle, CRYPT_IATTRIBUTE_DEVICEOBJECT ); if( cryptStatusError( status ) ) { krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT ); if( iCryptCert != CRYPT_UNUSED ) krnlSendNotifier( iCryptCert, IMESSAGE_DECREFCOUNT ); return( status ); } /* Set the object's label, send the keying information to the context, and mark it as initialised (i.e. with a key loaded). Setting the label requires special care because the label that we're setting matches that of an existing object, so trying to set it as a standard CRYPT_CTXINFO_LABEL will return a CRYPT_ERROR_DUPLICATE error when the context code checks for the existence of an existing label. To handle this, we use the attribute CRYPT_IATTRIBUTE_EXISTINGLABEL to indicate that we're setting a label that matches an existing object in the device */ if( keyInfo.labelLength <= 0 ) { /* If there's no label present, use a dummy value */ strlcpy_s( keyInfo.label, CRYPT_MAX_TEXTSIZE, "Label-less private key" ); keyInfo.labelLength = 22; } setMessageData( &msgData, keyInfo.label, keyInfo.labelLength ); status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_EXISTINGLABEL ); if( cryptStatusOK( status ) ) status = setPublicComponents( iLocalContext, keyInfo.cryptAlgo, &keyInfo.publicKeyInfo ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iLocalContext, IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_UNUSED, CRYPT_IATTRIBUTE_INITIALISED ); if( cryptStatusOK( status ) && ( iCryptCert != CRYPT_UNUSED ) ) { /* If there's a certificate present, attach it to the context. The certificate is an internal object used only by the context so we tell the kernel to mark it as owned by the context only */ status = krnlSendMessage( iLocalContext, IMESSAGE_SETDEPENDENT, ( MESSAGE_CAST ) &iCryptCert, SETDEP_OPTION_NOINCREF ); } if( cryptStatusError( status ) ) { krnlSendNotifier( iLocalContext, IMESSAGE_DECREFCOUNT ); if( iCryptCert != CRYPT_UNUSED ) krnlSendNotifier( iCryptCert, IMESSAGE_DECREFCOUNT ); } #endif /* 1 */ *iCryptContext = iLocalContext; return( CRYPT_OK ); }