File Coverage

Verify.xs
Criterion Covered Total %
statement 101 122 82.7
branch 56 94 59.5
condition n/a
subroutine n/a
pod n/a
total 157 216 72.6


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