File Coverage

lib/Crypt/Simple/SMIME.pm
Criterion Covered Total %
statement 117 163 71.7
branch 31 52 59.6
condition 1 3 33.3
subroutine 19 21 90.4
pod 8 11 72.7
total 176 250 70.4


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             ################################################################################
3             #
4             # Script Name : $RCSFile$
5             # Version : 1
6             # Company : Down Home Web Design, Inc
7             # Author : Duane Hinkley ( duane@dhwd.com )
8             # Website : www.DownHomeWebDesign.com
9             #
10             # Description:
11             #
12             # Program description.
13             #
14             #
15             # Copyright (c) 2003-2004 Down Home Web Design, Inc. All rights reserved.
16             #
17             # $Header: /home/cvs/simple_smime/lib/Crypt/Simple/SMIME.pm,v 0.9 2005/01/29 14:52:13 cvs Exp $
18             #
19             # $Log: SMIME.pm,v $
20             # Revision 0.9 2005/01/29 14:52:13 cvs
21             # Minor change to version assignement syntax.
22             #
23             # Revision 0.8 2005/01/29 14:47:24 cvs
24             # Changed SignedEmailCertificate method to elimiate a warning message when it tries to detect if a filename or certificate was provided to the method.
25             #
26             # Revision 0.7 2005/01/29 14:38:30 cvs
27             # Changed tmp file syntax to make more friendly with some ISP temp file access restrictions. Thanks to for E. v. Pappenheim for providing the patch.
28             #
29             # Revision 0.6 2004/11/01 16:53:38 cvs
30             # Fix so email sets the from address properly
31             #
32             # Revision 0.5 2004/10/10 21:11:41 cvs
33             # Minor fixes
34             #
35             # Revision 0.4 2004/10/10 19:07:26 cvs
36             # Improve error reporting
37             #
38             # Revision 0.1 2004/10/10 00:01:27 cvs
39             # Initial checkin
40             #
41             # Revision 1.1 2004/10/09 15:51:27 cvs
42             # Version one
43             #
44             #
45             #
46             ################################################################################
47              
48             =pod
49              
50             =head1 NAME
51              
52             Crypt::Simple::SMIME - Simple SMIME Email Encryptor
53              
54             =head1 SYNOPSIS
55              
56             use Crypt::Simple::SMIME;
57              
58             my $c = new Crypt::Simple::SMIME(
59             {
60             'openssl' => '/opt/openssl/bin/openssl',
61             'sendmail' => '/usr/sbin/sendmail'
62             'certificate' => '/home/bob/certificate.pem'
63             }
64             );
65              
66             or:
67              
68             my $c = new Crypt::Simple::SMIME();
69              
70             $c->OpenSSLPath('/opt/openssl/bin/openssl');
71              
72             $c->SendmailPath('/usr/sbin/sendmail');
73              
74             $c->CertificatePath('/home/bob/certificate.pem')
75              
76             $c->SendMail($from,$to,$subject,$message);
77              
78             $c->Close();
79              
80             or:
81              
82             my $c = new Crypt::Simple::SMIME();
83              
84             $c->SignedEmailCertificate($signed_email_text)
85              
86             $c->SendMail($to,$from,$subject,$message);
87              
88             $c->Close();
89              
90             or:
91              
92             my $c = new Crypt::Simple::SMIME();
93              
94             $c->SignedEmailCertificate($signed_email_file)
95              
96             $c->SendMail($to,$from,$subject,$message);
97              
98             $c->Close();
99              
100              
101             or:
102              
103             my $c = new Crypt::Simple::SMIME();
104              
105             $c->Certificate($certificate_text)
106              
107             $c->SendMail($to,$from,$subject,$message);
108              
109             $c->Close();
110              
111              
112             =head1 DESCRIPTION
113              
114             After looking around for a simple way to send encrypted email to Outlook,
115             Mozilla and Netscape email clients, the modules had requirements that
116             required installing and/or compiling other software. This module is a simple
117             and secure method of sending encrypted email.
118              
119             No encrypted files are written to the hard drive. So there's no chance
120             of others accessing the information. The only files stored on the hard drive
121             is public keys/certificates
122              
123             =head1 REQUIREMENTS
124              
125             The only two requirements are the openssl binary be installed on the system and
126             the system has Sendmail or a binary that emulates Sendmail. For example Qmail
127             provides a binary to emulate Sendmail.
128              
129             =head1 METHODS
130              
131             The methods described in this section are available for all
132             C objects.
133              
134             =cut
135              
136             ###############################################################################
137             #
138             package Crypt::Simple::SMIME;
139 1     1   2111 use strict;
  1         1  
  1         31  
140 1     1   1238 use File::Temp qw/ :mktemp /;
  1         28266  
  1         146  
141 1     1   7 use vars qw($VERSION);
  1         8  
  1         2036  
