File Coverage

blib/lib/Aut.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Aut;
2              
3             # $Id: Aut.pm,v 1.23 2004/04/11 19:17:10 cvs Exp $
4              
5 1     1   728 use 5.006;
  1         2  
  1         34  
6 1     1   4 use strict;
  1         2  
  1         27  
7              
8 1     1   1431 use Crypt::RSA;
  0            
  0            
9             use Crypt::RSA::Key::Public;
10             use Crypt::RSA::Key::Private;
11              
12             use Digest::MD5 qw(md5_base64);
13              
14             use Locale::Framework;
15              
16             use Config::Frontend;
17             use Config::Backend::String;
18              
19             use Aut::Crypt;
20             use Aut::Ticket;
21             use Aut::UI::Console;
22             use Aut::Base64;
23             use Aut::Backend::Conf;
24              
25             our $VERSION = '0.11';
26              
27             sub new {
28             my $class=shift;
29             my $string="";
30             my $args = {
31             Backend => new Aut::Backend::Conf(
32             new Config::Frontend(
33             new Config::Backend::String(\$string)
34             )
35             ),
36             UI => new Aut::UI::Console(),
37             RSA_Bits => 1024,
38             Levels => [ "admin", "mutate", "view" ],
39             Adminlevel => "admin",
40             @_
41             };
42              
43             my $backend=$args->{"Backend"};
44             my $ui=$args->{"UI"};
45             my $bits=$args->{"RSA_Bits"};
46             my $self;
47              
48             $self->{"string"}=\$string;
49             $self->{"status"}="NONE";
50             $self->{"errmsg"}="";
51              
52             if (not defined $backend) { die "You need to specify an Aut Backend"; }
53             if (not defined $ui) { die "You need to specify an Aut user interface"; }
54              
55             $self->{"backend"}=$backend;
56             $self->{"ui"}=$ui;
57             $self->{"levels"}=$args->{"Levels"};
58             $self->{"adminlevel"}=$args->{"Adminlevel"};
59             $self->{"ui"}->initialize($self->{"levels"},$self->{"adminlevel"});
60             $self->{"base64"}=new Aut::Base64();
61              
62             bless $self,$class;
63              
64             $self->initialize($bits);
65              
66             return $self;
67             }
68              
69             #####################################################################
70             # Initializing
71             #####################################################################
72              
73             # Internal function.
74             # Used to initialize the subsystem.
75              
76             sub initialize {
77             my $self=shift;
78             my $bits=shift;
79              
80             if (not defined $self->{"backend"}->get_keys()) {
81             my $pass=$self->{"ui"}->ask_pass($self,_T("Initializing cryptography system.\n".
82             "Please provide a password for the global\n".
83             "RSA key-pair. You need to keep this password\n".
84             "secret and will have to remember it!"));
85             if (not defined $pass) { die "Cannot continue without password"; }
86              
87             my $passagain=$self->{"ui"}->ask_pass($self,_T("Enter the RSA password again for verification."));
88             if ($pass ne $passagain) { die "Cannot continue with two different passwords"; }
89              
90              
91             $self->{"ui"}->message(_T("Generating keys, this will take some time...")."($bits bits)");
92             my $rsa = new Crypt::RSA;
93             my ($public,$private) =$rsa->keygen(
94             Identity => 'Aut module',
95             Size => $bits,
96             Password => "",
97             Verbosity => 0
98             ) or die $rsa->errstr();
99              
100              
101             my $private_str=private_key_to_string($private);
102             $private_str="private,".$private_str;
103              
104             my $cipher=new Aut::Crypt($pass);
105             $private_str=$cipher->encrypt($private_str);
106              
107             my $public_str=public_key_to_string($public);
108              
109             $public_str=$self->{"base64"}->encode($public_str);
110             $private_str=$self->{"base64"}->encode($private_str);
111              
112             $self->{"backend"}->set_keys($public_str,$private_str);
113              
114             $self->{"ui"}->message(_T("done."));
115             }
116             }
117              
118             #####################################################################
119             # Querying
120             #####################################################################
121              
122             sub has_accounts {
123             my $self=shift;
124             $self->{"backend"}->has_accounts();
125             }
126              
127             sub is_admin {
128             my $self=shift;
129             my $ticket=shift;
130             return ($ticket->valid()) && ($ticket->rights() eq $self->{"adminlevel"});
131             }
132              
133             sub status {
134             my $self=shift;
135             return $self->{"status"};
136             }
137              
138             sub last_error {
139             my $self=shift;
140             return $self->{"errmsg"};
141             }
142              
143             sub list_accounts {
144             my $self=shift;
145             return $self->{"backend"}->get_all_accounts();
146             }
147              
148             sub exists {
149             my $self=shift;
150             my $account=shift;
151             return $self->{"backend"}->exists($account);
152             }
153              
154             #####################################################################
155             # Bag functionality --> Backend as Configuration file
156             #####################################################################
157              
158             sub set {
159             my ($self,$ticket,$var,$val) = @_;
160             $val=$ticket->encrypt($val);
161             $self->{"backend"}->set($ticket->account(),$var,$val);
162             }
163              
164             sub get {
165             my ($self,$ticket,$var) = @_;
166             my $val=$self->{"backend"}->get($ticket->account(),$var);
167             $val=$ticket->decrypt($val);
168             return $val;
169             }
170              
171             sub del {
172             my ($self,$ticket,$var) = @_;
173             $self->{"backend"}->del($ticket->account(),$var);
174             }
175              
176             #####################################################################
177             # status and error
178             #####################################################################
179              
180             ### Internal Functions
181              
182             sub set_status {
183             my ($self,$status)=@_;
184             $self->{"status"}=$status;
185             }
186              
187             sub set_error {
188             my ($self,$msg)=@_;
189             $self->{"errmsg"}=$msg;
190             }
191              
192             #####################################################################
193             # rsa crypting/decrypting
194             #####################################################################
195              
196             ### Internal Functions
197             ### Using an undocumented feature here!
198              
199             sub public_key_to_string {
200             my $key=shift;
201             return $key->serialize();
202             }
203              
204             sub string_to_public_key {
205             my $string=shift;
206             my $key=new Crypt::RSA::Key::Public;
207             my @pub;
208             push @pub,$string;
209             return $key->deserialize(String => \@pub);
210             }
211              
212             sub private_key_to_string {
213             my $key=shift;
214             return $key->serialize();
215             }
216              
217             sub string_to_private_key {
218             my $string=shift;
219             my $key=new Crypt::RSA::Key::Private;
220             my @pub;
221             push @pub,$string;
222             return $key->deserialize(String => \@pub);
223             }
224             ### Using an undocumented feature here!
225              
226             sub rsa_crypt {
227             my $self=shift;
228             my $text=shift;
229             my $rsa=new Crypt::RSA;
230              
231             my ($public_str,$private_str)=$self->{"backend"}->get_keys();
232             my $public;
233              
234             $public_str=$self->{"base64"}->decode($public_str);
235             my $public=string_to_public_key($public_str);
236              
237             $text=$rsa->encrypt(
238             Message => $text,
239             Key => $public
240             );
241             if (not defined $text) { warn "cannot encrypt"; }
242              
243             $text=$self->{"base64"}->encode($text);
244             return $text;
245             }
246              
247             sub rsa_decrypt {
248             my $self=shift;
249             my $text=shift;
250             my $pass=shift;
251              
252             my $rsa=new Crypt::RSA;
253              
254             my ($public_str,$private_str)=$self->{"backend"}->get_keys();
255             $private_str=$self->{"base64"}->decode($private_str);
256              
257             if (not defined $pass) {
258             $pass=$self->{"ui"}->ask_pass($self,_T("Give the password for the account global RSA private key"));
259             }
260              
261             if (not defined $pass) {
262             return undef;
263             }
264              
265             my $cipher=new Aut::Crypt($pass);
266             $private_str=$cipher->decrypt($private_str);
267             if (substr($private_str,0,8) ne "private,") {
268             return undef;
269             }
270             else {
271              
272             $private_str=substr($private_str,8,length($private_str));
273             my $private=string_to_private_key($private_str);
274              
275             $text=$self->{"base64"}->decode($text);
276             $text=$rsa->decrypt(
277             Cyphertext => $text,
278             Key => $private,
279             );
280             if (not defined $text) {
281             warn "Unexpected! Cannot decrypt text with good private key!";
282             }
283             return $text;
284             }
285             }
286              
287             sub check_rsa_private_pass {
288             my $self=shift;
289             my $pass=shift;
290              
291             my ($public_str,$private_str)=$self->{"backend"}->get_keys();
292             $private_str=$self->{"base64"}->decode($private_str);
293              
294             my $cipher=new Aut::Crypt($pass);
295             $private_str=$cipher->decrypt($private_str);
296             if (substr($private_str,0,8) ne "private,") {
297             return 0;
298             }
299             else {
300             return 1;
301             }
302             }
303              
304             ### Internal Functions
305              
306             ################################################################
307             # Account handling
308             #####################################################################
309              
310             sub ticket_create {
311             my $self=shift;
312             $self->ticket_update(@_);
313             }
314              
315             sub ticket_update {
316             my $self=shift;
317             my $ticket=shift;
318              
319             my $account=$ticket->account();
320             my $pass=$ticket->pass();
321             my $rights=$ticket->rights();
322             my $seed=$ticket->seed();
323              
324             #my $md5pass=md5_base64($pass);
325             my $rsapass=$self->rsa_crypt($pass);
326              
327             my $cipher=new Aut::Crypt($pass);
328             my $crypt_seed=$cipher->encrypt($seed);
329             my $b64_seed=$self->{"base64"}->encode($crypt_seed);
330              
331             #$self->{"backend"}->set_md5pass($account,$md5pass);
332             #$self->{"backend"}->set_pass($account,$rsapass);
333             $self->{"backend"}->set_pass($account,$pass);
334             $self->{"backend"}->set_rsa_pass($account,$rsapass);
335             $self->{"backend"}->set_rights($account,$rights);
336             $self->{"backend"}->set_seed($account,$b64_seed);
337              
338             my $to_hash="$rights$b64_seed";
339             my $hash=md5_base64($to_hash);
340             $self->{"backend"}->set_sr_hash($account,$hash);
341             }
342              
343             sub ticket_get {
344             my $self=shift;
345             my $account=shift;
346             my $pass=shift;
347              
348             if (not defined $pass) { $pass=""; }
349              
350             my $ticket=new Aut::Ticket($account,$pass);
351              
352             if (not $self->{"backend"}->exists($account)) {
353             $ticket->invalidate();
354             $self->set_error(_T("Account does not exist :").$account);
355             $self->set_status("ENOEXIST");
356             return $ticket;
357             }
358              
359             #my $stored_pass=$self->{"backend"}->get_md5pass($account);
360             #my $md5pass=md5_base64($pass);
361              
362             if (not $self->{"backend"}->pass_ok($account,$pass)) {
363             $ticket->invalidate();
364             $self->set_error(_T("Invalid password for :").$account);
365             $self->set_status("EBADPASS");
366             }
367              
368             my $rights=$self->{"backend"}->get_rights($account);
369             my $seed=$self->{"backend"}->get_seed($account);
370              
371             my $to_hash="$rights$seed";
372             my $hash=md5_base64($to_hash);
373             if (($hash ne $self->{"backend"}->get_sr_hash($account))
374             and
375             $ticket->valid()
376             ) {
377             $self->set_error(_T("seed or rights have been modified for account ").$account);
378             $self->set_status("EBADINFO");
379             $ticket->invalidate();
380              
381             }
382              
383             $seed=$self->{"base64"}->decode($seed);
384             my $cipher=new Aut::Crypt($pass);
385             $seed=$cipher->decrypt($seed);
386              
387             $ticket->set_rights($rights);
388             $ticket->set_seed($seed);
389              
390             return $ticket;
391             }
392              
393             sub ticket_remove {
394             my $self=shift;
395             my $ticket=shift;
396             $self->{"backend"}->del_account($ticket->account());
397             $ticket->invalidate();
398             }
399              
400             #####################################################################
401             # Administrator functions
402             #####################################################################
403              
404             sub ticket_admin_get {
405             my $self=shift;
406             my $user_account=shift;
407             my $pass=shift;
408              
409             if (not $self->{"backend"}->exists($user_account)) {
410             my $ticket=new Aut::Ticket($user_account,"");
411             $ticket->invalidate();
412             return $ticket;
413             }
414              
415             my $user_pass=$self->{"backend"}->get_rsa_pass($user_account);
416             if (defined $pass) {
417             $user_pass=$self->rsa_decrypt($user_pass,$pass);
418             }
419             else {
420             $user_pass=$self->rsa_decrypt($user_pass);
421             }
422              
423             if (not defined $user_pass) {
424             $self->{"ui"}->message_ok(_T("With the RSA password you gave,\nthe password for account '$user_account' could not be decrypted"));
425             my $ticket=new Aut::Ticket($user_account,"");
426             $ticket->invalidate();
427             return $ticket;
428             }
429              
430             return $self->ticket_get($user_account,$user_pass);
431             }
432              
433             sub ticket_all_admin_get {
434             my $self=shift;
435             my @accounts=$self->{"backend"}->get_all_accounts();
436             my @tickets;
437              
438             my $pass=$self->{"ui"}->ask_pass($self,_T("Give the password for the secret RSA key"));
439             if (not defined $pass) {
440             $self->{"ui"}->message_ok(_T("You need to specify a password for the secret RSA key"));
441             return undef;
442             }
443             elsif (not $self->check_rsa_private_pass($pass)) {
444             $self->{"ui"}->message_ok(_T("The given password for the secret RSA key is not valid"));
445             return undef;
446             }
447              
448             for my $a (@accounts) {
449             my $ticket=$self->ticket_admin_get($a,$pass);
450             push @tickets,$ticket;
451             }
452              
453             return @tickets;
454             }
455              
456             #####################################################################
457             # User Interface functions
458             #####################################################################
459              
460             sub login {
461             my $self=shift;
462             return $self->{"ui"}->login($self,@_);
463             }
464              
465             sub logout {
466             my $self=shift;
467             my $ticket=shift;
468             if ($self->{"ui"}->logout($self,$ticket,@_)) {
469             $ticket->invalidate();
470             }
471             }
472              
473             sub admin {
474             my $self=shift;
475             my $ticket=shift;
476             if ($self->is_admin($ticket)) {
477             $self->{"ui"}->admin($self,$ticket,@_);
478             }
479             else {
480             $self->{"ui"}->message_ok(_T("You don't have administrator rights.\nYou cannot do administration of accounts."));
481             }
482             }
483              
484             sub change_pass {
485             my $self=shift;
486             my $ticket=shift;
487             if ($ticket->valid()) {
488             $self->{"ui"}->change_pass($self,$ticket,@_);
489             }
490             else {
491             $self->{"ui"}->message_ok(_T("You don't have a valid ticket. You cannot change your password"));
492             }
493             }
494              
495             #####################################################################
496             # UI Call back support functions
497             #####################################################################
498              
499             sub check_pass {
500             my $self=shift;
501             my $pass=shift;
502              
503             if (not defined $pass) { $pass=""; }
504              
505             my $status=undef;
506             my $str="";
507              
508             if (length($pass) lt 6) {
509             $status="EBADPASS";
510             $str=return "A password must be at least 6 characters";
511             }
512              
513             if (defined $status) {
514             $self->set_status($status);
515             $self->set_error($status);
516             }
517              
518             return $str;
519             }
520              
521             1;
522             __END__