Code

* Fixed permission for config files
[gosa.git] / gosa-si / gosa-si-client
index b69c34782bb7d468d56a3db5c3dc4365b16fd651..86e2f2afef55a5e094e2971e7945934bbef91246 100755 (executable)
@@ -8,7 +8,7 @@
 #  DESCRIPTION:
 #
 #      OPTIONS:  ---
-# REQUIREMENTS:  ---
+# REQUIREMENTS:  libnetaddr-ip-perl
 #         BUGS:  ---
 #        NOTES:
 #       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
@@ -35,17 +35,18 @@ use Data::Dumper;
 use Sys::Syslog qw( :DEFAULT setlogsock);
 use File::Spec;
 use Cwd;
+use NetAddr::IP;
 use GOSA::GosaSupportDaemon;
 
 
 my ($cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file);
 my ($server_address, $server_ip, $server_port, $server_domain, $server_passwd, $server_cipher, $server_timeout);
-my ($client_address, $client_ip, $client_port, $client_mac_address, $ldap_config, $pam_config, $nss_config);
-my ($input_socket, $rbits, $wbits, $ebits, $xml, $known_hosts);
+my ($client_address, $client_ip, $client_port, $client_mac_address, $network_interface, $ldap_config, $pam_config, $nss_config);
+my ($input_socket, $rbits, $wbits, $ebits, $xml, $known_hosts, $ldap_enabled);
 my (@events);
 
 # default variables
-my $event_dir = "/etc/gosa-si/client/events";
+my $event_dir = "/usr/lib/gosa-si/client/events";
 $known_hosts = {};
 $foreground = 0 ;
 %cfg_defaults =
