error_t dhcpv6DumpRelayMessageOption(const Dhcpv6Option *option, uint_t level) { uint8_t type; const char_t *label; //Check the length of the option if(!ntohs(option->length)) return ERROR_INVALID_OPTION; //Retrieve the message type type = option->value[0]; //Get the corresponding label label = (type < arraysize(messageLabel)) ? messageLabel[type] : "Unknown"; //Relay agent/server message? if(type == DHCPV6_MSG_TYPE_RELAY_FORW || type == DHCPV6_MSG_TYPE_RELAY_REPL) { //Get the inner message const Dhcpv6RelayMessage *message = (Dhcpv6RelayMessage *) option->value; //Ensure the length of the DHCPv6 message is acceptable if(ntohs(option->length) < sizeof(Dhcpv6RelayMessage)) return ERROR_INVALID_OPTION; //Dump message header TRACE_DEBUG("%sRelay Message option (%u bytes)\r\n", prefix[level], ntohs(option->length)); TRACE_DEBUG("%sMessage Type = %u (%s)\r\n", prefix[level + 1], message->msgType, label); TRACE_DEBUG("%sHop Count = %u\r\n", prefix[level + 1], message->hopCount); TRACE_DEBUG("%sLink Address = %s\r\n", prefix[level + 1], ipv6AddrToString(&message->linkAddress, NULL)); TRACE_DEBUG("%sPeer Address = %s\r\n", prefix[level + 1], ipv6AddrToString(&message->peerAddress, NULL)); //Dump message options return dhcpv6DumpOptions(message->options, ntohs(option->length) - sizeof(Dhcpv6RelayMessage), level + 1); } //Client/server message? else { //Get the inner message const Dhcpv6Message *message = (Dhcpv6Message *) option->value; //Ensure the length of the DHCPv6 message is acceptable if(ntohs(option->length) < sizeof(Dhcpv6Message)) return ERROR_INVALID_OPTION; //Dump message header TRACE_DEBUG("%sRelay Message option (%u bytes)\r\n", prefix[level], ntohs(option->length)); TRACE_DEBUG("%sMessage Type = %u (%s)\r\n", prefix[level + 1], message->msgType, label); TRACE_DEBUG("%sTransaction ID = 0x%06X\r\n", prefix[level + 1], LOAD24BE(message->transactionId)); //Dump message options return dhcpv6DumpOptions(message->options, ntohs(option->length) - sizeof(Dhcpv6Message), level + 1); } }
error_t tlsParseFinished(TlsContext *context, const TlsFinished *message, size_t length) { error_t error; //Debug message TRACE_INFO("Finished message received (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Check the length of the Finished message if(length < sizeof(TlsFinished)) return ERROR_DECODING_FAILED; //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check current state if(context->state != TLS_STATE_SERVER_FINISHED) return ERROR_UNEXPECTED_MESSAGE; //The verify data is generated from all messages in this //handshake up to but not including the Finished message error = tlsComputeVerifyData(context, TLS_CONNECTION_END_SERVER); } else { //Check current state if(context->state != TLS_STATE_CLIENT_FINISHED) return ERROR_UNEXPECTED_MESSAGE; //The verify data is generated from all messages in this //handshake up to but not including the Finished message error = tlsComputeVerifyData(context, TLS_CONNECTION_END_CLIENT); } //Unable to generate the verify data? if(error) return error; //Check message length if(LOAD24BE(message->length) != context->verifyDataLength) return ERROR_DECODING_FAILED; //Check the resulting verify data if(memcmp(message->verifyData, context->verifyData, context->verifyDataLength)) return ERROR_INVALID_SIGNATURE; //Update the hash value with the incoming handshake message tlsUpdateHandshakeHash(context, message, length); //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Use abbreviated or full handshake? if(context->resume) context->state = TLS_STATE_CLIENT_CHANGE_CIPHER_SPEC; else context->state = TLS_STATE_APPLICATION_DATA; } else { //Use abbreviated or full handshake? if(context->resume) context->state = TLS_STATE_APPLICATION_DATA; else context->state = TLS_STATE_SERVER_CHANGE_CIPHER_SPEC; } //Successful processing return NO_ERROR; }
error_t tlsParseCertificate(TlsContext *context, const TlsCertificate *message, size_t length) { error_t error; const uint8_t *p; size_t n; const char_t *pemCert; size_t pemCertLength; uint8_t *derCert; size_t derCertSize; size_t derCertLength; //X.509 certificates X509CertificateInfo *certInfo = NULL; X509CertificateInfo *issuerCertInfo = NULL; //Debug message TRACE_INFO("Certificate message received (%" PRIuSIZE " bytes)...\r\n", length); TRACE_DEBUG_ARRAY(" ", message, length); //Check the length of the Certificate message if(length < sizeof(TlsCertificate)) return ERROR_DECODING_FAILED; //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check current state if(context->state != TLS_STATE_SERVER_CERTIFICATE) return ERROR_UNEXPECTED_MESSAGE; } else { //Check current state if(context->state != TLS_STATE_CLIENT_CERTIFICATE) return ERROR_UNEXPECTED_MESSAGE; } //Update the hash value with the incoming handshake message tlsUpdateHandshakeHash(context, message, length); //Get the size occupied by the certificate list n = LOAD24BE(message->certificateListLength); //Remaining bytes to process length -= sizeof(TlsCertificate); //Ensure that the chain of certificates is valid if(n > length) return ERROR_DECODING_FAILED; //Compute the length of the certificate list length = n; //The sender's certificate must come first in the list p = message->certificateList; //Start of exception handling block do { //Assume an error... error = ERROR_OUT_OF_MEMORY; //Allocate a memory buffer to store X.509 certificate info certInfo = osAllocMem(sizeof(X509CertificateInfo)); //Failed to allocate memory? if(!certInfo) break; //Allocate a memory buffer to store the parent certificate issuerCertInfo = osAllocMem(sizeof(X509CertificateInfo)); //Failed to allocate memory? if(!issuerCertInfo) break; //TLS operates as a server? if(context->entity == TLS_CONNECTION_END_SERVER) { //Empty certificate list? if(!length) { //Check whether mutual authentication is required if(context->clientAuthMode == TLS_CLIENT_AUTH_REQUIRED) { //If client authentication is required by the server for the handshake //to continue, it may respond with a fatal handshake failure alert error = ERROR_HANDSHAKE_FAILED; break; } else { //Client authentication is optional context->peerCertType = TLS_CERT_NONE; //Exit immediately error = NO_ERROR; break; } } } //Each certificate is preceded by a 3-byte length field if(length < 3) { //Report an error error = ERROR_DECODING_FAILED; break; } //Get the size occupied by the certificate n = LOAD24BE(p); //Jump to the beginning of the DER encoded certificate p += 3; length -= 3; //Make sure that the certificate is valid if(n > length) { //Report an error error = ERROR_DECODING_FAILED; break; } //Display ASN.1 structure error = asn1DumpObject(p, n, 0); //Any error to report? if(error) break; //Parse X.509 certificate error = x509ParseCertificate(p, n, certInfo); //Failed to parse the X.509 certificate? if(error) break; #if (TLS_CLIENT_SUPPORT == ENABLED) //TLS operates as a client? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Check if the hostname must be verified if(context->serverName != NULL) { //Point to the last character of the common name int_t i = certInfo->subject.commonNameLen - 1; //Point to the last character of the hostname int_t j = strlen(context->serverName) - 1; //Check the common name in the server certificate against //the actual hostname that is being requested while(i >= 0 && j >= 0) { //Wildcard certificate found? if(certInfo->subject.commonName[i] == '*' && i == 0) { //The CN is acceptable j = 0; } //Perform case insensitive character comparison else if(tolower((uint8_t) certInfo->subject.commonName[i]) != context->serverName[j]) { break; } //Compare previous characters i--; j--; } //If the host names do not match, reject the certificate if(i >= 0 || j >= 0) { //Debug message TRACE_WARNING("Server name mismatch!\r\n"); //Report an error error = ERROR_BAD_CERTIFICATE; break; } } } #endif //The certificate contains a valid RSA public key? if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, RSA_ENCRYPTION_OID, sizeof(RSA_ENCRYPTION_OID))) { //Retrieve the RSA public key error = x509ReadRsaPublicKey(certInfo, &context->peerRsaPublicKey); //Any error to report if(error) break; //Save the certificate type context->peerCertType = TLS_CERT_RSA_SIGN; } //The certificate contains a valid DSA public key? else if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, DSA_OID, sizeof(DSA_OID))) { //Retrieve the DSA public key error = x509ReadDsaPublicKey(certInfo, &context->peerDsaPublicKey); //Any error to report if(error) break; //Save the certificate type context->peerCertType = TLS_CERT_DSS_SIGN; } //The certificate contains a valid EC public key? else if(!oidComp(certInfo->subjectPublicKeyInfo.oid, certInfo->subjectPublicKeyInfo.oidLen, EC_PUBLIC_KEY_OID, sizeof(EC_PUBLIC_KEY_OID))) { const EcCurveInfo *curveInfo; //Retrieve EC domain parameters curveInfo = ecGetCurveInfo(certInfo->subjectPublicKeyInfo.ecParams.namedCurve, certInfo->subjectPublicKeyInfo.ecParams.namedCurveLen); //Make sure the specified elliptic curve is supported if(curveInfo == NULL) { //Report an error error = ERROR_BAD_CERTIFICATE; //Exit immediately break; } //Load EC domain parameters error = ecLoadDomainParameters(&context->peerEcParams, curveInfo); //Any error to report? if(error) break; //Retrieve the EC public key error = ecImport(&context->peerEcParams, &context->peerEcPublicKey, certInfo->subjectPublicKeyInfo.ecPublicKey.q, certInfo->subjectPublicKeyInfo.ecPublicKey.qLen); //Any error to report if(error) break; //Save the certificate type context->peerCertType = TLS_CERT_ECDSA_SIGN; } //The certificate does not contain any valid public key? else { //Report an error error = ERROR_BAD_CERTIFICATE; break; } //Next certificate p += n; length -= n; //PKIX path validation while(length > 0) { //Each certificate is preceded by a 3-byte length field if(length < 3) { //Report an error error = ERROR_DECODING_FAILED; break; } //Get the size occupied by the certificate n = LOAD24BE(p); //Jump to the beginning of the DER encoded certificate p += 3; length -= 3; //Ensure that the certificate is valid if(n > length) { //Report an error error = ERROR_DECODING_FAILED; break; } //Display ASN.1 structure error = asn1DumpObject(p, n, 0); //Any error to report? if(error) break; //Parse X.509 certificate error = x509ParseCertificate(p, n, issuerCertInfo); //Failed to parse the X.509 certificate? if(error) break; //Validate current certificate error = x509ValidateCertificate(certInfo, issuerCertInfo); //Certificate validation failed? if(error) break; //Keep track of the issuer certificate memcpy(certInfo, issuerCertInfo, sizeof(X509CertificateInfo)); //Next certificate p += n; length -= n; } //Propagate exception if necessary... if(error) break; //Point to the first trusted CA certificate pemCert = context->trustedCaList; //Get the total length, in bytes, of the trusted CA list pemCertLength = context->trustedCaListLength; //DER encoded certificate derCert = NULL; derCertSize = 0; derCertLength = 0; //Loop through the list while(pemCertLength > 0) { //Decode PEM certificate error = pemReadCertificate(&pemCert, &pemCertLength, &derCert, &derCertSize, &derCertLength); //Any error to report? if(error) break; //Parse X.509 certificate error = x509ParseCertificate(derCert, derCertLength, issuerCertInfo); //Failed to parse the X.509 certificate? if(error) break; //Validate the certificate with the current trusted CA error = x509ValidateCertificate(certInfo, issuerCertInfo); //Certificate validation succeeded? if(!error) break; } //The certificate could not be matched with a known, trusted CA? if(error == ERROR_END_OF_FILE) error = ERROR_UNKNOWN_CA; //Free previously allocated memory osFreeMem(derCert); //End of exception handling block } while(0); //Free previously allocated memory osFreeMem(certInfo); osFreeMem(issuerCertInfo); //Clean up side effects if(error) { //Release previously allocated memory rsaFreePublicKey(&context->peerRsaPublicKey); dsaFreePublicKey(&context->peerDsaPublicKey); ecFreeDomainParameters(&context->peerEcParams); ecFree(&context->peerEcPublicKey); } //TLS operates as a client or a server? if(context->entity == TLS_CONNECTION_END_CLIENT) { //Update FSM state if(context->keyExchMethod != TLS_KEY_EXCH_RSA) context->state = TLS_STATE_SERVER_KEY_EXCHANGE; else context->state = TLS_STATE_CERTIFICATE_REQUEST; } else { //Prepare to receive ClientKeyExchange message... context->state = TLS_STATE_CLIENT_KEY_EXCHANGE; } //Return status code return error; }
error_t tlsReadProtocolData(TlsContext *context, void **data, size_t *length, TlsContentType *contentType) { error_t error; size_t n; TlsContentType type; TlsHandshake *message; //Clear status code error = NO_ERROR; //Fragment reassembly process do { //Empty receive buffer? if(context->rxBufferLength == 0) { //Read a TLS record error = tlsReadRecord(context, context->rxBuffer, TLS_RX_BUFFER_SIZE, &n, &type); //Any error to report? if(error) return error; //Save record type context->rxBufferType = type; //Number of bytes available for reading context->rxBufferLength = n; //Rewind to the beginning of the buffer context->rxBufferReadIndex = 0; //Set write index context->rxBufferWriteIndex = context->rxBufferLength; } //Imcomplete message received? else if(error == ERROR_MORE_DATA_REQUIRED) { //Make room at the end of the buffer if(context->rxBufferReadIndex > 0) { //Move unread data to the beginning of the buffer memmove(context->rxBuffer, context->rxBuffer + context->rxBufferReadIndex, context->rxBufferLength); //Rewind to the beginning of the buffer context->rxBufferReadIndex = 0; //Set write index context->rxBufferWriteIndex = context->rxBufferLength; } //Read a TLS record error = tlsReadRecord(context, context->rxBuffer + context->rxBufferWriteIndex, TLS_RX_BUFFER_SIZE - context->rxBufferLength, &n, &type); //Any error to report? if(error) return error; //Fragmented records with mixed types cannot be interleaved if(type != context->rxBufferType) return ERROR_UNEXPECTED_MESSAGE; //Number of bytes available for reading context->rxBufferLength += n; //Update write index context->rxBufferWriteIndex += n; } //Handshake message received? if(context->rxBufferType == TLS_TYPE_HANDSHAKE) { //A message may be fragmented across several records if(context->rxBufferLength < sizeof(TlsHandshake)) { //Read an additional record error = ERROR_MORE_DATA_REQUIRED; } else { //Point to the handshake message message = (TlsHandshake *) (context->rxBuffer + context->rxBufferReadIndex); //Retrieve the length of the handshake message n = sizeof(TlsHandshake) + LOAD24BE(message->length); //A message may be fragmented across several records if(context->rxBufferLength < n) { //Read an additional record error = ERROR_MORE_DATA_REQUIRED; } else { //Pass the handshake message to the higher layer error = NO_ERROR; } } } //ChangeCipherSpec message received? else if(context->rxBufferType == TLS_TYPE_CHANGE_CIPHER_SPEC) { //A message may be fragmented across several records if(context->rxBufferLength < sizeof(TlsChangeCipherSpec)) { //Read an additional record error = ERROR_MORE_DATA_REQUIRED; } else { //Length of the ChangeCipherSpec message n = sizeof(TlsChangeCipherSpec); //Pass the ChangeCipherSpec message to the higher layer error = NO_ERROR; } } //Alert message received? else if(context->rxBufferType == TLS_TYPE_ALERT) { //A message may be fragmented across several records if(context->rxBufferLength < sizeof(TlsAlert)) { //Read an additional record error = ERROR_MORE_DATA_REQUIRED; } else { //Length of the Alert message n = sizeof(TlsAlert); //Pass the Alert message to the higher layer error = NO_ERROR; } } //Application data received? else if(context->rxBufferType == TLS_TYPE_APPLICATION_DATA) { //Length of the application data n = context->rxBufferLength; //Pass the application data to the higher layer error = NO_ERROR; } //Unknown content type? else { //Report an error error = ERROR_UNEXPECTED_MESSAGE; } //Read as many records as necessary to reassemble the data } while(error == ERROR_MORE_DATA_REQUIRED); //Successful processing? if(!error) { //Pointer to the received data *data = context->rxBuffer + context->rxBufferReadIndex; //Length, in byte, of the data *length = n; //Protocol type *contentType = context->rxBufferType; } //Return status code return error; }
error_t dhcpv6DumpMessage(const void *message, size_t length) { error_t error; uint8_t type; const char_t *label; //Empty message? if(!length) return ERROR_INVALID_LENGTH; //Retrieve the message type type = *((uint8_t *) message); //Get the corresponding label label = (type < arraysize(messageLabel)) ? messageLabel[type] : "Unknown"; //Relay agent/server message? if(type == DHCPV6_MSG_TYPE_RELAY_FORW || type == DHCPV6_MSG_TYPE_RELAY_REPL) { //Ensure the length of the DHCPv6 message is acceptable if(length < sizeof(Dhcpv6RelayMessage)) { //Report an error error = ERROR_INVALID_LENGTH; } else { //Point to the DHCPv6 message const Dhcpv6RelayMessage *relayMessage = message; //Dump message header TRACE_DEBUG(" Message Type = %u (%s)\r\n", relayMessage->msgType, label); TRACE_DEBUG(" Hop Count = %u\r\n", relayMessage->hopCount); TRACE_DEBUG(" Link Address = %s\r\n", ipv6AddrToString(&relayMessage->linkAddress, NULL)); TRACE_DEBUG(" Peer Address = %s\r\n", ipv6AddrToString(&relayMessage->peerAddress, NULL)); //Dump message options error = dhcpv6DumpOptions(relayMessage->options, length - sizeof(Dhcpv6RelayMessage), 1); } } //Client/server message? else { //Ensure the length of the DHCPv6 message is acceptable if(length < sizeof(Dhcpv6Message)) { //Report an error error = ERROR_INVALID_LENGTH; } else { //Point to the DHCPv6 message const Dhcpv6Message *clientMessage = message; //Dump message header TRACE_DEBUG(" Message Type = %u (%s)\r\n", clientMessage->msgType, label); TRACE_DEBUG(" Transaction ID = 0x%06X\r\n", LOAD24BE(clientMessage->transactionId)); //Dump message options error = dhcpv6DumpOptions(clientMessage->options, length - sizeof(Dhcpv6Message), 1); } } //Did we encounter an error? if(error) { //Debug message TRACE_WARNING("DHCPv6 message is not valid!\r\n"); //Dump message contents for debugging purpose TRACE_DEBUG_ARRAY(" ", message, length); } //Return status code return error; }