efb794374354ee2b95792330008da34b7e2f61c3
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;
131 }
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 }
157 }
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 }
206 }
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);
232 }
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" ;
256 }
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;
295 }
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);
351 if ($answer != 0) { next; }
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);
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 }
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;
419 }
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);
550 #
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;
572 }
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) = @_ ;
585 #
586 # # fetch header for logging
587 # my $header = @{$msg_hash->{header}}[0];
588 #
589 # # generiere xml string
590 # my $msg_xml = &create_xml_string($msg_hash);
591 #
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 # }
603 #
604 # # erzeuge ein ciphering object
605 # my $act_cipher = &create_ciphering($passwd);
606 #
607 # # encrypt xml msg
608 # my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
609 #
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 # }
617 #
618 # # versende xml msg
619 # print $socket $crypted_msg."\n";
620 #
621 # # schließe socket
622 # close $socket;
623 #
624 # daemon_log("send '$header'-msg to $address", 5);
625 # daemon_log("crypted_msg:\n\t$crypted_msg", 7);
626 #
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;
655 }
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;
691 }
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;
719 }
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;
734 }
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;
767 }
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";
789 $known_hosts->{$hostname}->{$element} = $content;
790 $known_hosts->{$hostname}->{timestamp} = $t;
791 return;
792 }
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];
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);
856 return;
857 }
858 }
859 close(DIR);
860 daemon_log("could not assign the msg $header to an event", 5);
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;
869 }
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;
884 }
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");
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;
906 # registrated at new daemon
907 ®ister_at_server();
909 return;
910 }
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');
920 &add_content2known_hosts(hostname=>$target, status=>$header);
922 my $out_hash = &create_xml_hash("got_ping", $target, $source);
923 &send_msg_hash2address($out_hash, $source, $server_passwd);
925 return;
926 }
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;
1032 }
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;
1077 }
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);
1087 &send_msg_hash2address($out_hash, $server_address, $server_passwd);
1089 $server_passwd = $new_server_passwd;
1090 $server_cipher = $new_server_cipher;
1091 return;
1092 }
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 ) };
1127 }
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";
1133 }
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;
1140 }
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;
1160 }
1162 # register at server
1163 daemon_log(" ", 1);
1164 ®ister_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);
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 }
1204 }