File Coverage

Verify.xs
Criterion Covered Total %
statement 104 128 81.2
branch 53 98 54.0
condition n/a
subroutine n/a
pod n/a
total 157 226 69.4


line stmt bran cond sub pod time code
1             #include "EXTERN.h"
2             #include "perl.h"
3             #include "XSUB.h"
4             #define NEED_mg_findext
5             #define NEED_newRV_noinc
6             #define NEED_sv_2pv_flags
7             #include "ppport.h"
8              
9             #include
10             #include
11             #include
12             #include
13             #include
14             #include
15             #include
16             #include
17             #include
18             #include
19              
20             typedef X509 *Crypt__OpenSSL__X509;
21              
22             struct OPTIONS {
23             bool trust_expired;
24             bool trust_no_local;
25             bool trust_onelogin;
26             };
27              
28             =pod
29              
30             =head1 NAME
31              
32             Verify.xs - C interface to OpenSSL to verify certificates
33              
34             =head1 METHODS
35              
36             =head2 verify_cb(int ok, X509_STORE_CTX * ctx)
37             The C equivalent of the verify_callback perl sub
38             This code is due to be removed if the perl version
39             is permanent
40              
41             =cut
42              
43             #if DISABLED
44             int verify_cb(struct OPTIONS * options, int ok, X509_STORE_CTX * ctx)
45             {
46              
47             int cert_error = X509_STORE_CTX_get_error(ctx);
48              
49             if (!ok) {
50             /*
51             * Pretend that some errors are ok, so they don't stop further
52             * processing of the certificate chain. Setting ok = 1 does this.
53             * After X509_verify_cert() is done, we verify that there were
54             * no actual errors, even if the returned value was positive.
55             */
56             switch (cert_error) {
57             case X509_V_ERR_NO_EXPLICIT_POLICY:
58             /* fall thru */
59             case X509_V_ERR_CERT_HAS_EXPIRED:
60             if ( ! options->trust_expired ) {
61             break;
62             }
63             ok = 1;
64             break;
65             /* Continue even if the leaf is a self signed cert */
66             case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
67             /* Continue after extension errors too */
68             case X509_V_ERR_INVALID_CA:
69             case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
70             if ( !options->trust_onelogin )
71             break;
72             ok = 1;
73             break;
74             case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
75             if ( !options->trust_no_local )
76             break;
77             ok = 1;
78             break;
79             case X509_V_ERR_INVALID_NON_CA:
80             case X509_V_ERR_PATH_LENGTH_EXCEEDED:
81             case X509_V_ERR_INVALID_PURPOSE:
82             case X509_V_ERR_CRL_HAS_EXPIRED:
83             case X509_V_ERR_CRL_NOT_YET_VALID:
84             case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
85             ok = 1;
86             }
87             return ok;
88             }
89             return ok;
90             }
91             #endif
92              
93             =head2 int cb1(ok, ctx)
94              
95             The link to the Perl verify_callback() sub. This called by OpenSSL
96             during the verify of the certificates and in turn passes the parameters
97             to the Perl verify_callback() sub. It gets a return code from Perl
98             and returns it to OpenSSL
99              
100             =head3 Parameters
101              
102             =over
103              
104             =item * ok
105              
106             The result of the certificate verification in OpenSSL ok = 1, !ok =
107             0
108              
109             =item * ctx
110              
111             Pointer to the X509_Store_CTX that OpenSSL includes the error codes
112             in
113              
114             =back
115              
116             =cut
117              
118             static SV *callback = (SV *) NULL;
119              
120 8           static int cb1(ok, ctx)
121             int ok;
122             IV *ctx;
123             {
124 8           dSP;
125             int count;
126             int i;
127              
128             //printf("Callback pointer: %p\n", ctx);
129             //printf("Callback INT of pointer %lu\n", (unsigned long) PTR2IV(ctx));
130 8           ENTER;
131 8           SAVETMPS;
132              
133 8 50         PUSHMARK(SP);
134 8 50         EXTEND(SP, 2);
135              
136 8           PUSHs(newSViv(ok)); // Pass ok as integer on the stack
137 8           PUSHs(newSViv(PTR2IV(ctx))); // Pass pointer address as integer
138 8           PUTBACK;
139              
140 8           count = call_sv(callback, G_SCALAR); // Call the verify_callback()
141              
142 8           SPAGAIN;
143 8 50         if (count != 1)
144 0           croak("ERROR - Perl callback returned more than one value\n");
145              
146 8 50         i = POPi; // Get the return code from Perl verify_callback()
147 8           PUTBACK;
148 8 50         FREETMPS;
149 8           LEAVE;
150              
151 8           return i;
152             }
153              
154             =head2 ssl_error(void)
155              
156             Returns the string description of the ssl error
157              
158             =cut
159              
160 1           static const char *ssl_error(void)
161             {
162 1           return ERR_error_string(ERR_get_error(), NULL);
163             }
164              
165             =head2 ctx_error(void)
166              
167             Returns the string description of the ctx error
168              
169             =cut
170              
171 3           static const char *ctx_error(X509_STORE_CTX * ctx)
172             {
173 3           return X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx));
174             }
175              
176             // Taken from p5-Git-Raw
177 7           STATIC HV *ensure_hv(SV *sv, const char *identifier) {
178 7 50         if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
    50          
179 0           croak("Invalid type for '%s', expected a hash", identifier);
180              
181 7           return (HV *) SvRV(sv);
182             }
183              
184 12           static int ssl_store_destroy(pTHX_ SV* var, MAGIC* magic) {
185             X509_STORE * store;
186              
187 12           store = (X509_STORE *) magic->mg_ptr;
188 12 50         if (!store)
189 0           return 0;
190              
191 12           X509_STORE_free(store);
192 12           return 1;
193             }
194              
195             static const MGVTBL store_magic = { NULL, NULL, NULL, NULL, ssl_store_destroy };
196              
197             MODULE = Crypt::OpenSSL::Verify PACKAGE = Crypt::OpenSSL::Verify
198              
199             PROTOTYPES: DISABLE
200              
201             #if OPENSSL_API_COMPAT >= 0x10100000L
202             #undef ERR_load_crypto_strings
203             #define ERR_load_crypto_strings() /* nothing */
204             #undef OpenSSL_add_all_algorithms
205             #define OpenSSL_add_all_algorithms() /* nothing */
206             #endif
207             BOOT:
208 8           ERR_load_crypto_strings();
209 8           ERR_load_ERR_strings();
210 8           OpenSSL_add_all_algorithms();
211              
212             =head2 register_verify_cb()
213              
214             Called by the Perl code to register which Perl sub is
215             the OpenSSL Verify Callback
216              
217             =cut
218              
219             void register_verify_cb(fn)
220             SV *fn
221              
222             CODE:
223             /* this code seems to work fine as the perl function is called */
224             /* Remember the Perl sub */
225 8 50         if (callback == (SV *) NULL)
226 8           callback = newSVsv(fn);
227             else
228 0 0         SvSetSV(callback, fn);
229              
230             =head1 new
231              
232             Constructs the object ready to verify the certificates.
233             It also sets the callback function.
234              
235             Crypt::OpenSSL::Verify->new(CAfile, options);
236              
237             For users coming from L, you should
238             instantiate the object using:
239              
240             Crypt::OpenSSL::Verify->new(CAfile, { strict_certs => 0 } );
241              
242             User who do not want a CAfile but want to use the defaults please use:
243              
244             Crypt::OpenSSL::Verify->new(undef);
245              
246             The object created is similar to running the following command with the
247             C command line tool: C<< openssl verify [ -CApath
248             /path/to/certs ] [ -noCApath ] [ -noCAfile ] [ -CAfile /path/to/file ]
249             cert.pem >>
250              
251             =cut
252              
253             SV * new(class, ...)
254             const char * class
255              
256             PREINIT:
257              
258 13           SV * CAfile = NULL;
259              
260 13           HV * options = newHV();
261              
262 13           X509_LOOKUP * cafile_lookup = NULL;
263 13           X509_LOOKUP * cadir_lookup = NULL;
264 13           X509_STORE * x509_store = NULL;
265             SV **svp;
266 13           SV *CApath = NULL;
267 13           int noCApath = 0;
268 13           int noCAfile = 0;
269 13           int strict_certs = 1; // Default is strict openSSL verify
270 13           SV * store = newSV(0);
271              
272             CODE:
273              
274              
275 13 50         if (items > 1) {
276 13 50         if (ST(1) != NULL) {
277             // TODO: ensure_string_sv
278 13           CAfile = ST(1);
279 13 50         if (strlen(SvPV_nolen(CAfile)) == 0) {
    50          
280 0           CAfile = NULL;
281             }
282             }
283              
284 13 100         if (items > 2)
285 7           options = ensure_hv(ST(2), "options");
286              
287             }
288              
289 13 100         if (hv_exists(options, "noCAfile", strlen("noCAfile"))) {
290 4           svp = hv_fetch(options, "noCAfile", strlen("noCAfile"), 0);
291 4 50         if (SvIOKp(*svp)) {
292 4 50         noCAfile = SvIV(*svp);
293             }
294             }
295              
296 13 100         if (hv_exists(options, "CApath", strlen("CApath"))) {
297 6           svp = hv_fetch(options, "CApath", strlen("CApath"), 0);
298 6           CApath = *svp;
299             }
300              
301 13 50         if (hv_exists(options, "noCApath", strlen("noCApath"))) {
302 0           svp = hv_fetch(options, "noCApath", strlen("noCApath"), 0);
303 0 0         if (SvIOKp(*svp)) {
304 0 0         noCApath = SvIV(*svp);
305             }
306             }
307              
308 13 100         if (hv_exists(options, "strict_certs", strlen("strict_certs"))) {
309 3           svp = hv_fetch(options, "strict_certs", strlen("strict_certs"), 0);
310 3 50         if (SvIOKp(*svp)) {
311 3 50         strict_certs = SvIV(*svp);
312             }
313             }
314              
315 13           x509_store = X509_STORE_new();
316              
317 13 50         if (x509_store == NULL) {
318 0           X509_STORE_free(x509_store);
319 0           croak("failure to allocate x509 store: %s", ssl_error());
320             }
321              
322 13 100         if (!strict_certs)
323 2           X509_STORE_set_verify_cb_func(x509_store, cb1);
324              
325 13 50         if (CAfile != NULL || !noCAfile) {
    0          
326 13           cafile_lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
327 13 50         if (cafile_lookup == NULL) {
328 0           X509_STORE_free(x509_store);
329 0           croak("failure to add lookup to store: %s", ssl_error());
330             }
331 13 50         if (CAfile != NULL) {
332 13 50         if (!X509_LOOKUP_load_file(cafile_lookup, SvPV_nolen(CAfile), X509_FILETYPE_PEM)) {
    100          
333 1           X509_STORE_free(x509_store);
334 1 50         croak("Error loading file %s: %s\n", SvPV_nolen(CAfile),
335             ssl_error());
336             }
337             } else {
338 0           X509_LOOKUP_load_file(cafile_lookup, NULL, X509_FILETYPE_DEFAULT);
339             }
340             }
341              
342 12 100         if (CApath != NULL || !noCApath) {
    50          
343 12           cadir_lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_hash_dir());
344 12 50         if (cadir_lookup == NULL) {
345 0           X509_STORE_free(x509_store);
346 0           croak("failure to add lookup to store: %s", ssl_error());
347             }
348 12 100         if (CApath != NULL) {
349 6 50         if (!X509_LOOKUP_add_dir(cadir_lookup, SvPV_nolen(CApath), X509_FILETYPE_PEM)) {
    50          
350 0           X509_STORE_free(x509_store);
351 0 0         croak("Error loading directory %s\n", SvPV_nolen(CApath));
352             }
353             } else {
354 6           X509_LOOKUP_add_dir(cadir_lookup, NULL, X509_FILETYPE_DEFAULT);
355             }
356             }
357              
358 12           HV * attributes = newHV();
359              
360 12           SV *const self = newRV_noinc( (SV *)attributes );
361              
362 12           sv_magicext(store, NULL, PERL_MAGIC_ext,
363             &store_magic, (const char *)x509_store, 0);
364              
365 12 50         if((hv_store(attributes, "STORE", 5, store, 0)) == NULL)
366 0           croak("unable to init store");
367              
368 12           RETVAL = sv_bless( self, gv_stashpv( class, 0 ) );
369              
370             // Empty the currect thread error queue
371             // https://www.openssl.org/docs/man1.1.1/man3/ERR_clear_error.html
372 12           ERR_clear_error();
373              
374             OUTPUT:
375              
376             RETVAL
377              
378             =head2 ctx_error_code(ctx)
379              
380             Called by the Perl code's verify_callback() to get the error code
381             from SSL from the ctx
382              
383             Receives the pointer to the ctx as an integer that is converted back
384             to the point address to be used
385              
386             =cut
387              
388             int ctx_error_code(ctx)
389             IV ctx;
390              
391             PREINIT:
392              
393             CODE:
394             /* printf("ctx_error_code - int holding pointer: %lu\n", (unsigned long) ctx); */
395             /* printf("ctx_error_code - Pointer to ctx: %p\n", (void *) INT2PTR(SV * , ctx)); */
396              
397 8           RETVAL = X509_STORE_CTX_get_error((X509_STORE_CTX *) INT2PTR(SV *, ctx));
398              
399             OUTPUT:
400              
401             RETVAL
402              
403             =head2 verify(self, x509)
404              
405             The actual verify function that calls OpenSSL to verify the x509 Cert that
406             has been passed in as a parameter against the store that was setup in _new()
407              
408             =head3 Parameters
409              
410             =over
411              
412             =item self - self object
413              
414             Contains details about Crypt::OpenSSL::Verify including the STORE
415              
416             =item x509 - Crypt::OpenSSL::X509
417              
418             Certificate to verify
419              
420             =back
421              
422             =cut
423              
424             int verify(self, x509)
425             HV * self;
426             Crypt::OpenSSL::X509 x509;
427              
428             PREINIT:
429              
430             X509_STORE_CTX * csc;
431              
432             CODE:
433             SV **svp;
434             MAGIC* mg;
435 8           X509_STORE * store = NULL;
436             //bool strict_certs = 1;
437             //struct OPTIONS trust_options;
438             //trust_options.trust_expired = 0;
439             //trust_options.trust_no_local = 0;
440             //trust_options.trust_onelogin = 0r
441             //
442              
443 8 50         if (x509 == NULL)
444 0           croak("no cert to verify");
445              
446 8           csc = X509_STORE_CTX_new();
447 8 50         if (csc == NULL)
448 0           croak("X.509 store context allocation failed: %s", ssl_error());
449              
450 8 50         if (!hv_exists(self, "STORE", strlen("STORE")))
451 0           croak("STORE not found in self!\n");
452              
453 8           svp = hv_fetch(self, "STORE", strlen("STORE"), 0);
454              
455 8 50         if (!SvMAGICAL(*svp) || (mg = mg_findext(*svp, PERL_MAGIC_ext, &store_magic)) == NULL)
    50          
