b69c34782bb7d468d56a3db5c3dc4365b16fd651
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;
130 }
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 }
156 }
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 }
205 }
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);
231 }
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" ;
255 }
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;
294 }
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);
350 if ($answer != 0) { next; }
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);
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 }
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;
418 }
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);
549 #
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;
571 }
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) = @_ ;
584 #
585 # # fetch header for logging
586 # my $header = @{$msg_hash->{header}}[0];
587 #
588 # # generiere xml string
589 # my $msg_xml = &create_xml_string($msg_hash);
590 #
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 # }
602 #
603 # # erzeuge ein ciphering object
604 # my $act_cipher = &create_ciphering($passwd);
605 #
606 # # encrypt xml msg
607 # my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
608 #
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 # }
616 #
617 # # versende xml msg
618 # print $socket $crypted_msg."\n";
619 #
620 # # schließe socket
621 # close $socket;
622 #
623 # daemon_log("send '$header'-msg to $address", 5);
624 # daemon_log("crypted_msg:\n\t$crypted_msg", 7);
625 #
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;
654 }
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;
690 }
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;
718 }
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;
733 }
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;
766 }
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";
788 $known_hosts->{$hostname}->{$element} = $content;
789 $known_hosts->{$hostname}->{timestamp} = $t;
790 return;
791 }
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];
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);
855 return;
856 }
857 }
858 close(DIR);
859 daemon_log("could not assign the msg $header to an event", 5);
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;
870 }
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;
885 }
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");
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;
907 # registrated at new daemon
908 ®ister_at_server();
910 return;
911 }
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');
921 &add_content2known_hosts(hostname=>$target, status=>$header);
923 my $out_hash = &create_xml_hash("got_ping", $target, $source);
924 &send_msg_hash2address($out_hash, $source, $server_passwd);
926 return;
927 }
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_cfg$/) {
955 push (@ldap_options, "$element");
956 next;
957 }
958 if ($key =~ /^pam_cfg$/) {
959 push (@pam_options, "$element");
960 next;
961 }
962 if ($key =~ /^nss_cfg$/) {
963 push (@nss_options, "$element");
964 next;
965 }
966 }
967 }
969 # Setup ldap.conf
970 my $file1;
971 my $file2;
972 open(file1, "> $ldap_config");
973 print file1 "# This file was automatically generated by gosa-si-client. Do not change.\n";
974 print file1 "URI";
975 foreach $element (@ldap_uris) {
976 print file1 " $element";
977 }
978 print file1 "\nBASE $ldap_base\n";
979 foreach $element (@ldap_options) {
980 print file1 "$element\n";
981 }
982 close (file1);
983 daemon_log("wrote $ldap_config", 5);
985 # Setup pam_ldap.conf / libnss_ldap.conf
986 open(file1, "> $pam_config");
987 open(file2, "> $nss_config");
988 print file1 "# This file was automatically generated by gosa-si-client. Do not change.\n";
989 print file2 "# This file was automatically generated by gosa-si-client. Do not change.\n";
990 print file1 "uri";
991 print file2 "uri";
992 foreach $element (@ldap_uris) {
993 print file1 " $element";
994 print file2 " $element";
995 }
996 print file1 "\nbase $ldap_base\n";
997 print file2 "\nbase $ldap_base\n";
998 foreach $element (@pam_options) {
999 print file1 "$element\n";
1000 }
1001 foreach $element (@nss_options) {
1002 print file2 "$element\n";
1003 }
1004 close (file2);
1005 daemon_log("wrote $nss_config", 5);
1006 close (file1);
1007 daemon_log("wrote $pam_config", 5);
1009 return;
1011 }
1014 sub execute_event {
1015 my ($msg_hash)= @_;
1016 my $configdir= '/etc/gosa-si/client/events/';
1017 my $result;
1019 my $header = &get_content_from_xml_hash($msg_hash, 'header');
1020 my $source = &get_content_from_xml_hash($msg_hash, 'source');
1021 my $target = &get_content_from_xml_hash($msg_hash, 'target');
1024 if((not defined $source)
1025 && (not defined $target)
1026 && (not defined $header)) {
1027 daemon_log("ERROR: Entries missing in XML msg for gosa events under $configdir");
1028 } else {
1029 my $parameters="";
1030 my @params = &get_content_from_xml_hash($msg_hash, $header);
1031 my $params = join(", ", @params);
1032 daemon_log("execute_event: got parameters: $params", 5);
1034 if (@params) {
1035 foreach my $param (@params) {
1036 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
1037 daemon_log("execute_event: parameter -> value: $param -> $param_value", 7);
1038 $parameters.= " ".$param_value;
1039 }
1040 }
1042 my $cmd= $configdir.$header."$parameters";
1043 daemon_log("execute_event: executing cmd: $cmd", 7);
1044 $result= "";
1045 open(PIPE, "$cmd 2>&1 |");
1046 while(<PIPE>) {
1047 $result.=$_;
1048 }
1049 close(PIPE);
1050 }
1052 # process the event result
1055 return;
1056 }
1059 sub new_passwd {
1060 # my ($msg_hash) = @_ ;
1061 my $new_server_passwd = &create_passwd();
1062 my $new_server_cipher = &create_ciphering($new_server_passwd);
1064 my $out_hash = &create_xml_hash("new_passwd", $client_address, $server_address, $new_server_passwd);
1066 &send_msg_hash2address($out_hash, $server_address, $server_passwd);
1068 $server_passwd = $new_server_passwd;
1069 $server_cipher = $new_server_cipher;
1070 return;
1071 }
1076 #==== MAIN = main ==============================================================
1078 # parse commandline options
1079 Getopt::Long::Configure( "bundling" );
1080 GetOptions("h|help" => \&usage,
1081 "c|config=s" => \$cfg_file,
1082 "f|foreground" => \$foreground,
1083 "v|verbose+" => \$verbose,
1084 );
1086 # read and set config parameters
1087 &check_cmdline_param ;
1088 &read_configfile;
1089 &check_pid;
1091 # restart daemon log file
1092 if(-e $log_file ) { unlink $log_file }
1093 daemon_log(" ", 1);
1094 daemon_log("$0 started!", 1);
1096 # Just fork, if we"re not in foreground mode
1097 if( ! $foreground ) { $pid = fork(); }
1098 else { $pid = $$; }
1100 # Do something useful - put our PID into the pid_file
1101 if( 0 != $pid ) {
1102 open( LOCK_FILE, ">$pid_file" );
1103 print LOCK_FILE "$pid\n";
1104 close( LOCK_FILE );
1105 if( !$foreground ) { exit( 0 ) };
1106 }
1108 # detect own ip and mac address
1109 ($client_ip, $client_mac_address) = &get_ip_and_mac();
1110 if (not defined $client_ip) {
1111 die "EXIT: ip address of $0 could not be detected";
1112 }
1113 daemon_log("client ip address detected: $client_ip", 1);
1114 daemon_log("client mac address detected: $client_mac_address", 1);
1116 # prepare variables
1117 if (defined $server_ip && defined $server_port) {
1118 $server_address = $server_ip.":".$server_port;
1119 }
1120 $client_address = $client_ip.":".$client_port;
1122 # setup xml parser
1123 $xml = new XML::Simple();
1125 # create input socket
1126 daemon_log(" ", 1);
1127 $rbits = $wbits = $ebits = "";
1128 $input_socket = IO::Socket::INET->new(LocalPort => $client_port,
1129 Type => SOCK_STREAM,
1130 Reuse => 1,
1131 Listen => 20,
1132 );
1133 if(not defined $input_socket){
1134 daemon_log("cannot be a tcp server at $client_port : $@\n");
1135 } else {
1136 daemon_log("start client at $client_address",1) ;
1137 vec($rbits, fileno $input_socket, 1) = 1;
1138 vec($wbits, fileno $input_socket, 1) = 1;
1139 }
1141 # register at server
1142 daemon_log(" ", 1);
1143 ®ister_at_server();
1146 ##############
1147 # Debugging
1148 #############
1149 #sleep(2);
1150 #&update_status("ich_bin_ein_neuer_status");
1152 ###################################
1153 #everything ready, okay, lets start
1154 ###################################
1155 while(1) {
1156 my ($rout, $wout);
1157 my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
1159 # error handling
1160 if($nf < 0 ) {
1161 }
1163 # something is coming in
1164 if(vec $rout, fileno $input_socket, 1) {
1165 my $client = $input_socket->accept();
1166 my $other_end = getpeername($client);
1168 if(not defined $other_end) {
1169 daemon_log("client cannot be identified: $!");
1170 } else {
1171 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1172 my $actual_ip = inet_ntoa($iaddr);
1173 daemon_log("accept client from $actual_ip", 5);
1174 my $in_msg = &read_from_socket($client);
1175 if(defined $in_msg){
1176 chomp($in_msg);
1177 $in_msg = $in_msg.".".$actual_ip;
1178 &process_incoming_msg($in_msg);
1180 }
1181 }
1182 }
1183 }