]> WPIA git - cassiopeia.git/blob - src/io/recordHandler.cpp
7b48c6e4685e0b70eb6aeba0b93b2e1b0a2c8cd0
[cassiopeia.git] / src / io / recordHandler.cpp
1 #include "io/recordHandler.h"
2
3 #include <iostream>
4 #include <sstream>
5 #include <fstream>
6 #include <ctime>
7 #include <unordered_map>
8
9 #include <openssl/ssl.h>
10
11 #include "util.h"
12 #include "io/record.h"
13 #include "io/opensslBIO.h"
14 #include "io/slipBio.h"
15
16 #include "db/database.h"
17 #include "crypto/remoteSigner.h"
18 #include "crypto/sslUtil.h"
19 #include "crypto/simpleOpensslSigner.h"
20
21 #include "log/logger.hpp"
22
23 extern std::vector<Profile> profiles;
24 extern std::unordered_map<std::string, std::shared_ptr<CAConfig>> CAs;
25
26 class RecordHandlerSession {
27 public:
28     uint32_t sessid = 0;
29     uint32_t lastCommandCount = 0;
30
31     std::shared_ptr<TBSCertificate> tbs;
32     std::shared_ptr<SignedCertificate> result;
33
34     std::shared_ptr<SSL> ssl;
35
36     std::shared_ptr<OpensslBIOWrapper> io;
37     DefaultRecordHandler *parent;
38     std::shared_ptr<Signer> signer;
39
40     std::unique_ptr<std::ofstream> logFile;
41     //std::stringstream sessionlog;
42     std::vector<std::string> serials;
43     logger::logger_set logger;
44
45
46     RecordHandlerSession( DefaultRecordHandler *parent, std::shared_ptr<Signer> signer, std::shared_ptr<SSL_CTX> ctx, std::shared_ptr<BIO> output ) :
47         tbs( std::make_shared<TBSCertificate>() ),
48         logFile( openLogfile( "logs/log_" + timestamp() ) ),
49         logger{ std::cout, *logFile } {
50         this->parent = parent;
51         this->signer = signer;
52
53         ssl = std::shared_ptr<SSL>( SSL_new( ctx.get() ), SSL_free );
54         auto freeBIO = [output]( BIO * p ) {
55             BIO_free( p );
56         };
57         std::shared_ptr<BIO> bio( BIO_new( BIO_f_ssl() ), freeBIO );
58         SSL_set_accept_state( ssl.get() );
59         SSL_set_bio( ssl.get(), output.get(), output.get() );
60         BIO_set_ssl( bio.get(), ssl.get(), BIO_NOCLOSE );
61         io = std::make_shared<OpensslBIOWrapper>( bio );
62     }
63
64     void respondCommand( RecordHeader::SignerResult res, std::string payload ) {
65         RecordHeader rh;
66         rh.command = static_cast<uint16_t>( res );
67         rh.flags = 0;
68         rh.command_count = 0; // TODO i++
69         sendCommand( rh, payload, io );
70     }
71
72     void work() {
73         try {
74             RecordHeader head;
75             std::string all = parseCommandChunked( head, io );
76             execute( static_cast<RecordHeader::SignerCommand>( head.command ), all );
77         } catch( const std::exception& msg ) {
78             logger::error( "ERROR: ", msg.what() );
79             parent->reset();
80             return;
81         }
82     }
83
84     void execute( RecordHeader::SignerCommand command, std::string data ) {
85         switch( command ) {
86         case RecordHeader::SignerCommand::SET_CSR:
87             tbs->csr_content = data;
88             tbs->csr_type = "CSR";
89             logger::note( "INFO: CSR read:\n", tbs->csr_content );
90             break;
91
92         case RecordHeader::SignerCommand::SET_SPKAC:
93             tbs->csr_content = data;
94             tbs->csr_type = "SPKAC";
95             logger::note( "INFO: SPKAC read:\n", tbs->csr_content );
96             break;
97
98         case RecordHeader::SignerCommand::SET_SIGNATURE_TYPE:
99             tbs->md = data;
100             break;
101
102         case RecordHeader::SignerCommand::SET_PROFILE:
103             // TODO
104             tbs->profile = data;
105             break;
106
107         case RecordHeader::SignerCommand::SET_WISH_FROM:
108             tbs->wishFrom = data;
109             break;
110
111         case RecordHeader::SignerCommand::SET_WISH_TO:
112             tbs->wishTo = data;
113             break;
114
115         case RecordHeader::SignerCommand::ADD_SAN: {
116             size_t pos = data.find( "," );
117
118             if( pos == std::string::npos ) {
119                 // error
120             } else {
121                 auto san = std::make_shared<SAN>();
122                 san->type = data.substr( 0, pos );
123                 san->content = data.substr( pos + 1 );
124                 tbs->SANs.push_back( san );
125             }
126         }
127         break;
128
129         case RecordHeader::SignerCommand::ADD_AVA: {
130             size_t pos = data.find( "," );
131
132             if( pos == std::string::npos ) {
133                 // error
134             } else {
135                 auto ava = std::make_shared<AVA>();
136                 ava->name = data.substr( 0, pos );
137                 ava->value = data.substr( pos + 1 );
138                 tbs->AVAs.push_back( ava );
139             }
140         }
141         break;
142
143         case RecordHeader::SignerCommand::ADD_PROOF_LINE:
144             break;
145
146         case RecordHeader::SignerCommand::SIGN:
147             result = signer->sign( tbs );
148             logger::note( "INFO: signlog:\n", result->log );
149             logger::note( "INFO: res:\n", result->certificate );
150             respondCommand( RecordHeader::SignerResult::SAVE_LOG, result->log );
151             break;
152
153         case RecordHeader::SignerCommand::LOG_SAVED:
154             if( result ) {
155                 respondCommand( RecordHeader::SignerResult::SIGNING_CA, result->ca_name );
156                 respondCommand( RecordHeader::SignerResult::CERTIFICATE, result->certificate );
157             }
158
159             logger::note( "Shutting down SSL" );
160
161             if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) {
162                 logger::warn( "ERROR: SSL shutdown failed." );
163             }
164
165             io->ctrl( BIO_CTRL_FLUSH, 0, NULL );
166             logger::note( "Shutted down SSL" );
167
168             parent->reset(); // Connection ended
169
170             break;
171
172         case RecordHeader::SignerCommand::ADD_SERIAL:
173             serials.push_back( data );
174             break;
175
176         case RecordHeader::SignerCommand::REVOKE: {
177             logger::note( "Revoking: ", data );
178             std::string ca = data;
179             auto reqCA = CAs.at( ca );
180             logger::note( "CA found in recordHandler" );
181             std::shared_ptr<CRL> crl;
182             std::string date;
183             std::tie( crl, date ) = signer->revoke( reqCA, serials );
184
185             respondCommand( RecordHeader::SignerResult::REVOKED, date + crl->getSignature() );
186         }
187         break;
188
189         case RecordHeader::SignerCommand::GET_FULL_CRL: {
190             logger::note( "Requesting full CRL: ", data );
191             auto ca = CAs.at( data );
192             CRL c( ca->path + "/ca.crl" );
193             respondCommand( RecordHeader::SignerResult::FULL_CRL, c.toString() );
194
195             logger::note( "Shutting down SSL" );
196
197             if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) {
198                 logger::error( "ERROR: SSL shutdown failed." );
199             }
200
201             io->ctrl( BIO_CTRL_FLUSH, 0, NULL );
202             logger::note( "Shutted down SSL" );
203
204             parent->reset(); // Connection ended
205         }
206         break;
207
208         default:
209             throw std::runtime_error( "Unimplemented" );
210         }
211     }
212 };
213
214 DefaultRecordHandler::DefaultRecordHandler( std::shared_ptr<Signer> signer, std::shared_ptr<BIO> bio ) :
215     bio( bio ), ctx( generateSSLContext( true ) ), signer( signer ), currentSession() {
216 }
217
218 void DefaultRecordHandler::reset() {
219     currentSession = std::shared_ptr<RecordHandlerSession>();
220 }
221
222 void DefaultRecordHandler::handle() {
223     if( !currentSession ) {
224         ( void ) BIO_reset( bio.get() );
225         logger::note( "New session allocated." );
226         currentSession = std::make_shared<RecordHandlerSession>( this, signer, ctx, bio );
227     }
228
229     try {
230         currentSession->work();
231     } catch( eof_exception e ) {
232         reset();
233     }
234 }