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 >= 10100
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             #if OPENSSL_API_COMPAT < 10100
210 8           ERR_load_ERR_strings();
211             #endif
212 8           OpenSSL_add_all_algorithms();
213              
214             =head2 register_verify_cb()
215              
216             Called by the Perl code to register which Perl sub is
217             the OpenSSL Verify Callback
218              
219             =cut
220              
221             void register_verify_cb(fn)
222             SV *fn
223              
224             CODE:
225             /* this code seems to work fine as the perl function is called */
226             /* Remember the Perl sub */
227 8 50         if (callback == (SV *) NULL)
228 8           callback = newSVsv(fn);
229             else
230 0 0         SvSetSV(callback, fn);
231              
232             =head1 new
233              
234             Constructs the object ready to verify the certificates.
235             It also sets the callback function.
236              
237             Crypt::OpenSSL::Verify->new(CAfile, options);
238              
239             For users coming from L, you should
240             instantiate the object using:
241              
242             Crypt::OpenSSL::Verify->new(CAfile, { strict_certs => 0 } );
243              
244             User who do not want a CAfile but want to use the defaults please use:
245              
246             Crypt::OpenSSL::Verify->new(undef);
247              
248             The object created is similar to running the following command with the
249             C command line tool: C<< openssl verify [ -CApath
250             /path/to/certs ] [ -noCApath ] [ -noCAfile ] [ -CAfile /path/to/file ]
251             cert.pem >>
252              
253             =cut
254              
255             SV * new(class, ...)
256             const char * class
257              
258             PREINIT:
259              
260 13           SV * CAfile = NULL;
261              
262 13           HV * options = newHV();
263              
264 13           X509_LOOKUP * cafile_lookup = NULL;
265 13           X509_LOOKUP * cadir_lookup = NULL;
266 13           X509_STORE * x509_store = NULL;
267             SV **svp;
268 13           SV *CApath = NULL;
269 13           int noCApath = 0;
270 13           int noCAfile = 0;
271 13           int strict_certs = 1; // Default is strict openSSL verify
272 13           SV * store = newSV(0);
273              
274             CODE:
275              
276              
277 13 50         if (items > 1) {
278 13 50         if (ST(1) != NULL) {
279             // TODO: ensure_string_sv
280 13           CAfile = ST(1);
281 13 50         if (strlen(SvPV_nolen(CAfile)) == 0) {
    50          
282 0           CAfile = NULL;
283             }
284             }
285              
286 13 100         if (items > 2)
287 7           options = ensure_hv(ST(2), "options");
288              
289             }
290              
291 13 100         if (hv_exists(options, "noCAfile", strlen("noCAfile"))) {
292 4           svp = hv_fetch(options, "noCAfile", strlen("noCAfile"), 0);
293 4 50         if (SvIOKp(*svp)) {
294 4 50         noCAfile = SvIV(*svp);
295             }
296             }
297              
298 13 100         if (hv_exists(options, "CApath", strlen("CApath"))) {
299 6           svp = hv_fetch(options, "CApath", strlen("CApath"), 0);
300 6           CApath = *svp;
301             }
302              
303 13 50         if (hv_exists(options, "noCApath", strlen("noCApath"))) {
304 0           svp = hv_fetch(options, "noCApath", strlen("noCApath"), 0);
305 0 0         if (SvIOKp(*svp)) {
306 0 0         noCApath = SvIV(*svp);
307             }
308             }
309              
310 13 100         if (hv_exists(options, "strict_certs", strlen("strict_certs"))) {
311 3           svp = hv_fetch(options, "strict_certs", strlen("strict_certs"), 0);
312 3 50         if (SvIOKp(*svp)) {
313 3 50         strict_certs = SvIV(*svp);
314             }
315             }
316              
317 13           x509_store = X509_STORE_new();
318              
319 13 50         if (x509_store == NULL) {
320 0           X509_STORE_free(x509_store);
321 0           croak("failure to allocate x509 store: %s", ssl_error());
322             }
323              
324 13 100         if (!strict_certs)
325 2           X509_STORE_set_verify_cb_func(x509_store, cb1);
326              
327 13 50         if (CAfile != NULL || !noCAfile) {
    0          
328 13           cafile_lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_file());
329 13 50         if (cafile_lookup == NULL) {
330 0           X509_STORE_free(x509_store);
331 0           croak("failure to add lookup to store: %s", ssl_error());
332             }
333 13 50         if (CAfile != NULL) {
334 13 50         if (!X509_LOOKUP_load_file(cafile_lookup, SvPV_nolen(CAfile), X509_FILETYPE_PEM)) {
    100          
335 1           X509_STORE_free(x509_store);
336 1 50         croak("Error loading file %s: %s\n", SvPV_nolen(CAfile),
337             ssl_error());
338             }
339             } else {
340 0           X509_LOOKUP_load_file(cafile_lookup, NULL, X509_FILETYPE_DEFAULT);
341             }
342             }
343              
344 12 100         if (CApath != NULL || !noCApath) {
    50          
345 12           cadir_lookup = X509_STORE_add_lookup(x509_store, X509_LOOKUP_hash_dir());
346 12 50         if (cadir_lookup == NULL) {
347 0           X509_STORE_free(x509_store);
348 0           croak("failure to add lookup to store: %s", ssl_error());
349             }
350 12 100         if (CApath != NULL) {
351 6 50         if (!X509_LOOKUP_add_dir(cadir_lookup, SvPV_nolen(CApath), X509_FILETYPE_PEM)) {
    50          
352 0           X509_STORE_free(x509_store);
353 0 0         croak("Error loading directory %s\n", SvPV_nolen(CApath));
354             }
355             } else {
356 6           X509_LOOKUP_add_dir(cadir_lookup, NULL, X509_FILETYPE_DEFAULT);
357             }
358             }
359              
360 12           HV * attributes = newHV();
361              
362 12           SV *const self = newRV_noinc( (SV *)attributes );
363              
364 12           sv_magicext(store, NULL, PERL_MAGIC_ext,
365             &store_magic, (const char *)x509_store, 0);
366              
367 12 50         if((hv_store(attributes, "STORE", 5, store, 0)) == NULL)
368 0           croak("unable to init store");
369              
370 12           RETVAL = sv_bless( self, gv_stashpv( class, 0 ) );
371              
372             // Empty the currect thread error queue
373             // https://www.openssl.org/docs/man1.1.1/man3/ERR_clear_error.html
374 12           ERR_clear_error();
375              
376             OUTPUT:
377              
378             RETVAL
379              
380             =head2 ctx_error_code(ctx)
381              
382             Called by the Perl code's verify_callback() to get the error code
383             from SSL from the ctx
384              
385             Receives the pointer to the ctx as an integer that is converted back
386             to the point address to be used
387              
388             =cut
389              
390             int ctx_error_code(ctx)
391             IV ctx;
392              
393             PREINIT:
394              
395             CODE:
396             /* printf("ctx_error_code - int holding pointer: %lu\n", (unsigned long) ctx); */
397             /* printf("ctx_error_code - Pointer to ctx: %p\n", (void *) INT2PTR(SV * , ctx)); */
398              
399 8           RETVAL = X509_STORE_CTX_get_error((X509_STORE_CTX *) INT2PTR(SV *, ctx));
400              
401             OUTPUT:
402              
403             RETVAL
404              
405             =head2 verify(self, x509)
406              
407             The actual verify function that calls OpenSSL to verify the x509 Cert that
408             has been passed in as a parameter against the store that was setup in _new()
409              
410             =head3 Parameters
411              
412             =over
413              
414             =item self - self object
415              
416             Contains details about Crypt::OpenSSL::Verify including the STORE
417              
418             =item x509 - Crypt::OpenSSL::X509
419              
420             Certificate to verify
421              
422             =back
423              
424             =cut
425              
426             int verify(self, x509)
427             HV * self;
428             Crypt::OpenSSL::X509 x509;
429              
430             PREINIT:
431              
432             X509_STORE_CTX * csc;
433              
434             CODE:
435             SV **svp;
436             MAGIC* mg;
437 8           X509_STORE * store = NULL;
438             //bool strict_certs = 1;
439             //struct OPTIONS trust_options;
440             //trust_options.trust_expired = 0;
441             //trust_options.trust_no_local = 0;
442             //trust_options.trust_onelogin = 0r
443             //
444              
445 8 50         if (x509 == NULL)
446 0           croak("no cert to verify");
447              
448 8           csc = X509_STORE_CTX_new();
449 8 50         if (csc == NULL)
450 0           croak("X.509 store context allocation failed: %s", ssl_error());
451              
452 8 50         if (!hv_exists(self, "STORE", strlen("STORE")))
453 0           croak("STORE not found in self!\n");
454              
455 8           svp = hv_fetch(self, "STORE", strlen("STORE"), 0);
456              
457 8 50         if (!SvMAGICAL(*svp) || (mg = mg_findext(*svp, PERL_MAGIC_ext, &store_magic)) == NULL)
    50          
