]> WPIA git - cassiopeia.git/blob - src/recordHandler.cpp
b1c5ac4280b083158fcefb49581ff16c73803c49
[cassiopeia.git] / src / recordHandler.cpp
1 #include "recordHandler.h"
2
3 #include <sys/types.h>
4 #include <sys/socket.h>
5 #include <netinet/in.h>
6 #include <arpa/inet.h>
7 #include <termios.h>
8 #include <unistd.h>
9
10 #include <iostream>
11
12 #include <openssl/ssl.h>
13
14 #include "database.h"
15 #include "record.h"
16 #include "opensslBIO.h"
17 #include "simpleOpensslSigner.h"
18 #include "slipBio.h"
19
20 int gencb( int a, int b, BN_GENCB* g ) {
21     ( void ) a;
22     ( void ) b;
23     ( void ) g;
24
25     std::cout << ( a == 0 ? "." : "+" ) << std::flush;
26
27     return 1;
28 }
29
30 static std::shared_ptr<DH> dh_param;
31
32 std::shared_ptr<SSL_CTX> generateSSLContext( bool server ) {
33     std::shared_ptr<SSL_CTX> ctx = std::shared_ptr<SSL_CTX>( SSL_CTX_new( TLSv1_2_method() ), SSL_CTX_free );
34
35     if( !SSL_CTX_set_cipher_list( ctx.get(), "HIGH:+CAMELLIA256:!eNull:!aNULL:!ADH:!MD5:-RSA+AES+SHA1:!RC4:!DES:!3DES:!SEED:!EXP:!AES128:!CAMELLIA128" ) ) {
36         throw "Cannot set cipher list. Your source is broken.";
37     }
38
39     if( server ) {
40         if( !dh_param ) {
41             FILE* paramfile = fopen( "dh_param.pem", "r" );
42
43             if( paramfile ) {
44                 dh_param = std::shared_ptr<DH>( PEM_read_DHparams( paramfile, NULL, NULL, NULL ), DH_free );
45                 fclose( paramfile );
46             } else {
47                 dh_param = std::shared_ptr<DH>( DH_new(), DH_free );
48                 std::cout << "Generating DH params" << std::endl;
49                 BN_GENCB cb;
50                 cb.ver = 2;
51                 cb.arg = 0;
52                 cb.cb.cb_2 = gencb;
53
54                 if( !DH_generate_parameters_ex( dh_param.get(), 2048, 5, &cb ) ) {
55                     throw "DH generation failed";
56                 }
57
58                 std::cout << std::endl;
59                 paramfile = fopen( "dh_param.pem", "w" );
60
61                 if( paramfile ) {
62                     PEM_write_DHparams( paramfile, dh_param.get() );
63                     fclose( paramfile );
64                 }
65             }
66         }
67
68         if( !SSL_CTX_set_tmp_dh( ctx.get(), dh_param.get() ) ) {
69             throw "Cannot set tmp dh.";
70         }
71     }
72
73     return ctx;
74 }
75
76 class RecordHandlerSession {
77 public:
78     uint32_t sessid;
79     uint32_t lastCommandCount;
80
81     std::shared_ptr<TBSCertificate> tbs;
82     std::shared_ptr<SignedCertificate> result;
83
84     SSL* ssl;
85
86     std::shared_ptr<OpensslBIOWrapper> io;
87     DefaultRecordHandler* parent;
88     std::shared_ptr<Signer> signer;
89
90     RecordHandlerSession( DefaultRecordHandler* parent, std::shared_ptr<Signer> signer, std::shared_ptr<SSL_CTX> ctx, BIO* output ) :
91         tbs( new TBSCertificate() ) {
92         this->parent = parent;
93         this->signer = signer;
94
95         ssl = SSL_new( ctx.get() );
96         BIO* bio = BIO_new( BIO_f_ssl() );
97         SSL_set_accept_state( ssl );
98         SSL_set_bio( ssl, output, output );
99         BIO_set_ssl( bio, ssl, BIO_NOCLOSE );
100         io = std::shared_ptr<OpensslBIOWrapper>( new OpensslBIOWrapper( bio ) );
101     }
102
103     void respondCommand( RecordHeader::SignerResult res, std::string payload ) {
104         RecordHeader rh;
105         rh.command = ( uint16_t ) res;
106         rh.flags = 0;
107         rh.command_count = 0; // TODO i++
108         rh.totalLength = payload.size();
109         sendCommand( rh, payload, io );
110     }
111
112     void work() {
113         std::vector<char> buffer( 2048, 0 );
114         int res = io->read( buffer.data(), buffer.capacity() );
115
116         if( res <= 0 ) {
117             parent->reset();
118             return;
119         }
120
121         std::string content( buffer.data(), res );
122
123         try {
124             RecordHeader head;
125             std::string payload = parseCommand( head, content );
126             execute( head, payload );
127         } catch( const char* msg ) {
128             std::cout << msg << std::endl;
129             parent->reset();
130             return;
131         }
132     }
133
134     void execute( RecordHeader& head, std::string data ) {
135         if( head.totalLength != head.payloadLength || head.offset != 0 ) {
136             throw "Error, chunking not supported yet";
137         }
138
139         switch( ( RecordHeader::SignerCommand ) head.command ) {
140         case RecordHeader::SignerCommand::SET_CSR: // setCSR
141             tbs->csr_content = data;
142             tbs->csr_type = "CSR";
143             std::cout << "CSR read" << std::endl;
144             break;
145
146         case RecordHeader::SignerCommand::SET_SIGNATURE_TYPE:
147             tbs->md = "sha256"; // TODO use content ;-)
148             break;
149
150         case RecordHeader::SignerCommand::SET_PROFILE:
151             // TODO
152             tbs->profile = data;
153             break;
154
155         case RecordHeader::SignerCommand::ADD_SAN: {
156             size_t pos = data.find( "," );
157
158             if( pos == std::string::npos ) {
159             } else {
160                 std::shared_ptr<SAN> san( new SAN() );
161                 san->type = data.substr( 0, pos );
162                 san->content = data.substr( pos + 1 );
163                 tbs->SANs.push_back( san );
164             }
165         }
166         break;
167
168         case RecordHeader::SignerCommand::ADD_AVA: {
169             size_t pos = data.find( "," );
170
171             if( pos == std::string::npos ) {
172                 // error
173             } else {
174                 std::shared_ptr<AVA> ava( new AVA() );
175                 ava->name = data.substr( 0, pos );
176                 ava->value = data.substr( pos + 1 );
177                 tbs->AVAs.push_back( ava );
178             }
179         }
180         break;
181
182         case RecordHeader::SignerCommand::ADD_PROOF_LINE:
183             break;
184
185         case RecordHeader::SignerCommand::SIGN:
186             result = signer->sign( tbs );
187             std::cout << "res: " << result->certificate << std::endl;
188             result->log = "I am a dummy log.\nI signed that thing ;-) \n";
189             respondCommand( RecordHeader::SignerResult::SAVE_LOG, result->log );
190             break;
191
192         case RecordHeader::SignerCommand::LOG_SAVED:
193             if( result ) {
194                 respondCommand( RecordHeader::SignerResult::CERTIFICATE, result->certificate );
195             }
196
197             break;
198
199         default:
200             throw "Unimplemented";
201         }
202     }
203 };
204
205 DefaultRecordHandler::DefaultRecordHandler( std::shared_ptr<Signer> signer, BIO* bio ) :
206     currentSession() {
207
208     this->signer = signer;
209
210     ctx = generateSSLContext( true );
211
212     SSL_CTX_use_certificate_file( ctx.get(), "testdata/server.crt", SSL_FILETYPE_PEM );
213     SSL_CTX_use_PrivateKey_file( ctx.get(), "testdata/server.key", SSL_FILETYPE_PEM );
214
215     this->bio = bio;
216 }
217
218 void DefaultRecordHandler::reset() {
219     currentSession = std::shared_ptr<RecordHandlerSession>();
220 }
221
222 void DefaultRecordHandler::handle() {
223     if( !currentSession ) {
224         std::cout << "session allocated" << std::endl;
225         currentSession = std::shared_ptr<RecordHandlerSession>( new RecordHandlerSession( this, signer, ctx, bio ) );
226     }
227
228     currentSession->work();
229 }
230
231 int count = 0;
232
233 void send( std::shared_ptr<OpensslBIOWrapper> bio, RecordHeader& head, RecordHeader::SignerCommand cmd, std::string data ) {
234     head.command = ( uint16_t ) cmd;
235     head.command_count++;
236     head.totalLength = data.size();
237     sendCommand( head, data, bio );
238 }
239
240 void setupSerial( FILE* f ) {
241     struct termios attr;
242
243     if( tcgetattr( fileno( f ), &attr ) ) {
244         throw "failed to get attrs";
245     }
246
247     attr.c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON );
248     attr.c_oflag &= ~OPOST;
249     attr.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
250     attr.c_cflag &= ~( CSIZE | PARENB );
251     attr.c_cflag |= CS8;
252
253     if( tcsetattr( fileno( f ), TCSANOW, &attr ) ) {
254         throw "failed to get attrs";
255     }
256 }
257
258 int handlermain( int argc, const char* argv[] ) {
259     ( void ) argc;
260     ( void ) argv;
261     std::shared_ptr<OpensslBIOWrapper> bio( new OpensslBIOWrapper( BIO_new_fd( 0, 0 ) ) );
262     std::string data =
263         "-----BEGIN CERTIFICATE REQUEST-----\n"
264         "MIIBSzCBtQIBADAMMQowCAYDVQQDDAFhMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB\n"
265         "iQKBgQDerBEpIShJlx3zzl4AOS1NcwEg4iAWknQeTtI8B5dnk+l5HkOdTxqeehZn\n"
266         "iZnuIuYXA+JWmoECg/w69+N5zw2BabelgK3cSvRqycwPEU/gceGJZTaBfkkN0hBk\n"
267         "rpXDiLSlox5oeR150MrsHvVc+W2e+0jW1tuhz4QLzn8/uI/toQIDAQABoAAwDQYJ\n"
268         "KoZIhvcNAQELBQADgYEATQU5VrgQAkvpCvIwRUyjj9YAa9E014tNY0jMcBdv95fy\n"
269         "/f49zIcVtUJuZuEwY6uDZQqfAm+8CLNpOCICH/Qw7YOe+s/Yw7a8rk5VqLtgxR4M\n"
270         "z6DUeVL0zYFoLUxIje9yDU3pWmPvyVaBPdo0DguZwFMfiWwzhkUDeQgyeaiMvQA=\n"
271         "-----END CERTIFICATE REQUEST-----";
272     RecordHeader head;
273     head.flags = 0;
274     head.sessid = 13;
275
276     //---
277
278     SSL_library_init();
279
280     if( argc >= 2 ) {
281         FILE* f = fopen( "/dev/ttyUSB0", "r+" );
282
283         if( !f ) {
284             std::cout << "Opening /dev/ttyUSB0 bio failed" << std::endl;
285             return -1;
286         }
287
288         setupSerial( f );
289
290         BIO* b = BIO_new_fd( fileno( f ), 0 );
291         BIO* slip1 = BIO_new( toBio<SlipBIO>() );
292         ( ( SlipBIO* )slip1->ptr )->setTarget( std::shared_ptr<OpensslBIO>( new OpensslBIOWrapper( b ) ) );
293         std::cout << "Initing tlsv1_2" << std::endl;
294         std::shared_ptr<SSL_CTX> ctx = generateSSLContext( false );
295         SSL* ssl = SSL_new( ctx.get() );
296         BIO* bio = BIO_new( BIO_f_ssl() );
297         SSL_set_connect_state( ssl );
298         SSL_set_bio( ssl, slip1, slip1 );
299         BIO_set_ssl( bio, ssl, BIO_NOCLOSE );
300         std::shared_ptr<OpensslBIOWrapper> conn( new OpensslBIOWrapper( bio ) );
301         send( conn, head, RecordHeader::SignerCommand::SET_CSR, data );
302         send( conn, head, RecordHeader::SignerCommand::SET_SIGNATURE_TYPE, "sha256" );
303         send( conn, head, RecordHeader::SignerCommand::SET_PROFILE, "1" );
304         send( conn, head, RecordHeader::SignerCommand::ADD_AVA, "CN,commonName" );
305         send( conn, head, RecordHeader::SignerCommand::ADD_SAN, "DNS,*.example.com" );
306         send( conn, head, RecordHeader::SignerCommand::SIGN, "" );
307         send( conn, head, RecordHeader::SignerCommand::LOG_SAVED, "" );
308         std::vector<char> buffer( 2048 * 4 );
309
310         for( int i = 0; i < 2; i++ ) {
311             try {
312                 int length = conn->read( buffer.data(), buffer.size() );
313                 RecordHeader head;
314                 std::string payload = parseCommand( head, std::string( buffer.data(), length ) );
315                 std::cout << "Data: " << std::endl << payload << std::endl;
316             } catch( const char* msg ) {
317                 std::cout << msg << std::endl;
318                 return -1;
319             }
320         }
321
322         std::cout << "sent things" << std::endl;
323
324         return 0;
325     }
326
327     FILE* f = fopen( "/dev/ttyS0", "r+" );
328
329     if( !f ) {
330         std::cout << "Opening /dev/ttyS0 bio failed" << std::endl;
331         return -1;
332     }
333
334     setupSerial( f );
335
336     BIO* conn =  BIO_new_fd( fileno( f ), 0 );
337     BIO* slip1 = BIO_new( toBio<SlipBIO>() );
338     ( ( SlipBIO* )slip1->ptr )->setTarget( std::shared_ptr<OpensslBIO>( new OpensslBIOWrapper( conn ) ) );
339
340     try {
341         DefaultRecordHandler* dh = new DefaultRecordHandler( std::shared_ptr<Signer>( new SimpleOpensslSigner() ), slip1 );
342
343         while( true ) {
344             dh->handle();
345         }
346     } catch( char const* ch ) {
347         std::cout << "Exception: " << ch << std::endl;
348     }
349
350     return 0;
351 }