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