458 0           croak("STORE is invalid");
459              
460 8           store = (X509_STORE *) mg->mg_ptr;
461              
462 8           X509_STORE_set_flags(store, 0);
463              
464 8 50         if (!X509_STORE_CTX_init(csc, store, x509, NULL)) {
465 0           X509_STORE_CTX_free(csc);
466 0           croak("store ctx init: %s", ssl_error());
467             }
468              
469 8           RETVAL = X509_verify_cert(csc);
470              
471             //if (hv_exists(self, "strict_certs", strlen("strict_certs"))) {
472             // svp = hv_fetch(self, "strict_certs", strlen("strict_certs"), 0);
473             // if (SvIOKp(*svp)) {
474             // strict_certs = SvIV(*svp);
475             // }
476             //}
477             //if (hv_exists(self, "trust_expired", strlen("trust_expired"))) {
478             // svp = hv_fetch(self, "trust_expired", strlen("trust_expired"), 0);
479             // if (SvIOKp(*svp)) {
480             // trust_options.trust_expired = SvIV(*svp);
481             // }
482             //}
483             //if (hv_exists(self, "trust_onelogin", strlen("trust_onelogin"))) {
484             // svp = hv_fetch(self, "trust_onelogin", strlen("trust_onelogin"), 0);
485             // if (SvIOKp(*svp)) {
486             // trust_options.trust_onelogin = SvIV(*svp);
487             // }
488             //}
489             //if (hv_exists(self, "trust_no_local", strlen("trust_no_local"))) {
490             // svp = hv_fetch(self, "trust_no_local", strlen("trust_no_local"), 0);
491             // if (SvIOKp(*svp)) {
492             // trust_options.trust_no_local = SvIV(*svp);
493             // }
494             //}
495             //
496             //This actually does not accomplish what we want as it essentially
497             //checks only the last certificate not the chain that might have
498             //acceptable errors. Original code considered errors on this last
499             //certificate as real errors.
500             //if ( !RETVAL && !strict_certs ) {
501             // int cb = verify_cb(&trust_options, RETVAL, csc);
502             // RETVAL = cb;
503             //}
504              
505 8 100         if (!RETVAL)
506 3           croak("verify: %s", ctx_error(csc));
507              
508 5           X509_STORE_CTX_free(csc);
509              
510             OUTPUT:
511              
512             RETVAL
513              
514             #if OPENSSL_API_COMPAT >= 10100
515             void __X509_cleanup(void)
516              
517             PPCODE:
518             /* deinitialisation is done automatically */
519              
520             #else
521             void __X509_cleanup(void)
522              
523             PPCODE:
524              
525 8           CRYPTO_cleanup_all_ex_data();
526 8           ERR_free_strings();
527 8           ERR_remove_state(0);
528 8           EVP_cleanup();
529              
530             #endif
531