#!/usr/local/bin/perl # save_shared.cgi # Update, create or delete a shared network require './dhcpd-lib.pl'; require './params-lib.pl'; &ReadParse(); &lock_file($config{'dhcpd_conf'}); ($par, $sha, $indent) = &get_branch('sha', $in{'new'}); $parconf = $par->{'members'}; # check acls %access = &get_module_acl(); &error_setup($text{'eacl_aviol'}); if ($in{'delete'}) { &error("$text{'eacl_np'} $text{'eacl_pdn'}") if !&can('rw', \%access, $sha, 1); } elsif ($in{'options'}) { &error("$text{'eacl_np'} $text{'eacl_psn'}") if !&can('r', \%access, $sha); } elsif ($in{'new'}) { &error("$text{'eacl_np'} $text{'eacl_pin'}") unless &can('c', \%access, $sha) && &can('rw', \%access, $par); # restrict duplicates if($access{'uniq_sha'}) { foreach $s (&find("shared-network", &get_config())) { &error("$text{'eacl_np'} $text{'eacl_uniq'}") if lc $s->{'values'}->[0] eq lc $in{'name'}; } } } else { &error("$text{'eacl_np'} $text{'eacl_pun'}") if !&can('rw', \%access, $sha); } # save if ($in{'options'}) { # Redirect to client options &redirect("edit_options.cgi?idx=$in{'idx'}"); exit; } else { if ($in{'delete'}) { &error_setup($text{'sshared_faildel'}); } else { &error_setup($text{'sshared_failsave'}); $in{'name'} =~ /^\S+$/ || &error($text{'sshared_invalidsname'}); $sha->{'values'} = [ $in{'name'} ]; $sha->{'comment'} = $in{'desc'}; } # Move hosts, groups and subnets into or out of this shared network @wasin = &find("host", $sha->{'members'}); foreach $hn (split(/\0/, $in{'hosts'})) { if ($hn =~ /(\d+),(\d+)/) { push(@nowin, $parconf->[$2]->{'members'}->[$1]); $nowpr{$parconf->[$2]->{'members'}->[$1]} = $parconf->[$2]; } elsif ($hn =~ /(\d+),/) { push(@nowin, $parconf->[$1]); $nowpr{$parconf->[$1]} = $par; } if ($nowin[$#nowin]->{'name'} ne "host") { &error($text{'sgroup_echanged'}); } } @wasgin = &find("group", $sha->{'members'}); foreach $gn (split(/\0/, $in{'groups'})) { if ($gn =~ /(\d+),(\d+)/) { push(@nowgin, $parconf->[$2]->{'members'}->[$1]); $nowgpr{$parconf->[$2]->{'members'}->[$1]} = $parconf->[$2]; } elsif ($gn =~ /(\d+),/) { push(@nowgin, $parconf->[$1]); $nowgpr{$parconf->[$1]} = $par; } if ($nowgin[$#nowgin]->{'name'} ne "group") { &error($text{'sgroup_echanged'}); } } @wasuin = &find("subnet", $sha->{'members'}); foreach $un (split(/\0/, $in{'subnets'})) { if ($un =~ /(\d+),(\d+)/) { push(@nowuin, $parconf->[$2]->{'members'}->[$1]); $nowupr{$parconf->[$2]->{'members'}->[$1]} = $parconf->[$2]; } elsif ($un =~ /(\d+),/) { push(@nowuin, $parconf->[$1]); $nowupr{$parconf->[$1]} = $par; } if ($nowuin[$#nowuin]->{'name'} ne "subnet") { &error($text{'sgroup_echanged'}); } } &error_setup($text{'eacl_aviol'}); foreach $h (&unique(@wasin, @nowin)) { $was = &indexof($h, @wasin) != -1; $now = &indexof($h, @nowin) != -1; # per-host ACLs for new or updated hosts if ($was != $now && !&can('rw', \%access, $h)) { &error("$text{'eacl_np'} $text{'eacl_pun'}"); } if ($was && !$now) { # Move out of the shared network &save_directive($sha, [ $h ], [ ], 0); &save_directive($par, [ ], [ $h ], 0); } elsif ($now && !$was) { # Move into the shared network (maybe from another) &save_directive($nowpr{$h}, [ $h ], [ ], 0); &save_directive($sha, [ ], [ $h ], 1); } } foreach $g (&unique(@wasgin, @nowgin)) { $was = &indexof($g, @wasgin) != -1; $now = &indexof($g, @nowgin) != -1; # per-group ACLs for new or updated groups if ($was != $now && !&can('rw', \%access, $g)) { &error("$text{'eacl_np'} $text{'eacl_pun'}"); } if ($was && !$now) { # Move out of the shared network &save_directive($sha, [ $g ], [ ], 0); &save_directive($par, [ ], [ $g ], 0); } elsif ($now && !$was) { # Move into the shared network (maybe from another) &save_directive($nowgpr{$g}, [ $g ], [ ], 0); &save_directive($sha, [ ], [ $g ], 1); } } foreach $u (&unique(@wasuin, @nowuin)) { $was = &indexof($u, @wasuin) != -1; $now = &indexof($u, @nowuin) != -1; # per-subnet ACLs for new or updated subnetss if ($was != $now && !&can('rw', \%access, $u)) { &error("$text{'eacl_np'} $text{'eacl_pun'}"); } if ($was && !$now) { # Move out of the shared network &save_directive($sha, [ $u ], [ ], 0); &save_directive($par, [ ], [ $u ], 0); if ($par->{'name'} eq "shared-network") { &fix_sequence($par); } } elsif ($now && !$was) { # Move into the shared network (maybe from another) &save_directive($nowupr{$u}, [ $u ], [ ], 0); &save_directive($sha, [ ], [ $u ], 1); if ($nowupr{$u}->{'name'} eq "shared-network") { &check_subnets($nowupr{$u}); } } } &check_subnets($sha); &fix_sequence($sha); if (!$in{'delete'}) { &parse_params($sha); if ($in{'new'}) { # Add this shared net &save_directive($par, [ ], [ $sha ], 0); } else { # Update shared net &save_directive($par, [ $sha ], [ $sha ], 0); } } } &flush_file_lines(); if ($in{'delete'}) { # Delete this net if ($in{'hosts'} eq "" && $in{'groups'} eq "" && $in{'subnets'} eq "") { &save_directive($par, [ $sha ], [ ], 0); &flush_file_lines(); } else { &unlock_file($config{'dhcpd_conf'}); &redirect("confirm_delete.cgi?idx=$in{'idx'}\&type=0"); exit; } } &unlock_file($config{'dhcpd_conf'}); &webmin_log($in{'delete'} ? 'delete' : $in{'new'} ? 'create' : 'modify', 'shared', $sha->{'values'}->[0], \%in); &redirect(""); # check whether thist shared network contains any subnet sub check_subnets { local(@subnets); @subnets = &find("subnet", $_[0]->{'members'}); if (@subnets == 0) { &error_setup($text{'sshared_failsave'}); &error(&text('sshared_nosubnet', $_[0]->{'values'}->[0])); } } # force hosts and groups to follow subnets sub fix_sequence { local(@subnets, $max, $u, $i); @subnets = &find("subnet", $_[0]->{'members'}); $max = -1; foreach $u (@subnets) { $max = $u->{'index'} > $max ? $u->{'index'} : $max; } for ($i = 0; $i < $max; $i++) { $u = $_[0]->{'members'}->[$i]; if ($u->{'name'} eq "host" || $u->{'name'} eq "group") { # move to the end of list &save_directive($_[0], [ $u ], [ ], 0); &save_directive($_[0], [ ], [ $u ], 0); } } }