1eff3a641c11bf8f2b53eec1f65a432e33bd9316
1 package SIPackages;
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 receives 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 Data::Dumper;
15 use NetAddr::IP;
16 use Net::LDAP;
17 use Socket;
18 use Net::hostent;
19 use Net::DNS;
21 BEGIN{}
22 END {}
24 my ($known_clients_file_name);
25 my ($server_activ, $server_ip, $server_mac_address, $server_port, $SIPackages_key, $max_clients, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $server_interface);
26 my ($bus_activ, $bus_key, $bus_ip, $bus_port);
27 my $server;
28 my $network_interface;
29 my $no_bus;
30 my (@ldap_cfg, @pam_cfg, @nss_cfg, $goto_admin, $goto_secret);
33 my %cfg_defaults =
34 (
35 "server" =>
36 {"server_activ" => [\$server_activ, "on"],
37 "server_ip" => [\$server_ip, "0.0.0.0"],
38 "server_mac_address" => [\$server_mac_address, "00:00:00:00:00"],
39 "server_port" => [\$server_port, "20081"],
40 "SIPackages_key" => [\$SIPackages_key, ""],
41 "max_clients" => [\$max_clients, 100],
42 "ldap_uri" => [\$ldap_uri, ""],
43 "ldap_base" => [\$ldap_base, ""],
44 "ldap_admin_dn" => [\$ldap_admin_dn, ""],
45 "ldap_admin_password" => [\$ldap_admin_password, ""],
46 },
47 "bus" =>
48 {"bus_activ" => [\$bus_activ, "on"],
49 "bus_passwd" => [\$bus_key, ""],
50 "bus_ip" => [\$bus_ip, ""],
51 "bus_port" => [\$bus_port, "20080"],
52 },
53 );
55 ### START #####################################################################
57 # read configfile and import variables
58 &read_configfile();
60 $network_interface= &get_interface_for_ip($server_ip);
61 $server_mac_address= &get_mac($network_interface);
63 # complete addresses
64 #if( $server_ip eq "0.0.0.0" ) {
65 # $server_ip = "127.0.0.1";
66 #}
67 my $server_address = "$server_ip:$server_port";
68 $main::server_address = $server_address;
69 my $bus_address = "$bus_ip:$bus_port";
70 $main::bus_address = $bus_address;
72 # create general settings for this module
73 my $xml = new XML::Simple();
75 # register at bus
76 if ($main::no_bus > 0) {
77 $bus_activ = "off"
78 }
79 if($bus_activ eq "on") {
80 ®ister_at_bus();
81 }
83 # add myself to known_server_db
84 my $res = $main::known_server_db->add_dbentry( {table=>'known_server',
85 primkey=>'hostname',
86 hostname=>$server_address,
87 status=>'myself',
88 hostkey=>$SIPackages_key,
89 timestamp=>&get_time,
90 } );
94 ### functions #################################################################
97 sub get_module_info {
98 my @info = ($server_address,
99 $SIPackages_key,
100 $server,
101 $server_activ,
102 "socket",
103 );
104 return \@info;
105 }
109 sub do_wake {
110 my $host = shift;
111 my $ipaddr = shift || '255.255.255.255';
112 my $port = getservbyname('discard', 'udp');
114 my ($raddr, $them, $proto);
115 my ($hwaddr, $hwaddr_re, $pkt);
117 # get the hardware address (ethernet address)
119 $hwaddr_re = join(':', ('[0-9A-Fa-f]{1,2}') x 6);
120 if ($host =~ m/^$hwaddr_re$/) {
121 $hwaddr = $host;
122 } else {
123 # $host is not a hardware address, try to resolve it
124 my $ip_re = join('\.', ('([0-9]|[1-9][0-9]|1[0-9]{2}|2([0-4][0-9]|5[0-5]))') x 4);
125 my $ip_addr;
126 if ($host =~ m/^$ip_re$/) {
127 $ip_addr = $host;
128 } else {
129 my $h;
130 unless ($h = gethost($host)) {
131 return undef;
132 }
133 $ip_addr = inet_ntoa($h->addr);
134 }
135 }
137 # Generate magic sequence
138 foreach (split /:/, $hwaddr) {
139 $pkt .= chr(hex($_));
140 }
141 $pkt = chr(0xFF) x 6 . $pkt x 16;
143 # Allocate socket and send packet
145 $raddr = gethostbyname($ipaddr)->addr;
146 $them = pack_sockaddr_in($port, $raddr);
147 $proto = getprotobyname('udp');
149 socket(S, AF_INET, SOCK_DGRAM, $proto) or die "socket : $!";
150 setsockopt(S, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt : $!";
152 send(S, $pkt, 0, $them) or die "send : $!";
153 close S;
154 }
157 #=== FUNCTION ================================================================
158 # NAME: read_configfile
159 # PARAMETERS: cfg_file - string -
160 # RETURNS: nothing
161 # DESCRIPTION: read cfg_file and set variables
162 #===============================================================================
163 sub read_configfile {
164 my $cfg;
165 if( defined( $main::cfg_file) && ( length($main::cfg_file) > 0 )) {
166 if( -r $main::cfg_file ) {
167 $cfg = Config::IniFiles->new( -file => $main::cfg_file );
168 } else {
169 print STDERR "Couldn't read config file!";
170 }
171 } else {
172 $cfg = Config::IniFiles->new() ;
173 }
174 foreach my $section (keys %cfg_defaults) {
175 foreach my $param (keys %{$cfg_defaults{ $section }}) {
176 my $pinfo = $cfg_defaults{ $section }{ $param };
177 ${@$pinfo[0]} = $cfg->val( $section, $param, @$pinfo[1] );
178 }
179 }
181 # Read non predefined sections
182 my $param;
183 if ($cfg->SectionExists('ldap')){
184 foreach $param ($cfg->Parameters('ldap')){
185 push (@ldap_cfg, "$param ".$cfg->val('ldap', $param));
186 }
187 }
188 if ($cfg->SectionExists('pam_ldap')){
189 foreach $param ($cfg->Parameters('pam_ldap')){
190 push (@pam_cfg, "$param ".$cfg->val('pam_ldap', $param));
191 }
192 }
193 if ($cfg->SectionExists('nss_ldap')){
194 foreach $param ($cfg->Parameters('nss_ldap')){
195 push (@nss_cfg, "$param ".$cfg->val('nss_ldap', $param));
196 }
197 }
198 if ($cfg->SectionExists('goto')){
199 $goto_admin= $cfg->val('goto', 'terminal_admin');
200 $goto_secret= $cfg->val('goto', 'terminal_secret');
201 } else {
202 $goto_admin= undef;
203 $goto_secret= undef;
204 }
206 }
208 #=== FUNCTION ================================================================
209 # NAME: get_interface_for_ip
210 # PARAMETERS: ip address (i.e. 192.168.0.1)
211 # RETURNS: array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
212 # DESCRIPTION: Uses proc fs (/proc/net/dev) to get list of interfaces.
213 #===============================================================================
214 sub get_interface_for_ip {
215 my $result;
216 my $ip= shift;
217 if ($ip && length($ip) > 0) {
218 my @ifs= &get_interfaces();
219 if($ip eq "0.0.0.0") {
220 $result = "all";
221 } else {
222 foreach (@ifs) {
223 my $if=$_;
224 if(get_ip($if) eq $ip) {
225 $result = $if;
226 }
227 }
228 }
229 }
230 return $result;
231 }
233 #=== FUNCTION ================================================================
234 # NAME: get_interfaces
235 # PARAMETERS: none
236 # RETURNS: (list of interfaces)
237 # DESCRIPTION: Uses proc fs (/proc/net/dev) to get list of interfaces.
238 #===============================================================================
239 sub get_interfaces {
240 my @result;
241 my $PROC_NET_DEV= ('/proc/net/dev');
243 open(PROC_NET_DEV, "<$PROC_NET_DEV")
244 or die "Could not open $PROC_NET_DEV";
246 my @ifs = <PROC_NET_DEV>;
248 close(PROC_NET_DEV);
250 # Eat first two line
251 shift @ifs;
252 shift @ifs;
254 chomp @ifs;
255 foreach my $line(@ifs) {
256 my $if= (split /:/, $line)[0];
257 $if =~ s/^\s+//;
258 push @result, $if;
259 }
261 return @result;
262 }
264 #=== FUNCTION ================================================================
265 # NAME: get_mac
266 # PARAMETERS: interface name (i.e. eth0)
267 # RETURNS: (mac address)
268 # DESCRIPTION: Uses ioctl to get mac address directly from system.
269 #===============================================================================
270 sub get_mac {
271 my $ifreq= shift;
272 my $result;
273 if ($ifreq && length($ifreq) > 0) {
274 if($ifreq eq "all") {
275 $result = "00:00:00:00:00:00";
276 } else {
277 my $SIOCGIFHWADDR= 0x8927; # man 2 ioctl_list
279 # A configured MAC Address should always override a guessed value
280 if ($server_mac_address and length($server_mac_address) > 0) {
281 $result= $server_mac_address;
282 }
284 socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip')
285 or die "socket: $!";
287 if(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) {
288 my ($if, $mac)= unpack 'h36 H12', $ifreq;
290 if (length($mac) > 0) {
291 $mac=~ m/^([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$/;
292 $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6);
293 $result = $mac;
294 }
295 }
296 }
297 }
298 return $result;
299 }
302 #=== FUNCTION ================================================================
303 # NAME: register_at_bus
304 # PARAMETERS: nothing
305 # RETURNS: nothing
306 # DESCRIPTION: creates an entry in known_daemons and send a 'here_i_am' msg to bus
307 #===============================================================================
308 sub register_at_bus {
310 # add bus to known_server_db
311 my $res = $main::known_server_db->add_dbentry( {table=>'known_server',
312 primkey=>'hostname',
313 hostname=>$bus_address,
314 status=>'bus',
315 hostkey=>$bus_key,
316 timestamp=>&get_time,
317 } );
318 my $msg_hash = &create_xml_hash("here_i_am", $server_address, $bus_address);
319 my $msg = &create_xml_string($msg_hash);
321 &main::send_msg_to_target($msg, $bus_address, $bus_key, "here_i_am");
322 return $msg;
323 }
326 #=== FUNCTION ================================================================
327 # NAME: process_incoming_msg
328 # PARAMETERS: crypted_msg - string - incoming crypted message
329 # RETURNS: nothing
330 # DESCRIPTION: handels the proceeded distribution to the appropriated functions
331 #===============================================================================
332 sub process_incoming_msg {
333 my ($msg, $msg_hash, $remote_ip) = @_ ;
334 my $error = 0;
335 my $host_name;
336 my $host_key;
337 my @out_msg_l;
339 # process incoming msg
340 my $header = @{$msg_hash->{header}}[0];
341 my @target_l = @{$msg_hash->{target}};
343 &main::daemon_log("SIPackages: msg to process: $header", 3);
344 &main::daemon_log("$msg", 8);
346 if( 0 == length @target_l){
347 &main::daemon_log("ERROR: no target specified for msg $header", 1);
348 $error++;
349 }
351 if( 1 == length @target_l) {
352 my $target = $target_l[0];
353 if(&server_matches($target)) {
354 if ($header eq 'new_key') {
355 @out_msg_l = &new_key($msg_hash)
356 } elsif ($header eq 'here_i_am') {
357 @out_msg_l = &here_i_am($msg_hash)
358 } elsif ($header eq 'who_has') {
359 @out_msg_l = &who_has($msg_hash)
360 } elsif ($header eq 'who_has_i_do') {
361 @out_msg_l = &who_has_i_do($msg_hash)
362 } elsif ($header eq 'got_ping') {
363 @out_msg_l = &got_ping($msg_hash)
364 } elsif ($header eq 'get_load') {
365 @out_msg_l = &execute_actions($msg_hash)
366 } elsif ($header eq 'detected_hardware') {
367 @out_msg_l = &process_detected_hardware($msg_hash)
368 } elsif ($header eq 'trigger_wake') {
369 foreach (@{$msg_hash->{macAddress}}){
370 &main::daemon_log("SIPackages: trigger wake for $_", 1);
371 do_wake($_);
372 }
374 } else {
375 &main::daemon_log("ERROR: $header is an unknown core function", 1);
376 $error++;
377 }
378 }
379 else {
380 &main::daemon_log("msg is not for gosa-si-server '$server_address', deliver it to target '$target'", 5);
381 push(@out_msg_l, $msg);
382 }
383 }
385 # if( $error == 0) {
386 # if( 0 == @out_msg_l ) {
387 # push(@out_msg_l, $msg);
388 # }
389 # }
391 return \@out_msg_l;
392 }
395 #=== FUNCTION ================================================================
396 # NAME: got_ping
397 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
398 # RETURNS: nothing
399 # DESCRIPTION: process this incoming message
400 #===============================================================================
401 sub got_ping {
402 my ($msg_hash) = @_;
404 my $source = @{$msg_hash->{source}}[0];
405 my $target = @{$msg_hash->{target}}[0];
406 my $header = @{$msg_hash->{header}}[0];
408 if(exists $main::known_daemons->{$source}) {
409 &main::add_content2known_daemons(hostname=>$source, status=>$header);
410 } else {
411 &main::add_content2known_clients(hostname=>$source, status=>$header);
412 }
414 return;
415 }
418 #=== FUNCTION ================================================================
419 # NAME: new_passwd
420 # PARAMETERS: msg_hash - ref - hash from function create_xml_hash
421 # RETURNS: nothing
422 # DESCRIPTION: process this incoming message
423 #===============================================================================
424 sub new_key {
425 my ($msg_hash) = @_;
426 my @out_msg_l;
428 my $header = @{$msg_hash->{header}}[0];
429 my $source_name = @{$msg_hash->{source}}[0];
430 my $source_key = @{$msg_hash->{new_key}}[0];
431 my $query_res;
433 # check known_clients_db
434 my $sql_statement = "SELECT * FROM known_clients WHERE hostname='$source_name'";
435 $query_res = $main::known_clients_db->select_dbentry( $sql_statement );
436 if( 1 == keys %{$query_res} ) {
437 my $act_time = &get_time;
438 my $sql_statement= "UPDATE known_clients ".
439 "SET hostkey='$source_key', timestamp='$act_time' ".
440 "WHERE hostname='$source_name'";
441 my $res = $main::known_clients_db->update_dbentry( $sql_statement );
442 my $hash = &create_xml_hash("confirm_new_key", $server_address, $source_name);
443 my $out_msg = &create_xml_string($hash);
444 push(@out_msg_l, $out_msg);
445 }
447 # only do if host still not found
448 if( 0 == @out_msg_l ) {
449 # check known_server_db
450 $sql_statement = "SELECT * FROM known_server WHERE hostname='$source_name'";
451 $query_res = $main::known_server_db->select_dbentry( $sql_statement );
452 if( 1 == keys %{$query_res} ) {
453 my $act_time = &get_time;
454 my $sql_statement= "UPDATE known_server ".
455 "SET hostkey='$source_key', timestamp='$act_time' ".
456 "WHERE hostname='$source_name'";
457 my $res = $main::known_server_db->update_dbentry( $sql_statement );
459 my $hash = &create_xml_hash("confirm_new_key", $server_address, $source_name);
460 my $out_msg = &create_xml_string($hash);
461 push(@out_msg_l, $out_msg);
462 }
463 }
465 return @out_msg_l;
466 }
469 #=== FUNCTION ================================================================
470 # NAME: here_i_am
471 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
472 # RETURNS: nothing
473 # DESCRIPTION: process this incoming message
474 #===============================================================================
475 sub here_i_am {
476 my ($msg_hash) = @_;
477 my @out_msg_l;
478 my $out_hash;
480 my $source = @{$msg_hash->{source}}[0];
481 my $mac_address = @{$msg_hash->{mac_address}}[0];
482 my $gotoHardwareChecksum = @{$msg_hash->{gotoHardwareChecksum}}[0];
484 # number of known clients
485 my $nu_clients= $main::known_clients_db->count_dbentries('known_clients');
487 # check wether client address or mac address is already known
488 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$source'";
489 my $db_res= $main::known_clients_db->select_dbentry( $sql_statement );
491 if ( 1 == keys %{$db_res} ) {
492 &main::daemon_log("WARNING: $source is already known as a client", 1);
493 &main::daemon_log("WARNING: values for $source are being overwritten", 1);
494 $nu_clients --;
495 }
497 # number of actual activ clients
498 my $act_nu_clients = $nu_clients;
500 &main::daemon_log("number of actual activ clients: $act_nu_clients", 5);
501 &main::daemon_log("number of maximal allowed clients: $max_clients", 5);
503 if($max_clients <= $act_nu_clients) {
504 my $out_hash = &create_xml_hash("denied", $server_address, $source);
505 &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
506 my $passwd = @{$msg_hash->{new_passwd}}[0];
507 &send_msg_hash2address($out_hash, $source, $passwd);
508 return;
509 }
511 # new client accepted
512 my $new_passwd = @{$msg_hash->{new_passwd}}[0];
514 # create entry in known_clients
515 my $events = @{$msg_hash->{events}}[0];
518 # add entry to known_clients_db
519 my $act_timestamp = &get_time;
520 my $res = $main::known_clients_db->add_dbentry( {table=>'known_clients',
521 primkey=>'hostname',
522 hostname=>$source,
523 events=>$events,
524 macaddress=>$mac_address,
525 status=>'registered',
526 hostkey=>$new_passwd,
527 timestamp=>$act_timestamp,
528 } );
530 if ($res != 0) {
531 &main::daemon_log("ERROR: cannot add entry to known_clients: $res");
532 return;
533 }
535 # return acknowledgement to client
536 $out_hash = &create_xml_hash("registered", $server_address, $source);
537 my $register_out = &create_xml_string($out_hash);
538 push(@out_msg_l, $register_out);
540 # notify registered client to bus
541 if( $bus_activ eq "on") {
542 # fetch actual bus key
543 my $sql_statement= "SELECT * FROM known_server WHERE status='bus'";
544 my $query_res = $main::known_server_db->select_dbentry( $sql_statement );
545 my $hostkey = $query_res->{1}->{'hostkey'};
547 # send update msg to bus
548 $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
549 &add_content2xml_hash($out_hash, "macaddress", $mac_address);
550 &add_content2xml_hash($out_hash, "timestamp", $act_timestamp);
551 my $new_client_out = &create_xml_string($out_hash);
552 push(@out_msg_l, $new_client_out);
553 &main::daemon_log("send bus msg that client '$source' has registerd at server '$server_address'", 3);
554 }
556 # give the new client his ldap config
557 my $new_ldap_config_out = &new_ldap_config($source);
558 if( $new_ldap_config_out ) {
559 push(@out_msg_l, $new_ldap_config_out);
560 }
562 my $hardware_config_out = &hardware_config($source, $gotoHardwareChecksum);
563 if( $hardware_config_out ) {
564 push(@out_msg_l, $hardware_config_out);
565 }
567 return @out_msg_l;
568 }
571 #=== FUNCTION ================================================================
572 # NAME: who_has
573 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
574 # RETURNS: nothing
575 # DESCRIPTION: process this incoming message
576 #===============================================================================
577 sub who_has {
578 my ($msg_hash) = @_ ;
579 my @out_msg_l;
581 # what is your search pattern
582 my $search_pattern = @{$msg_hash->{who_has}}[0];
583 my $search_element = @{$msg_hash->{$search_pattern}}[0];
584 &main::daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
586 # scanning known_clients for search_pattern
587 my @host_addresses = keys %$main::known_clients;
588 my $known_clients_entries = length @host_addresses;
589 my $host_address;
590 foreach my $host (@host_addresses) {
591 my $client_element = $main::known_clients->{$host}->{$search_pattern};
592 if ($search_element eq $client_element) {
593 $host_address = $host;
594 last;
595 }
596 }
598 # search was successful
599 if (defined $host_address) {
600 my $source = @{$msg_hash->{source}}[0];
601 my $out_hash = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
602 &add_content2xml_hash($out_hash, "mac_address", $search_element);
603 my $out_msg = &create_xml_string($out_hash);
604 push(@out_msg_l, $out_msg);
605 }
606 return @out_msg_l;
607 }
610 sub who_has_i_do {
611 my ($msg_hash) = @_ ;
612 my $header = @{$msg_hash->{header}}[0];
613 my $source = @{$msg_hash->{source}}[0];
614 my $search_param = @{$msg_hash->{$header}}[0];
615 my $search_value = @{$msg_hash->{$search_param}}[0];
616 print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
617 }
619 #=== FUNCTION ================================================================
620 # NAME: new_ldap_config
621 # PARAMETERS: address - string - ip address and port of a host
622 # RETURNS: nothing
623 # DESCRIPTION: send to address the ldap configuration found for dn gotoLdapServer
624 #===============================================================================
625 sub new_ldap_config {
626 my ($address) = @_ ;
628 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$address'";
629 my $res = $main::known_clients_db->select_dbentry( $sql_statement );
631 # check hit
632 my $hit_counter = keys %{$res};
633 if( not $hit_counter == 1 ) {
634 &main::daemon_log("ERROR: more or no hit found in known_clients_db by query by '$address'", 1);
635 }
637 my $macaddress = $res->{1}->{macaddress};
638 my $hostkey = $res->{1}->{hostkey};
640 if (not defined $macaddress) {
641 &main::daemon_log("ERROR: no mac address found for client $address", 1);
642 return;
643 }
645 # Build LDAP connection
646 my $ldap = Net::LDAP->new($ldap_uri);
647 if( not defined $ldap ) {
648 &main::daemon_log("ERROR: cannot connect to ldap: $ldap_uri", 1);
649 return;
650 }
653 # Bind to a directory with dn and password
654 my $mesg= $ldap->bind($ldap_admin_dn, password => $ldap_admin_password);
656 # Perform search
657 $mesg = $ldap->search( base => $ldap_base,
658 scope => 'sub',
659 attrs => ['dn', 'gotoLdapServer', 'gosaUnitTag'],
660 filter => "(&(objectClass=GOhard)(macaddress=$macaddress))");
661 #$mesg->code && die $mesg->error;
662 if($mesg->code) {
663 &main::daemon_log($mesg->error, 1);
664 return;
665 }
667 # Sanity check
668 if ($mesg->count != 1) {
669 &main::daemon_log("WARNING: client mac address $macaddress not found/not unique in ldap search", 1);
670 &main::daemon_log("\tbase: $ldap_base", 1);
671 &main::daemon_log("\tscope: sub", 1);
672 &main::daemon_log("\tattrs: dn, gotoLdapServer", 1);
673 &main::daemon_log("\tfilter: (&(objectClass=GOhard)(macaddress=$macaddress))", 1);
674 return;
675 }
677 my $entry= $mesg->entry(0);
678 my $dn= $entry->dn;
679 my @servers= $entry->get_value("gotoLdapServer");
680 my $unit_tag= $entry->get_value("gosaUnitTag");
681 my @ldap_uris;
682 my $server;
683 my $base;
685 # Do we need to look at an object class?
686 if (length(@servers) < 1){
687 $mesg = $ldap->search( base => $ldap_base,
688 scope => 'sub',
689 attrs => ['dn', 'gotoLdapServer'],
690 filter => "(&(objectClass=gosaGroupOfNames)(member=$dn))");
691 #$mesg->code && die $mesg->error;
692 if($mesg->code) {
693 &main::daemon_log($mesg->error, 1);
694 return;
695 }
697 # Sanity check
698 if ($mesg->count != 1) {
699 &main::daemon_log("WARNING: no LDAP information found for client mac $macaddress", 1);
700 return;
701 }
703 $entry= $mesg->entry(0);
704 $dn= $entry->dn;
705 @servers= $entry->get_value("gotoLdapServer");
706 }
708 @servers= sort (@servers);
710 foreach $server (@servers){
711 $base= $server;
712 $server =~ s%^[^:]+:[^:]+:(ldap.*://[^/]+)/.*$%$1%;
713 $base =~ s%^[^:]+:[^:]+:ldap.*://[^/]+/(.*)$%$1%;
714 push (@ldap_uris, $server);
715 }
717 # Assemble data package
718 my %data = ( 'ldap_uri' => \@ldap_uris, 'ldap_base' => $base,
719 'ldap_cfg' => \@ldap_cfg, 'pam_cfg' => \@pam_cfg,'nss_cfg' => \@nss_cfg );
721 # Need to append GOto settings?
722 if (defined $goto_admin and defined $goto_secret){
723 $data{'goto_admin'}= $goto_admin;
724 $data{'goto_secret'}= $goto_secret;
725 }
727 # Append unit tag if needed
728 if (defined $unit_tag){
730 # Find admin base and department name
731 $mesg = $ldap->search( base => $ldap_base,
732 scope => 'sub',
733 attrs => ['dn', 'ou', 'FAIclass'],
734 filter => "(&(objectClass=gosaAdministrativeUnit)(gosaUnitTag=$unit_tag))");
735 #$mesg->code && die $mesg->error;
736 if($mesg->code) {
737 &main::daemon_log($mesg->error, 1);
738 return;
739 }
741 # Sanity check
742 if ($mesg->count != 1) {
743 &main::daemon_log("WARNING: cannot find administrative unit for client with tag $unit_tag", 1);
744 return;
745 }
747 $entry= $mesg->entry(0);
748 $data{'admin_base'}= $entry->dn;
749 $data{'department'}= $entry->get_value("ou");
751 # Append unit Tag
752 $data{'unit_tag'}= $unit_tag;
753 }
755 # Fill release if available
756 my $FAIclass= $entry->get_value("FAIclass");
757 if (defined $FAIclass && $FAIclass =~ /^.* :([A-Za-z0-9\/.]+).*$/) {
758 $data{'release'}= $1;
759 }
762 # Unbind
763 $mesg = $ldap->unbind;
765 # Send information
766 return send_msg("new_ldap_config", $server_address, $address, \%data);
767 }
769 sub process_detected_hardware {
770 my $msg_hash = shift;
771 my $address = $msg_hash->{source}[0];
772 my $gotoHardwareChecksum= $msg_hash->{detected_hardware}[0]->{gotoHardwareChecksum};
774 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$address'";
775 my $res = $main::known_clients_db->select_dbentry( $sql_statement );
777 # check hit
778 my $hit_counter = keys %{$res};
779 if( not $hit_counter == 1 ) {
780 &main::daemon_log("ERROR: more or no hit found in known_clients_db by query by '$address'", 1);
781 return;
782 }
784 my $macaddress = $res->{1}->{macaddress};
785 my $hostkey = $res->{1}->{hostkey};
787 if (not defined $macaddress) {
788 &main::daemon_log("ERROR: no mac address found for client $address", 1);
789 return;
790 }
791 # Build LDAP connection
792 my $ldap = Net::LDAP->new($ldap_uri);
793 if( not defined $ldap ) {
794 &main::daemon_log("ERROR: cannot connect to ldap: $ldap_uri", 1);
795 return;
796 }
798 # Bind to a directory with dn and password
799 my $mesg= $ldap->bind($ldap_admin_dn, password => $ldap_admin_password);
801 # Perform search
802 $mesg = $ldap->search(
803 base => $ldap_base,
804 scope => 'sub',
805 filter => "(&(objectClass=GOhard)(|(macAddress=$macaddress)(dhcpHWaddress=ethernet $macaddress)))"
806 );
808 # We need to create a base entry first (if not done from ArpHandler)
809 if($mesg->count == 0) {
810 &main::daemon_log("Need to create a new LDAP Entry for client $address", 1);
811 my $resolver=Net::DNS::Resolver->new;
812 my $ipaddress= $1 if $address =~ /^([0-9\.]*?):.*$/;
813 my $dnsresult= $resolver->search($ipaddress);
814 my $dnsname= (defined($dnsresult))?$dnsresult->{answer}[0]->{ptrdname}:$ipaddress;
815 my $cn = (($dnsname =~ /^(\d){1,3}\.(\d){1,3}\.(\d){1,3}\.(\d){1,3}/) ? $dnsname : sprintf "%s", $dnsname =~ /([^\.]+)\.?/);
816 my $dn = "cn=$cn,ou=incoming,$ldap_base";
817 &main::daemon_log("Creating entry for $dn",6);
818 my $entry= Net::LDAP::Entry->new( $dn );
819 $entry->dn($dn);
820 $entry->add("objectClass" => "goHard");
821 $entry->add("cn" => $cn);
822 $entry->add("macAddress" => $macaddress);
823 $entry->add("gotomode" => "locked");
824 $entry->add("gotoSysStatus" => "new-system");
825 $entry->add("ipHostNumber" => $ipaddress);
826 if(my $res=$entry->update($ldap)) {
827 # Fill $mesg again
828 $mesg = $ldap->search(
829 base => $ldap_base,
830 scope => 'sub',
831 filter => "(&(objectClass=GOhard)(|(macAddress=$macaddress)(dhcpHWaddress=ethernet $macaddress)))"
832 );
833 } else {
834 &main::daemon_log("ERROR: There was a problem adding the entry", 1);
835 }
837 }
839 if($mesg->count == 1) {
840 my $entry= $mesg->entry(0);
841 $entry->changetype("modify");
842 foreach my $attribute (
843 "gotoSndModule", "ghNetNic", "gotoXResolution", "ghSoundAdapter", "ghCpuType", "gotoXkbModel",
844 "ghGfxAdapter", "gotoXMousePort", "ghMemSize", "gotoXMouseType", "ghUsbSupport", "gotoXHsync",
845 "gotoXDriver", "gotoXVsync", "gotoXMonitor", "gotoHardwareChecksum") {
846 if(defined($msg_hash->{detected_hardware}[0]->{$attribute})) {
847 if(defined($entry->get_value($attribute))) {
848 $entry->delete($attribute);
849 }
850 &main::daemon_log("Adding attribute $attribute with value ".$msg_hash->{detected_hardware}[0]->{$attribute},1);
851 $entry->add($attribute => $msg_hash->{detected_hardware}[0]->{$attribute});
852 }
853 }
854 foreach my $attribute (
855 "gotoModules", "ghScsiDev", "ghIdeDev") {
856 if(defined($msg_hash->{detected_hardware}[0]->{$attribute})) {
857 if(defined($entry->get_value($attribute))) {
858 $entry->delete($attribute);
859 }
860 foreach my $array_entry (@{$msg_hash->{detected_hardware}[0]->{$attribute}}) {
861 $entry->add($attribute => $array_entry);
862 }
863 }
865 }
867 if($entry->update($ldap)) {
868 &main::daemon_log("Added Hardware configuration to LDAP", 4);
869 }
871 }
872 return;
873 }
874 #=== FUNCTION ================================================================
875 # NAME: hardware_config
876 # PARAMETERS: address - string - ip address and port of a host
877 # RETURNS:
878 # DESCRIPTION:
879 #===============================================================================
880 sub hardware_config {
881 my ($address, $gotoHardwareChecksum) = @_ ;
883 my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$address'";
884 my $res = $main::known_clients_db->select_dbentry( $sql_statement );
886 # check hit
887 my $hit_counter = keys %{$res};
888 if( not $hit_counter == 1 ) {
889 &main::daemon_log("ERROR: more or no hit found in known_clients_db by query by '$address'", 1);
890 }
892 my $macaddress = $res->{1}->{macaddress};
893 my $hostkey = $res->{1}->{hostkey};
895 if (not defined $macaddress) {
896 &main::daemon_log("ERROR: no mac address found for client $address", 1);
897 return;
898 }
900 # Build LDAP connection
901 my $ldap = Net::LDAP->new($ldap_uri);
902 if( not defined $ldap ) {
903 &main::daemon_log("ERROR: cannot connect to ldap: $ldap_uri", 1);
904 return;
905 }
907 # Bind to a directory with dn and password
908 my $mesg= $ldap->bind($ldap_admin_dn, password => $ldap_admin_password);
910 # Perform search
911 $mesg = $ldap->search(
912 base => $ldap_base,
913 scope => 'sub',
914 filter => "(&(objectClass=GOhard)(|(macAddress=$macaddress)(dhcpHWaddress=ethernet $macaddress)))"
915 );
917 if($mesg->count() == 0) {
918 &main::daemon_log("Host was not found in LDAP!", 1);
919 } else {
920 my $entry= $mesg->entry(0);
921 my $dn= $entry->dn;
922 if(defined($entry->get_value("gotoHardwareChecksum"))) {
923 if(! $entry->get_value("gotoHardwareChecksum") eq $gotoHardwareChecksum) {
924 $entry->replace(gotoHardwareChecksum => $gotoHardwareChecksum);
925 if($entry->update($ldap)) {
926 &main::daemon_log("Hardware changed! Detection triggered.", 4);
927 }
928 } else {
929 # Nothing to do
930 return;
931 }
932 }
933 }
934 # need to fill it to LDAP
935 #$entry->add(gotoHardwareChecksum => $gotoHardwareChecksum);
936 #if($entry->update($ldap)) {
937 # &main::daemon_log("gotoHardwareChecksum $gotoHardwareChecksum was added to LDAP", 4);
938 #}
940 ## Look if there another host with this checksum to use the hardware config
941 #$mesg = $ldap->search(
942 # base => $ldap_base,
943 # scope => 'sub',
944 # filter => "(&(objectClass=GOhard)(gotoHardwareChecksum=$gotoHardwareChecksum))"
945 #);
947 #if($mesg->count>1) {
948 # my $clone_entry= $mesg->entry(0);
949 # $entry->changetype("modify");
950 # foreach my $attribute (
951 # "gotoSndModule", "ghNetNic", "gotoXResolution", "ghSoundAdapter", "ghCpuType", "gotoXkbModel",
952 # "ghGfxAdapter", "gotoXMousePort", "ghMemSize", "gotoXMouseType", "ghUsbSupport", "gotoXHsync",
953 # "gotoXDriver", "gotoXVsync", "gotoXMonitor") {
954 # my $value= $clone_entry->get_value($attribute);
955 # if(defined($value)) {
956 # if(defined($entry->get_value($attribute))) {
957 # $entry->delete($attribute);
958 # }
959 # &main::daemon_log("Adding attribute $attribute with value $value",1);
960 # $entry->add($attribute => $value);
961 # }
962 # }
963 # foreach my $attribute (
964 # "gotoModules", "ghScsiDev", "ghIdeDev") {
965 # my $array= $clone_entry->get_value($attribute, 'as_ref' => 1);
966 # if(defined($array)) {
967 # if(defined($entry->get_value($attribute))) {
968 # $entry->delete($attribute);
969 # }
970 # foreach my $array_entry (@{$array}) {
971 # $entry->add($attribute => $array_entry);
972 # }
973 # }
975 # }
976 # if($entry->update($ldap)) {
977 # &main::daemon_log("Added Hardware configuration to LDAP", 4);
978 # }
980 #}
983 # Assemble data package
984 my %data = ();
986 # Need to append GOto settings?
987 if (defined $goto_admin and defined $goto_secret){
988 $data{'goto_admin'}= $goto_admin;
989 $data{'goto_secret'}= $goto_secret;
990 }
992 # Unbind
993 $mesg = $ldap->unbind;
995 &main::daemon_log("Send detect_hardware message to $address", 4);
997 # Send information
998 return send_msg("detect_hardware", $server_address, $address, \%data);
999 }
1001 sub server_matches {
1002 my $target = shift;
1003 my $target_ip = sprintf("%s", $target =~ /^([0-9\.]*?):.*$/);
1004 my $result = 0;
1006 if($server_ip eq $target_ip) {
1007 $result= 1;
1008 } elsif ($server_ip eq "0.0.0.0") {
1009 if ($target_ip eq "127.0.0.1") {
1010 $result= 1;
1011 } else {
1012 my $PROC_NET_ROUTE= ('/proc/net/route');
1014 open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
1015 or die "Could not open $PROC_NET_ROUTE";
1017 my @ifs = <PROC_NET_ROUTE>;
1019 close(PROC_NET_ROUTE);
1021 # Eat header line
1022 shift @ifs;
1023 chomp @ifs;
1024 foreach my $line(@ifs) {
1025 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
1026 my $destination;
1027 my $mask;
1028 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
1029 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
1030 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
1031 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
1032 if(new NetAddr::IP($target_ip)->within(new NetAddr::IP($destination, $mask))) {
1033 # destination matches route, save mac and exit
1034 $result= 1;
1035 last;
1036 }
1037 }
1038 }
1039 } else {
1040 &main::daemon_log("Target ip $target_ip does not match Server ip $server_ip",1);
1041 }
1043 return $result;
1044 }
1047 #=== FUNCTION ================================================================
1048 # NAME: execute_actions
1049 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
1050 # RETURNS: nothing
1051 # DESCRIPTION: invokes the script specified in msg_hash which is located under
1052 # /etc/gosad/actions
1053 #===============================================================================
1054 sub execute_actions {
1055 my ($msg_hash) = @_ ;
1056 my $configdir= '/etc/gosad/actions/';
1057 my $result;
1059 my $header = @{$msg_hash->{header}}[0];
1060 my $source = @{$msg_hash->{source}}[0];
1061 my $target = @{$msg_hash->{target}}[0];
1063 if((not defined $source)
1064 && (not defined $target)
1065 && (not defined $header)) {
1066 &main::daemon_log("ERROR: Entries missing in XML msg for gosad actions under /etc/gosad/actions");
1067 } else {
1068 my $parameters="";
1069 my @params = @{$msg_hash->{$header}};
1070 my $params = join(", ", @params);
1071 &main::daemon_log("execute_actions: got parameters: $params", 5);
1073 if (@params) {
1074 foreach my $param (@params) {
1075 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
1076 &main::daemon_log("execute_actions: parameter -> value: $param -> $param_value", 7);
1077 $parameters.= " ".$param_value;
1078 }
1079 }
1081 my $cmd= $configdir.$header."$parameters";
1082 &main::daemon_log("execute_actions: executing cmd: $cmd", 7);
1083 $result= "";
1084 open(PIPE, "$cmd 2>&1 |");
1085 while(<PIPE>) {
1086 $result.=$_;
1087 }
1088 close(PIPE);
1089 }
1091 # process the event result
1094 return;
1095 }
1098 1;