846eed111f3acbc65ec3c8868647b89d553be24e
1 package ServerPackages;
3 use Exporter;
4 @ISA = ("Exporter");
6 # Each module has to have a function 'process_incoming_msg'. This function works as a interface to gosa-sd and recieves the msg hash from gosa-sd. 'process_incoming_function checks, wether it has a function to process the incoming msg and forward the msg to it.
9 use strict;
10 use warnings;
11 use GOSA::GosaSupportDaemon;
12 use IO::Socket::INET;
13 use XML::Simple;
14 use Net::LDAP;
16 BEGIN{}
17 END {}
20 my ($server_activ, $server_port, $server_passwd, $max_clients, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password);
21 my ($bus_activ, $bus_passwd, $bus_ip, $bus_port);
22 my $server;
23 my $no_bus;
24 my (@ldap_cfg, @pam_cfg, @nss_cfg, $goto_admin, $goto_secret);
26 my %cfg_defaults =
27 ("server" =>
28 {"server_activ" => [\$server_activ, "on"],
29 "server_port" => [\$server_port, "20081"],
30 "server_passwd" => [\$server_passwd, ""],
31 "max_clients" => [\$max_clients, 100],
32 "ldap_uri" => [\$ldap_uri, ""],
33 "ldap_base" => [\$ldap_base, ""],
34 "ldap_admin_dn" => [\$ldap_admin_dn, ""],
35 "ldap_admin_password" => [\$ldap_admin_password, ""],
36 },
37 "bus" =>
38 {"bus_activ" => [\$bus_activ, "on"],
39 "bus_passwd" => [\$bus_passwd, ""],
40 "bus_ip" => [\$bus_ip, ""],
41 "bus_port" => [\$bus_port, "20080"],
42 },
43 );
45 ### START #####################################################################
48 # read configfile and import variables
49 &read_configfile();
51 # detect own ip and mac address
52 my ($server_ip, $server_mac_address) = &get_ip_and_mac();
53 if (not defined $server_ip) {
54 die "EXIT: ip address of $0 could not be detected";
55 }
56 &main::daemon_log("server ip address detected: $server_ip", 1);
57 &main::daemon_log("server mac address detected: $server_mac_address", 1);
59 # complete addresses
60 my $server_address = "$server_ip:$server_port";
61 my $bus_address = "$bus_ip:$bus_port";
63 # create general settings for this module
64 my $xml = new XML::Simple();
66 # open server socket
67 if($server_activ eq "on"){
68 &main::daemon_log(" ", 1);
69 $server = IO::Socket::INET->new(LocalPort => $server_port,
70 Type => SOCK_STREAM,
71 Reuse => 1,
72 Listen => 20,
73 );
74 if(not defined $server){
75 &main::daemon_log("cannot be a tcp server at $server_port : $@");
76 } else {
77 &main::daemon_log("start server: $server_address", 1);
78 }
79 }
81 # register at bus
82 if ($main::no_bus > 0) {
83 $bus_activ = "off"
84 }
85 if($bus_activ eq "on") {
86 &main::daemon_log(" ", 1);
87 ®ister_at_bus();
88 }
90 ### functions #################################################################
92 #sub get_module_tags {
93 #
94 # # lese config file aus dort gibt es eine section Basic
95 # # dort stehen drei packettypen, für die sich das modul anmelden kann, gosa-admin-packages,
96 # # server-packages, client-packages
97 # my %tag_hash = (gosa_admin_packages => "yes",
98 # server_packages => "yes",
99 # client_packages => "yes",
100 # );
101 # return \%tag_hash;
102 #}
105 sub get_module_info {
106 my @info = ($server_address,
107 $server_passwd,
108 $server,
109 $server_activ,
110 "socket",
111 );
112 return \@info;
113 }
116 #=== FUNCTION ================================================================
117 # NAME: read_configfile
118 # PARAMETERS: cfg_file - string -
119 # RETURNS: nothing
120 # DESCRIPTION: read cfg_file and set variables
121 #===============================================================================
122 sub read_configfile {
123 my $cfg;
124 if( defined( $main::cfg_file) && ( length($main::cfg_file) > 0 )) {
125 if( -r $main::cfg_file ) {
126 $cfg = Config::IniFiles->new( -file => $main::cfg_file );
127 } else {
128 print STDERR "Couldn't read config file!";
129 }
130 } else {
131 $cfg = Config::IniFiles->new() ;
132 }
133 foreach my $section (keys %cfg_defaults) {
134 foreach my $param (keys %{$cfg_defaults{ $section }}) {
135 my $pinfo = $cfg_defaults{ $section }{ $param };
136 ${@$pinfo[0]} = $cfg->val( $section, $param, @$pinfo[1] );
137 }
138 }
140 # Read non predefined sections
141 my $param;
142 if ($cfg->SectionExists('ldap')){
143 foreach $param ($cfg->Parameters('ldap')){
144 push (@ldap_cfg, "$param ".$cfg->val('ldap', $param));
145 }
146 }
147 if ($cfg->SectionExists('pam_ldap')){
148 foreach $param ($cfg->Parameters('pam_ldap')){
149 push (@pam_cfg, "$param ".$cfg->val('pam_ldap', $param));
150 }
151 }
152 if ($cfg->SectionExists('nss_ldap')){
153 foreach $param ($cfg->Parameters('nss_ldap')){
154 push (@nss_cfg, "$param ".$cfg->val('nss_ldap', $param));
155 }
156 }
157 if ($cfg->SectionExists('goto')){
158 $goto_admin= $cfg->val('goto', 'terminal_admin');
159 $goto_secret= $cfg->val('goto', 'terminal_secret');
160 } else {
161 $goto_admin= undef;
162 $goto_secret= undef;
163 }
165 }
168 #=== FUNCTION ================================================================
169 # NAME: get_ip_and_mac
170 # PARAMETERS: nothing
171 # RETURNS: (ip, mac)
172 # DESCRIPTION: executes /sbin/ifconfig and parses the output, the first occurence
173 # of a inet address is returned as well as the mac address in the line
174 # above the inet address
175 #===============================================================================
176 sub get_ip_and_mac {
177 my $ip = "0.0.0.0.0"; # Defualt-IP
178 my $mac = "00:00:00:00:00:00"; # Default-MAC
179 my @ifconfig = qx(/sbin/ifconfig);
180 foreach(@ifconfig) {
181 if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
182 $mac = "$1:$2:$3:$4:$5:$6";
183 next;
184 }
185 if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
186 $ip = "$1.$2.$3.$4";
187 last;
188 }
189 }
190 return ($ip, $mac);
191 }
194 #=== FUNCTION ================================================================
195 # NAME: open_socket
196 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
197 # [PeerPort] string necessary if port not appended by PeerAddr
198 # RETURNS: socket IO::Socket::INET
199 # DESCRIPTION: open a socket to PeerAddr
200 #===============================================================================
201 sub open_socket {
202 my ($PeerAddr, $PeerPort) = @_ ;
203 if(defined($PeerPort)){
204 $PeerAddr = $PeerAddr.":".$PeerPort;
205 }
206 my $socket;
207 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
208 Porto => "tcp" ,
209 Type => SOCK_STREAM,
210 Timeout => 5,
211 );
212 if(not defined $socket) {
213 return;
214 }
215 &main::daemon_log("open_socket to: $PeerAddr", 7);
216 return $socket;
217 }
219 #=== FUNCTION ================================================================
220 # NAME: register_at_bus
221 # PARAMETERS: nothing
222 # RETURNS: nothing
223 # DESCRIPTION: creates an entry in known_daemons and send a 'here_i_am' msg to bus
224 #===============================================================================
225 sub register_at_bus {
227 # create known_daemons entry
228 &main::create_known_daemon($bus_address);
229 &main::add_content2known_daemons(hostname=>$bus_address, status=>"register_at_bus", passwd=>$bus_passwd);
231 my $msg_hash = &create_xml_hash("here_i_am", $server_address, $bus_address);
232 my $answer = "";
233 $answer = &send_msg_hash2address($msg_hash, $bus_address);
234 if ($answer == 0) {
235 &main::daemon_log("register at bus: $bus_address", 1);
236 } else {
237 &main::daemon_log("unable to send 'register'-msg to bus: $bus_address", 1);
238 }
239 return;
240 }
242 #=== FUNCTION ================================================================
243 # NAME: process_incoming_msg
244 # PARAMETERS: crypted_msg - string - incoming crypted message
245 # RETURNS: nothing
246 # DESCRIPTION: handels the proceeded distribution to the appropriated functions
247 #===============================================================================
248 sub process_incoming_msg {
249 my ($crypted_msg) = @_ ;
250 if(not defined $crypted_msg) {
251 &main::daemon_log("function 'process_incoming_msg': got no msg", 7);
252 }
254 $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
255 $crypted_msg = $1;
256 my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
258 # collect addresses from possible incoming clients
259 my @valid_keys;
260 my @host_keys = keys %$main::known_daemons;
261 foreach my $host_key (@host_keys) {
262 if($host_key =~ "^$host") {
263 push(@valid_keys, $host_key);
264 }
265 }
266 my @client_keys = keys %$main::known_clients;
267 foreach my $client_key (@client_keys) {
268 if($client_key =~ "^$host"){
269 push(@valid_keys, $client_key);
270 }
271 }
272 push(@valid_keys, $server_address);
274 my $l = @valid_keys;
275 my $msg_hash;
276 my $msg_flag = 0;
277 my $msg = "";
279 # determine the correct passwd for deciphering of the incoming msgs
280 foreach my $host_key (@valid_keys) {
281 eval{
282 &main::daemon_log("ServerPackage: host_key: $host_key", 7);
283 my $key_passwd;
284 if (exists $main::known_daemons->{$host_key}) {
285 $key_passwd = $main::known_daemons->{$host_key}->{passwd};
286 } elsif (exists $main::known_clients->{$host_key}) {
287 $key_passwd = $main::known_clients->{$host_key}->{passwd};
288 } elsif ($host_key eq $server_address) {
289 $key_passwd = $server_passwd;
290 }
291 &main::daemon_log("ServerPackage: key_passwd: $key_passwd", 7);
292 my $key_cipher = &create_ciphering($key_passwd);
293 $msg = &decrypt_msg($crypted_msg, $key_cipher);
294 &main::daemon_log("ServerPackages: decrypted msg: $msg", 7);
295 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
296 #my $tmp = printf Dumper $msg_hash;
297 #&main::daemon_log("DEBUG: ServerPackages: xml hash: $tmp", 7);
298 };
299 if($@) {
300 &main::daemon_log("ServerPackage: key raise error: $@", 7);
301 $msg_flag += 1;
302 } else {
303 last;
304 }
305 }
307 if($msg_flag >= $l) {
308 &main::daemon_log("WARNING: ServerPackage do not understand the message:", 5);
309 &main::daemon_log("$@", 7);
310 return;
311 }
313 # process incoming msg
314 my $header = @{$msg_hash->{header}}[0];
315 my $source = @{$msg_hash->{source}}[0];
317 &main::daemon_log("recieve '$header' at ServerPackages from $host", 1);
318 # &main::daemon_log("ServerPackages: msg from host:", 5);
319 # &main::daemon_log("\t$host", 5);
320 # &main::daemon_log("ServerPackages: header from msg:", 5);
321 # &main::daemon_log("\t$header", 5);
322 &main::daemon_log("ServerPackages: msg to process:", 5);
323 &main::daemon_log("\t$msg", 5);
325 my @targets = @{$msg_hash->{target}};
326 my $len_targets = @targets;
327 if ($len_targets == 0){
328 &main::daemon_log("ERROR: ServerPackages: no target specified for msg $header", 1);
330 } elsif ($len_targets == 1){
331 # we have only one target symbol
333 my $target = $targets[0];
334 &main::daemon_log("SeverPackages: msg is for:", 7);
335 &main::daemon_log("\t$target", 7);
337 if ($target eq $server_address) {
338 # msg is for server
339 if ($header eq 'new_passwd'){ &new_passwd($msg_hash)}
340 elsif ($header eq 'here_i_am') { &here_i_am($msg_hash)}
341 elsif ($header eq 'who_has') { &who_has($msg_hash) }
342 elsif ($header eq 'who_has_i_do') { &who_has_i_do($msg_hash)}
343 elsif ($header eq 'update_status') { &update_status($msg_hash) }
344 elsif ($header eq 'got_ping') { &got_ping($msg_hash)}
345 elsif ($header eq 'get_load') { &execute_actions($msg_hash)}
346 else { &main::daemon_log("ERROR: ServerPackages: no function assigned to this msg", 5) }
349 } elsif ($target eq "*") {
350 # msg is for all clients
352 my @target_addresses = keys(%$main::known_clients);
353 foreach my $target_address (@target_addresses) {
354 if ($target_address eq $source) { next; }
355 $msg_hash->{target} = [$target_address];
356 &send_msg_hash2address($msg_hash, $target_address);
357 }
358 } else {
359 # msg is for one host
361 if (exists $main::known_clients->{$target}) {
362 &send_msg_hash2address($msg_hash, $target);
363 } elsif (exists $main::known_daemons->{$target}) {
364 # target is known
365 &send_msg_hash2address($msg_hash, $target);
366 } else {
367 # target is not known
368 &main::daemon_log("ERROR: ServerPackages: target $target is not known neither in known_clients nor in known_daemons", 1);
369 }
370 }
371 }
373 return ;
374 }
377 #=== FUNCTION ================================================================
378 # NAME: got_ping
379 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
380 # RETURNS: nothing
381 # DESCRIPTION: process this incoming message
382 #===============================================================================
383 sub got_ping {
384 my ($msg_hash) = @_;
386 my $source = @{$msg_hash->{source}}[0];
387 my $target = @{$msg_hash->{target}}[0];
388 my $header = @{$msg_hash->{header}}[0];
390 if(exists $main::known_daemons->{$source}) {
391 &main::add_content2known_daemons(hostname=>$source, status=>$header);
392 } else {
393 &main::add_content2known_clients(hostname=>$source, status=>$header);
394 }
396 return;
397 }
400 #=== FUNCTION ================================================================
401 # NAME: new_passwd
402 # PARAMETERS: msg_hash - ref - hash from function create_xml_hash
403 # RETURNS: nothing
404 # DESCRIPTION: process this incoming message
405 #===============================================================================
406 sub new_passwd {
407 my ($msg_hash) = @_;
409 my $source = @{$msg_hash->{source}}[0];
410 my $passwd = @{$msg_hash->{new_passwd}}[0];
412 if (exists $main::known_daemons->{$source}) {
413 &main::add_content2known_daemons(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
414 my $hash = &create_xml_hash("confirm_new_passwd", $server_address, $source);
415 &send_msg_hash2address($hash, $source);
417 } elsif (exists $main::known_clients->{$source}) {
418 &main::add_content2known_clients(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
420 } else {
421 &main::daemon_log("ERROR: $source not known, neither in known_daemons nor in known_clients", 1)
422 }
424 return;
425 }
428 #=== FUNCTION ================================================================
429 # NAME: here_i_am
430 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
431 # RETURNS: nothing
432 # DESCRIPTION: process this incoming message
433 #===============================================================================
434 sub here_i_am {
435 my ($msg_hash) = @_;
437 my $source = @{$msg_hash->{source}}[0];
438 my $mac_address = @{$msg_hash->{mac_address}}[0];
439 my $out_hash;
441 # number of known clients
442 my $nu_clients = keys %$main::known_clients;
444 # check wether client address or mac address is already known
445 if (exists $main::known_clients->{$source}) {
446 &main::daemon_log("WARNING: $source is already known as a client", 1);
447 &main::daemon_log("WARNING: values for $source are being overwritten", 1);
448 $nu_clients --;
449 }
451 # number of actual activ clients
452 my $act_nu_clients = $nu_clients;
454 &main::daemon_log("number of actual activ clients: $act_nu_clients", 5);
455 &main::daemon_log("number of maximal allowed clients: $max_clients", 5);
457 if($max_clients <= $act_nu_clients) {
458 my $out_hash = &create_xml_hash("denied", $server_address, $source);
459 &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
460 my $passwd = @{$msg_hash->{new_passwd}}[0];
461 &send_msg_hash2address($out_hash, $source, $passwd);
462 return;
463 }
465 # new client accepted
466 my $new_passwd = @{$msg_hash->{new_passwd}}[0];
468 # create known_daemons entry
469 my $events = @{$msg_hash->{events}}[0];
470 &main::create_known_client($source);
471 &main::add_content2known_clients(hostname=>$source, events=>$events, mac_address=>$mac_address,
472 status=>"registered", passwd=>$new_passwd);
474 # return acknowledgement to client
475 $out_hash = &create_xml_hash("registered", $server_address, $source);
476 &send_msg_hash2address($out_hash, $source);
478 # notify registered client to bus
479 $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
480 #&main::send_msg_hash2bus($out_hash);
481 &send_msg_hash2address($out_hash, $bus_address);
483 # give the new client his ldap config
484 &new_ldap_config($source);
486 return;
487 }
490 #=== FUNCTION ================================================================
491 # NAME: who_has
492 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
493 # RETURNS: nothing
494 # DESCRIPTION: process this incoming message
495 #===============================================================================
496 sub who_has {
497 my ($msg_hash) = @_ ;
499 # what is your search pattern
500 my $search_pattern = @{$msg_hash->{who_has}}[0];
501 my $search_element = @{$msg_hash->{$search_pattern}}[0];
502 &main::daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
504 # scanning known_clients for search_pattern
505 my @host_addresses = keys %$main::known_clients;
506 my $known_clients_entries = length @host_addresses;
507 my $host_address;
508 foreach my $host (@host_addresses) {
509 my $client_element = $main::known_clients->{$host}->{$search_pattern};
510 if ($search_element eq $client_element) {
511 $host_address = $host;
512 last;
513 }
514 }
516 # search was successful
517 if (defined $host_address) {
518 my $source = @{$msg_hash->{source}}[0];
519 my $out_msg = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
520 &add_content2xml_hash($out_msg, "mac_address", $search_element);
521 &send_msg_hash2address($out_msg, $bus_address);
522 }
523 return;
524 }
527 sub who_has_i_do {
528 my ($msg_hash) = @_ ;
529 my $header = @{$msg_hash->{header}}[0];
530 my $source = @{$msg_hash->{source}}[0];
531 my $search_param = @{$msg_hash->{$header}}[0];
532 my $search_value = @{$msg_hash->{$search_param}}[0];
533 print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
534 }
537 #=== FUNCTION ================================================================
538 # NAME: new_ldap_config
539 # PARAMETERS: address - string - ip address and port of a host
540 # RETURNS: nothing
541 # DESCRIPTION: send to address the ldap configuration found for dn gotoLdapServer
542 #===============================================================================
543 sub new_ldap_config {
544 my ($address) = @_ ;
546 if (not exists $main::known_clients->{$address}) {
547 &main::daemon_log("ERROR: $address does not exist in known_clients, cannot send him his ldap config", 1);
548 return;
549 }
551 my $mac_address = $main::known_clients->{$address}->{"mac_address"};
552 if (not defined $mac_address) {
553 &main::daemon_log("ERROR: no mac address found for client $address", 1);
554 return;
555 }
557 # Build LDAP connection
558 my $ldap;
559 $ldap= Net::LDAP->new($ldap_uri);
561 # Bind to a directory with dn and password
562 my $mesg= $ldap->bind($ldap_admin_dn, $ldap_admin_password);
564 # Perform search
565 $mesg = $ldap->search( base => $ldap_base,
566 scope => 'sub',
567 attrs => ['dn', 'gotoLdapServer'],
568 filter => "(&(objectClass=GOhard)(macaddress=$mac_address))");
569 $mesg->code && die $mesg->error;
571 # Sanity check
572 if ($mesg->count != 1) {
573 &main::daemon_log("WARNING: client mac address $mac_address not found/not unique", 1);
574 return;
575 }
577 my $entry= $mesg->entry(0);
578 my $dn= $entry->dn;
579 my @servers= $entry->get_value("gotoLdapServer");
580 my @ldap_uris;
581 my $server;
582 my $base;
584 # Do we need to look at an object class?
585 if ($#servers < 1){
586 $mesg = $ldap->search( base => $ldap_base,
587 scope => 'sub',
588 attrs => ['dn', 'gotoLdapServer'],
589 filter => "(&(objectClass=gosaGroupOfNames)(member=$dn))");
590 $mesg->code && die $mesg->error;
592 # Sanity check
593 if ($mesg->count != 1) {
594 &main::daemon_log("WARNING: no LDAP information found for client mac $mac_address", 1);
595 return;
596 }
598 $entry= $mesg->entry(0);
599 $dn= $entry->dn;
600 @servers= $entry->get_value("gotoLdapServer");
601 }
603 @servers= sort (@servers);
605 foreach $server (@servers){
606 $base= $server;
607 $server =~ s%^[^:]+:[^:]+:(ldap.*://[^/]+)/.*$%$1%;
608 $base =~ s%^[^:]+:[^:]+:ldap.*://[^/]+/(.*)$%$1%;
609 push (@ldap_uris, $server);
610 }
612 # Unbind
613 $mesg = $ldap->unbind;
615 # Assemble data package
616 my %data = ( 'ldap_uri' => \@ldap_uris, 'ldap_base' => $base,
617 'ldap_cfg' => \@ldap_cfg, 'pam_cfg' => \@pam_cfg,'nss_cfg' => \@nss_cfg );
619 # Need to append GOto settings?
620 if (defined $goto_admin and defined $goto_secret){
621 $data{'goto_admin'}= $goto_admin;
622 $data{'goto_secret'}= $goto_secret;
623 }
625 # Send information
626 send_msg("new_ldap_config", $server_address, $address, \%data);
628 return;
629 }
632 #=== FUNCTION ================================================================
633 # NAME: execute_actions
634 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
635 # RETURNS: nothing
636 # DESCRIPTION: invokes the script specified in msg_hash which is located under
637 # /etc/gosad/actions
638 #===============================================================================
639 sub execute_actions {
640 my ($msg_hash) = @_ ;
641 my $configdir= '/etc/gosad/actions/';
642 my $result;
644 my $header = @{$msg_hash->{header}}[0];
645 my $source = @{$msg_hash->{source}}[0];
646 my $target = @{$msg_hash->{target}}[0];
648 if((not defined $source)
649 && (not defined $target)
650 && (not defined $header)) {
651 &main::daemon_log("ERROR: Entries missing in XML msg for gosad actions under /etc/gosad/actions");
652 } else {
653 my $parameters="";
654 my @params = @{$msg_hash->{$header}};
655 my $params = join(", ", @params);
656 &main::daemon_log("execute_actions: got parameters: $params", 5);
658 if (@params) {
659 foreach my $param (@params) {
660 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
661 &main::daemon_log("execute_actions: parameter -> value: $param -> $param_value", 7);
662 $parameters.= " ".$param_value;
663 }
664 }
666 my $cmd= $configdir.$header."$parameters";
667 &main::daemon_log("execute_actions: executing cmd: $cmd", 7);
668 $result= "";
669 open(PIPE, "$cmd 2>&1 |");
670 while(<PIPE>) {
671 $result.=$_;
672 }
673 close(PIPE);
674 }
676 # process the event result
679 return;
680 }
683 1;