blob: 879223a53bd28ad9649ce968911730d8e208af48 [file] [log] [blame]
Richard Marian Thomaiyar14fddef2018-07-13 23:55:56 +05301package Bastille::API::ServiceAdmin;
2use strict;
3
4use Bastille::API;
5
6use Bastille::API::HPSpecific;
7use Bastille::API::FileContent;
8
9require Exporter;
10our @ISA = qw(Exporter);
11our @EXPORT_OK = qw(
12B_chkconfig_on
13B_chkconfig_off
14B_service_start
15B_service_stop
16B_service_restart
17B_is_service_off
18checkServiceOnLinux
19remoteServiceCheck
20remoteNISPlusServiceCheck
21B_create_nsswitch_file
22);
23our @EXPORT = @EXPORT_OK;
24
25
26#######
27# &B_chkconfig_on and &B_chkconfig_off() are great for systems that didn't use
28# a more modern init system. This is a bit of a problem on Fedora, though,
29# which used upstart from Fedora 9 to Fedora 14, then switched to a new
30# Red Hat-created system called systemd for Fedora 15 and 16 (so far).
31# OpenSUSE also moved to systemd, starting with 12.1. Version 11.4 did not
32# use systemd.
33# It is also a problem on Ubuntu, starting at version 6.10, where they also
34# used upstart.
35#####
36
37
38
39
40###########################################################################
41# &B_chkconfig_on ($daemon_name) creates the symbolic links that are
42# named in the "# chkconfig: ___ _ _ " portion of the init.d files. We
43# need this utility, in place of the distro's chkconfig, because of both
44# our need to add revert functionality and our need to harden distros that
45# are not mounted on /.
46#
47# It uses the following global variables to find the links and the init
48# scripts, respectively:
49#
50# &getGlobal('DIR', "rcd") -- directory where the rc_.d subdirs can be found
51# &getGlobal('DIR', "initd") -- directory the rc_.d directories link to
52#
53# Here an example of where you might use this:
54#
55# You'd like to tell the system to run the firewall at boot:
56# B_chkconfig_on("bastille-firewall")
57#
58###########################################################################
59
60# PW: Blech. Copied B_chkconfig_off() and changed a few things,
61# then changed a few more things....
62
63sub B_chkconfig_on {
64
65 my $startup_script=$_[0];
66 my $retval=1;
67
68 my $chkconfig_line;
69 my ($runlevelinfo,@runlevels);
70 my ($start_order,$stop_order,$filetolink);
71
72 &B_log("ACTION","# chkconfig_on enabling $startup_script\n");
73
74 # In Debian system there is no chkconfig script, run levels are checked
75 # one by one (jfs)
76 if (&GetDistro =~/^DB.*/) {
77 $filetolink = &getGlobal('DIR', "initd") . "/$startup_script";
78 if (-x $filetolink)
79 {
80 foreach my $level ("0","1","2","3","4","5","6" ) {
81 my $link = '';
82 $link = &getGlobal('DIR', "rcd") . "/rc" . "$level" . ".d/K50" . "$startup_script";
83 $retval=symlink($filetolink,$link);
84 }
85 }
86 return $retval;
87 }
88 #
89 # On SUSE, chkconfig-based rc scripts have been replaced with a whole different
90 # system. chkconfig on SUSE is actually a shell script that does some stuff and then
91 # calls insserv, their replacement.
92 #
93
94 if (&GetDistro =~ /^SE/) {
95 # only try to chkconfig on if init script is found
96 if ( -e (&getGlobal('DIR', "initd") . "/$startup_script") ) {
97 $chkconfig_line=&getGlobal('BIN','chkconfig');
98 &B_System("$chkconfig_line $startup_script on", "$chkconfig_line $startup_script off");
99 # chkconfig doesn't take affect until reboot, need to restart service also
100 B_service_restart("$startup_script");
101 return 1; #success
102 }
103 return 0; #failure
104 }
105
106 #
107 # Run through the init script looking for the chkconfig line...
108 #
109 $retval = open CHKCONFIG,&getGlobal('DIR', "initd") . "/$startup_script";
110 unless ($retval) {
111 &B_log("ACTION","# Didn't chkconfig_on $startup_script because we couldn't open " . &getGlobal('DIR', "initd") . "/$startup_script\n");
112 }
113 else {
114
115 READ_LOOP:
116 while (my $line=<CHKCONFIG>) {
117
118 # We're looking for lines like this one:
119 # # chkconfig: 2345 10 90
120 # OR this
121 # # chkconfig: - 10 90
122
123 if ($line =~ /^#\s*chkconfig:\s*([-\d]+)\s*(\d+)\s*(\d+)/ ) {
124 $runlevelinfo = $1;
125 $start_order = $2;
126 $stop_order = $3;
127 # handle a run levels arg of '-'
128 if ( $runlevelinfo eq '-' ) {
129 &B_log("ACTION","chkconfig_on saw '-' for run levels for \"$startup_script\", is defaulting to levels 3,4,5\n");
130 $runlevelinfo = '345';
131 }
132 @runlevels = split(//,$runlevelinfo);
133 # make sure the orders have 2 digits
134 $start_order =~ s/^(\d)$/0$1/;
135 $stop_order =~ s/^(\d)$/0$1/;
136 last READ_LOOP;
137 }
138 }
139 close CHKCONFIG;
140
141 # Do we have what we need?
142 if ( (scalar(@runlevels) < 1) || (! $start_order =~ /^\d{2}$/) || (! $stop_order =~ /^\d{2}$/) ) {
143 # problem
144 &B_log("ERROR","# B_chkconfig_on $startup_script failed -- no valid run level/start/stop info found\n");
145 return(-1);
146 }
147
148 # Now, run through creating symlinks...
149 &B_log("ACTION","# chkconfig_on will use run levels ".join(",",@runlevels)." for \"$startup_script\" with S order $start_order and K order $stop_order\n");
150
151 $retval=0;
152 # BUG: we really ought to readdir() on &getGlobal('DIR', "rcd") to get all levels
153 foreach my $level ( "0","1","2","3","4","5","6" ) {
154 my $link = '';
155 # we make K links in run levels not specified in the chkconfig line
156 $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/K$stop_order" . $startup_script;
157 my $klink = $link;
158 # now we see if this is a specified run level; if so, make an S link
159 foreach my $markedlevel ( @runlevels ) {
160 if ( $level == $markedlevel) {
161 $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/S$start_order" . $startup_script;
162 }
163 }
164 my $target = &getGlobal('DIR', "initd") ."/" . $startup_script;
165 my $local_return;
166
167 if ( (-e "$klink") && ($klink ne $link) ) {
168 # there's a K link, but this level needs an S link
169 unless ($GLOBAL_LOGONLY) {
170 $local_return = unlink("$klink");
171 if ( ! $local_return ) {
172 # unlinking old, bad $klink failed
173 &B_log("ERROR","Unlinking $klink failed\n");
174 } else {
175 &B_log("ACTION","Removed link $klink\n");
176 # If we removed the link, add a link command to the revert file
177 &B_revert_log (&getGlobal('BIN','ln') . " -s $target $klink\n");
178 } # close what to do if unlink works
179 } # if not GLOBAL_LOGONLY
180 } # if $klink exists and ne $link
181
182 # OK, we've disposed of any old K links, make what we need
183 if ( (! ( -e "$link" )) && ($link ne '') ) {
184 # link doesn't exist and the start/stop number is OK; make it
185 unless ($GLOBAL_LOGONLY) {
186 # create the link
187 $local_return = &B_symlink($target,$link);
188 if ($local_return) {
189 $retval++;
190 &B_log("ACTION","Created link $link\n");
191 } else {
192 &B_log("ERROR","Couldn't create $link when trying to chkconfig on $startup_script\n");
193 }
194 }
195
196 } # link doesn't exist
197 } # foreach level
198
199 }
200
201 if ($retval < @runlevels) {
202 $retval=0;
203 }
204
205 $retval;
206
207}
208
209
210###########################################################################
211# &B_chkconfig_off ($daemon_name) deletes the symbolic links that are
212# named in the "# chkconfig: ___ _ _ " portion of the init.d files. We
213# need this utility, in place of the distro's chkconfig, because of both
214# our need to add revert functionality and our need to harden distros that
215# are not mounted on /.
216#
217# chkconfig allows for a REVERT of its work by writing to an executable
218# file &getGlobal('BFILE', "removed-symlinks").
219#
220# It uses the following global variables to find the links and the init
221# scripts, respectively:
222#
223# &getGlobal('DIR', "rcd") -- directory where the rc_.d subdirs can be found
224# &getGlobal('DIR', "initd") -- directory the rc_.d directories link to
225#
226# Here an example of where you might use this:
227#
228# You'd like to tell stop running sendmail in daemon mode on boot:
229# B_chkconfig_off("sendmail")
230#
231###########################################################################
232
233
234
235sub B_chkconfig_off {
236
237 my $startup_script=$_[0];
238 my $retval=1;
239
240 my $chkconfig_line;
241 my @runlevels;
242 my ($start_order,$stop_order,$filetolink);
243
244 if (&GetDistro =~/^DB.*/) {
245 $filetolink = &getGlobal('DIR', "initd") . "/$startup_script";
246 if (-x $filetolink)
247 {
248 # Three ways to do this in Debian:
249 # 1.- have the initd script set to 600 mode
250 # 2.- Remove the links in rcd (re-installing the package
251 # will break it)
252 # 3.- Use update-rc.d --remove (same as 2.)
253 # (jfs)
254 &B_chmod(0600,$filetolink);
255 $retval=6;
256
257 # The second option
258 #foreach my $level ("0","1","2","3","4","5","6" ) {
259 #my $link = '';
260 #$link = &getGlobal('DIR', "rcd") . "/rc" . "$level" . ".d/K50" . "$startup_script";
261 #unlink($link);
262 #}
263 }
264 }
265
266 #
267 # On SUSE, chkconfig-based rc scripts have been replaced with a whole different
268 # system. chkconfig on SUSE is actually a shell script that does some stuff and then
269 # calls insserv, their replacement.
270 #
271 elsif (&GetDistro =~ /^SE/) {
272 # only try to chkconfig off if init script is found
273 if ( -e (&getGlobal('DIR', "initd") . "/$startup_script") ) {
274 $chkconfig_line=&getGlobal('BIN','chkconfig');
275 &B_System("$chkconfig_line $startup_script on", "$chkconfig_line $startup_script off");
276 # chkconfig doesn't take affect until reboot, need to stop service
277 # since expectation is that the daemons are disabled even without a reboot
278 B_service_stop("$startup_script");
279 return 1; #success
280 }
281 return 0; #failure
282 }
283 else {
284
285 # Run through the init script looking for the chkconfig line...
286
287
288 $retval = open CHKCONFIG,&getGlobal('DIR', "initd") . "/$startup_script";
289 unless ($retval) {
290 &B_log("ACTION","Didn't chkconfig_off $startup_script because we couldn't open " . &getGlobal('DIR', "initd") . "/$startup_script\n");
291 }
292 else {
293
294 READ_LOOP:
295 while (my $line=<CHKCONFIG>) {
296
297 # We're looking for lines like this one:
298 # # chkconfig: 2345 10 90
299
300 if ($line =~ /^#\s*chkconfig:\s*([-\d]+)\s*(\d+)\s*(\d+)/ ) {
301 @runlevels=split //,$1;
302 $start_order=$2;
303 $stop_order=$3;
304
305
306 # Change single digit run levels to double digit -- otherwise,
307 # the alphabetic ordering chkconfig depends on fails.
308 if ($start_order =~ /^\d$/ ) {
309 $start_order = "0" . $start_order;
310 &B_log("ACTION","chkconfig_off converted start order to $start_order\n");
311 }
312 if ($stop_order =~ /^\d$/ ) {
313 $stop_order = "0" . $stop_order;
314 &B_log("ACTION","chkconfig_off converted stop order to $stop_order\n");
315 }
316
317 last READ_LOOP;
318 }
319 }
320 close CHKCONFIG;
321
322 # If we never found a chkconfig line, can we just run through all 5
323 # rcX.d dirs from 1 to 5...?
324
325 # unless ( $start_order and $stop_order ) {
326 # @runlevels=("1","2","3","4","5");
327 # $start_order = "*"; $stop_order="*";
328 # }
329
330 # Now, run through removing symlinks...
331
332
333
334 $retval=0;
335
336 # Handle the special case that the run level specified is solely "-"
337 if ($runlevels[0] =~ /-/) {
338 @runlevels = ( "0","1","2","3","4","5","6" );
339 }
340
341 foreach my $level ( @runlevels ) {
342 my $link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/S$start_order" . $startup_script;
343 my $new_link = &getGlobal('DIR', "rcd") . "/rc" . $level . ".d/K$stop_order" . $startup_script;
344 my $target = &getGlobal('DIR', "initd") ."/" . $startup_script;
345 my $local_return;
346
347
348 # Replace the S__ link in this level with a K__ link.
349 if ( -e $link ) {
350 unless ($GLOBAL_LOGONLY) {
351 $local_return=unlink $link;
352 if ($local_return) {
353 $local_return=symlink $target,$new_link;
354 unless ($local_return) {
355 &B_log("ERROR","Linking $target to $new_link failed.\n");
356 }
357 }
358 else { # unlinking failed
359 &B_log("ERROR","Unlinking $link failed\n");
360 }
361
362 }
363 if ($local_return) {
364 $retval++;
365 &B_log("ACTION","Removed link $link\n");
366
367 #
368 # If we removed the link, add a link command to the revert file
369 # Write out the revert information for recreating the S__
370 # symlink and deleting the K__ symlink.
371 &B_revert_log(&getGlobal('BIN',"ln") . " -s $target $link\n");
372 &B_revert_log(&getGlobal('BIN',"rm") . " -f $new_link\n");
373 }
374 else {
375 &B_log("ERROR","B_chkconfig_off $startup_script failed\n");
376 }
377
378 }
379 } # foreach
380
381 } # else-unless
382
383 } # else-DB
384 if ($retval < @runlevels) {
385 $retval=0;
386 }
387
388 $retval;
389
390}
391
392
393###########################################################################
394# &B_service_start ($daemon_name)
395# Starts service on RedHat/SUSE-based Linux distributions which have the
396# service command:
397#
398# service $daemon_name start
399#
400# Other Linux distros that also support this method of starting
401# services can be added to use this function.
402#
403# Here an example of where you might use this:
404#
405# You'd like to tell the system to start the vsftpd daemon:
406# &B_service_start("vsftpd")
407#
408# Uses &B_System in HP_API.pm
409# To match how the &B_System command works this method:
410# returns 1 on success
411# returns 0 on failure
412###########################################################################
413
414sub B_service_start {
415
416 my $daemon=$_[0];
417
418 if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
419 (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
420 &B_log("ERROR","Tried to call service_start on a system lacking a service command! Internal Bastille error.");
421 return undef;
422 }
423
424 # only start service if init script is found
425 if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
426 &B_log("ACTION","# service_start enabling $daemon\n");
427
428 my $service_cmd=&getGlobal('BIN', 'service');
429 if ($service_cmd) {
430 # Start the service,
431 # Also provide &B_System revert command
432
433 return (&B_System("$service_cmd $daemon start",
434 "$service_cmd $daemon stop"));
435 }
436 }
437
438 # init script not found, do not try to start, return failure
439 return 0;
440}
441
442###########################################################################
443# &B_service_stop ($daemon_name)
444# Stops service on RedHat/SUSE-based Linux distributions which have the
445# service command:
446#
447# service $daemon_name stop
448#
449# Other Linux distros that also support this method of starting
450# services can be added to use this function.
451# Stops service.
452#
453#
454# Here an example of where you might use this:
455#
456# You'd like to tell the system to stop the vsftpd daemon:
457# &B_service_stop("vsftpd")
458#
459# Uses &B_System in HP_API.pm
460# To match how the &B_System command works this method:
461# returns 1 on success
462# returns 0 on failure
463###########################################################################
464
465sub B_service_stop {
466
467 my $daemon=$_[0];
468
469 if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
470 (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
471 &B_log("ERROR","Tried to call service_stop on a system lacking a service command! Internal Bastille error.");
472 return undef;
473 }
474
475 # only stop service if init script is found
476 if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
477 &B_log("ACTION","# service_stop disabling $daemon\n");
478
479 my $service_cmd=&getGlobal('BIN', 'service');
480 if ($service_cmd) {
481
482 # Stop the service,
483 # Also provide &B_System revert command
484
485 return (&B_System("$service_cmd $daemon stop",
486 "$service_cmd $daemon start"));
487 }
488 }
489
490 # init script not found, do not try to stop, return failure
491 return 0;
492}
493
494
495###########################################################################
496# &B_service_restart ($daemon_name)
497# Restarts service on RedHat/SUSE-based Linux distributions which have the
498# service command:
499#
500# service $daemon_name restart
501#
502# Other Linux distros that also support this method of starting
503# services can be added to use this function.
504#
505# Here an example of where you might use this:
506#
507# You'd like to tell the system to restart the vsftpd daemon:
508# &B_service_restart("vsftpd")
509#
510# Uses &B_System in HP_API.pm
511# To match how the &B_System command works this method:
512# returns 1 on success
513# returns 0 on failure
514###########################################################################
515
516sub B_service_restart {
517
518 my $daemon=$_[0];
519
520 if ( (&GetDistro !~ /^SE/) and (&GetDistro !~ /^RH/) and
521 (&GetDistro !~ /^RHFC/) and (&GetDistro !~ /^MN/) ) {
522 &B_log("ERROR","Tried to call service_restart on a system lacking a service command! Internal Bastille error.");
523 return undef;
524 }
525
526 # only restart service if init script is found
527 if ( -e (&getGlobal('DIR', 'initd') . "/$daemon") ) {
528 &B_log("ACTION","# service_restart re-enabling $daemon\n");
529
530 my $service_cmd=&getGlobal('BIN', 'service');
531 if ($service_cmd) {
532
533 # Restart the service
534 return (&B_System("$service_cmd $daemon restart",
535 "$service_cmd $daemon restart"));
536 }
537 }
538
539 # init script not found, do not try to restart, return failure
540 return 0;
541}
542
543###########################################################################
544# &B_is_service_off($;$)
545#
546# Runs the specified test to determine whether or not the question should
547# be answered.
548#
549# return values:
550# NOTSECURE_CAN_CHANGE()/0: service is on
551# SECURE_CANT_CHANGE()/1: service is off
552# undef: test is not defined
553###########################################################################
554
555sub B_is_service_off ($){
556 my $service=$_[0];
557
558 if(&GetDistro =~ "^HP-UX"){
559 #die "Why do I think I'm on HPUX?!\n";
560 return &checkServiceOnHPUX($service);
561 }
562 elsif ( (&GetDistro =~ "^RH") || (&GetDistro =~ "^SE") ) {
563 return &checkServiceOnLinux($service);
564 }
565 else {
566 &B_log("DEBUG","B_is_service off called for unsupported OS");
567 # not yet implemented for other distributions of Linux
568 # when GLOBAL_SERVICE, GLOBAL_SERVTYPE and GLOBAL_PROCESS are filled
569 # in for Linux, then
570 # at least inetd and inittab services should be similar to the above,
571 # whereas chkconfig would be used on some Linux distros to determine
572 # if non-inetd/inittab services are running at boot time. Looking at
573 # processes should be similar.
574 return undef;
575 }
576}
577
578###########################################################################
579# &checkServiceOnLinux($service);
580#
581# Checks if the given service is running on a Linux system. This is
582# called by B_is_Service_Off(), which is the function that Bastille
583# modules should call.
584#
585# Return values:
586# NOTSECURE_CAN_CHANGE() if the service is on
587# SECURE_CANT_CHANGE() if the service is off
588# undef if the state of the service cannot be determined
589#
590###########################################################################
591sub checkServiceOnLinux($) {
592 my $service=$_[0];
593
594 # get the list of parameters which could be used to initiate the service
595 # (could be in /etc/rc.d/rc?.d, /etc/inetd.conf, or /etc/inittab, so we
596 # check all of them)
597
598 my @params = @{ &getGlobal('SERVICE', $service) };
599 my $chkconfig = &getGlobal('BIN', 'chkconfig');
600 my $grep = &getGlobal('BIN', 'grep');
601 my $inittab = &getGlobal('FILE', 'inittab');
602 my $serviceType = &getGlobal('SERVTYPE', $service);;
603
604 # A kludge to get things running because &getGlobal('SERVICE' doesn't
605 # return the expected values.
606 @params = ();
607 push (@params, $service);
608
609 foreach my $param (@params) {
610 &B_log("DEBUG","Checking to see if service $service is off.\n");
611
612 if ($serviceType =~ /rc/) {
613 my $on = &B_Backtick("$chkconfig --list $param 2>&1");
614 if ($on =~ /^$param:\s+unknown/) {
615 # This service isn't installed on the system
616 return NOT_INSTALLED();
617 }
618 if ($on =~ /^error reading information on service $param: No such file or directory/) {
619 # This service isn't installed on the system
620 return NOT_INSTALLED();
621 }
622 if ($on =~ /^error/) {
623 # This probably
624 &B_log("DEBUG","chkconfig returned: $param=$on\n");
625 return undef;
626 }
627 $on =~ s/^$param\s+//; # remove the service name and spaces
628 $on =~ s/[0-6]:off\s*//g; # remove any runlevel:off entries
629 $on =~ s/:on\s*//g; # remove the :on from the runlevels
630 # what remains is a list of runlevels in which the service is on,
631 # or a null string if it is never turned on
632 chomp $on; # newline should be gone already (\s)
633 &B_log("DEBUG","chkconfig returned: $param=$on\n");
634
635 if ($on =~ /^\d+$/) {
636 # service is not off
637 ########################### BREAK out, don't skip question
638 return NOTSECURE_CAN_CHANGE();
639 }
640 }
641 elsif ($serviceType =~ /inet/) {
642 my $on = &B_Backtick("$chkconfig --list $param 2>&1");
643 if ($on =~ /^$param:\s+unknown/) {
644 # This service isn't installed on the system
645 return NOT_INSTALLED();
646 }
647 if ($on =~ /^error reading information on service $param: No such file or directory/) {
648 # This service isn't installed on the system
649 return NOT_INSTALLED();
650 }
651 if ($on =~ /^error/ ) {
652 # Something else is wrong?
653 # return undef
654 return undef;
655 }
656 if ($on =~ tr/\n// > 1) {
657 $on =~ s/^xinetd.+\n//;
658 }
659 $on =~ s/^\s*$param:?\s+//; # remove the service name and spaces
660 chomp $on; # newline should be gone already (\s)
661 &B_log("DEBUG","chkconfig returned: $param=$on\n");
662
663 if ($on =~ /^on$/) {
664 # service is not off
665 ########################### BREAK out, don't skip question
666 return NOTSECURE_CAN_CHANGE();
667 }
668 }
669 else {
670 # perhaps the service is started by inittab
671 my $inittabline = &B_Backtick("$grep -E '^[^#].{0,3}:.*:.+:.*$param' $inittab");
672 if ($inittabline =~ /.+/) { # . matches anything except newlines
673 # service is not off
674 &B_log("DEBUG","Checking inittab; found $inittabline\n");
675 ########################### BREAK out, don't skip question
676 return NOTSECURE_CAN_CHANGE();
677 }
678 }
679 } # foreach my $param
680
681
682 # boot-time parameters are not set; check processes
683 # Note the checkProcsforService returns INCONSISTENT() if a process is found
684 # assuming the checks above
685 return &checkProcsForService($service);
686}
687
6881;
689
690