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