142              
143             ( $VERSION ) = '$Revision: 0.9 $' =~ /\$Revision:\s+([^\s]+)/;
144              
145              
146              
147             ###############################################################################
148              
149             =over
150              
151             =item new(%hash)
152              
153             The new method is the constructor. The input hash can inlude the following:
154              
155             openssl / Path to the openssl binary on your system (optional)
156             sendmail / Path to the sendmail binary on your system (optional)
157              
158             my $2 = new Crypt::Simple::SMIME(
159             {
160             'openssl' => '/opt/openssl/bin/openssl',
161             'sendmail' => '/usr/sbin/sendmail'
162             'certificate' => '/home/bob/certificate.pem'
163             }
164             );
165              
166             or:
167              
168             my $c = new Crypt::Simple::SMIME();
169              
170             =cut
171              
172             sub new {
173              
174 6     6 1 283 my $type = shift;
175 6         16 my ($opt) = @_;
176 6         15 my $self = {};
177              
178 6         56 $self->{open_ssl_path} = $opt->{openssl};
179 6         24 $self->{sendmail_path} = $opt->{sendmail};
180 6         15 $self->{certificate_path} = $opt->{certificate};
181 6         230 $self->{encrypt_command} = undef;
182              
183 6         27 $self->{error_message} = undef;
184              
185 6         35 bless $self, $type;
186              
187             # If openssl path wasn't provided, try to find it
188             #
189 6 100       260 if ( ! $opt->{openssl} ) {
190              
191 5         36 $self->_find_open_ssl();
192             }
193              
194 6 100       21 if ( ! $opt->{sendmail} ) {
195              
196 5         30 $self->_find_sendmail();
197             }
198              
199 6         25 return $self;
200             }
201             ###############################################################################
202             # EXTERNAL METHODS
203             #
204              
205              
206             =item $c->SendMail($from,$to,$subject,$message)
207              
208             Given the from address, to address, subject and the message, encrypts and sends
209             the message to the given address.
210              
211             =cut
212              
213              
214             sub SendMail(){
215              
216 8     8 1 54 my $self = shift;
217 8         34 my ($from,$to,$subject,$message) = @_;
218 8         29 my $rtn = 1;
219              
220 8 100       497 if ( ! $from ) {
    100          
    100          
    100          
    100          
    100          
    50          
221              
222 1         4 $self->Error("From address missing in method SendMail");
223 1         2 $rtn = 0;
224             }
225             elsif ( ! $to ) {
226              
227 1         3 $self->Error("To address missing in method SendMail");
228 1         25 $rtn = 0;
229             }
230             elsif ( ! $subject ) {
231              
232 1         3 $self->Error("Subject missing in method SendMail");
233 1         2 $rtn = 0;
234             }
235             elsif ( ! $message ) {
236              
237 1         3 $self->Error("Message missing in method SendMail");
238 1         1 $rtn = 0;
239             }
240             elsif ( ! -f $self->{open_ssl_path} ) {
241              
242 1         4 $self->Error("Can't find openssl binary");
243 1         2 $rtn = 0;
244             }
245              
246             elsif ( ! -f $self->{sendmail_path} ) {
247              
248 2         659 $self->Error("Can't find sendmail binary");
249 2         5 $rtn = 0;
250             }
251              
252             elsif ( ! -f $self->{certificate_path} ) {
253              
254 1         21 $self->Error("Can't find certificate file");
255 1         7 $rtn = 0;
256             }
257             else {
258              
259 0         0 my $openssl = $self->{open_ssl_path};
260 0         0 my $pub_cert = $self->{certificate_path};
261 0         0 my $sendmail = $self->{sendmail_path};
262              
263 0         0 my $openssl_err = mktemp('/tmp/smimeXXXXXXX');
264 0         0 my $sendmail_out = mktemp('/tmp/smimeXXXXXXX');
265 0         0 my $sendmail_err = mktemp('/tmp/smimeXXXXXXX');
266              
267 0         0 $subject =~ s/'/\\'/g;
268              
269 0         0 my $result;
270              
271 0         0 $self->{encrypt_command} = "echo '\n" . $self->_str_replace('"', '\\"', $message ) . "' | $openssl smime -to '$to' -subject '$subject' -from '$from' -encrypt $pub_cert 2> $openssl_err | $sendmail -f$from -t > $sendmail_out 2> $sendmail_err";
272              
273 0         0 $result = system($self->{encrypt_command});
274              
275 0 0       0 if ( $result ) {
276              
277 0         0 my $message = "Unknown error sending encrypted mail\n";
278              
279 0         0 $message .= "openssl STDERR: " . $self->_read_file($openssl_err) . "\n\n";
280 0         0 $message .= "sendmail STDOUT: " . $self->_read_file($sendmail_out) . "\n\n";
281 0         0 $message .= "sendmail STDOUT: " . $self->_read_file($sendmail_err) . "\n\n";
282              
283 0         0 $self->Error($message);
284 0         0 $rtn = 0;
285             }
286 0 0       0 if ( -f $openssl_err ) { unlink($openssl_err); }
  0         0  
287 0 0       0 if ( -f $sendmail_out ) { unlink($sendmail_out); }
  0         0  
288 0 0       0 if ( -f $sendmail_err ) { unlink($sendmail_err); }
  0         0  
289              
290             }
291 8         21 return $rtn;
292             }
293              
294             =item $c->Close()
295              
296             Cleans up after the module by deleting temporary files.
297              
298             =cut
299              
300              
301             sub Close(){
302              
303 1     1 1 10 my $self = shift;
304              
305 1 50       91 if ( -f $self->{tmp_cert_file} ) {
306              
307 0         0 unlink( $self->{tmp_cert_file} );
308             }
309              
310 1 50       19 if ( -f $self->{tmp_msg_file} ) {
311              
312 0         0 unlink( $self->{tmp_msg_file} );
313             }
314              
315 1 50       33 if ( -f $self->{tmp_signed_cert_path} ) {
316              
317 1         197 unlink( $self->{tmp_signed_cert_path} );
318             }
319             }
320              
321              
322             ###############################################################################
323             # INTERNAL METHODS
324             #
325              
326             sub _str_replace {
327 1     1   4 my $self = shift;
328 1         7 my ($search,$replace,$text) = @_;
329              
330 1         40 $text =~ s/$search/$replace/g;
331              
332 1         55 return $text;
333             }
334             # Looks for openssl binary at common locations
335             #
336             sub _find_open_ssl {
337              
338 5     5   9 my $self = shift;
339 5         10 my ($var) = @_;
340              
341 5 50       215 if ( -f '/usr/bin/openssl' ) {
    0          
342              
343 5         24 $self->{open_ssl_path} = '/usr/bin/openssl';
344             }
345             elsif ( -f '/usr/local/bin/openssl' ) {
346              
347 0         0 $self->{open_ssl_path} = '/usr/local/bin/openssl';
348             }
349             }
350             # Looks for sendmail binary at common locations
351             #
352             sub _find_sendmail {
353              
354 5     5   11 my $self = shift;
355 5         10 my ($var) = @_;
356              
357 5 50       299 if ( -f '/usr/bin/sendmail' ) {
    50          
    50          
358              
359 0         0 $self->{sendmail_path} = '/usr/bin/sendmail';
360             }
361             elsif ( -f '/usr/local/bin/sendmail' ) {
362              
363 0         0 $self->{sendmail_path} = '/usr/local/bin/sendmail';
364             }
365             elsif ( -f '/usr/lib/sendmail' ) {
366              
367 0         0 $self->{sendmail_path} = '/usr/lib/sendmail';
368             }
369             }
370              
371             sub _assessor_util(){
372              
373 39     39   60 my $self = shift;
374 39         83 my ($value,$key) = @_;
375              
376 39 100       89 if ($value) {
377              
378 21         52 $self->{$key} = $value;
379             }
380 39         579 return $self->{$key};
381             }
382              
383             sub _read_file {
384 0     0   0 my $self = shift;
385 0         0 my ($filename) = @_;
386 0         0 my $contents;
387              
388 0         0 open(IN, "< $filename");
389              
390 0         0 while ( my $line = ) {
391              
392 0         0 $contents .= $line;
393             }
394 0         0 return $contents;
395             }
396             # Called if using a Netscape certificate is being used
397             #
398             sub _convert_signed_certificate {
399              
400 2     2   10 my $self = shift;
401 2         5 my $rtn = 1;
402              
403 2         16 $self->_write_signed_email_to_temp_file();
404              
405              
406 2         10 my $pemfile = mktemp('/tmp/smimeXXXXXXX') . ".pem";
407              
408 2         511 $self->CertificatePath($pemfile);
409 2         9 $self->{tmp_cert_file} = $pemfile;
410              
411 2         5 my $signedemailfile = $self->{signed_cert_path};
412              
413 2         8 my $msgfile = mktemp('/tmp/smimeXXXXXXX');
414 2         391 $self->{tmp_msg_file} = $pemfile;
415              
416 2         10 my $openssl = $self->OpenSSLPath();
417              
418 2         11 my $cmd = "$openssl smime -verify -in $signedemailfile -signer $pemfile -out $msgfile 2>/dev/null > /dev/null";
419              
420 2         37672 my $result = system($cmd);
421              
422 2 50       69 if (! $result ) {
423              
424 0         0 $self->Error("Unknown error extracting certificate from signed email");
425 0         0 $rtn = 0;
426             }
427 2         77 return $rtn;
428             }
429              
430             sub _write_signed_email_to_temp_file {
431              
432 2     2   4 my $self = shift;
433              
434 2         57 $self->{signed_cert_path} = mktemp('/tmp/smimeXXXXXXX') . ".p12";
435 2         1034 my $filename = $self->{signed_cert_path};
436              
437 2         15 $self->{tmp_signed_cert_path} = $filename;
438              
439 2         444 open( CRT, "> $filename");
440 2         17 print CRT $self->SignedEmailCertificate();
441 2         145 close(CRT);
442             }
443              
444              
445             =head1 DATA ACCESSORS
446              
447             The methods described in this section allow setting and reading data.
448              
449             =cut
450              
451              
452             ###############################################################################
453             # ACCESSORS
454             #
455             =item $c->OpenSSLPath($openssl_path)
456              
457             If a the open sll binary path is passed, this accessor will set the value.
458             It will always return the value stored.
459              
460             =cut
461              
462             sub OpenSSLPath(){
463              
464 7     7 0 1405 my $self = shift;
465 7         13 my ($var) = @_;
466              
467 7         19 return $self->_assessor_util($var,'open_ssl_path');
468             }
469              
470             =item $c->SendmailPath($sendmail_path)
471              
472             If a the sendmail binary path is passed, this accessor will set the value.
473             It will always return the value stored.
474              
475             =cut
476              
477             sub SendmailPath(){
478              
479 5     5 1 9748 my $self = shift;
480 5         26 my ($var) = @_;
481              
482 5         25 return $self->_assessor_util($var,'sendmail_path');
483             }
484              
485             =item $c->CertificatePath($certificate_path)
486              
487             If a the sendmail binary path is passed, this accessor will set the value.
488             It will always return the value stored.
489              
490             =cut
491              
492              
493             sub CertificatePath(){
494              
495 7     7 1 7213 my $self = shift;
496 7         19 my ($var) = @_;
497              
498 7         34 return $self->_assessor_util($var,'certificate_path');
499             }
500              
501             =item $c->SignedEmailCertificate($certificate)
502              
503             Accepts the text of a signed email or the path to a file that contains the
504             email. It returns the text of a signed email.
505              
506             To get a certificate from asigned email, save the message to a file and and
507             pass the contents this routine.
508              
509             =cut
510              
511              
512             sub SignedEmailCertificate(){
513              
514 4     4 1 15195 my $self = shift;
515 4         22 my ($var) = @_;
516              
517 4 100       33 if ($var) {
518              
519 2 50 33     29 if ( ! $var =~ /\n/ && -f $var ) {
520              
521 0         0 open(FILE,"< $var");
522 0         0 $var = '';
523 0         0 while( my $line = ) {
524              
525 0         0 $var .= $line;
526             }
527 0         0 close(FILE);
528             }
529              
530 2         16 $self->{signed_certificate} = $var;
531 2         27 $self->_convert_signed_certificate();
532             }
533 4         179 return $self->{signed_certificate};
534             }
535             =item $c->Certificate($certificate)
536              
537             Accepts the certificate contents from a variable to use to encrypt the message.
538              
539             =cut
540              
541              
542             sub Certificate(){
543              
544 1     1 0 16 my $self = shift;
545 1         6 my ($var) = @_;
546              
547 1 50       5 if ($var) {
548              
549 1         5 $self->{certificate} = $var;
550              
551 1         32 my $pemfile = mktemp('/tmp/smimeXXXXXXX') . ".pem";
552              
553 1         357 $self->CertificatePath($pemfile);
554 1         3 $self->{tmp_cert_file} = $pemfile;
555              
556 1         760 open(FILE,"> $pemfile");
557 1         18 print FILE $var;
558 1         64 close(FILE);
559              
560             }
561 1         16 return $self->{certificate};
562             }
563              
564             =item $c->Error()
565              
566             Returns true if the module encountered an error.
567              
568             =cut
569              
570              
571             sub Error(){
572              
573 19     19 1 327 my $self = shift;
574 19         45 my ($var) = @_;
575              
576 19         562 return $self->_assessor_util($var,'error_message');
577             }
578              
579             =item $c->ErrorMessage()
580              
581             Returns the error message module encountered an error.
582              
583             =cut
584              
585              
586             sub ErrorMessage(){
587              
588 0     0 1 0 my $self = shift;
589 0         0 my ($var) = @_;
590              
591 0         0 return $self->_assessor_util($var,'error_message');
592             }
593             =item $c->EncryptCommand()
594              
595             Returns the command used to encrypt the email.
596              
597             =cut
598              
599              
600             sub EncryptCommand(){
601              
602 1     1 0 6 my $self = shift;
603 1         5 my ($var) = @_;
604              
605 1         11 return $self->_assessor_util($var,'encrypt_command');
606             }
607              
608              
609              
610             #########################################################################################33
611             # End of class
612              
613             1;
614             __END__