456 0           croak("STORE is invalid");
457              
458 8           store = (X509_STORE *) mg->mg_ptr;
459              
460 8           X509_STORE_set_flags(store, 0);
461              
462 8 50         if (!X509_STORE_CTX_init(csc, store, x509, NULL)) {
463 0           X509_STORE_CTX_free(csc);
464 0           croak("store ctx init: %s", ssl_error());
465             }
466              
467 8           RETVAL = X509_verify_cert(csc);
468              
469             //if (hv_exists(self, "strict_certs", strlen("strict_certs"))) {
470             // svp = hv_fetch(self, "strict_certs", strlen("strict_certs"), 0);
471             // if (SvIOKp(*svp)) {
472             // strict_certs = SvIV(*svp);
473             // }
474             //}
475             //if (hv_exists(self, "trust_expired", strlen("trust_expired"))) {
476             // svp = hv_fetch(self, "trust_expired", strlen("trust_expired"), 0);
477             // if (SvIOKp(*svp)) {
478             // trust_options.trust_expired = SvIV(*svp);
479             // }
480             //}
481             //if (hv_exists(self, "trust_onelogin", strlen("trust_onelogin"))) {
482             // svp = hv_fetch(self, "trust_onelogin", strlen("trust_onelogin"), 0);
483             // if (SvIOKp(*svp)) {
484             // trust_options.trust_onelogin = SvIV(*svp);
485             // }
486             //}
487             //if (hv_exists(self, "trust_no_local", strlen("trust_no_local"))) {
488             // svp = hv_fetch(self, "trust_no_local", strlen("trust_no_local"), 0);
489             // if (SvIOKp(*svp)) {
490             // trust_options.trust_no_local = SvIV(*svp);
491             // }
492             //}
493             //
494             //This actually does not accomplish what we want as it essentially
495             //checks only the last certificate not the chain that might have
496             //acceptable errors. Original code considered errors on this last
497             //certificate as real errors.
498             //if ( !RETVAL && !strict_certs ) {
499             // int cb = verify_cb(&trust_options, RETVAL, csc);
500             // RETVAL = cb;
501             //}
502              
503 8 100         if (!RETVAL)
504 3           croak("verify: %s", ctx_error(csc));
505              
506 5           X509_STORE_CTX_free(csc);
507              
508             OUTPUT:
509              
510             RETVAL
511              
512             #if OPENSSL_API_COMPAT >= 0x10100000L
513             void __X509_cleanup(void)
514              
515             PPCODE:
516             /* deinitialisation is done automatically */
517              
518             #else
519             void __X509_cleanup(void)
520              
521             PPCODE:
522              
523 8           CRYPTO_cleanup_all_ex_data();
524 8           ERR_free_strings();
525 8           ERR_remove_state(0);
526 8           EVP_cleanup();
527              
528             #endif
529