]> WPIA git - cassiopeia.git/blob - src/crypto/sslUtil.cpp
94e963e3bdd83d5c7e4966efdfaa0771ff933c2a
[cassiopeia.git] / src / crypto / sslUtil.cpp
1 #include "sslUtil.h"
2
3 #include <sys/types.h>
4 #include <termios.h>
5 #include <unistd.h>
6
7 #include <iostream>
8
9 #include "crypto/CRL.h"
10 #include "log/logger.hpp"
11
12 // *INDENT-OFF*
13 std::shared_ptr<int> ssl_lib_ref(
14     new int( SSL_library_init() ),
15     []( int* ref ) {
16         delete ref;
17
18         EVP_cleanup();
19         CRYPTO_cleanup_all_ex_data();
20     } );
21 // *INDENT-ON*
22
23 std::shared_ptr<X509> loadX509FromFile( const std::string& filename ) {
24     std::shared_ptr<FILE> f( fopen( filename.c_str(), "r" ), fclose );
25
26     if( !f ) {
27         return std::shared_ptr<X509>();
28     }
29
30     X509 *key = PEM_read_X509( f.get(), NULL, NULL, 0 );
31
32     if( !key ) {
33         return std::shared_ptr<X509>();
34     }
35
36     auto freeX509 = []( X509 * ref ) {
37         X509_free( ref );
38     };
39     return std::shared_ptr<X509>( key, freeX509 );
40 }
41
42 std::shared_ptr<EVP_PKEY> loadPkeyFromFile( const std::string& filename ) {
43     auto freeFile =  []( FILE * ptr ) {
44         if( ptr ) {
45             fclose( ptr );
46         }
47     };
48     std::shared_ptr<FILE> f( fopen( filename.c_str(), "r" ), freeFile );
49
50     if( !f ) {
51         return std::shared_ptr<EVP_PKEY>();
52     }
53
54     EVP_PKEY *key = PEM_read_PrivateKey( f.get(), NULL, NULL, 0 );
55
56     if( !key ) {
57         return std::shared_ptr<EVP_PKEY>();
58     }
59
60     auto freeKey = []( EVP_PKEY * ref ) {
61         EVP_PKEY_free( ref );
62     };
63     return std::shared_ptr<EVP_PKEY>( key, freeKey );
64 }
65
66 int gencb( int a, int b, BN_GENCB *g ) {
67     ( void ) a;
68     ( void ) b;
69     ( void ) g;
70
71     std::cout << ( a == 0 ? "." : "+" ) << std::flush;
72
73     return 1;
74 }
75
76 static int verify_callback( int preverify_ok, X509_STORE_CTX *ctx ) {
77     if( !preverify_ok ) {
78         //auto cert = X509_STORE_CTX_get_current_cert(ctx);
79         //BIO *o = BIO_new_fp(stdout,BIO_NOCLOSE);
80         //X509_print_ex(o, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
81         //BIO_free(o);
82
83         logger::errorf( "Verification failed: %s because %s", preverify_ok, X509_STORE_CTX_get_error( ctx ) );
84     }
85
86     return preverify_ok;
87 }
88
89 static std::shared_ptr<DH> dh_param;
90
91 std::shared_ptr<SSL_CTX> generateSSLContext( bool server ) {
92     auto freeSSL = []( SSL_CTX * p ) {
93         SSL_CTX_free( p );
94     };
95     std::shared_ptr<SSL_CTX> ctx = std::shared_ptr<SSL_CTX>( SSL_CTX_new( TLS_method() ), freeSSL );
96
97     if( !SSL_CTX_set_cipher_list( ctx.get(), "HIGH:+CAMELLIA256:!eNull:!aNULL:!ADH:!MD5:-RSA+AES+SHA1:!RC4:!DES:!3DES:!SEED:!EXP:!AES128:!CAMELLIA128" ) ) {
98         throw std::runtime_error( "Cannot set cipher list. Your source is broken." );
99     }
100
101     SSL_CTX_set_verify( ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback );
102     SSL_CTX_use_certificate_file( ctx.get(), server ? "keys/signer_server.crt" : "keys/signer_client.crt", SSL_FILETYPE_PEM );
103     SSL_CTX_use_PrivateKey_file( ctx.get(), server ? "keys/signer_server.key" : "keys/signer_client.key", SSL_FILETYPE_PEM );
104
105     if( 1 != SSL_CTX_load_verify_locations( ctx.get(), "keys/ca.crt", 0 ) ) {
106         throw std::runtime_error( "Cannot load CA store for certificate validation." );
107     }
108
109     if( server ) {
110         STACK_OF( X509_NAME ) *names = SSL_load_client_CA_file( "keys/env.crt" );
111
112         if( names ) {
113             SSL_CTX_set_client_CA_list( ctx.get(), names );
114         } else {
115             // error
116         }
117
118         if( !dh_param ) {
119             std::shared_ptr<FILE> paramfile( fopen( "dh_param.pem", "r" ), fclose );
120
121             if( paramfile ) {
122                 dh_param = std::shared_ptr<DH>( PEM_read_DHparams( paramfile.get(), NULL, NULL, NULL ), DH_free );
123             } else {
124                 dh_param = std::shared_ptr<DH>( DH_new(), DH_free );
125                 logger::note( "Generating DH params" );
126                 BN_GENCB *cb = BN_GENCB_new();
127                 BN_GENCB_set( cb, gencb, NULL );
128
129                 if( !DH_generate_parameters_ex( dh_param.get(), 2048, 5, cb ) ) {
130                     throw std::runtime_error( "DH generation failed" );
131                 }
132
133                 BN_GENCB_free( cb );
134
135                 std::cout << std::endl;
136                 paramfile = std::shared_ptr<FILE>( fopen( "dh_param.pem", "w" ), fclose );
137
138                 if( paramfile ) {
139                     PEM_write_DHparams( paramfile.get(), dh_param.get() );
140                 }
141             }
142         }
143
144         if( !SSL_CTX_set_tmp_dh( ctx.get(), dh_param.get() ) ) {
145             throw std::runtime_error( "Cannot set tmp dh." );
146         }
147     }
148
149     return ctx;
150 }
151
152 void setupSerial( std::shared_ptr<FILE> f ) {
153     struct termios attr;
154
155     if( tcgetattr( fileno( f.get() ), &attr ) ) {
156         throw std::runtime_error( "failed to get attrs" );
157     }
158
159     attr.c_iflag &= ~( IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON );
160     attr.c_oflag &= ~OPOST;
161     attr.c_lflag &= ~( ECHO | ECHONL | ICANON | ISIG | IEXTEN );
162     attr.c_cflag &= ~( CSIZE | PARENB );
163     attr.c_cflag |= CS8;
164
165     cfsetispeed( &attr, B115200 );
166     cfsetospeed( &attr, B115200 );
167
168     if( tcsetattr( fileno( f.get() ), TCSANOW, &attr ) ) {
169         throw std::runtime_error( "failed to get attrs" );
170     }
171 }
172
173 std::shared_ptr<BIO> openSerial( const std::string& name ) {
174     std::shared_ptr<FILE> f( fopen( name.c_str(), "r+" ), fclose );
175
176     if( !f ) {
177         logger::error( "Opening serial device failed." );
178         return std::shared_ptr<BIO>();
179     }
180
181     setupSerial( f );
182     auto freeBIO = [f]( BIO * b ) {
183         BIO_free( b );
184     };
185     return std::shared_ptr<BIO>( BIO_new_fd( fileno( f.get() ), 0 ), freeBIO );
186 }
187
188 extern std::string crlPrefix;
189 extern std::string crtPrefix;
190
191 CAConfig::CAConfig( const std::string& name ) : path( "ca/" + name ), name( name ) {
192     ca = loadX509FromFile( path + "/ca.crt" );
193
194     if( !ca ) {
195         throw new std::invalid_argument( "ca name: " + name + " contains unreadable certificate." );
196     }
197
198     caKey = loadPkeyFromFile( path + "/ca.key" );
199
200     ASN1_TIME *tm = X509_get_notBefore( ca.get() ); // tm MUST NOT be free'd; duplicate for owning copy.
201     notBefore = std::shared_ptr<ASN1_TIME>( ASN1_STRING_dup( tm ), ASN1_TIME_free );
202
203     std::size_t pos = name.find( "_" );
204
205     if( pos == std::string::npos ) {
206         throw new std::invalid_argument( "ca name: " + name + " is malformed." );
207     }
208
209     std::size_t pos2 = name.find( "_", pos + 1 );
210
211     if( pos2 == std::string::npos ) {
212         throw new std::invalid_argument( "ca name: " + name + " is malformed." );
213     }
214
215     crlURL = crlPrefix + "/g2/" + name.substr( pos + 1, pos2 - pos - 1 ) + "/" + name.substr( 0, pos ) + "-" + name.substr( pos2 + 1 ) + ".crl";
216     crtURL = crtPrefix + "/g2/" + name.substr( pos + 1, pos2 - pos - 1 ) + "/" + name.substr( 0, pos ) + "-" + name.substr( pos2 + 1 ) + ".crt";
217 }
218
219 std::string timeToString( std::shared_ptr<ASN1_TIME> time ) {
220     std::shared_ptr<ASN1_GENERALIZEDTIME> gtime( ASN1_TIME_to_generalizedtime( time.get(), 0 ), ASN1_GENERALIZEDTIME_free );
221     std::string strdate( ( char * ) ASN1_STRING_get0_data( gtime.get() ), ASN1_STRING_length( gtime.get() ) );
222
223     logger::notef( "openssl formatted me a date: %s", strdate );
224
225     if( strdate[strdate.size() - 1] != 'Z' ) {
226         throw std::runtime_error( "Got invalid date?" );
227     }
228
229     return strdate.substr( 0, strdate.size() - 1 );
230 }
231
232 void extractTimes( std::shared_ptr<X509> target,  std::shared_ptr<SignedCertificate> cert ) {
233     cert->before = timeToString( std::shared_ptr<ASN1_TIME>( X509_get_notBefore( target.get() ), [target]( auto p ) {
234         ( void )p;
235     } ) );
236     cert->after = timeToString( std::shared_ptr<ASN1_TIME>( X509_get_notAfter( target.get() ), [target]( auto p ) {
237         ( void )p;
238     } ) );
239 }
240
241 bool CAConfig::crlNeedsResign() {
242     auto crl = std::make_shared<CRL>( path + "/ca.crl" );
243     return crl->needsResign();
244 }