void echo_stream() { int acceptor, sock; if (signal(SIGCHLD, reaper) == SIG_ERR) { die(-1, "unable to catch SIGCHLD"); } say("Stream service started\n"); /* Prepare XSSL context and get SID (based on RSA key) */ XSSL_CTX *ctx = XSSL_CTX_new(); if (ctx == NULL) { die(-5, "Unable to create new XSSL_CTX\n"); } char *SID = SID_from_keypair(ctx->keypair); /* Prepare listen socket, binding to generated SID */ if ((acceptor = Xsocket(AF_XIA, SOCK_STREAM, 0)) < 0) die(-2, "unable to create the stream socket\n"); struct addrinfo *ai; if (Xgetaddrinfo(NULL, SID, NULL, &ai) != 0) die(-1, "getaddrinfo failure!\n"); Graph g((sockaddr_x*)ai->ai_addr); sockaddr_x *sa = (sockaddr_x*)ai->ai_addr; printf("\nStream DAG\n%s\n", g.dag_string().c_str()); if (XregisterName(STREAM_NAME, sa) < 0 ) die(-1, "error registering name: %s\n", STREAM_NAME); say("registered name: \n%s\n", STREAM_NAME); if (Xbind(acceptor, (struct sockaddr *)sa, sizeof(sockaddr_x)) < 0) { die(-3, "unable to bind to the dag\n"); } Xlisten(acceptor, 5); while (1) { say("Xsock %4d waiting for a new connection.\n", acceptor); if ((sock = Xaccept(acceptor, NULL, 0)) < 0) { warn("Xsock %d accept failure! error = %d\n", acceptor, errno); // FIXME: should we die here instead or try and recover? continue; } say ("Xsock %4d new session\n", sock); pid_t pid = fork(); if (pid == -1) { die(-1, "FORK FAILED\n"); } else if (pid == 0) { process(sock, ctx); exit(0); } else { // FIXME: we need to close the socket in the main process or the file // descriptor limit will be hit. But if Xclose is called, the connection // is torn down in click as well keeping the child process from using it. // for now use a regular close to shut it here without affecting the child. close(sock); } } Xclose(acceptor); XSSL_CTX_free(ctx); free(SID); }
/** * @brief Initiate an XSSL connection with server. * * @param xssl * * @return 1 on success, <1 on failure */ int XSSL_connect(XSSL *xssl) { char buf[XIA_MAXBUF]; int n; /* Send CLIENT HELLO */ sprintf(buf, "CLIENT HELLO"); if ((n = Xsend(xssl->sockfd, buf, strlen(buf), 0)) != strlen(buf)) { ERROR("ERROR sending CLIENT HELLO"); return 0; } /* Wait for SERVER HELLO */ // Receive public key + temporary public key signed by server's long-term private key // Continue receiving until we see "SERVER DONE" int offset = 0; while ( offset < 11 || strcmp("SERVER DONE", &buf[offset-11]) != 0 ) { n = Xrecv(xssl->sockfd, &buf[offset], sizeof(buf)-offset, 0); if (n < 0) { ERROR("ERROR receiving SERVER HELLO"); return n; } if (n == 0) { ERROR("ERROR: server closed connection during SERVER HELLO"); return n; } offset += n; } /* Parse public keys from SERVER HELLO */ offset = strlen("SERVER HELLO"); char *keys = &buf[offset]; // start of signed portion of message uint32_t *keybufsizeptr = (uint32_t*)&buf[offset]; uint32_t keybufsize = *keybufsizeptr; // TODO: error checking here offset += sizeof(uint32_t); RSA *pubkey = deserialize_rsa_pub_key(&buf[offset], keybufsize); offset += keybufsize; uint32_t *tempkeybufsizeptr = (uint32_t*)&buf[offset]; uint32_t tempkeybufsize = *tempkeybufsizeptr; offset += sizeof(uint32_t); RSA *sessionPubkey = deserialize_rsa_pub_key(&buf[offset], tempkeybufsize); offset += tempkeybufsize; uint32_t *siglenptr = (uint32_t*)&buf[offset]; uint32_t siglen = *siglenptr; offset += sizeof(uint32_t); char* sig = &buf[offset]; offset += siglen; DBGF("Received keys:\n\tkeylen: %d\n\ttempkeylen: %d\n\tsiglen: %d", keybufsize, tempkeybufsize, siglen); /* Verify two things: * 1) hash(key) == SID so we trust the signature * 2) verify the sig so we trust the temp key */ sockaddr_x *sa = (sockaddr_x*)malloc(sizeof(sockaddr_x)); socklen_t len = sizeof(sockaddr_x); if (Xgetpeername(xssl->sockfd, (struct sockaddr*)sa, &len) < 0) { ERRORF("Error in Xgetpeername on socket %d: %s", xssl->sockfd, strerror(errno)); return 0; } Graph g(sa); Node sid_node = g.get_final_intent(); DBGF("SID: %s", sid_node.to_string().c_str()); char *sid_from_key_hash = SID_from_keypair(pubkey); DBGF("Pub key hash: %s", sid_from_key_hash); if (strcmp(sid_node.to_string().c_str(), sid_from_key_hash) != 0) { WARN("Hash of received public key does not match SID! Closing connection."); return 0; } if (verify(pubkey, keys, keybufsize+tempkeybufsize+2*sizeof(uint32_t), sig, siglen) != 1) { WARN("Unable to verify signature on temporary RSA keypair! Closing connection."); return 0; } /* Generate pre-master secret and send to server, encrypted with sessionPubKey */ unsigned char* pms = (unsigned char*)malloc(PRE_MASTER_SECRET_LENGTH); if (RAND_bytes(pms, PRE_MASTER_SECRET_LENGTH) != 1) { ERROR("ERROR: Couldn't generate pre-master secret"); return 0; } if (VERBOSITY >= V_DEBUG) { DBG("Pre-Master Secret:"); print_bytes(pms, PRE_MASTER_SECRET_LENGTH); } int ciphertext_len; if ( (ciphertext_len = pub_encrypt(sessionPubkey, pms, PRE_MASTER_SECRET_LENGTH, buf, XIA_MAXBUF)) == -1 ) { ERROR("ERROR: Unable to encrypt session key"); return 0; } n = 0; offset = 0; while (offset < ciphertext_len) { if ((n = Xsend(xssl->sockfd, &buf[offset], ciphertext_len-offset, 0)) < 0) { ERROR("ERROR sending pre-master secret"); return 0; } offset += n; } /* Init symmetric session ciphers with pre-master secret. Client encrypt context initialized with same key data as server decrypt context and vice versa. */ uint32_t salt[] = SALT; if (aes_init(pms, PRE_MASTER_SECRET_LENGTH/2, &pms[PRE_MASTER_SECRET_LENGTH/2], PRE_MASTER_SECRET_LENGTH/2, (unsigned char *)&salt, xssl->en, xssl->de)) { ERROR("ERROR initializing AES ciphers"); return 0; } free(pms); /* For now, omitting CLIENT DONE */ return 1; }