Code

Updated
[gosa.git] / gosa-si / modules / SIPackages.pm
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;
20 use utf8;
22 my $event_dir = "/usr/lib/gosa-si/server/events";
23 use lib "/usr/lib/gosa-si/server/events";
25 BEGIN{}
26 END {}
28 my ($server_ip, $server_mac_address, $server_port, $SIPackages_key, $max_clients, $ldap_uri, $ldap_base, $ldap_admin_dn, $ldap_admin_password, $server_interface);
29 my ($bus_activ, $bus_key, $bus_ip, $bus_port);
30 my $server;
31 my $event_hash;
32 my $network_interface;
33 my $no_bus;
34 my (@ldap_cfg, @pam_cfg, @nss_cfg, $goto_admin, $goto_secret);
35 my $mesg;
37 my %cfg_defaults = (
38 "bus" => {
39     "activ" => [\$bus_activ, "on"],
40     "key" => [\$bus_key, ""],
41     "ip" => [\$bus_ip, ""],
42     "port" => [\$bus_port, "20080"],
43     },
44 "server" => {
45     "ip" => [\$server_ip, "0.0.0.0"],
46     "mac-address" => [\$server_mac_address, "00:00:00:00:00"],
47     "port" => [\$server_port, "20081"],
48     "ldap-uri" => [\$ldap_uri, ""],
49     "ldap-base" => [\$ldap_base, ""],
50     "ldap-admin-dn" => [\$ldap_admin_dn, ""],
51     "ldap-admin-password" => [\$ldap_admin_password, ""],
52     "max-clients" => [\$max_clients, 100],
53     },
54 "SIPackages" => {
55     "key" => [\$SIPackages_key, ""],
56     },
57 );
59 ### START #####################################################################
61 # read configfile and import variables
62 &read_configfile();
65 # if server_ip is not an ip address but a name
66 if( inet_aton($server_ip) ){ $server_ip = inet_ntoa(inet_aton($server_ip)); } 
67 $network_interface= &get_interface_for_ip($server_ip);
68 $server_mac_address= &get_mac($network_interface);
70 &import_events();
72 # Unit tag can be defined in config
73 if((not defined($main::gosa_unit_tag)) || length($main::gosa_unit_tag) == 0) {
74         # Read gosaUnitTag from LDAP
75   &main::refresh_ldap_handle();
76         if( defined($main::ldap_handle) ) {
77                 &main::daemon_log("INFO: Searching for servers gosaUnitTag with mac address $server_mac_address",5);
78                 # Perform search for Unit Tag
79                 $mesg = $main::ldap_handle->search(
80                         base   => $ldap_base,
81                         scope  => 'sub',
82                         attrs  => ['gosaUnitTag'],
83                         filter => "(macaddress=$server_mac_address)"
84                 );
86                 if ($mesg->count == 1) {
87                         my $entry= $mesg->entry(0);
88                         my $unit_tag= $entry->get_value("gosaUnitTag");
89                         if(defined($unit_tag) && length($unit_tag) > 0) {
90                                 &main::daemon_log("INFO: Detected gosaUnitTag $unit_tag for creating entries", 5);
91                                 $main::gosa_unit_tag= $unit_tag;
92                         }
93                 } else {
94                         # Perform another search for Unit Tag
95                         my $hostname= `hostname -f`;
96                         chomp($hostname);
97                         &main::daemon_log("INFO: Searching for servers gosaUnitTag with hostname $hostname",5);
98                         $mesg = $main::ldap_handle->search(
99                                 base   => $ldap_base,
100                                 scope  => 'sub',
101                                 attrs  => ['gosaUnitTag'],
102                                 filter => "(&(cn=$hostname)(objectClass=goServer))"
103                         );
104                         if ($mesg->count == 1) {
105                                 my $entry= $mesg->entry(0);
106                                 my $unit_tag= $entry->get_value("gosaUnitTag");
107                                 if(defined($unit_tag) && length($unit_tag) > 0) {
108                                         &main::daemon_log("INFO: Detected gosaUnitTag $unit_tag for creating entries", 5);
109                                         $main::gosa_unit_tag= $unit_tag;
110                                 }
111                         } else {
112                                 # Perform another search for Unit Tag
113                                 $hostname= `hostname -s`;
114                                 chomp($hostname);
115                                 &main::daemon_log("INFO: Searching for servers gosaUnitTag with hostname $hostname",5);
116                                 $mesg = $main::ldap_handle->search(
117                                         base   => $ldap_base,
118                                         scope  => 'sub',
119                                         attrs  => ['gosaUnitTag'],
120                                         filter => "(&(cn=$hostname)(objectClass=goServer))"
121                                 );
122                                 if ($mesg->count == 1) {
123                                         my $entry= $mesg->entry(0);
124                                         my $unit_tag= $entry->get_value("gosaUnitTag");
125                                         if(defined($unit_tag) && length($unit_tag) > 0) {
126                                                 &main::daemon_log("INFO: Detected gosaUnitTag $unit_tag for creating entries", 5);
127                                                 $main::gosa_unit_tag= $unit_tag;
128                                         }
129                                 } else {
130                                         &main::daemon_log("WARNING: No gosaUnitTag detected. Not using gosaUnitTag", 3);
131                                 }
132                         }
133                 }
134         } else {
135                 &main::daemon_log("INFO: Using gosaUnitTag from config-file: $main::gosa_unit_tag",5);
136         }
140 my $server_address = "$server_ip:$server_port";
141 $main::server_address = $server_address;
144 if( inet_aton($bus_ip) ){ $bus_ip = inet_ntoa(inet_aton($bus_ip)); } 
145 ######################################################
146 # to change
147 if( $bus_ip eq "127.0.1.1" ) { $bus_ip = "127.0.0.1" }
148 ######################################################
149 my $bus_address = "$bus_ip:$bus_port";
150 $main::bus_address = $bus_address;
152 # create general settings for this module
153 my $xml = new XML::Simple();
155 # register at bus
156 if ($main::no_bus > 0) {
157     $bus_activ = "off"
159 if($bus_activ eq "on") {
160     &register_at_bus();
163 # add myself to known_server_db
164 my $res = $main::known_server_db->add_dbentry( {table=>'known_server',
165         primkey=>['hostname'],
166         hostname=>$server_address,
167         status=>'myself',
168         hostkey=>$SIPackages_key,
169         timestamp=>&get_time,
170         } );
174 ### functions #################################################################
177 sub get_module_info {
178     my @info = ($server_address,
179                 $SIPackages_key,
180                 );
181     return \@info;
185 #===  FUNCTION  ================================================================
186 #         NAME:  read_configfile
187 #   PARAMETERS:  cfg_file - string -
188 #      RETURNS:  nothing
189 #  DESCRIPTION:  read cfg_file and set variables
190 #===============================================================================
191 sub read_configfile {
192     my $cfg;
193     if( defined( $main::cfg_file) && ( length($main::cfg_file) > 0 )) {
194         if( -r $main::cfg_file ) {
195             $cfg = Config::IniFiles->new( -file => $main::cfg_file );
196         } else {
197             print STDERR "Couldn't read config file!";
198         }
199     } else {
200         $cfg = Config::IniFiles->new() ;
201     }
202     foreach my $section (keys %cfg_defaults) {
203         foreach my $param (keys %{$cfg_defaults{ $section }}) {
204             my $pinfo = $cfg_defaults{ $section }{ $param };
205             ${@$pinfo[0]} = $cfg->val( $section, $param, @$pinfo[1] );
206         }
207     }
209     # Read non predefined sections
210     my $param;
211     if ($cfg->SectionExists('ldap')){
212                 foreach $param ($cfg->Parameters('ldap')){
213                         push (@ldap_cfg, "$param ".$cfg->val('ldap', $param));
214                 }
215     }
216     if ($cfg->SectionExists('pam_ldap')){
217                 foreach $param ($cfg->Parameters('pam_ldap')){
218                         push (@pam_cfg, "$param ".$cfg->val('pam_ldap', $param));
219                 }
220     }
221     if ($cfg->SectionExists('nss_ldap')){
222                 foreach $param ($cfg->Parameters('nss_ldap')){
223                         push (@nss_cfg, "$param ".$cfg->val('nss_ldap', $param));
224                 }
225     }
226     if ($cfg->SectionExists('goto')){
227         $goto_admin= $cfg->val('goto', 'terminal_admin');
228         $goto_secret= $cfg->val('goto', 'terminal_secret');
229     } else {
230         $goto_admin= undef;
231         $goto_secret= undef;
232     }
236 #===  FUNCTION  ================================================================
237 #         NAME:  get_interface_for_ip
238 #   PARAMETERS:  ip address (i.e. 192.168.0.1)
239 #      RETURNS:  array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
240 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
241 #===============================================================================
242 sub get_interface_for_ip {
243         my $result;
244         my $ip= shift;
245         if ($ip && length($ip) > 0) {
246                 my @ifs= &get_interfaces();
247                 if($ip eq "0.0.0.0") {
248                         $result = "all";
249                 } else {
250                         foreach (@ifs) {
251                                 my $if=$_;
252                                 if(get_ip($if) eq $ip) {
253                                         $result = $if;
254                                 }
255                         }       
256                 }
257         }       
258         return $result;
261 #===  FUNCTION  ================================================================
262 #         NAME:  get_interfaces 
263 #   PARAMETERS:  none
264 #      RETURNS:  (list of interfaces) 
265 #  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
266 #===============================================================================
267 sub get_interfaces {
268         my @result;
269         my $PROC_NET_DEV= ('/proc/net/dev');
271         open(PROC_NET_DEV, "<$PROC_NET_DEV")
272                 or die "Could not open $PROC_NET_DEV";
274         my @ifs = <PROC_NET_DEV>;
276         close(PROC_NET_DEV);
278         # Eat first two line
279         shift @ifs;
280         shift @ifs;
282         chomp @ifs;
283         foreach my $line(@ifs) {
284                 my $if= (split /:/, $line)[0];
285                 $if =~ s/^\s+//;
286                 push @result, $if;
287         }
289         return @result;
292 #===  FUNCTION  ================================================================
293 #         NAME:  get_mac 
294 #   PARAMETERS:  interface name (i.e. eth0)
295 #      RETURNS:  (mac address) 
296 #  DESCRIPTION:  Uses ioctl to get mac address directly from system.
297 #===============================================================================
298 sub get_mac {
299         my $ifreq= shift;
300         my $result;
301         if ($ifreq && length($ifreq) > 0) { 
302                 if($ifreq eq "all") {
303                         $result = "00:00:00:00:00:00";
304                 } else {
305                         my $SIOCGIFHWADDR= 0x8927;     # man 2 ioctl_list
307                         # A configured MAC Address should always override a guessed value
308                         if ($server_mac_address and length($server_mac_address) > 0) {
309                                 $result= $server_mac_address;
310                         }
312                         socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip')
313                                 or die "socket: $!";
315                         if(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) {
316                                 my ($if, $mac)= unpack 'h36 H12', $ifreq;
318                                 if (length($mac) > 0) {
319                                         $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])$/;
320                                         $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6);
321                                         $result = $mac;
322                                 }
323                         }
324                 }
325         }
326         return $result;
330 #===  FUNCTION  ================================================================
331 #         NAME:  register_at_bus
332 #   PARAMETERS:  nothing
333 #      RETURNS:  nothing
334 #  DESCRIPTION:  creates an entry in known_daemons and send a 'here_i_am' msg to bus
335 #===============================================================================
336 sub register_at_bus {
338     # add bus to known_server_db
339     my $res = $main::known_server_db->add_dbentry( {table=>'known_server',
340                                                     primkey=>['hostname'],
341                                                     hostname=>$bus_address,
342                                                     status=>'bus',
343                                                     hostkey=>$bus_key,
344                                                     timestamp=>&get_time,
345                                                 } );
346     my $msg_hash = &create_xml_hash("here_i_am", $server_address, $bus_address);
347     my $msg = &create_xml_string($msg_hash);
349     &main::send_msg_to_target($msg, $bus_address, $bus_key, "here_i_am");
350     return $msg;
354 sub import_events {
355     if (not -e $event_dir) {
356         &main::daemon_log("ERROR: cannot find directory or directory is not readable: $event_dir", 1);   
357     }
358     opendir (DIR, $event_dir) or die "ERROR while loading gosa-si-events from directory $event_dir : $!\n";
360     while (defined (my $event = readdir (DIR))) {
361         if( $event eq "." || $event eq ".." ) { next; }  
362         if( $event eq "gosaTriggered.pm" ) { next; }    # only GOsa specific events
364         eval{ require $event; };
365         if( $@ ) {
366             &main::daemon_log("import of event module '$event' failed", 1);
367             &main::daemon_log("$@", 8);
368             next;
369         }
371         $event =~ /(\S*?).pm$/;
372         my $event_module = $1;
373         my $events_l = eval( $1."::get_events()") ;
374         foreach my $event_name (@{$events_l}) {
375             $event_hash->{$event_name} = $event_module;
376         }
377         my $events_string = join( ", ", @{$events_l});
378         &main::daemon_log("INFO: SIPackages imported events $events_string", 5);
379     }
383 #===  FUNCTION  ================================================================
384 #         NAME:  process_incoming_msg
385 #   PARAMETERS:  crypted_msg - string - incoming crypted message
386 #      RETURNS:  nothing
387 #  DESCRIPTION:  handels the proceeded distribution to the appropriated functions
388 #===============================================================================
389 sub process_incoming_msg {
390     my ($msg, $msg_hash, $session_id) = @_ ;
391     my $error = 0;
392     my $host_name;
393     my $host_key;
394     my @out_msg_l = ();
396     # process incoming msg
397     my $header = @{$msg_hash->{header}}[0]; 
398     my @target_l = @{$msg_hash->{target}};
400     # skip PREFIX
401     $header =~ s/^CLMSG_//;
403     &main::daemon_log("DEBUG: SIPackages: msg to process: $header", 7);
404     &main::daemon_log("$msg", 8);
406     if( 0 == length @target_l){     
407         &main::daemon_log("ERROR: no target specified for msg $header", 1);
408         $error++;
409     }
411     if( 1 == length @target_l) {
412         my $target = $target_l[0];
413                 if(&server_matches($target)) {
416             if ($header eq 'new_key') {
417                 @out_msg_l = &new_key($msg_hash)
418             } elsif ($header eq 'here_i_am') {
419                 @out_msg_l = &here_i_am($msg_hash)
420             } else {
421                 if( exists $event_hash->{$header} ) {
422                     # a event exists with the header as name
423                     &main::daemon_log("INFO: found event '$header' at event-module '".$event_hash->{$header}."'", 5);
424                     no strict 'refs';
425                     @out_msg_l = &{$event_hash->{$header}."::$header"}($msg, $msg_hash, $session_id);
426                 }
427             }
429             # if delivery not possible raise error and return 
430             if( not @out_msg_l ) {
431                 &main::daemon_log("WARNING: SIPackages got not answer from event handler '$header'", 3);
432             } elsif( 0 == @out_msg_l) {
433                 &main::daemon_log("ERROR: SIPackages: no event handler or core function defined for '$header'", 1);
434             } 
435         }
436                 else {
437                         &main::daemon_log("INFO: msg is not for gosa-si-server '$server_address', deliver it to target '$target'", 5);
438                         push(@out_msg_l, $msg);
439                 }
440     }
442     return \@out_msg_l;
446 #===  FUNCTION  ================================================================
447 #         NAME:  new_passwd
448 #   PARAMETERS:  msg_hash - ref - hash from function create_xml_hash
449 #      RETURNS:  nothing
450 #  DESCRIPTION:  process this incoming message
451 #===============================================================================
452 sub new_key {
453     my ($msg_hash) = @_;
454     my @out_msg_l;
455     
456     my $header = @{$msg_hash->{header}}[0];
457     my $source_name = @{$msg_hash->{source}}[0];
458     my $source_key = @{$msg_hash->{new_key}}[0];
459     my $query_res;
461     # check known_clients_db
462     my $sql_statement = "SELECT * FROM known_clients WHERE hostname='$source_name'";
463     $query_res = $main::known_clients_db->select_dbentry( $sql_statement );
464     if( 1 == keys %{$query_res} ) {
465         my $act_time = &get_time;
466         my $sql_statement= "UPDATE known_clients ".
467             "SET hostkey='$source_key', timestamp='$act_time' ".
468             "WHERE hostname='$source_name'";
469         my $res = $main::known_clients_db->update_dbentry( $sql_statement );
470         my $hash = &create_xml_hash("confirm_new_key", $server_address, $source_name);
471         my $out_msg = &create_xml_string($hash);
472         push(@out_msg_l, $out_msg);
473     }
475     # only do if host still not found
476     if( 0 == @out_msg_l ) {
477         # check known_server_db
478         $sql_statement = "SELECT * FROM known_server WHERE hostname='$source_name'";
479         $query_res = $main::known_server_db->select_dbentry( $sql_statement );
480         if( 1 == keys %{$query_res} ) {
481             my $act_time = &get_time;
482             my $sql_statement= "UPDATE known_server ".
483                 "SET hostkey='$source_key', timestamp='$act_time' ".
484                 "WHERE hostname='$source_name'";
485             my $res = $main::known_server_db->update_dbentry( $sql_statement );
487             my $hash = &create_xml_hash("confirm_new_key", $server_address, $source_name);
488             my $out_msg = &create_xml_string($hash);
489             push(@out_msg_l, $out_msg);
490         }
491     }
493     return @out_msg_l;
497 #===  FUNCTION  ================================================================
498 #         NAME:  here_i_am
499 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
500 #      RETURNS:  nothing
501 #  DESCRIPTION:  process this incoming message
502 #===============================================================================
503 sub here_i_am {
504     my ($msg_hash) = @_;
505     my @out_msg_l;
506     my $out_hash;
508     my $source = @{$msg_hash->{source}}[0];
509     my $mac_address = @{$msg_hash->{mac_address}}[0];
510         my $gotoHardwareChecksum = @{$msg_hash->{gotoHardwareChecksum}}[0];
512     # number of known clients
513     my $nu_clients= $main::known_clients_db->count_dbentries('known_clients');
515     # check wether client address or mac address is already known
516     my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$source'";
517     my $db_res= $main::known_clients_db->select_dbentry( $sql_statement );
518     
519     if ( 1 == keys %{$db_res} ) {
520         &main::daemon_log("WARNING: $source is already known as a client", 1);
521         &main::daemon_log("WARNING: values for $source are being overwritten", 1);   
522         $nu_clients --;
523     }
525     # number of actual activ clients
526     my $act_nu_clients = $nu_clients;
528     &main::daemon_log("INFO: number of actual activ clients: $act_nu_clients", 5);
529     &main::daemon_log("INFO: number of maximal allowed clients: $max_clients", 5);
531     if($max_clients <= $act_nu_clients) {
532         my $out_hash = &create_xml_hash("denied", $server_address, $source);
533         &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
534         my $passwd = @{$msg_hash->{new_passwd}}[0]; 
535         &send_msg_hash2address($out_hash, $source, $passwd);
536         return;
537     }
538     
539     # new client accepted
540     my $new_passwd = @{$msg_hash->{new_passwd}}[0];
542     # create entry in known_clients
543     my $events = @{$msg_hash->{events}}[0];
544     
546     # add entry to known_clients_db
547     my $act_timestamp = &get_time;
548     my $res = $main::known_clients_db->add_dbentry( {table=>'known_clients', 
549                                                 primkey=>['hostname'],
550                                                 hostname=>$source,
551                                                 events=>$events,
552                                                 macaddress=>$mac_address,
553                                                 status=>'registered',
554                                                 hostkey=>$new_passwd,
555                                                 timestamp=>$act_timestamp,
556                                                 } );
558     if ($res != 0)  {
559         &main::daemon_log("ERROR: cannot add entry to known_clients: $res");
560         return;
561     }
562     
563     # return acknowledgement to client
564     $out_hash = &create_xml_hash("registered", $server_address, $source);
565     my $register_out = &create_xml_string($out_hash);
566     push(@out_msg_l, $register_out);
568     # notify registered client to bus
569     if( $bus_activ eq "on") {
570         # fetch actual bus key
571         my $sql_statement= "SELECT * FROM known_server WHERE status='bus'";
572         my $query_res = $main::known_server_db->select_dbentry( $sql_statement );
573         my $hostkey = $query_res->{1}->{'hostkey'};
575         # send update msg to bus
576         $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
577         &add_content2xml_hash($out_hash, "macaddress", $mac_address);
578         &add_content2xml_hash($out_hash, "timestamp", $act_timestamp);
579         my $new_client_out = &create_xml_string($out_hash);
580         push(@out_msg_l, $new_client_out);
581         &main::daemon_log("INFO: send bus msg that client '$source' has registerd at server '$server_address'", 5);
582     }
584     # give the new client his ldap config
585     my $new_ldap_config_out = &new_ldap_config($source);
586     if( $new_ldap_config_out ) {
587         push(@out_msg_l, $new_ldap_config_out);
588     }
590         my $hardware_config_out = &hardware_config($source, $gotoHardwareChecksum);
591         if( $hardware_config_out ) {
592                 push(@out_msg_l, $hardware_config_out);
593         }
595     return @out_msg_l;
599 #===  FUNCTION  ================================================================
600 #         NAME:  who_has
601 #   PARAMETERS:  msg_hash - hash - hash from function create_xml_hash
602 #      RETURNS:  nothing 
603 #  DESCRIPTION:  process this incoming message
604 #===============================================================================
605 sub who_has {
606     my ($msg_hash) = @_ ;
607     my @out_msg_l;
608     
609     # what is your search pattern
610     my $search_pattern = @{$msg_hash->{who_has}}[0];
611     my $search_element = @{$msg_hash->{$search_pattern}}[0];
612     &main::daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
614     # scanning known_clients for search_pattern
615     my @host_addresses = keys %$main::known_clients;
616     my $known_clients_entries = length @host_addresses;
617     my $host_address;
618     foreach my $host (@host_addresses) {
619         my $client_element = $main::known_clients->{$host}->{$search_pattern};
620         if ($search_element eq $client_element) {
621             $host_address = $host;
622             last;
623         }
624     }
625         
626     # search was successful
627     if (defined $host_address) {
628         my $source = @{$msg_hash->{source}}[0];
629         my $out_hash = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
630         &add_content2xml_hash($out_hash, "mac_address", $search_element);
631         my $out_msg = &create_xml_string($out_hash);
632         push(@out_msg_l, $out_msg);
633     }
634     return @out_msg_l;
638 sub who_has_i_do {
639     my ($msg_hash) = @_ ;
640     my $header = @{$msg_hash->{header}}[0];
641     my $source = @{$msg_hash->{source}}[0];
642     my $search_param = @{$msg_hash->{$header}}[0];
643     my $search_value = @{$msg_hash->{$search_param}}[0];
644     print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
647 #===  FUNCTION  ================================================================
648 #         NAME:  new_ldap_config
649 #   PARAMETERS:  address - string - ip address and port of a host
650 #      RETURNS:  nothing
651 #  DESCRIPTION:  send to address the ldap configuration found for dn gotoLdapServer
652 #===============================================================================
653 sub new_ldap_config {
654         my ($address) = @_ ;
656         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$address' OR macaddress='$address'";
657         my $res = $main::known_clients_db->select_dbentry( $sql_statement );
659         # check hit
660         my $hit_counter = keys %{$res};
661         if( not $hit_counter == 1 ) {
662                 &main::daemon_log("ERROR: more or no hit found in known_clients_db by query by '$address'", 1);
663         }
665     $address = $res->{1}->{hostname};
666         my $macaddress = $res->{1}->{macaddress};
667         my $hostkey = $res->{1}->{hostkey};
669         if (not defined $macaddress) {
670                 &main::daemon_log("ERROR: no mac address found for client $address", 1);
671                 return;
672         }
674         # Build LDAP connection
675   &main::refresh_ldap_handle();
676         if( not defined $main::ldap_handle ) {
677                 &main::daemon_log("ERROR: cannot connect to ldap: $ldap_uri", 1);
678                 return;
679         } 
681         # Perform search
682         $mesg = $main::ldap_handle->search( base   => $ldap_base,
683                 scope  => 'sub',
684                 attrs => ['dn', 'gotoLdapServer', 'gosaUnitTag', 'FAIclass'],
685                 filter => "(&(objectClass=GOhard)(macaddress=$macaddress))");
686         #$mesg->code && die $mesg->error;
687         if($mesg->code) {
688                 &main::daemon_log($mesg->error, 1);
689                 return;
690         }
692         # Sanity check
693         if ($mesg->count != 1) {
694                 &main::daemon_log("WARNING: client mac address $macaddress not found/not unique in ldap search", 1);
695                 &main::daemon_log("\tbase: $ldap_base", 1);
696                 &main::daemon_log("\tscope: sub", 1);
697                 &main::daemon_log("\tattrs: dn, gotoLdapServer", 1);
698                 &main::daemon_log("\tfilter: (&(objectClass=GOhard)(macaddress=$macaddress))", 1);
699                 return;
700         }
702         my $entry= $mesg->entry(0);
703         my $dn= $entry->dn;
704         my @servers= $entry->get_value("gotoLdapServer");
705         my $unit_tag= $entry->get_value("gosaUnitTag");
706         my @ldap_uris;
707         my $server;
708         my $base;
709         my $release;
711         # Fill release if available
712         my $FAIclass= $entry->get_value("FAIclass");
713         if (defined $FAIclass && $FAIclass =~ /^.* :([A-Za-z0-9\/.]+).*$/) {
714                 $release= $1;
715         }
717         # Do we need to look at an object class?
718         if (length(@servers) < 1){
719                 $mesg = $main::ldap_handle->search( base   => $ldap_base,
720                         scope  => 'sub',
721                         attrs => ['dn', 'gotoLdapServer', 'FAIclass'],
722                         filter => "(&(objectClass=gosaGroupOfNames)(member=$dn))");
723                 #$mesg->code && die $mesg->error;
724                 if($mesg->code) {
725                         &main::daemon_log($mesg->error, 1);
726                         return;
727                 }
729                 # Sanity check
730                 if ($mesg->count != 1) {
731                         &main::daemon_log("WARNING: no LDAP information found for client mac $macaddress", 1);
732                         return;
733                 }
735                 $entry= $mesg->entry(0);
736                 $dn= $entry->dn;
737                 @servers= $entry->get_value("gotoLdapServer");
739                 if (not defined $release){
740                         $FAIclass= $entry->get_value("FAIclass");
741                         if (defined $FAIclass && $FAIclass =~ /^.* :([A-Za-z0-9\/.]+).*$/) {
742                                 $release= $1;
743                         }
744                 }
745         }
747         @servers= sort (@servers);
749         foreach $server (@servers){
750                 # Conversation for backward compatibility
751                 if (not $server =~ /^\d+:[^:]+:ldap[^:]*:\/\// ) {
752                     if ($server =~ /^([^:]+):(.*)$/ ) {
753                       $server= "1:dummy:ldap://$1/$2";
754                     }
755                 }
757                 $base= $server;
758                 $server =~ s%^[^:]+:[^:]+:(ldap.*://[^/]+)/.*$%$1%;
759                 $base =~ s%^[^:]+:[^:]+:ldap.*://[^/]+/(.*)$%$1%;
760                 push (@ldap_uris, $server);
761         }
763         # Assemble data package
764         my %data = ( 'ldap_uri'  => \@ldap_uris, 'ldap_base' => $base,
765                 'ldap_cfg' => \@ldap_cfg, 'pam_cfg' => \@pam_cfg,'nss_cfg' => \@nss_cfg );
766         if (defined $release){
767                 $data{'release'}= $release;
768         }
770         # Need to append GOto settings?
771         if (defined $goto_admin and defined $goto_secret){
772                 $data{'goto_admin'}= $goto_admin;
773                 $data{'goto_secret'}= $goto_secret;
774         }
776         # Append unit tag if needed
777         if (defined $unit_tag){
779                 # Find admin base and department name
780                 $mesg = $main::ldap_handle->search( base   => $ldap_base,
781                         scope  => 'sub',
782                         attrs => ['dn', 'ou'],
783                         filter => "(&(objectClass=gosaAdministrativeUnit)(gosaUnitTag=$unit_tag))");
784                 #$mesg->code && die $mesg->error;
785                 if($mesg->code) {
786                         &main::daemon_log($mesg->error, 1);
787                         return;
788                 }
790                 # Sanity check
791                 if ($mesg->count != 1) {
792                         &main::daemon_log("WARNING: cannot find administrative unit for client with tag $unit_tag", 1);
793                         return;
794                 }
796                 $entry= $mesg->entry(0);
797                 $data{'admin_base'}= $entry->dn;
798                 $data{'department'}= $entry->get_value("ou");
800                 # Append unit Tag
801                 $data{'unit_tag'}= $unit_tag;
802         }
804         # Send information
805         return &build_msg("new_ldap_config", $server_address, $address, \%data);
809 #===  FUNCTION  ================================================================
810 #         NAME:  hardware_config
811 #   PARAMETERS:  address - string - ip address and port of a host
812 #      RETURNS:  
813 #  DESCRIPTION:  
814 #===============================================================================
815 sub hardware_config {
816         my ($address, $gotoHardwareChecksum) = @_ ;
818         my $sql_statement= "SELECT * FROM known_clients WHERE hostname='$address'";
819         my $res = $main::known_clients_db->select_dbentry( $sql_statement );
821         # check hit
822         my $hit_counter = keys %{$res};
823         if( not $hit_counter == 1 ) {
824                 &main::daemon_log("ERROR: more or no hit found in known_clients_db by query by '$address'", 1);
825         }
827         my $macaddress = $res->{1}->{macaddress};
828         my $hostkey = $res->{1}->{hostkey};
830         if (not defined $macaddress) {
831                 &main::daemon_log("ERROR: no mac address found for client $address", 1);
832                 return;
833         }
835         # Build LDAP connection
836   &main::refresh_ldap_handle();
837         if( not defined $main::ldap_handle ) {
838                 &main::daemon_log("ERROR: cannot connect to ldap: $ldap_uri", 1);
839                 return;
840         } 
842         # Perform search
843         $mesg = $main::ldap_handle->search(
844                 base   => $ldap_base,
845                 scope  => 'sub',
846                 filter => "(&(objectClass=GOhard)(|(macAddress=$macaddress)(dhcpHWaddress=ethernet $macaddress)))"
847         );
849         if($mesg->count() == 0) {
850                 &main::daemon_log("Host was not found in LDAP!", 1);
851         } else {
852                 my $entry= $mesg->entry(0);
853                 my $dn= $entry->dn;
854                 if(defined($entry->get_value("gotoHardwareChecksum"))) {
855                         if(! $entry->get_value("gotoHardwareChecksum") eq $gotoHardwareChecksum) {
856                                 $entry->replace(gotoHardwareChecksum => $gotoHardwareChecksum);
857                                 if($entry->update($main::ldap_handle)) {
858                                         &main::daemon_log("Hardware changed! Detection triggered.", 4);
859                                 }
860                         } else {
861                                 # Nothing to do
862                                 return;
863                         }
864                 }
865         } 
867         # Assemble data package
868         my %data = ();
870         # Need to append GOto settings?
871         if (defined $goto_admin and defined $goto_secret){
872                 $data{'goto_admin'}= $goto_admin;
873                 $data{'goto_secret'}= $goto_secret;
874         }
876         &main::daemon_log("Send detect_hardware message to $address", 4);
878         # Send information
879         return &build_msg("detect_hardware", $server_address, $address, \%data);
882 sub server_matches {
883         my $target = shift;
884         my $target_ip = sprintf("%s", $target =~ /^([0-9\.]*?):.*$/);
885         my $result = 0;
887         if($server_ip eq $target_ip) {
888                 $result= 1;
889         } elsif ($server_ip eq "0.0.0.0") {     
890                 if ($target_ip eq "127.0.0.1") {
891                         $result= 1;
892                 } else {
893                         my $PROC_NET_ROUTE= ('/proc/net/route');
895                         open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
896                                 or die "Could not open $PROC_NET_ROUTE";
898                         my @ifs = <PROC_NET_ROUTE>;
900                         close(PROC_NET_ROUTE);
902                         # Eat header line
903                         shift @ifs;
904                         chomp @ifs;
905                         foreach my $line(@ifs) {
906                                 my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
907                                 my $destination;
908                                 my $mask;
909                                 my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
910                                 $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
911                                 ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
912                                 $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
913                                 if(new NetAddr::IP($target_ip)->within(new NetAddr::IP($destination, $mask))) {
914                                         # destination matches route, save mac and exit
915                                         $result= 1;
916                                         last;
917                                 }
918                         }
919                 }
920         } else {
921                 &main::daemon_log("Target ip $target_ip does not match Server ip $server_ip",1);
922         }
924         return $result;
927 1;