]> WPIA git - cassiopeia.git/blob - src/recordHandler.cpp
fix: clean SSL shutdown, reset, allowing deamon operation
[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 "remoteSigner.h"
18 #include "simpleOpensslSigner.h"
19 #include "sslUtil.h"
20 #include "slipBio.h"
21
22 extern std::vector<Profile> profiles;
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     RecordHandlerSession( DefaultRecordHandler* parent, std::shared_ptr<Signer> signer, std::shared_ptr<SSL_CTX> ctx, std::shared_ptr<BIO> output ) :
39         tbs( new TBSCertificate() ) {
40         this->parent = parent;
41         this->signer = signer;
42
43         ssl = std::shared_ptr<SSL>( SSL_new( ctx.get() ), SSL_free );
44         std::shared_ptr<BIO> bio(
45             BIO_new( BIO_f_ssl() ),
46             [output]( BIO * p ) {
47                 BIO_free( p );
48             } );
49         SSL_set_accept_state( ssl.get() );
50         SSL_set_bio( ssl.get(), output.get(), output.get() );
51         BIO_set_ssl( bio.get(), ssl.get(), BIO_NOCLOSE );
52         io = std::shared_ptr<OpensslBIOWrapper>( new OpensslBIOWrapper( bio ) );
53     }
54
55     void respondCommand( RecordHeader::SignerResult res, std::string payload ) {
56         RecordHeader rh;
57         rh.command = ( uint16_t ) res;
58         rh.flags = 0;
59         rh.command_count = 0; // TODO i++
60         rh.totalLength = payload.size();
61         sendCommand( rh, payload, io );
62     }
63
64     void work() {
65         std::vector<char> buffer( 2048, 0 );
66         int res = io->read( buffer.data(), buffer.capacity() );
67
68         if( res <= 0 ) {
69             parent->reset();
70             return;
71         }
72
73         std::string content( buffer.data(), res );
74
75         try {
76             RecordHeader head;
77             std::string payload = parseCommand( head, content );
78             execute( head, payload );
79         } catch( const char* msg ) {
80             std::cout << msg << std::endl;
81             parent->reset();
82             return;
83         }
84     }
85
86     void execute( RecordHeader& head, std::string data ) {
87         if( head.totalLength != head.payloadLength || head.offset != 0 ) {
88             throw "Error, chunking not supported yet";
89         }
90
91         switch( ( RecordHeader::SignerCommand ) head.command ) {
92         case RecordHeader::SignerCommand::SET_CSR: // setCSR
93             tbs->csr_content = data;
94             tbs->csr_type = "CSR";
95             std::cout << "CSR read" << std::endl;
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::ADD_SAN: {
108             size_t pos = data.find( "," );
109
110             if( pos == std::string::npos ) {
111             } else {
112                 std::shared_ptr<SAN> san( new SAN() );
113                 san->type = data.substr( 0, pos );
114                 san->content = data.substr( pos + 1 );
115                 tbs->SANs.push_back( san );
116             }
117         }
118         break;
119
120         case RecordHeader::SignerCommand::ADD_AVA: {
121             size_t pos = data.find( "," );
122
123             if( pos == std::string::npos ) {
124                 // error
125             } else {
126                 std::shared_ptr<AVA> ava( new AVA() );
127                 ava->name = data.substr( 0, pos );
128                 ava->value = data.substr( pos + 1 );
129                 tbs->AVAs.push_back( ava );
130             }
131         }
132         break;
133
134         case RecordHeader::SignerCommand::ADD_PROOF_LINE:
135             break;
136
137         case RecordHeader::SignerCommand::SIGN:
138             result = signer->sign( tbs );
139             std::cout << "res: " << result->certificate << std::endl;
140             result->log = "I am a dummy log.\nI signed that thing ;-) \n";
141             respondCommand( RecordHeader::SignerResult::SAVE_LOG, result->log );
142             break;
143
144         case RecordHeader::SignerCommand::LOG_SAVED:
145             if( result ) {
146                 respondCommand( RecordHeader::SignerResult::CERTIFICATE, result->certificate );
147             }
148
149             if( !SSL_shutdown( ssl.get() ) && !SSL_shutdown( ssl.get() ) ) {
150                 std::cout << "SSL close failed" << std::endl;
151             }
152
153             break;
154
155         default:
156             throw "Unimplemented";
157         }
158     }
159 };
160
161 DefaultRecordHandler::DefaultRecordHandler( std::shared_ptr<Signer> signer, std::shared_ptr<BIO> bio ) :
162     currentSession() {
163
164     this->signer = signer;
165
166     ctx = generateSSLContext( true );
167
168     this->bio = bio;
169 }
170
171 void DefaultRecordHandler::reset() {
172     currentSession = std::shared_ptr<RecordHandlerSession>();
173 }
174
175 void DefaultRecordHandler::handle() {
176     if( !currentSession ) {
177         std::cout << "session allocated" << std::endl;
178         currentSession = std::shared_ptr<RecordHandlerSession>( new RecordHandlerSession( this, signer, ctx, bio ) );
179     }
180
181     currentSession->work();
182 }