void kssl_write(SSL *ssl, kssl_header *k, kssl_operation *r) { BYTE *req; int req_len, n; flatten_operation(k, r, &req, &req_len); dump_header(k, "send"); dump_request(r); n = SSL_write(ssl, req, req_len); if (n != req_len) { fatal_error("Failed to send KSSL header"); } free(req); }
// kssl: send a KSSL message to the server and read the response kssl_header *kssl(SSL *ssl, kssl_header *k, kssl_operation *r) { BYTE buf[KSSL_HEADER_SIZE]; BYTE *req; int req_len; int n; kssl_header h; kssl_header *to_return; flatten_operation(k, r, &req, &req_len); dump_header(k, "send"); dump_request(r); n = SSL_write(ssl, req, req_len); if (n != req_len) { fatal_error("Failed to send KSSL header"); } free(req); while (1) { n = SSL_read(ssl, buf, KSSL_HEADER_SIZE); if (n <= 0) { int x = SSL_get_error(ssl, n); if (x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) { continue; } else if (x == SSL_ERROR_ZERO_RETURN) { fatal_error("Connection closed while reading header\n"); } else { fatal_error("Error performing SSL_read: %x\n", x); } } else { if (n != KSSL_HEADER_SIZE) { fatal_error("Error receiving KSSL header, size: %d", n); } } break; } parse_header(buf, &h); if (h.version_maj != KSSL_VERSION_MAJ) { fatal_error("Version mismatch %d != %d", h.version_maj, KSSL_VERSION_MAJ); } if (k->id != h.id) { fatal_error("ID mismatch %08x != %08x", k->id, h.id); } dump_header(&h, "recv"); to_return = (kssl_header *)malloc(sizeof(kssl_header)); memcpy(to_return, &h, sizeof(kssl_header)); if (h.length > 0) { BYTE *payload = (BYTE *)malloc(h.length); while (1) { n = SSL_read(ssl, payload, h.length); if (n <= 0) { int x = SSL_get_error(ssl, n); if (x == SSL_ERROR_WANT_READ || x == SSL_ERROR_WANT_WRITE) { continue; } else if (x == SSL_ERROR_ZERO_RETURN) { fatal_error("Connection closed while reading payload\n"); } else { fatal_error("Error performing SSL_read: %x\n", x); } } else { if (n != h.length) { fatal_error("Error receiving KSSL payload, size: %d", n); } } break; } if (n != h.length) { fatal_error("Failed to read payload got length %d wanted %d", n, h.length); } dump_payload(h.length, payload); to_return->data = payload; } return to_return; }
// kssl_operate: create a serialized response from a KSSL request // header and payload kssl_error_code kssl_operate(kssl_header *header, BYTE *payload, pk_list privates, BYTE **out_response, int *out_response_len) { kssl_error_code err = KSSL_ERROR_NONE; BYTE *local_resp = NULL; int local_resp_len = 0; // Parse the indices of the items out of the payload kssl_header out_header; kssl_operation request; kssl_operation response; BYTE *out_payload = NULL; zero_operation(&request); zero_operation(&response); *out_response = 0; *out_response_len = 0; // Extract the items from the payload err = parse_message_payload(payload, header->length, &request); if (err != KSSL_ERROR_NONE) { goto exit; } if (silent == 0) { log_operation(header, &request); } switch (request.opcode) { // Other side sent response, error or pong: unexpected case KSSL_OP_RESPONSE: case KSSL_OP_ERROR: case KSSL_OP_PONG: { err = KSSL_ERROR_UNEXPECTED_OPCODE; break; } // Echo is trivial, it just echos the complete state->header back // including the payload item case KSSL_OP_PING: { response.is_payload_set = 1; response.payload = request.payload; response.payload_len = request.payload_len; response.is_opcode_set = 1; response.opcode = KSSL_OP_PONG; break; } // Decrypt or sign the payload using the private key case KSSL_OP_RSA_DECRYPT: case KSSL_OP_RSA_SIGN_MD5SHA1: case KSSL_OP_RSA_SIGN_SHA1: case KSSL_OP_RSA_SIGN_SHA224: case KSSL_OP_RSA_SIGN_SHA256: case KSSL_OP_RSA_SIGN_SHA384: case KSSL_OP_RSA_SIGN_SHA512: { unsigned int payload_size; int max_payload_size; int key_id; if (request.is_digest_set == 0) { err = KSSL_ERROR_FORMAT; break; } // Identify private key from request digest key_id = find_private_key(privates, request.digest); if (key_id < 0) { err = KSSL_ERROR_KEY_NOT_FOUND; break; } // Allocate buffer to hold output of private key operation max_payload_size = key_size(privates, key_id); out_payload = malloc(max_payload_size); if (out_payload == NULL) { err = KSSL_ERROR_INTERNAL; break; } // Operate on payload err = private_key_operation(privates, key_id, request.opcode, request.payload_len, request.payload, out_payload, &payload_size); if (err != KSSL_ERROR_NONE) { err = KSSL_ERROR_CRYPTO_FAILED; break; } response.is_payload_set = 1; response.payload = out_payload; response.payload_len = payload_size; response.is_opcode_set = 1; response.opcode = KSSL_OP_RESPONSE; break; } // This should not occur default: { err = KSSL_ERROR_BAD_OPCODE; break; } } exit: if (err != KSSL_ERROR_NONE) { err = kssl_error(header->id, err, &local_resp, &local_resp_len); } else { // Create output header out_header.version_maj = KSSL_VERSION_MAJ; out_header.version_min = KSSL_VERSION_MIN; out_header.id = header->id; // Note that the response in &local_resp is dynamically allocated // and needs to be freed err = flatten_operation(&out_header, &response, &local_resp, &local_resp_len); } if (out_payload != NULL) { free(out_payload); } if (err == KSSL_ERROR_NONE) { *out_response = local_resp; *out_response_len = local_resp_len; } return err; }