Code

c26b4835001f06d95e07d8d858b63f50383098d2
[gosa.git] / gosa-si / new-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:  libnetaddr-ip-perl
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;
27 use POE qw(Component::Server::TCP);
28 use IO::Socket::INET;
29 use NetAddr::IP;
30 use Data::Dumper;
31 use Crypt::Rijndael;
32 use GOSA::GosaSupportDaemon;
33 use Digest::MD5  qw(md5_hex);
34 use MIME::Base64;
35 use XML::Simple;
36 #use Fcntl;
37 #use Sys::Syslog qw( :DEFAULT setlogsock);
38 #use File::Spec;
39 #use Cwd;
41 my $event_dir = "/usr/lib/gosa-si/client/events";
42 use lib "/usr/lib/gosa-si/client/events";
44 my ($cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file);
45 my ($server_ip, $server_port, $server_key, $server_timeout, $server_domain);
46 my ($client_ip, $client_port, $ldap_enabled, $ldap_config, $pam_config, $nss_config);
47 my $xml;
48 my $default_server_key;
49 my $event_hash;
50 my @servers;
52 # globalise variables which are used in imported events
53 our $cfg_file;
54 our $server_address;
55 our $client_address;
56 our $client_mac_address;
57 our $server_key;
59 # default variables
60 our $REGISTERED_FLAG = 1;
62 %cfg_defaults = (
63 "general" =>
64     {"log_file" => [\$log_file, "/var/run/".$0.".log"],
65     "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
66     },
67 "client" => 
68     {"client_port" => [\$client_port, "20083"],
69      "client_ip" => [\$client_ip, "0.0.0.0"],
70      "ldap" => [\$ldap_enabled, 1],
71      "ldap_config" => [\$ldap_config, "/etc/ldap/ldap.conf"],
72      "pam_config" => [\$pam_config, "/etc/pam_ldap.conf"],
73      "nss_config" => [\$nss_config, "/etc/libnss_ldap.conf"],
74     },
75 "server" =>
76     {"server_ip" => [\$server_ip, "127.0.0.1"],
77     "server_port" => [\$server_port, "20081"],
78     "server_key" => [\$server_key, ""],
79     "server_timeout" => [\$server_timeout, 10],
80     "server_domain" => [\$server_domain, ""],
81     },
83 );
86 #=== FUNCTIONS = functions =====================================================
88 #===  FUNCTION  ================================================================
89 #         NAME: check_cmdline_param
90 #   PARAMETERS: 
91 #      RETURNS:  
92 #  DESCRIPTION: 
93 #===============================================================================
94 sub check_cmdline_param () {
95     my $err_config;
96     my $err_counter = 0;
97         if(not defined($cfg_file)) {
98                 $cfg_file = "/etc/gosa-si/client.conf";
99                 if(! -r $cfg_file) {
100                         $err_config = "please specify a config file";
101                         $err_counter += 1;
102                 }
103     }
104     if( $err_counter > 0 ) {
105         &usage( "", 1 );
106         if( defined( $err_config)) { print STDERR "$err_config\n"}
107         print STDERR "\n";
108         exit( -1 );
109     }
113 #===  FUNCTION  ================================================================
114 #         NAME:  read_configfile
115 #   PARAMETERS:  cfg_file - string - 
116 #      RETURNS:  
117 #  DESCRIPTION: 
118 #===============================================================================
119 sub read_configfile {
120     my ($cfg_file, %cfg_defaults) = @_ ;
121     my $cfg;
122     if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
123         if( -r $cfg_file ) {
124             $cfg = Config::IniFiles->new( -file => $cfg_file );
125         } else {
126             print STDERR "Couldn't read config file!";
127         }
128     } else {
129         $cfg = Config::IniFiles->new() ;
130     }
131     foreach my $section (keys %cfg_defaults) {
132         foreach my $param (keys %{$cfg_defaults{ $section }}) {
133             my $pinfo = $cfg_defaults{ $section }{ $param };
134             ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
135         }
136     }
140 #===  FUNCTION  ================================================================
141 #         NAME: check_pid
142 #   PARAMETERS:
143 #      RETURNS:
144 #  DESCRIPTION:
145 #===============================================================================
146 sub check_pid {
147     $pid = -1;
148     # Check, if we are already running
149     if( open(LOCK_FILE, "<$pid_file") ) {
150         $pid = <LOCK_FILE>;
151         if( defined $pid ) {
152             chomp( $pid );
153             if( -f "/proc/$pid/stat" ) {
154                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
155                 if( $0 eq $stat ) {
156                     close( LOCK_FILE );
157                     exit -1;
158                 }
159             }
160         }
161         close( LOCK_FILE );
162         unlink( $pid_file );
163     }
165     # create a syslog msg if it is not to possible to open PID file
166     if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
167         my($msg) = "Couldn't obtain lockfile '$pid_file' ";
168         if (open(LOCK_FILE, '<', $pid_file)
169                 && ($pid = <LOCK_FILE>))
170         {
171             chomp($pid);
172             $msg .= "(PID $pid)\n";
173         } else {
174             $msg .= "(unable to read PID)\n";
175         }
176         if( ! ($foreground) ) {
177             openlog( $0, "cons,pid", "daemon" );
178             syslog( "warning", $msg );
179             closelog();
180         }
181         else {
182             print( STDERR " $msg " );
183         }
184         exit( -1 );
185     }
189 #===  FUNCTION  ================================================================
190 #         NAME:  logging
191 #   PARAMETERS:  level - string - default 'info' 
192 #                msg - string - 
193 #                facility - string - default 'LOG_DAEMON' 
194 #      RETURNS:  
195 #  DESCRIPTION: 
196 #===============================================================================
197 sub daemon_log {
198     # log into log_file
199     my( $msg, $level ) = @_;
200     if(not defined $msg) { return }
201     if(not defined $level) { $level = 1 }
202     if(defined $log_file){
203         open(LOG_HANDLE, ">>$log_file");
204         if(not defined open( LOG_HANDLE, ">>$log_file" )) {
205             print STDERR "cannot open $log_file: $!";
206             return }
207             chomp($msg);
208             if($level <= $verbose){
209                 my ($seconds, $minutes, $hours, $monthday, $month,
210                         $year, $weekday, $yearday, $sommertime) = localtime(time);
211                 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
212                 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
213                 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
214                 my @monthnames = ("Jan", "Feb", "Mar", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec");
215                 $month = $monthnames[$month];
216                 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
217                 $year+=1900;
218                 my $name = $0;
219                 $name =~ s/\.\///;
221                 my $log_msg = "$month $monthday $hours:$minutes:$seconds $name $msg\n";
222                 print LOG_HANDLE $log_msg;
223                 if( $foreground ) { 
224                     print STDERR $log_msg;
225                 }
226             }
227         close( LOG_HANDLE );
228     }
229 #log into syslog
230 #    my ($msg, $level, $facility) = @_;
231 #    if(not defined $msg) {return}
232 #    if(not defined $level) {$level = "info"}
233 #    if(not defined $facility) {$facility = "LOG_DAEMON"}
234 #    openlog($0, "pid,cons,", $facility);
235 #    syslog($level, $msg);
236 #    closelog;
237 #    return;
241 #===  FUNCTION  ================================================================
242 #         NAME:  get_interfaces 
243 #   PARAMETERS:  none
244 #      RETURNS:  (list of interfaces) 
245 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
246 #===============================================================================
247 sub get_interfaces {
248     my @result;
249     my $PROC_NET_DEV= ('/proc/net/dev');
251     open(PROC_NET_DEV, "<$PROC_NET_DEV")
252         or die "Could not open $PROC_NET_DEV";
254     my @ifs = <PROC_NET_DEV>;
256     close(PROC_NET_DEV);
258     # Eat first two line
259     shift @ifs;
260     shift @ifs;
262     chomp @ifs;
263     foreach my $line(@ifs) {
264         my $if= (split /:/, $line)[0];
265         $if =~ s/^\s+//;
266         push @result, $if;
267     }
269     return @result;
272 #===  FUNCTION  ================================================================
273 #         NAME:  get_mac 
274 #   PARAMETERS:  interface name (i.e. eth0)
275 #      RETURNS:  (mac address) 
276 #  DESCRIPTION:  Uses ioctl to get mac address directly from system.
277 #===============================================================================
278 sub get_mac {
279     my $ifreq= shift;
280     my $result;
281     if ($ifreq && length($ifreq) > 0) { 
282         if($ifreq eq "all") {
283             if(defined($server_ip)) {
284                 $result = &get_local_mac_for_remote_ip($server_ip);
285                         } 
286             elsif ($client_mac_address && length($client_mac_address) > 0){
287                                 $result = &client_mac_address;
288             } 
289             else {
290                 $result = "00:00:00:00:00:00";
291             }
292         } else {
293             my $SIOCGIFHWADDR= 0x8927;     # man 2 ioctl_list
295                 # A configured MAC Address should always override a guessed value
296                 if ($client_mac_address and length($client_mac_address) > 0) {
297                     $result= $client_mac_address;
298                 }
300             socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip')
301                 or die "socket: $!";
303             if(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) {
304                 my ($if, $mac)= unpack 'h36 H12', $ifreq;
306                 if (length($mac) > 0) {
307                     $mac=~ m/^([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$/;
308                     $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6);
309                     $result = $mac;
310                 }
311             }
312         }
313     }
314     return $result;
318 #===  FUNCTION  ================================================================
319 #         NAME:  get_interface_for_ip
320 #   PARAMETERS:  ip address (i.e. 192.168.0.1)
321 #      RETURNS:  array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
322 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
323 #===============================================================================
324 sub get_interface_for_ip {
325     my $result;
326     my $ip= shift;
327     if ($ip && length($ip) > 0) {
328         my @ifs= &get_interfaces();
329         if($ip eq "0.0.0.0") {
330             $result = "all";
331         } else {
332             foreach (@ifs) {
333                 my $if=$_;
334                 if(get_ip($if) eq $ip) {
335                     $result = $if;
336                     last;
337                 }
338             }       
339         }
340     }       
341     return $result;
345 #===  FUNCTION  ================================================================
346 #         NAME:  get_ip 
347 #   PARAMETERS:  interface name (i.e. eth0)
348 #      RETURNS:  (ip address) 
349 #  DESCRIPTION:  Uses ioctl to get ip address directly from system.
350 #===============================================================================
351 sub get_ip {
352     my $ifreq= shift;
353     my $result= "";
354     my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
355         my $proto= getprotobyname('ip');
357     socket SOCKET, PF_INET, SOCK_DGRAM, $proto
358         or die "socket: $!";
360     if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
361         my ($if, $sin)    = unpack 'a16 a16', $ifreq;
362         my ($port, $addr) = sockaddr_in $sin;
363         my $ip            = inet_ntoa $addr;
365         if ($ip && length($ip) > 0) {
366             $result = $ip;
367         }
368     }
370     return $result;
374 #===  FUNCTION  ================================================================
375 #         NAME:  get_local_mac_for_remote_ip
376 #   PARAMETERS:  none (takes server_ip from global variable)
377 #      RETURNS:  (ip address from interface that is used for communication) 
378 #  DESCRIPTION:  Uses ioctl to get routing table from system, checks which entry
379 #                matches (defaultroute last).
380 #===============================================================================
381 sub get_local_mac_for_remote_ip {
382     my $ifreq= shift;
383     my $result= "00:00:00:00:00:00";
384     my $PROC_NET_ROUTE= ('/proc/net/route');
386     open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
387         or die "Could not open $PROC_NET_ROUTE";
389     my @ifs = <PROC_NET_ROUTE>;
391     close(PROC_NET_ROUTE);
393     # Eat header line
394     shift @ifs;
395     chomp @ifs;
396     foreach my $line(@ifs) {
397         my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
398         my $destination;
399         my $mask;
400         my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
401         $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
402         ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
403         $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
404         if(new NetAddr::IP($server_ip)->within(new NetAddr::IP($destination, $mask))) {
405             # destination matches route, save mac and exit
406             $result= &get_mac($Iface);
407             last;
408         }
409     }
412     return $result;
416 sub new_ldap_config {
417     my ($msg_hash) = @_ ;
418     my $element;
419     my @ldap_uris;
420     my $ldap_base;
421     my @ldap_options;
422     my @pam_options;
423     my @nss_options;
424     my $goto_admin;
425     my $goto_secret;
426     my $admin_base= "";
427     my $department= "";
428     my $unit_tag;
430     # Transform input into array
431     while ( my ($key, $value) = each(%$msg_hash) ) {
432         if ($key =~ /^(source|target|header)$/) {
433                 next;
434         }
436         foreach $element (@$value) {
437                 if ($key =~ /^ldap_uri$/) {
438                         push (@ldap_uris, $element);
439                         next;
440                 }
441                 if ($key =~ /^ldap_base$/) {
442                         $ldap_base= $element;
443                         next;
444                 }
445                 if ($key =~ /^goto_admin$/) {
446                         $goto_admin= $element;
447                         next;
448                 }
449                 if ($key =~ /^goto_secret$/) {
450                         $goto_secret= $element;
451                         next;
452                 }
453                 if ($key =~ /^ldap_cfg$/) {
454                         push (@ldap_options, "$element");
455                         next;
456                 }
457                 if ($key =~ /^pam_cfg$/) {
458                         push (@pam_options, "$element");
459                         next;
460                 }
461                 if ($key =~ /^nss_cfg$/) {
462                         push (@nss_options, "$element");
463                         next;
464                 }
465                 if ($key =~ /^admin_base$/) {
466                         $admin_base= $element;
467                         next;
468                 }
469                 if ($key =~ /^department$/) {
470                         $department= $element;
471                         next;
472                 }
473                 if ($key =~ /^unit_tag$/) {
474                         $unit_tag= $element;
475                         next;
476                 }
477         }
478     }
480     # Unit tagging enabled?
481     if (defined $unit_tag){
482             push (@pam_options, "pam_filter gosaUnitTag=$unit_tag");
483             push (@nss_options, "nss_base_passwd  $admin_base?sub?gosaUnitTag=$unit_tag");
484             push (@nss_options, "nss_base_group   $admin_base?sub?gosaUnitTag=$unit_tag");
485     }
487     # Setup ldap.conf
488     my $file1;
489     my $file2;
490     open(file1, "> $ldap_config");
491     print file1 "# This file was automatically generated by gosa-si-client. Do not change.\n";
492     print file1 "URI";
493     foreach $element (@ldap_uris) {
494         print file1 " $element";
495     }
496     print file1 "\nBASE $ldap_base\n";
497     foreach $element (@ldap_options) {
498         print file1 "$element\n";
499     }
500     close (file1);
501     daemon_log("wrote $ldap_config", 5);
503     # Setup pam_ldap.conf / libnss_ldap.conf
504     open(file1, "> $pam_config");
505     open(file2, "> $nss_config");
506     print file1 "# This file was automatically generated by gosa-si-client. Do not change.\n";
507     print file2 "# This file was automatically generated by gosa-si-client. Do not change.\n";
508     print file1 "uri";
509     print file2 "uri";
510     foreach $element (@ldap_uris) {
511         print file1 " $element";
512         print file2 " $element";
513     }
514     print file1 "\nbase $ldap_base\n";
515     print file2 "\nbase $ldap_base\n";
516     foreach $element (@pam_options) {
517         print file1 "$element\n";
518     }
519     foreach $element (@nss_options) {
520         print file2 "$element\n";
521     }
522     close (file2);
523     daemon_log("wrote $nss_config", 5);
524     close (file1);
525     daemon_log("wrote $pam_config", 5);
527     # Create goto.secrets if told so - for compatibility reasons
528     if (defined $goto_admin){
529             open(file1, "> /etc/goto/secret");
530             close(file1);
531             chown(0,0, "/etc/goto/secret");
532             chmod(0600, "/etc/goto/secret");
533             open(file1, "> /etc/goto/secret");
534             print file1 "GOTOADMIN=\"$goto_admin\"\nGOTOSECRET=\"$goto_secret\"\n";
535             close(file1);
536             daemon_log("wrote /etc/goto/secret", 5);
537     }
539     
541     # Write shell based config
542     my $cfg_name= dirname($ldap_config)."/ldap-shell.conf";
543     open(file1, "> $cfg_name");
544     print file1 "LDAP_BASE=\"$ldap_base\"\n";
545     print file1 "ADMIN_BASE=\"$admin_base\"\n";
546     print file1 "DEPARTMENT=\"$department\"\n";
547     print file1 "UNIT_TAG=\"".(defined $unit_tag ? "$unit_tag" : "")."\"\n";
548     print file1 "UNIT_TAG_FILTER=\"".(defined $unit_tag ? "(gosaUnitTag=$unit_tag)" : "")."\"\n";
549     close(file1);
550     daemon_log("wrote $cfg_name", 5);
552     return;
557 sub create_passwd {
558     my $new_passwd = "";
559     for(my $i=0; $i<31; $i++) {
560         $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
561     }
563     return $new_passwd;
567 sub get_server_addresses {
568     my $domain= shift;
569     my @result;
570     my $dig_cmd= 'dig +nocomments srv _gosad._tcp.'.$domain;
572     my $output= `$dig_cmd 2>&1`;
573     open (PIPE, "$dig_cmd 2>&1 |");
574     while(<PIPE>) {
575         chomp $_;
576         # If it's not a comment
577         if($_ =~ m/^[^;]/) {
578             my @matches= split /\s+/;
580             # Push hostname with port
581             if($matches[3] eq 'SRV') {
582                 push @result, $matches[7].':'.$matches[6];
583             } elsif ($matches[3] eq 'A') {
584                 my $i=0;
586                 # Substitute the hostname with the ip address of the matching A record
587                 foreach my $host (@result) {
588                     if ((split /\:/, $host)[0] eq $matches[0]) {
589                         $result[$i]= $matches[4].':'.(split /\:/, $host)[1];
590                     }
591                     $i++;
592                 }
593             }
594         }
595     }
596     close(PIPE);
597     return @result;
601 ##===  FUNCTION  ================================================================
602 ##         NAME:  create_ciphering
603 ##   PARAMETERS:  passwd - string - used to create ciphering
604 ##      RETURNS:  cipher - object
605 ##  DESCRIPTION:  creates a Crypt::Rijndael::MODE_CBC object with passwd as key
606 ##===============================================================================
607 #sub create_ciphering {
608 #    my ($passwd) = @_;
609 #    $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
610 #    my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
612 #    #daemon_log("iv: $iv", 7);
613 #    #daemon_log("key: $passwd", 7);
614 #    my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
615 #    $my_cipher->set_iv($iv);
616 #    return $my_cipher;
617 #}
620 #sub create_ciphering {
621 #    my ($passwd) = @_;
622 #    $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
623 #    my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
624 #    my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
625 #    $my_cipher->set_iv($iv);
626 #    return $my_cipher;
627 #}
630 #sub encrypt_msg {
631 #    my ($msg, $key) = @_;
632 #    my $my_cipher = &create_ciphering($key);
633 #    {
634 #      use bytes;
635 #      $msg = "\0"x(16-length($msg)%16).$msg;
636 #    }
637 #    $msg = $my_cipher->encrypt($msg);
638 #    chomp($msg = &encode_base64($msg));
639 #    # there are no newlines allowed inside msg
640 #    $msg=~ s/\n//g;
641 #    return $msg;
642 #}
645 #sub decrypt_msg {
646 #    my ($msg, $key) = @_ ;
647 #    $msg = &decode_base64($msg);
648 #    my $my_cipher = &create_ciphering($key);
649 #    $msg = $my_cipher->decrypt($msg); 
650 #    $msg =~ s/\0*//g;
651 #    return $msg;
652 #}
655 #===  FUNCTION  ================================================================
656 #         NAME:  send_msg_hash2address
657 #   PARAMETERS:  msg_hash - hash - xml_hash created with function create_xml_hash
658 #                PeerAddr string - socket address to send msg
659 #                PeerPort string - socket port, if not included in socket address
660 #      RETURNS:  nothing
661 #  DESCRIPTION:  ????
662 #===============================================================================
663 sub send_msg_hash2address {
664     my ($msg_hash, $address, $passwd) = @_ ;
666     # fetch header for logging
667     my $header = @{$msg_hash->{header}}[0];  
669     # generate xml string
670     my $msg_xml = &create_xml_string($msg_hash);
671     
672     # encrypt xml msg
673     my $crypted_msg = &encrypt_msg($msg_xml, $passwd);
675     # opensocket
676     my $socket = &open_socket($address);
677     if(not defined $socket){
678         daemon_log("cannot send '$header'-msg to $address , server not reachable", 5);
679         return 1;
680     }
681     
682     # send xml msg
683     print $socket $crypted_msg."\n";
684     
685     close $socket;
687     daemon_log("send '$header'-msg to $address", 1);
688     daemon_log("message:\n$msg_xml", 8);
689     return 0;
693 sub send_msg_to_target {
694     my ($msg, $address, $encrypt_key, $msg_header) = @_ ;
695     my $error = 0;
697     if( $msg_header ) {
698         $msg_header = "'$msg_header'-";
699     }
700     else {
701         $msg_header = "";
702     }
704     # encrypt xml msg
705     my $crypted_msg = &encrypt_msg($msg, $encrypt_key);
707     # opensocket
708     my $socket = &open_socket($address);
709     if( !$socket ) {
710         daemon_log("cannot send ".$msg_header."msg to $address , host not reachable", 1);
711         $error++;
712     }
713     
714     if( $error == 0 ) {
715         # send xml msg
716         print $socket $crypted_msg."\n";
718         daemon_log("send ".$msg_header."msg to $address", 1);
719         daemon_log("message:\n$msg", 8);
721     }
723     # close socket in any case
724     if( $socket ) {
725         close $socket;
726     }
728     return;
732 sub open_socket {
733     my ($PeerAddr, $PeerPort) = @_ ;
734     if(defined($PeerPort)){
735         $PeerAddr = $PeerAddr.":".$PeerPort;
736     }
737     my $socket;
738     $socket = new IO::Socket::INET(PeerAddr => $PeerAddr,
739             Porto => "tcp",
740             Type => SOCK_STREAM,
741             Timeout => 5,
742             );
743     if(not defined $socket) {
744         return;
745     }
746     &daemon_log("open_socket: $PeerAddr", 7);
747     return $socket;
751 #===  FUNCTION  ================================================================
752 #         NAME:  register_at_server
753 #   PARAMETERS:  
754 #      RETURNS:  
755 #  DESCRIPTION:  
756 #===============================================================================
757 sub register_at_gosa_si_server {
758     my ($kernel) = $_[KERNEL];
760     if( $REGISTERED_FLAG == 1 ) {
762         # create new passwd and ciphering object for client-server communication
763         $server_key = &create_passwd();
765         my $events = join( ", ", keys %{$event_hash} );
767         while(1) {
769             # fetch first gosa-si-server from @servers
770             my $server = shift(@servers);
772             if( !$server ) {
773                 daemon_log("no gosa-si-server left in list of servers", 1);
774                 daemon_log("unable to register at a gosa-si-server, force shutdown", 1);
775                 exit(1);
776             }
778             # create registration msg
779             my $register_hash = &create_xml_hash("here_i_am", $client_address, $server);
780             &add_content2xml_hash($register_hash, "new_passwd", $server_key);
781             &add_content2xml_hash($register_hash, "mac_address", $client_mac_address);
782             &add_content2xml_hash($register_hash, "events", $events);
784             # send xml hash to server with general server passwd
785             my $res = &send_msg_hash2address($register_hash, $server, $default_server_key);
786 #            if( $res == 1 ) {
787 #                next;
788 #            }
790             last;
791         }
792         daemon_log("waiting for msg 'register_at_gosa_si_server'",1);
793         $kernel->delay_set('register_at_gosa_si_server',2);
794     }
795     return;
797     
799 #    my ($rout, $wout, $reg_server);
800 #    foreach my $server (@servers) {
802 #        # create msg hash
803 #        my $register_hash = &create_xml_hash("here_i_am", $client_address, $server);
804 #        &add_content2xml_hash($register_hash, "new_passwd", $new_server_key);
805 #        &add_content2xml_hash($register_hash, "mac_address", $client_mac_address);
806 #        &add_content2xml_hash($register_hash, "events", $events);
808 #        # send xml hash to server with general server passwd
809 #        my $answer = &send_msg_hash2address($register_hash, $server, $server_passwd);
810
811 #        if ($answer != 0) { next; }
812 #       
813 #        # waiting for response
814 #        daemon_log("waiting for response...\n", 5);
815 #        my $nf = select($rout=$rbits, $wout=$wbits, undef, $server_timeout);
817 #        # something is coming in
818 #        if(vec $rout, fileno $input_socket, 1) {
819 #            my $crypted_msg;
820 #            my $client = $input_socket->accept();
821 #            my $other_end = getpeername($client);
822 #            if(not defined $other_end) {
823 #                daemon_log("client cannot be identified: $!\n");
824 #            } else {
825 #                my ($port, $iaddr) = unpack_sockaddr_in($other_end);
826 #                my $actual_ip = inet_ntoa($iaddr);
827 #                daemon_log("\naccept client from $actual_ip\n", 5);
828 #                my $in_msg = &read_from_socket($client);
829 #                if(defined $in_msg){
830 #                    chomp($in_msg);
831 #                    $crypted_msg = $in_msg;
832 #                } else {
833 #                    daemon_log("cannot read from $actual_ip\n", 5);
834 #                }
835 #            }
836 #            close($client);
837 #            
838 #            # validate acknowledge msg from server
839 #            $new_server_cipher = &create_ciphering($new_server_passwd);
840 #            my $msg_hash;
841 #            eval {
842 #                my $decrypted_msg = &decrypt_msg($crypted_msg, $new_server_cipher);
843 #                daemon_log("decrypted register msg: $decrypted_msg", 5);
844 #                $msg_hash = $xml->XMLin($decrypted_msg, ForceArray=>1);
845 #            };
846 #            if($@) {
847 #                daemon_log("ERROR: do not understand the incoming message:" , 5);  
848 #                daemon_log("$@", 7); 
849 #            } else {
850 #                my $header = @{$msg_hash->{header}}[0];
851 #                if($header eq "registered") {
852 #                    $reg_server = $server;
853 #                    last;
854 #                } elsif($header eq "denied") {
855 #                    my $reason = (&get_content_from_xml_hash($msg_hash, "denied"))[0];
856 #                    daemon_log("registration at $server denied: $reason", 1);
857 #                } else {
858 #                    daemon_log("cannot register at $server", 1);
859 #                }
860 #            }
861 #        }
862 #        # if no answer arrive, try next server in list
864 #    }
865 #    
866 #    if(defined $reg_server) {
867 #        daemon_log("registered at $reg_server", 1);
868 #    } else {
869 #        daemon_log("cannot register at any server", 1);
870 #        daemon_log("exiting!!!", 1);
871 #        exit(1);
872 #    }
874 #    # update the global available variables
875 #    $server_address = $reg_server;
876 #    $server_passwd = $new_server_passwd;
877 #    $server_cipher = $new_server_cipher;
880 #    return;
881 #}
885 sub check_key_and_xml_validity {
886     my ($crypted_msg, $module_key) = @_;
887 #print STDERR "crypted_msg:$crypted_msg\n";
888 #print STDERR "modul_key:$module_key\n";
890     my $msg;
891     my $msg_hash;
892     eval{
893         $msg = &decrypt_msg($crypted_msg, $module_key);
894         &main::daemon_log("decrypted_msg: \n$msg", 8);
896         $msg_hash = $xml->XMLin($msg, ForceArray=>1);
898         # check header
899         my $header_l = $msg_hash->{'header'};
900         if( 1 != @{$header_l} ) {
901             die 'no or more headers specified';
902         }
903         my $header = @{$header_l}[0];
904         if( 0 == length $header) {
905             die 'header has length 0';
906         }
908         # check source
909         my $source_l = $msg_hash->{'source'};
910         if( 1 != @{$source_l} ) {
911             die 'no or more sources specified';
912         }
913         my $source = @{$source_l}[0];
914         if( 0 == length $source) {
915             die 'source has length 0';
916         }
918         # check target
919         my $target_l = $msg_hash->{'target'};
920         if( 1 != @{$target_l} ) {
921             die 'no or more targets specified ';
922         }
923         my $target = @{$target_l}[0];
924         if( 0 == length $target) {
925             die 'target has length 0 ';
926         }
928     };
929     if($@) {
930         &main::daemon_log("WARNING: do not understand the message or msg is not gosa-si envelope conform:", 5);
931         &main::daemon_log("$@", 8);
932     }
934     return ($msg, $msg_hash);
938 sub import_events {
940     if (not -e $event_dir) {
941         daemon_log("ERROR: cannot find directory or directory is not readable: $event_dir", 1);   
942     }
943     opendir (DIR, $event_dir) or die "ERROR while loading gosa-si-events from directory $event_dir : $!\n";
945     while (defined (my $event = readdir (DIR))) {
946         if( $event eq "." || $event eq ".." ) { next; }    
948         eval{ require $event; };
949         if( $@ ) {
950             daemon_log("import of event module '$event' failed", 1);
951             daemon_log("$@", 8);
952             next;
953         }
955         $event =~ /(\S*?).pm$/;
956         my $event_module = $1;
957         my $events_l = eval( $1."::get_events()") ;
958         foreach my $event_name (@{$events_l}) {
959             $event_hash->{$event_name} = $event_module;
960         }
962     }
966 sub server_input {
967     my ($heap,$input,$wheel) = @_[HEAP, ARG0, ARG1];
968     my $error = 0;
969     my $answer;
970     
971     daemon_log("Incoming msg:\n$input\n", 8);
973     my ($msg, $msg_hash) = &check_key_and_xml_validity($input, $server_key);
974     if( (!$msg) || (!$msg_hash) ) {
975         daemon_log("Deciphering of incoming msg failed", 5);
976         $error++;
977     }
979     ######################
980     # process incoming msg
981     if( $error == 0 ) {
982         my $header = @{$msg_hash->{header}}[0];
983         my $source = @{$msg_hash->{source}}[0];
985         if( exists $event_hash->{$header} ) {
986             # a event exists with the header as name
987             daemon_log("found event '$header' at event-module '".$event_hash->{$header}."'", 5);
988             no strict 'refs';
989             $answer = &{$event_hash->{$header}."::$header"}($msg, $msg_hash);
990          }
991 #        else {
992 #            # maybe header is a core function
993 #            daemon_log("WARNING: no event assigned to msg $header", 5);
994 #            if ($header eq 'new_ldap_config') { if ($ldap_enabled == 1) {&new_ldap_config($msg_hash)}}
995 #            elsif ($header eq 'ping') { &got_ping($msg_hash) }
996 #            elsif ($header eq 'wake_up') { &execute_event($msg_hash)}
997 #            elsif ($header eq 'new_passwd') { &new_passwd()}
998 #            elsif ($header eq 'compute_hardware') { &compute_hardware() }
999 #            else { daemon_log("ERROR: no core function assigned to msg $header", 5) }
1000 #        }
1001     }
1003     ########
1004     # answer
1005     if( $answer ) {
1006         if( $answer =~ "<header>registered</header>") {
1007             $REGISTERED_FLAG = 0;
1008         } 
1009         else {
1010         &send_msg_to_address($answer, $server_address, $server_key);
1011         }
1012     }
1014     return;
1017 #==== MAIN = main ==============================================================
1018 #  parse commandline options
1019 Getopt::Long::Configure( "bundling" );
1020 GetOptions("h|help" => \&usage,
1021            "c|config=s" => \$cfg_file,
1022            "f|foreground" => \$foreground,
1023            "v|verbose+" => \$verbose,
1024            );
1026 #  read and set config parameters
1027 &check_cmdline_param ;
1028 &read_configfile($cfg_file, %cfg_defaults);
1029 &check_pid;
1032 # forward error messages to logfile
1033 if ( ! $foreground ) {
1034         open STDIN, '/dev/null' or die "Can’t read /dev/null: $!";
1035         open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
1036         open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
1039 # Just fork, if we are not in foreground mode
1040 if( ! $foreground ) { 
1041     chdir '/'                 or die "Can't chdir to /: $!";
1042     $pid = fork;
1043     setsid                    or die "Can't start a new session: $!";
1044     umask 0;
1045
1046 else { 
1047     $pid = $$; 
1050 # Do something useful - put our PID into the pid_file
1051 if( 0 != $pid ) {
1052     open( LOCK_FILE, ">$pid_file" );
1053     print LOCK_FILE "$pid\n";
1054     close( LOCK_FILE );
1055     if( !$foreground ) { 
1056         exit( 0 ) 
1057     };
1060 daemon_log(" ", 1);
1061 daemon_log("$0 started!", 1);
1063 # delete old DBsqlite lock files
1064 system('rm -f /tmp/gosa_si_lock*gosa-si-client*');
1067 # complete client_address
1068 $client_address = $client_ip.":".$client_port;
1071 # detect own ip and mac address
1072 my $network_interface= &get_interface_for_ip($client_ip);
1073 $client_mac_address= &get_mac($network_interface);
1074 daemon_log("gosa-si-client ip address detected: $client_ip", 1);
1075 daemon_log("gosa-si-client mac address detected: $client_mac_address", 1);
1078 # import events
1079 &import_events();
1082 # create socket for incoming xml messages
1083 POE::Component::Server::TCP->new(
1084     Alias => 'gosa-si-client',
1085         Port => $client_port,
1086         ClientInput => \&server_input,
1087 );
1088 daemon_log("start socket for incoming xml messages at port '$client_port' ", 1);
1091 # prepare variables
1092 if (defined $server_ip && defined $server_port) {
1093     $server_address = $server_ip.":".$server_port;
1095 $xml = new XML::Simple();
1096 $default_server_key = $server_key;
1099 # find all possible gosa-si-servers in DNS
1100 if (defined $server_domain) {
1101     my @tmp_servers = &get_server_addresses($server_domain);
1102     foreach my $server (@tmp_servers) { 
1103         unshift(@servers, $server); 
1104     }
1106 # add gosa-si-server address from config file at first position of server list
1107 if (defined $server_address) {
1108     unshift(@servers, $server_address);
1110 my $servers_string = join(", ", @servers);
1111 daemon_log("found servers in configuration file and via DNS: $servers_string", 5);
1114 POE::Session->create(
1115         inline_states => {
1116                 _start => \&register_at_gosa_si_server ,
1117         register_at_gosa_si_server => \&register_at_gosa_si_server,
1118         }
1119 );
1121 POE::Kernel->run();
1122 exit;