Code

b261e3d8c4ccd51f2eac29388a1bef2c75db35eb
[gosa.git] / gosa-si / gosa-si-client
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 #         FILE:  gosa-server
5 #
6 #        USAGE:  gosa-si-client
7 #
8 #  DESCRIPTION:
9 #
10 #      OPTIONS:  ---
11 # REQUIREMENTS:  ---
12 #         BUGS:  ---
13 #        NOTES:
14 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
15 #      COMPANY:
16 #      VERSION:  1.0
17 #      CREATED:  12.09.2007 08:54:41 CEST
18 #     REVISION:  ---
19 #===============================================================================
21 use strict;
22 use warnings;
23 use Getopt::Long;
24 use Config::IniFiles;
25 use POSIX;
26 use Time::HiRes qw( gettimeofday );
28 use Fcntl;
29 use IO::Socket::INET;
30 use Crypt::Rijndael;
31 use MIME::Base64;
32 use Digest::MD5  qw(md5 md5_hex md5_base64);
33 use XML::Simple;
34 use Data::Dumper;
35 use Sys::Syslog qw( :DEFAULT setlogsock);
36 use File::Spec;
37 use Cwd;
38 use GOSA::GosaSupportDaemon;
41 my ($cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file);
42 my ($server_address, $server_ip, $server_port, $server_domain, $server_passwd, $server_cipher, $server_timeout);
43 my ($client_address, $client_ip, $client_port, $client_mac_address, $ldap_config, $pam_config, $nss_config);
44 my ($input_socket, $rbits, $wbits, $ebits, $xml, $known_hosts);
45 my (@events);
47 # default variables
48 my $event_dir = "/etc/gosa-si/client/events";
49 $known_hosts = {};
50 $foreground = 0 ;
51 %cfg_defaults =
52 ("general" =>
53     {"log_file" => [\$log_file, "/var/run/".$0.".log"],
54     "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
55     },
56 "client" => 
57     {"client_port" => [\$client_port, "20083"],
58      "ldap_config" => [\$ldap_config, "/etc/ldap/ldap.conf"],
59      "pam_config" => [\$pam_config, "/etc/pam_ldap.conf"],
60      "nss_config" => [\$nss_config, "/etc/libnss_ldap.conf"],
61     },
62 "server" =>
63     {"server_ip" => [\$server_ip, ""],
64     "server_port" => [\$server_port, "20081"],
65     "server_passwd" => [\$server_passwd, ""],
66     "server_timeout" => [\$server_timeout, 10],
67     "server_domain" => [\$server_domain, ""],
68     },
69     );
72 #===  FUNCTION  ================================================================
73 #         NAME:  read_configfile
74 #   PARAMETERS:  cfg_file - string - 
75 #      RETURNS:  
76 #  DESCRIPTION: 
77 #===============================================================================
78 sub read_configfile {
79     my $cfg;
80     if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
81         if( -r $cfg_file ) {
82             $cfg = Config::IniFiles->new( -file => $cfg_file );
83         } else {
84             print STDERR "Couldn't read config file!";
85         }
86     } else {
87         $cfg = Config::IniFiles->new() ;
88     }
89     foreach my $section (keys %cfg_defaults) {
90         foreach my $param (keys %{$cfg_defaults{ $section }}) {
91             my $pinfo = $cfg_defaults{ $section }{ $param };
92             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
93         }
94     }
95 }
98 #===  FUNCTION  ================================================================
99 #         NAME:  logging
100 #   PARAMETERS:  level - string - default 'info' 
101 #                msg - string - 
102 #                facility - string - default 'LOG_DAEMON' 
103 #      RETURNS:  
104 #  DESCRIPTION: 
105 #===============================================================================
106 sub daemon_log {
107     my( $msg, $level ) = @_;
108     if(not defined $msg) { return }
109     if(not defined $level) { $level = 1 }
110     if(defined $log_file){
111         open(LOG_HANDLE, ">>$log_file");
112         if(not defined open( LOG_HANDLE, ">>$log_file" )) { 
113             print STDERR "cannot open $log_file: $!";
114             return }
115         chomp($msg);
116         if($level <= $verbose){
117             print LOG_HANDLE $msg."\n";
118             if(defined $foreground) { print $msg."\n" }
119         }
120     }
121     close( LOG_HANDLE );
122 #    my ($msg, $level, $facility) = @_;
123 #    if(not defined $msg) {return}
124 #    if(not defined $level) {$level = "info"}
125 #    if(not defined $facility) {$facility = "LOG_DAEMON"}
126 #    openlog($0, "pid,cons,", $facility);
127 #    syslog($level, $msg);
128 #    closelog;
129 #    return;
133 #===  FUNCTION  ================================================================
134 #         NAME: check_cmdline_param
135 #   PARAMETERS: 
136 #      RETURNS:  
137 #  DESCRIPTION: 
138 #===============================================================================
139 sub check_cmdline_param () {
140     my $err_config;
141     my $err_counter = 0;
142     if( not defined( $cfg_file)) {
143         #$err_config = "please specify a config file";
144         #$err_counter += 1;
145         my $cwd = getcwd;
146         my $name = "/etc/gosa-si/client.conf";
147         $cfg_file = File::Spec->catfile( $cwd, $name );
148         print STDERR "no conf file specified\n   try to use default: $cfg_file\n";        
149     }
150     if( $err_counter > 0 ) {
151         &usage( "", 1 );
152         if( defined( $err_config)) { print STDERR "$err_config\n"}
153         print STDERR "\n";
154         exit( -1 );
155     }
159 #===  FUNCTION  ================================================================
160 #         NAME: check_pid
161 #   PARAMETERS:
162 #      RETURNS:
163 #  DESCRIPTION:
164 #===============================================================================
165 sub check_pid {
166     $pid = -1;
167     # Check, if we are already running
168     if( open(LOCK_FILE, "<$pid_file") ) {
169         $pid = <LOCK_FILE>;
170         if( defined $pid ) {
171             chomp( $pid );
172             if( -f "/proc/$pid/stat" ) {
173                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
174                 if( $0 eq $stat ) {
175                     close( LOCK_FILE );
176                     exit -1;
177                 }
178             }
179         }
180         close( LOCK_FILE );
181         unlink( $pid_file );
182     }
184     # create a syslog msg if it is not to possible to open PID file
185     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
186         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
187         if (open(LOCK_FILE, '<', $pid_file)
188                 && ($pid = <LOCK_FILE>))
189         {
190             chomp($pid);
191             $msg .= "(PID $pid)\n";
192         } else {
193             $msg .= "(unable to read PID)\n";
194         }
195         if( ! ($foreground) ) {
196             openlog( $0, "cons,pid", "daemon" );
197             syslog( "warning", $msg );
198             closelog();
199         }
200         else {
201             print( STDERR " $msg " );
202         }
203         exit( -1 );
204     }
208 #===  FUNCTION  ================================================================
209 #         NAME:  get_ip_and_mac 
210 #   PARAMETERS:  nothing
211 #      RETURNS:  (ip, mac) 
212 #  DESCRIPTION:  executes /sbin/ifconfig and parses the output, the first occurence 
213 #                of a inet address is returned as well as the mac address in the line
214 #                above the inet address
215 #===============================================================================
216 sub get_ip_and_mac {
217     my $ip = "0.0.0.0.0"; # Defualt-IP
218     my $mac = "00:00:00:00:00:00";  # Default-MAC
219     my @ifconfig = qx(/sbin/ifconfig);
220     foreach(@ifconfig) {
221         if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
222             $mac = "$1:$2:$3:$4:$5:$6";
223             next;
224         }
225         if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
226             $ip = "$1.$2.$3.$4";
227             last;
228         }
229     }
230     return ($ip, $mac);
234 #===  FUNCTION  ================================================================
235 #         NAME:  usage
236 #   PARAMETERS: 
237 #      RETURNS:  
238 #  DESCRIPTION: 
239 #===============================================================================
240 sub usage {
241         my( $text, $help ) = @_;
242         $text = undef if( "h" eq $text );
243         (defined $text) && print STDERR "\n$text\n";
244         if( (defined $help && $help) || (!defined $help && !defined $text) ) {
245                 print STDERR << "EOF" ;
246 usage: $0 [-hvf] [-c config]
248     -h        : this (help) message
249     -c <file> : config file
250     -f        : foreground, process will not be forked to background
251     -v        : be verbose (multiple to increase verbosity)
252 EOF
253         }
254         print "\n" ;
257 #===  FUNCTION  ================================================================
258 #         NAME:  get_server_addresses
259 #   PARAMETERS:  
260 #      RETURNS:  
261 #  DESCRIPTION:  
262 #===============================================================================
263 sub get_server_addresses {
264     my $domain= shift;
265     my @result;
266     my $dig_cmd= 'dig +nocomments srv _gosad._tcp.'.$domain;
268     my $output= `$dig_cmd 2>&1`;
269     open (PIPE, "$dig_cmd 2>&1 |");
270     while(<PIPE>) {
271         chomp $_;
272         # If it's not a comment
273         if($_ =~ m/^[^;]/) {
274             my @matches= split /\s+/;
276             # Push hostname with port
277             if($matches[3] eq 'SRV') {
278                 push @result, $matches[7].':'.$matches[6];
279             } elsif ($matches[3] eq 'A') {
280                 my $i=0;
282                 # Substitute the hostname with the ip address of the matching A record
283                 foreach my $host (@result) {
284                     if ((split /\:/, $host)[0] eq $matches[0]) {
285                         $result[$i]= $matches[4].':'.(split /\:/, $host)[1];
286                     }
287                     $i++;
288                 }
289             }
290         }
291     }
292     close(PIPE);
293     return @result;
297 #===  FUNCTION  ================================================================
298 #         NAME:  register_at_server
299 #   PARAMETERS:  
300 #      RETURNS:  
301 #  DESCRIPTION:  
302 #===============================================================================
303 sub register_at_server {
304     my ($tmp) = @_;
306     # create new passwd and ciphering object for client-server communication
307     my $new_server_passwd = &create_passwd();
308     my $new_server_cipher;
310     # detect all client accepted events
311     opendir(DIR, $event_dir) 
312         or daemon_log("cannot find directory $event_dir!\ngosa-si-client starts without any accepting events!", 1);
313     my $file_name;
314     @events = ();
315     while(defined($file_name = readdir(DIR))){
316         if ($file_name eq "." || $file_name eq "..") {
317             next;
318         }
319         push(@events, $file_name);
320     }
321     my $events = join(",", @events);
322     daemon_log("found events: $events", 1);
324     # fill in all possible servers
325     my @servers;
326     if (defined $server_domain) {
327         my @tmp_servers = &get_server_addresses($server_domain);
328         foreach my $server (@tmp_servers) { unshift(@servers, $server); }
329     }
330     # add server address from config file at first position of server list
331     if (defined $server_address) {
332         unshift(@servers, $server_address);
333     }
334     daemon_log("found servers in configuration file and via DNS:", 5);
335     foreach my $server (@servers) {
336         daemon_log("\t$server", 5);
337     }
339     my ($rout, $wout, $reg_server);
340     foreach my $server (@servers) {
341         # create msg hash
342         my $register_hash = &create_xml_hash("here_i_am", $client_address, $server);
343         &add_content2xml_hash($register_hash, "new_passwd", $new_server_passwd);
344         &add_content2xml_hash($register_hash, "mac_address", $client_mac_address);
345         &add_content2xml_hash($register_hash, "events", $events);
347         # send xml hash to server with general server passwd
348         my $answer = &send_msg_hash2address($register_hash, $server, $server_passwd);
349  
350         if ($answer != 0) { next; }
351        
352         # waiting for response
353         daemon_log("waiting for response...\n", 5);
354         my $nf = select($rout=$rbits, $wout=$wbits, undef, $server_timeout);
356         # something is coming in
357         if(vec $rout, fileno $input_socket, 1) {
358             my $crypted_msg;
359             my $client = $input_socket->accept();
360             my $other_end = getpeername($client);
361             if(not defined $other_end) {
362                 daemon_log("client cannot be identified: $!\n");
363             } else {
364                 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
365                 my $actual_ip = inet_ntoa($iaddr);
366                 daemon_log("\naccept client from $actual_ip\n", 5);
367                 my $in_msg = &read_from_socket($client);
368                 if(defined $in_msg){
369                     chomp($in_msg);
370                     $crypted_msg = $in_msg;
371                 } else {
372                     daemon_log("cannot read from $actual_ip\n", 5);
373                 }
374             }
375             close($client);
376             
377             # validate acknowledge msg from server
378             $new_server_cipher = &create_ciphering($new_server_passwd);
379             my $msg_hash;
380             eval {
381                 my $decrypted_msg = &decrypt_msg($crypted_msg, $new_server_cipher);
382                 daemon_log("decrypted register msg: $decrypted_msg", 5);
383                 $msg_hash = $xml->XMLin($decrypted_msg, ForceArray=>1);
384             };
385             if($@) {
386                 daemon_log("ERROR: do not understand the incoming message:" , 5);  
387                 daemon_log("$@", 7); 
388             } else {
389                 my $header = @{$msg_hash->{header}}[0];
390                 if($header eq "registered") {
391                     $reg_server = $server;
392                     last;
393                 } elsif($header eq "denied") {
394                     my $reason = (&get_content_from_xml_hash($msg_hash, "denied"))[0];
395                     daemon_log("registration at $server denied: $reason", 1);
396                 } else {
397                     daemon_log("cannot register at $server", 1);
398                 }
399             }
400         }
401         # if no answer arrive, try next server in list
403     }
404     
405     if(defined $reg_server) {
406         daemon_log("registered at $reg_server", 1);
407     } else {
408         daemon_log("cannot register at any server", 1);
409         daemon_log("exiting!!!", 1);
410         exit(1);
411     }
413     # update the global available variables
414     $server_address = $reg_server;
415     $server_passwd = $new_server_passwd;
416     $server_cipher = $new_server_cipher;
417     return;
421 #===  FUNCTION  ================================================================
422 #         NAME:  create_xml_hash
423 #   PARAMETERS:  
424 #      RETURNS:
425 #  DESCRIPTION:
426 #===============================================================================
427 #sub create_xml_hash {
428 #    my ($header, $source, $target, $header_value) = @_;
429 #    my $hash = {
430 #            header => [$header],
431 #            source => [$source],
432 #            target => [$target],
433 #            $header => [$header_value],
434 #    };
435 #    daemon_log("create_xml_hash:", 7),
436 #    chomp(my $tmp = Dumper $hash);
437 #    daemon_log("\t$tmp\n", 7);
438 #    return $hash
439 #}
442 #===  FUNCTION  ================================================================
443 #         NAME:  create_xml_string
444 #   PARAMETERS:  
445 #      RETURNS:
446 #  DESCRIPTION:
447 #===============================================================================
448 #sub create_xml_string {
449 #    my ($xml_hash) = @_ ;
450 #    my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
451 #    $xml_string =~ s/[\n]+//g;
452 #    daemon_log("create_xml_string:\n\t$xml_string\n", 7);
453 #    return $xml_string;
454 #}
457 #===  FUNCTION  ================================================================
458 #         NAME:  add_content2xml_hash
459 #   PARAMETERS:  
460 #      RETURNS:
461 #  DESCRIPTION:
462 #===============================================================================
463 #sub add_content2xml_hash {
464 #    my ($xml_ref, $element, $content) = @_;
465 #    if(not exists $$xml_ref{$element} ) {
466 #        $$xml_ref{$element} = [];
467 #    }
468 #    my $tmp = $$xml_ref{$element};
469 #    push(@$tmp, $content);
470 #    return;
471 #}
474 #===  FUNCTION  ================================================================
475 #         NAME:  get_content_from_xml_hash
476 #   PARAMETERS:  ref : reference to the xml hash
477 #                string: key of the value you want
478 #      RETURNS:  STRING AND ARRAY
479 #  DESCRIPTION:  if key of the hash is either 'header', 'target' or 'source' the 
480 #                function returns a string cause it is expected that these keys
481 #                do just have one value, all other keys returns an array!!!
482 #===============================================================================
483 #sub get_content_from_xml_hash {
484 #    my ($xml_ref, $element) = @_;
485 #    my $result = $xml_ref->{$element};
486 #    if( $element eq "header" || $element eq "target" || $element eq "source") {
487 #        return @$result[0];
488 #    }
489 #    return @$result;
490 #}
492 #    my ($xml_ref, $element) = @_;
493 #    if (exists $xml_ref->{$element}) {
494 #        my $result = $xml_ref->{$element};
495 #        if( $element eq "header" || $element eq "target" || $element eq "source") {
496 #            return @$result[0];
497 #        } else {
498 #            return @$result;
499 #        }
500 #        
501 #    } else {
502 #        my $result = ();
503 #        return @$result;
504 #    }
505 #}
508 #===  FUNCTION  ================================================================
509 #         NAME:  encrypt_msg
510 #   PARAMETERS:
511 #      RETURNS:
512 #  DESCRIPTION:
513 #===============================================================================
514 #sub encrypt_msg {
515 #    my ($msg, $my_cipher) = @_;
516 #    if(not defined $my_cipher) { print "no cipher object\n"; }
517 #    $msg = "\0"x(16-length($msg)%16).$msg;
518 #    my $crypted_msg = $my_cipher->encrypt($msg);
519 #    chomp($crypted_msg = &encode_base64($crypted_msg));
520 #    return $crypted_msg;
521 #}
524 #===  FUNCTION  ================================================================
525 #         NAME:  decrypt_msg
526 #   PARAMETERS:
527 #      RETURNS:
528 #  DESCRIPTION:
529 #===============================================================================
530 #sub decrypt_msg {
531 #    my ($crypted_msg, $my_cipher) = @_ ;
532 #    $crypted_msg = &decode_base64($crypted_msg);
533 #    my $msg = $my_cipher->decrypt($crypted_msg); 
534 #    $msg =~ s/\0*//g;
535 #    return $msg;
536 #}
539 #===  FUNCTION  ================================================================
540 #         NAME:  create_ciphering
541 #   PARAMETERS:  
542 #      RETURNS:  cipher object
543 #  DESCRIPTION:  
544 #===============================================================================
545 #sub create_ciphering {
546 #    my ($passwd) = @_;
547 #    $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
548 #    my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
550 #    #daemon_log("iv: $iv", 7);
551 #    #daemon_log("key: $passwd", 7);
552 #    my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
553 #    $my_cipher->set_iv($iv);
554 #    return $my_cipher;
555 #}
558 #===  FUNCTION  ================================================================
559 #         NAME:  create_passwd
560 #   PARAMETERS:
561 #      RETURNS:  cipher object
562 #  DESCRIPTION:
563 #===============================================================================
564 sub create_passwd {
565     my $new_passwd = "";
566     for(my $i=0; $i<31; $i++) {
567         $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
568     }
570     return $new_passwd;
574 #===  FUNCTION  ================================================================
575 #         NAME:  send_msg_hash2address
576 #   PARAMETERS:  msg string - xml message
577 #                PeerAddr string - socket address to send msg
578 #                PeerPort string - socket port, if not included in socket address
579 #      RETURNS:  nothing
580 #  DESCRIPTION:  ????
581 #===============================================================================
582 #sub send_msg_hash2address {
583 #    my ($msg_hash, $address, $passwd) = @_ ;
585 #    # fetch header for logging
586 #    my $header = @{$msg_hash->{header}}[0];
588 #    # generiere xml string
589 #    my $msg_xml = &create_xml_string($msg_hash);
591 #    # hole das entsprechende passwd aus dem hash
592 #    if(not defined $passwd) {
593 #        if(exists $known_hosts->{$address}) {
594 #            $passwd = $known_hosts->{$address}->{passwd};
595 #        } elsif ($address eq $server_address) {
596 #            $passwd = $server_passwd;
597 #        } else {
598 #            daemon_log("$address not known, neither as server nor as client", 1);
599 #            return "failed";
600 #        }
601 #    }
603 #    # erzeuge ein ciphering object
604 #    my $act_cipher = &create_ciphering($passwd);
606 #    # encrypt xml msg
607 #    my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
609 #    # Ã¶ffne socket
610 #    my $socket = &open_socket($address);
611 #    if(not defined $socket){
612 #        daemon_log("cannot open socket to $address, server not reachable", 1);
613 #        daemon_log("cannot send '$header'-msg", 1);
614 #        return "failed";
615 #    }
617 #    # versende xml msg
618 #    print $socket $crypted_msg."\n";
620 #    # schließe socket
621 #    close $socket;
623 #    daemon_log("send '$header'-msg to $address", 5);
624 #    daemon_log("crypted_msg:\n\t$crypted_msg", 7);
626 #    return "done";
627 #}
630 #===  FUNCTION  ================================================================
631 #         NAME:  open_socket
632 #   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
633 #                [PeerPort] string necessary if port not appended by PeerAddr
634 #      RETURNS:  socket IO::Socket::INET
635 #  DESCRIPTION:
636 #===============================================================================
637 sub open_socket {
638     my ($PeerAddr, $PeerPort) = @_ ;
639     if(defined($PeerPort)){
640         $PeerAddr = $PeerAddr.":".$PeerPort;
641     }
642     my $socket;
643     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
644             Porto => "tcp" ,
645             Type => SOCK_STREAM,
646             Timeout => 5,
647             );
648     if(not defined $socket) {
649         #daemon_log("cannot connect to socket at $PeerAddr, $@\n");
650         return;
651     }
652     daemon_log("open_socket:\n\t$PeerAddr", 7);
653     return $socket;
657 #===  FUNCTION  ================================================================
658 #         NAME:  read_from_socket
659 #   PARAMETERS:  socket fh - 
660 #      RETURNS:  result string - readed characters from socket
661 #  DESCRIPTION:  reads data from socket in 16 byte steps
662 #===============================================================================
663 sub read_from_socket {
664     my ($socket) = @_;
665     my $result = "";
667     $socket->blocking(1);
668     $result = <$socket>;
670     $socket->blocking(0);
671     while ( my $char = <$socket> ) {
672         if (not defined $char) { last }
673         $result .= $char;
674     }
675     return $result;
679 #    my ($socket) = @_;
680 #    my $result = "";
681 #    my $len = 16;
682 #    while($len == 16){
683 #        my $char;
684 #        $len = sysread($socket, $char, 16);
685 #        if($len != 16) { last }
686 #        if($len != 16) { last }
687 #        $result .= $char;
688 #    }
689 #    return $result;
693 #===  FUNCTION  ================================================================
694 #         NAME:  print_known_hosts_hash
695 #   PARAMETERS:
696 #      RETURNS: 
697 #  DESCRIPTION: 
698 #===============================================================================
699 sub print_known_hosts_hash {
700     my ($tmp) = @_;
701     print "####################################\n";
702     print "# status of known_hosts\n";
703     my $hosts;
704     my $host_hash;
705     my @hosts = keys %$known_hosts;
706     foreach my $host (@hosts) {
707         #my @elements = keys %$known_hosts->{$host};
708         my $status = $known_hosts->{$host}->{status} ;
709         my $passwd = $known_hosts->{$host}->{passwd};
710         my $timestamp = $known_hosts->{$host}->{timestamp};
711         print "$host\n";
712         print "\t$status\n";
713         print "\t$passwd\n";
714         print "\t$timestamp\n";
715     }
716     print "####################################\n";
717     return;
720 #===  FUNCTION  ================================================================
721 #         NAME:  
722 #   PARAMETERS:
723 #      RETURNS: 
724 #  DESCRIPTION: 
725 #===============================================================================
726 sub create_known_hosts_entry {
727     my ($hostname) = @_;
728     $known_hosts->{$hostname} = {};
729     $known_hosts->{$hostname}->{status} = "none";
730     $known_hosts->{$hostname}->{passwd} = "none";
731     $known_hosts->{$hostname}->{timestamp} = "none";
732     return;  
736 #===  FUNCTION  ================================================================
737 #         NAME:  
738 #   PARAMETERS:
739 #      RETURNS: 
740 #  DESCRIPTION: 
741 #===============================================================================
742 sub update_known_hosts_entry {
743     my ($hostname, $status, $passwd, $timestamp) = @_;
744     my ($seconds, $minutes, $hours, $monthday, $month,
745     $year, $weekday, $yearday, $sommertime) = localtime(time);
746     $hours = $hours < 10 ? $hours = "0".$hours : $hours;
747     $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
748     $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
749     $month+=1;
750     $month = $month < 10 ? $month = "0".$month : $month;
751     $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
752     $year+=1900;
753     my $t = "$year$month$monthday$hours$minutes$seconds";
755     if($status) {
756         $known_hosts->{$hostname}->{status} = $status;
757     }
758     if($passwd) {
759         $known_hosts->{$hostname}->{passwd} = $passwd;
760     }
761     if($timestamp) {
762         $t = $timestamp;
763     }
764     $known_hosts->{$hostname}->{timestamp} = $t;
765     return;  
769 #===  FUNCTION  ================================================================
770 #         NAME:  
771 #   PARAMETERS:
772 #      RETURNS: 
773 #  DESCRIPTION: 
774 #===============================================================================
775 sub add_content2known_hosts {
776     my ($hostname, $element, $content) = @_;
777     my ($seconds, $minutes, $hours, $monthday, $month,
778     $year, $weekday, $yearday, $sommertime) = localtime(time);
779     $hours = $hours < 10 ? $hours = "0".$hours : $hours;
780     $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
781     $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
782     $month+=1;
783     $month = $month < 10 ? $month = "0".$month : $month;
784     $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
785     $year+=1900;
786     my $t = "$year$month$monthday$hours$minutes$seconds";
787     
788     $known_hosts->{$hostname}->{$element} = $content;
789     $known_hosts->{$hostname}->{timestamp} = $t;
790     return;
794 #===  FUNCTION  ================================================================
795 #         NAME:  
796 #   PARAMETERS:
797 #      RETURNS: 
798 #  DESCRIPTION: 
799 #===============================================================================
800 sub process_incoming_msg {
801     my ($crypted_msg) = @_;
802     if(not defined $crypted_msg) {
803         daemon_log("function 'process_incoming_msg': got no msg", 7);
804     }
805     $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
806     $crypted_msg = $1;
807     my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
808     daemon_log("msg from host:", 1);
809     daemon_log("\t$host", 1);
810     daemon_log("crypted msg:", 7);
811     daemon_log("\t$crypted_msg", 7);
813     my $act_cipher = &create_ciphering($server_passwd);
815     # try to decrypt incoming msg
816     my ($msg, $msg_hash);
817     eval{
818         $msg = &decrypt_msg($crypted_msg, $act_cipher);
819         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
820     };
821     if($@) {
822         daemon_log("ERROR: incoming msg cannot be decrypted with server passwd", 1);
823         return;
824     } 
826     my $header = @{$msg_hash->{header}}[0];
827     
828     daemon_log("recieve '$header' from $host", 1);
829 #    daemon_log("header from msg:", 1);
830 #    daemon_log("\t$header", 1);
831 #    daemon_log("msg to process:", 7);
832 #    daemon_log("\t$msg", 7);
834     #check whether msg to process is a event 
835     opendir(DIR, $event_dir) 
836         or daemon_log("cannot find directory $event_dir, no events specified", 5);
837     my $file_name;
838     while(defined($file_name = readdir(DIR))){
839         if ($file_name eq "." || $file_name eq "..") {
840             next;
841         }
842         if ($file_name eq $header) {
843             my $cmd = "$event_dir/$file_name '$msg'";
844             my $result_xml = "";
845             open(PIPE, "$cmd 2>&1 |");
846             while(<PIPE>) {
847                 $result_xml.=$_;
848                 last;
849             }
850             close(PIPE);
851             my $res_hash = &transform_msg2hash($result_xml);
852             my $res_target = @{$res_hash->{target}}[0];
853             &send_msg_hash2address($res_hash, $server_address);
854             
855             return;
856         }
857     }
858     close(DIR);
859     daemon_log("could not assign the msg $header to an event", 5);
860     
863     if ($header eq 'new_ldap_config') { &new_ldap_config($msg_hash)}
864     elsif ($header eq 'ping') { &got_ping($msg_hash) }
865     elsif ($header eq 'wake_up') { &execute_event($msg_hash)}
866     elsif ($header eq 'new_passwd') { &new_passwd()}
867     else { daemon_log("ERROR: no function assigned to msg $header", 5) }
869     return;
873 #===  FUNCTION  ================================================================
874 #         NAME:  
875 #   PARAMETERS:
876 #      RETURNS: 
877 #  DESCRIPTION: 
878 #===============================================================================
879 sub update_status { 
880     my ($new_status) = @_ ;
881     my $out_hash = &create_xml_hash("update_status", $client_address, $server_address);      
882     &add_content2xml_hash($out_hash, "update_status", $new_status);
883     &send_msg_hash2address($out_hash, $server_address);
884     return;
888 #===  FUNCTION  ================================================================
889 #         NAME:  
890 #   PARAMETERS:
891 #      RETURNS: 
892 #  DESCRIPTION: 
893 #===============================================================================
894 sub server_leaving {
895     my ($msg_hash) = @_ ;
896     my $source = &get_content_from_xml_hash("source");
897     my $header = &get_content_from_xml_hash("header");
898     
899     daemon_log("gosa daemon $source is going down, cause registration procedure", 1);
900     my $server_address = "none";
901     my $server_passwd = "none";
902     my $server_cipher = "none";
904     # reinitialization of default values in config file
905     &read_configfile;
906     
907     # registrated at new daemon
908     &register_at_server();
909        
910     return;   
914 sub got_ping {
915     my ($msg_hash) = @_ ;
917     my $source = &get_content_from_xml_hash($msg_hash, 'source');
918     my $target = &get_content_from_xml_hash($msg_hash, 'target');
919     my $header = &get_content_from_xml_hash($msg_hash, 'header');    
920     
921     &add_content2known_hosts(hostname=>$target, status=>$header);
922     
923     my $out_hash = &create_xml_hash("got_ping", $target, $source);
924     &send_msg_hash2address($out_hash, $source, $server_passwd);
926     return;
930 sub new_ldap_config {
931     my ($msg_hash) = @_ ;
932     my $element;
933     my @ldap_uris;
934     my $ldap_base;
935     my @ldap_options;
936     my @pam_options;
937     my @nss_options;
939     # Transform input into array
940     while ( my ($key, $value) = each(%$msg_hash) ) {
941         if ($key =~ /^(source|target|header)$/) {
942                 next;
943         }
945         foreach $element (@$value) {
946                 if ($key =~ /^ldap_uri$/) {
947                         push (@ldap_uris, $element);
948                         next;
949                 }
950                 if ($key =~ /^ldap_base$/) {
951                         $ldap_base= $element;
952                         next;
953                 }
954                 if ($key =~ /^ldap_/) {
955                         my $post =~ s/^ldap_//;
956                         push (@ldap_options, "$post $element");
957                         next;
958                 }
959                 if ($key =~ /^pam_/) {
960                         my $post =~ s/^pam_//;
961                         push (@pam_options, "$post $element");
962                         next;
963                 }
964                 if ($key =~ /^nss_/) {
965                         my $post =~ s/^nss_//;
966                         push (@nss_options, "$post $element");
967                         next;
968                 }
969         }
970     }
972     # Setup ldap.conf
973     my $file;
974     my $file2;
975     open(file, "> $ldap_config");
976     print file "# This file was automatically generated by gosa-si-client. Do not change.\n";
977     print file "URI";
978     foreach $element (@ldap_uris) {
979         print file " $element";
980     }
981     print file "\nBASE $ldap_base\n";
982     foreach $element (@ldap_options) {
983         print file "$element";
984     }
985     close (file);
986     daemon_log("wrote $ldap_config", 5);
988     # Setup pam_ldap.conf / libnss_ldap.conf
989     open(file, "> $pam_config");
990     open(file2, "> $nss_config");
991     print file "# This file was automatically generated by gosa-si-client. Do not change.\n";
992     print file2 "# This file was automatically generated by gosa-si-client. Do not change.\n";
993     print file "uri";
994     print file2 "uri";
995     foreach $element (@ldap_uris) {
996         print file " $element";
997         print file2 " $element";
998     }
999     print file "\nbase $ldap_base\n";
1000     foreach $element (@pam_options) {
1001         print file "$element";
1002     }
1003     foreach $element (@nss_options) {
1004         print file2 "$element";
1005     }
1006     close (file2);
1007     daemon_log("wrote $nss_config", 5);
1008     close (file);
1009     daemon_log("wrote $pam_config", 5);
1011     return;
1016 sub execute_event {
1017     my ($msg_hash)= @_;
1018     my $configdir= '/etc/gosa-si/client/events/';
1019     my $result;
1021     my $header = &get_content_from_xml_hash($msg_hash, 'header');
1022     my $source = &get_content_from_xml_hash($msg_hash, 'source');
1023     my $target = &get_content_from_xml_hash($msg_hash, 'target');
1026     if((not defined $source)
1027             && (not defined $target)
1028             && (not defined $header)) {
1029         daemon_log("ERROR: Entries missing in XML msg for gosa events under $configdir");
1030     } else {
1031         my $parameters="";
1032         my @params = &get_content_from_xml_hash($msg_hash, $header);
1033         my $params = join(", ", @params);
1034         daemon_log("execute_event: got parameters: $params", 5);
1036         if (@params) {
1037             foreach my $param (@params) {
1038                 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
1039                 daemon_log("execute_event: parameter -> value: $param -> $param_value", 7);
1040                 $parameters.= " ".$param_value;
1041             }
1042         }
1044         my $cmd= $configdir.$header."$parameters";
1045         daemon_log("execute_event: executing cmd: $cmd", 7);
1046         $result= "";
1047         open(PIPE, "$cmd 2>&1 |");
1048         while(<PIPE>) {
1049             $result.=$_;
1050         }
1051         close(PIPE);
1052     }
1054     # process the event result
1057     return;
1061 sub new_passwd {
1062     # my ($msg_hash) = @_ ;
1063     my $new_server_passwd = &create_passwd();
1064     my $new_server_cipher = &create_ciphering($new_server_passwd);
1066     my $out_hash = &create_xml_hash("new_passwd", $client_address, $server_address, $new_server_passwd);
1067     
1068     &send_msg_hash2address($out_hash, $server_address, $server_passwd);
1070     $server_passwd = $new_server_passwd;
1071     $server_cipher = $new_server_cipher;
1072     return; 
1078 #==== MAIN = main ==============================================================
1080 #  parse commandline options
1081 Getopt::Long::Configure( "bundling" );
1082 GetOptions("h|help" => \&usage,
1083            "c|config=s" => \$cfg_file,
1084            "f|foreground" => \$foreground,
1085            "v|verbose+" => \$verbose,
1086            );
1088 #  read and set config parameters
1089 &check_cmdline_param ;
1090 &read_configfile;
1091 &check_pid;
1093 # restart daemon log file
1094 if(-e $log_file ) { unlink $log_file }
1095 daemon_log(" ", 1);
1096 daemon_log("$0 started!", 1);
1098 # Just fork, if we"re not in foreground mode
1099 if( ! $foreground ) { $pid = fork(); }
1100 else { $pid = $$; }
1102 # Do something useful - put our PID into the pid_file
1103 if( 0 != $pid ) {
1104     open( LOCK_FILE, ">$pid_file" );
1105     print LOCK_FILE "$pid\n";
1106     close( LOCK_FILE );
1107     if( !$foreground ) { exit( 0 ) };
1110 # detect own ip and mac address
1111 ($client_ip, $client_mac_address) = &get_ip_and_mac(); 
1112 if (not defined $client_ip) {
1113     die "EXIT: ip address of $0 could not be detected";
1115 daemon_log("client ip address detected: $client_ip", 1);
1116 daemon_log("client mac address detected: $client_mac_address", 1);
1118 # prepare variables
1119 if (defined $server_ip && defined $server_port) {
1120     $server_address = $server_ip.":".$server_port;
1122 $client_address = $client_ip.":".$client_port;
1124 # setup xml parser
1125 $xml = new XML::Simple();
1127 # create input socket
1128 daemon_log(" ", 1);
1129 $rbits = $wbits = $ebits = "";
1130 $input_socket = IO::Socket::INET->new(LocalPort => $client_port,
1131         Type => SOCK_STREAM,
1132         Reuse => 1,
1133         Listen => 20,
1134         ); 
1135 if(not defined $input_socket){
1136     daemon_log("cannot be a tcp server at $client_port : $@\n");
1137 } else {
1138     daemon_log("start client at $client_address",1) ;
1139     vec($rbits, fileno $input_socket, 1) = 1;
1140     vec($wbits, fileno $input_socket, 1) = 1;
1143 # register at server
1144 daemon_log(" ", 1);
1145 &register_at_server();
1148 ##############
1149 # Debugging
1150 #############
1151 #sleep(2);
1152 #&update_status("ich_bin_ein_neuer_status");
1154 ###################################
1155 #everything ready, okay, lets start
1156 ###################################
1157 while(1) {
1158     my ($rout, $wout);
1159     my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
1161     # error handling
1162     if($nf < 0 ) {
1163     }
1165     # something is coming in
1166     if(vec $rout, fileno $input_socket, 1) {
1167         my $client = $input_socket->accept();
1168         my $other_end = getpeername($client);
1169         
1170         if(not defined $other_end) {
1171             daemon_log("client cannot be identified: $!");
1172         } else {
1173             my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1174             my $actual_ip = inet_ntoa($iaddr);
1175             daemon_log("accept client from $actual_ip", 5);
1176             my $in_msg = &read_from_socket($client);
1177             if(defined $in_msg){
1178                 chomp($in_msg);
1179                 $in_msg = $in_msg.".".$actual_ip;
1180                 &process_incoming_msg($in_msg);
1182             }
1183         }
1184     }
1186