@@ -55,6 +56,8 @@ $foreground = 0 ;
     },
 "client" => 
     {"client_port" => [\$client_port, "20083"],
+     "client_ip" => [\$client_ip, "0.0.0.0"],
+     "ldap" => [\$ldap_enabled, 1],
      "ldap_config" => [\$ldap_config, "/etc/ldap/ldap.conf"],
      "pam_config" => [\$pam_config, "/etc/pam_ldap.conf"],
      "nss_config" => [\$nss_config, "/etc/libnss_ldap.conf"],
@@ -139,13 +142,12 @@ sub daemon_log {
 sub check_cmdline_param () {
     my $err_config;
     my $err_counter = 0;
-    if( not defined( $cfg_file)) {
-        #$err_config = "please specify a config file";
-        #$err_counter += 1;
-        my $cwd = getcwd;
-        my $name = "/etc/gosa-si/client.conf";
-        $cfg_file = File::Spec->catfile( $cwd, $name );
-        print STDERR "no conf file specified\n   try to use default: $cfg_file\n";        
+       if(not defined($cfg_file)) {
+               $cfg_file = "/etc/gosa-si/client.conf";
+               if(! -r $cfg_file) {
+                       $err_config = "please specify a config file";
+                       $err_counter += 1;
+               }
     }
     if( $err_counter > 0 ) {
         &usage( "", 1 );
@@ -204,32 +206,172 @@ sub check_pid {
     }
 }
 
+#===  FUNCTION  ================================================================
+#         NAME:  get_interface_for_ip
+#   PARAMETERS:  ip address (i.e. 192.168.0.1)
+#      RETURNS:  array: list of interfaces if ip=0.0.0.0, matching interface if found, undef else
+#  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
+#===============================================================================
+sub get_interface_for_ip {
+        my $result;
+        my $ip= shift;
+        if ($ip && length($ip) > 0) {
+                my @ifs= &get_interfaces();
+                if($ip eq "0.0.0.0") {
+                        $result = "all";
+                } else {
+                        foreach (@ifs) {
+                                my $if=$_;
+                                if(get_ip($if) eq $ip) {
+                                        $result = $if;
+                                       last;
+                                }
+                        }       
+                }
+        }       
+        return $result;
+}
 
 #===  FUNCTION  ================================================================
-#         NAME:  get_ip_and_mac 
-#   PARAMETERS:  nothing
-#      RETURNS:  (ip, mac) 
-#  DESCRIPTION:  executes /sbin/ifconfig and parses the output, the first occurence 
-#                of a inet address is returned as well as the mac address in the line
-#                above the inet address
+#         NAME:  get_interfaces 
+#   PARAMETERS:  none
+#      RETURNS:  (list of interfaces) 
+#  DESCRIPTION:  Uses proc fs (/proc/net/dev) to get list of interfaces.
 #===============================================================================
-sub get_ip_and_mac {
-    my $ip = "0.0.0.0.0"; # Defualt-IP
-    my $mac = "00:00:00:00:00:00";  # Default-MAC
-    my @ifconfig = qx(/sbin/ifconfig);
-    foreach(@ifconfig) {
-        if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
-            $mac = "$1:$2:$3:$4:$5:$6";
-            next;
+sub get_interfaces {
+        my @result;
+        my $PROC_NET_DEV= ('/proc/net/dev');
+
+        open(PROC_NET_DEV, "<$PROC_NET_DEV")
+                or die "Could not open $PROC_NET_DEV";
+
+        my @ifs = <PROC_NET_DEV>;
+
+        close(PROC_NET_DEV);
+
+        # Eat first two line
+        shift @ifs;
+        shift @ifs;
+
+        chomp @ifs;
+        foreach my $line(@ifs) {
+                my $if= (split /:/, $line)[0];
+                $if =~ s/^\s+//;
+                push @result, $if;
         }
-        if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
-            $ip = "$1.$2.$3.$4";
-            last;
+
+        return @result;
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_mac 
+#   PARAMETERS:  interface name (i.e. eth0)
+#      RETURNS:  (mac address) 
+#  DESCRIPTION:  Uses ioctl to get mac address directly from system.
+#===============================================================================
+sub get_mac {
+        my $ifreq= shift;
+        my $result;
+        if ($ifreq && length($ifreq) > 0) { 
+                if($ifreq eq "all") {
+                       if(defined($server_ip)) {
+                               $result = &get_local_mac_for_remote_ip($server_ip);
+                       } else {
+                               $result = "00:00:00:00:00:00";
+                       }
+                } else {
+                        my $SIOCGIFHWADDR= 0x8927;     # man 2 ioctl_list
+
+                        # A configured MAC Address should always override a guessed value
+                        if ($client_mac_address and length($client_mac_address) > 0) {
+                                $result= $client_mac_address;
+                        }
+
+                        socket SOCKET, PF_INET, SOCK_DGRAM, getprotobyname('ip')
+                                or die "socket: $!";
+
+                        if(ioctl SOCKET, $SIOCGIFHWADDR, $ifreq) {
+                                my ($if, $mac)= unpack 'h36 H12', $ifreq;
+
+                                if (length($mac) > 0) {
+                                        $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])$/;
+                                        $mac= sprintf("%s:%s:%s:%s:%s:%s", $1, $2, $3, $4, $5, $6);
+                                        $result = $mac;
+                                }
+                        }
+                }
         }
-    }
-    return ($ip, $mac);
+        return $result;
 }
 
+#===  FUNCTION  ================================================================
+#         NAME:  get_ip 
+#   PARAMETERS:  interface name (i.e. eth0)
+#      RETURNS:  (ip address) 
+#  DESCRIPTION:  Uses ioctl to get ip address directly from system.
+#===============================================================================
+sub get_ip {
+        my $ifreq= shift;
+        my $result= "";
+        my $SIOCGIFADDR= 0x8915;       # man 2 ioctl_list
+        my $proto= getprotobyname('ip');
+
+        socket SOCKET, PF_INET, SOCK_DGRAM, $proto
+                or die "socket: $!";
+
+        if(ioctl SOCKET, $SIOCGIFADDR, $ifreq) {
+                my ($if, $sin)    = unpack 'a16 a16', $ifreq;
+                my ($port, $addr) = sockaddr_in $sin;
+                my $ip            = inet_ntoa $addr;
+
+                if ($ip && length($ip) > 0) {
+                        $result = $ip;
+                }
+        }
+
+        return $result;
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_local_mac_for_remote_ip
+#   PARAMETERS:  none (takes server_ip from global variable)
+#      RETURNS:  (ip address from interface that is used for communication) 
+#  DESCRIPTION:  Uses ioctl to get routing table from system, checks which entry
+#                matches (defaultroute last).
+#===============================================================================
+sub get_local_mac_for_remote_ip {
+        my $ifreq= shift;
+        my $result= "00:00:00:00:00:00";
+        my $PROC_NET_ROUTE= ('/proc/net/route');
+
+        open(PROC_NET_ROUTE, "<$PROC_NET_ROUTE")
+                or die "Could not open $PROC_NET_ROUTE";
+
+        my @ifs = <PROC_NET_ROUTE>;
+
+        close(PROC_NET_ROUTE);
+
+       # Eat header line
+       shift @ifs;
+       chomp @ifs;
+       foreach my $line(@ifs) {
+               my ($Iface,$Destination,$Gateway,$Flags,$RefCnt,$Use,$Metric,$Mask,$MTU,$Window,$IRTT)=split(/\s/, $line);
+               my $destination;
+               my $mask;
+               my ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Destination);
+               $destination= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
+               ($d,$c,$b,$a)=unpack('a2 a2 a2 a2', $Mask);
+               $mask= sprintf("%d.%d.%d.%d", hex($a), hex($b), hex($c), hex($d));
+               if(new NetAddr::IP($server_ip)->within(new NetAddr::IP($destination, $mask))) {
+                       # destination matches route, save mac and exit
+                       $result= &get_mac($Iface);
+                       last;
+               }
+       }
+
+
+        return $result;
+}
 
 #===  FUNCTION  ================================================================
 #         NAME:  usage
@@ -311,14 +453,14 @@ sub register_at_server {
     opendir(DIR, $event_dir) 
         or daemon_log("cannot find directory $event_dir!\ngosa-si-client starts without any accepting events!", 1);
     my $file_name;
-    @events = ();
+    my @events_list = ();
     while(defined($file_name = readdir(DIR))){
         if ($file_name eq "." || $file_name eq "..") {
             next;
         }
-        push(@events, $file_name);
+        push(@events_list, $file_name);
     }
-    my $events = join(",", @events);
+    my $events = join(",", @events_list);
     daemon_log("found events: $events", 1);
 
     # fill in all possible servers
@@ -338,6 +480,7 @@ sub register_at_server {
 
     my ($rout, $wout, $reg_server);
     foreach my $server (@servers) {
+
         # create msg hash
         my $register_hash = &create_xml_hash("here_i_am", $client_address, $server);
         &add_content2xml_hash($register_hash, "new_passwd", $new_server_passwd);
@@ -825,7 +968,7 @@ sub process_incoming_msg {
 
     my $header = @{$msg_hash->{header}}[0];
     
-    daemon_log("recieve '$header' from $host", 1);
+    daemon_log("receive '$header' from $host", 1);
 #    daemon_log("header from msg:", 1);
 #    daemon_log("\t$header", 1);
 #    daemon_log("msg to process:", 7);
@@ -858,9 +1001,7 @@ sub process_incoming_msg {
     close(DIR);
     daemon_log("could not assign the msg $header to an event", 5);
     
-
-
-    if ($header eq 'new_ldap_config') { &new_ldap_config($msg_hash)}
+    if ($header eq 'new_ldap_config') { if ($ldap_enabled == 1) {&new_ldap_config($msg_hash)}}
     elsif ($header eq 'ping') { &got_ping($msg_hash) }
     elsif ($header eq 'wake_up') { &execute_event($msg_hash)}
     elsif ($header eq 'new_passwd') { &new_passwd()}
@@ -935,6 +1076,8 @@ sub new_ldap_config {
     my @ldap_options;
     my @pam_options;
     my @nss_options;
+    my $goto_admin;
+    my $goto_secret;
 
     # Transform input into array
     while ( my ($key, $value) = each(%$msg_hash) ) {
@@ -951,6 +1094,14 @@ sub new_ldap_config {
                        $ldap_base= $element;
                        next;
                }
+               if ($key =~ /^goto_admin$/) {
+                       $goto_admin= $element;
+                       next;
+               }
+               if ($key =~ /^goto_secret$/) {
+                       $goto_secret= $element;
+                       next;
+               }
                if ($key =~ /^ldap_cfg$/) {
                        push (@ldap_options, "$element");
                        next;
@@ -1006,6 +1157,26 @@ sub new_ldap_config {
     close (file1);
     daemon_log("wrote $pam_config", 5);
 
+    # Create goto.secrets if told so - for compatibility reasons
+    if (defined $goto_admin){
+           open(file1, "> /etc/goto/secret");
+           close(file1);
+           chown(0,0, "/etc/goto/secret");
+           chmod(0600, "/etc/goto/secret");
+           open(file1, "> /etc/goto/secret");
+           print file1 "GOTOADMIN=\"$goto_admin\"\nGOTOSECRET=\"$goto_secret\"\n";
+           close(file1);
+           daemon_log("wrote /etc/goto/secret", 5);
+    }
+
+
+    # TODO: write these values to /etc/ldap/ldap-shell.conf
+    # LDAP_BASE=
+    # ADMIN_BASE=
+    # DEPARTMENT=
+    # UNIT_TAG=
+    # UNIT_TAG_FILTER=
+
     return;
 
 }
@@ -1088,6 +1259,13 @@ GetOptions("h|help" => \&usage,
 &read_configfile;
 &check_pid;
 
+if ( ! $foreground ) {
+       open STDIN, '/dev/null' or die "Can’t read /dev/null: $!";
+       open STDOUT, '>>/dev/null' or die "Can't write to /dev/null: $!";
+       open STDERR, '>>/dev/null' or die "Can't write to /dev/null: $!";
+}
+
+
 # restart daemon log file
 if(-e $log_file ) { unlink $log_file }
 daemon_log(" ", 1);
@@ -1106,10 +1284,13 @@ if( 0 != $pid ) {
 }
 
 # detect own ip and mac address
-($client_ip, $client_mac_address) = &get_ip_and_mac(); 
-if (not defined $client_ip) {
-    die "EXIT: ip address of $0 could not be detected";
-}
+$network_interface= &get_interface_for_ip($client_ip);
+$client_mac_address= &get_mac($network_interface);
+
+# ($client_ip, $client_mac_address) = &get_ip_and_mac(); 
+#if (not defined $client_ip) {
+#    die "EXIT: ip address of $0 could not be detected";
+#}
 daemon_log("client ip address detected: $client_ip", 1);
 daemon_log("client mac address detected: $client_mac_address", 1);