Code

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