Handle hostnames with upper-case letters
[webmin.git] / pam / pam-lib.pl
1 # pam-lib.pl
2 # Functions for manipulating the PAM services file(s)
3
4 BEGIN { push(@INC, ".."); };
5 use WebminCore;
6 &init_config();
7
8 # get_pam_config()
9 # Returns a list of services and their modules
10 sub get_pam_config
11 {
12 local @rv;
13 local @ignore = split(/\s+/, $config{'ignore'});
14 opendir(DIR, &translate_filename($config{'pam_dir'}));
15 FILE: foreach $f (readdir(DIR)) {
16         next if ($f =~ /^\./);
17         foreach $i (@ignore) {
18                 next FILE if ($f =~ /^$i$/i);
19                 }
20         local $serv = { 'name' => $f,
21                         'file' => "$config{'pam_dir'}/$f",
22                         'index' => scalar(@rv) };
23         local $lnum = 0;
24         &open_tempfile(FILE, $serv->{'file'});
25         while(<FILE>) {
26                 s/\r|\n//g;
27                 if (/^\s*#+\s*description:\s*([A-Za-z].*)/i &&
28                     !$serv->{'mods'}) {
29                         $serv->{'desc'} = $1;
30                         }
31                 s/#.*$//g;
32                 if (/^\s*\@include\s+(\S+)/) {
33                         # Special include line
34                         local $mod = { 'include' => $1,
35                                        'line' => $lnum,
36                                        'index' => @{$serv->{'mods'}}+0 };
37                         push(@{$serv->{'mods'}}, $mod);
38                         }
39                 elsif (/^\s*(\S+)\s+\[([^\]]*)\]\s+(\S+)\s*(.*)$/) {
40                         # Line with special rules .. ignore for now
41                         }
42                 elsif (/^\s*(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) {
43                         # Regular line
44                         local $mod = { 'type' => $1,   'control' => $2,
45                                        'module' => $3, 'args' => $4,
46                                        'line' => $lnum,
47                                        'index' => @{$serv->{'mods'}}+0 };
48                         push(@{$serv->{'mods'}}, $mod);
49                         }
50                 $lnum++;
51                 }
52         &close_tempfile(FILE);
53         push(@rv, $serv);
54         }
55 closedir(DIR);
56 return @rv;
57 }
58
59 # create_module(service, &module)
60 # Add a PAM module to some service
61 sub create_module
62 {
63 local $lref = &read_file_lines("$config{'pam_dir'}/$_[0]");
64 push(@$lref, &module_line($_[1]));
65 &flush_file_lines();
66 }
67
68 # modify_module(service, &module)
69 # Update a PAM module in some service
70 sub modify_module
71 {
72 local $lref = &read_file_lines("$config{'pam_dir'}/$_[0]");
73 splice(@$lref, $_[1]->{'line'}, 1, &module_line($_[1]));
74 &flush_file_lines();
75 }
76
77 # delete_module(service, &module)
78 # Delete a PAM module from some service
79 sub delete_module
80 {
81 local $lref = &read_file_lines("$config{'pam_dir'}/$_[0]");
82 splice(@$lref, $_[1]->{'line'}, 1);
83 &flush_file_lines();
84 }
85
86 # swap_modules(service, &module1, &module2)
87 # Swap two PAM module entries in a service
88 sub swap_modules
89 {
90 local $lref = &read_file_lines("$config{'pam_dir'}/$_[0]");
91 local $line = $lref->[$_[1]->{'line'}];
92 $lref->[$_[1]->{'line'}] = $lref->[$_[2]->{'line'}];
93 $lref->[$_[2]->{'line'}] = $line;
94 &flush_file_lines();
95 }
96
97 # module_line(&module)
98 # Returns text for a PAM module line
99 sub module_line
100 {
101 if ($_[0]->{'include'}) {
102         # Special include line
103         return "\@include ".$_[0]->{'include'};
104         }
105 else {
106         # A regular module
107         local $l = join("\t", $_[0]->{'type'}, $_[0]->{'control'},
108                               $_[0]->{'module'});
109         $l .= "\t$_[0]->{'args'}" if ($_[0]->{'args'});
110         return $l;
111         }
112 }
113
114 # list_modules()
115 # Returns a list of all PAM shared libraries
116 sub list_modules
117 {
118 local (@rv, %done, %hasmod);
119 foreach $d (split(/\s+/, $config{'lib_dirs'})) {
120         opendir(DIR, &translate_filename($d));
121         foreach $f (readdir(DIR)) {
122                 local @st = stat(&translate_filename("$d/$f"));
123                 push(@rv, $f) if (!$done{$st[1]}++ && $f =~ /^pam_.*\.so$/);
124                 $hasmod{$f}++ if ($f =~ /^pam_.*\.so$/);
125                 }
126         closedir(DIR);
127         }
128 foreach $q (split(/\s+/, $config{'mod_equiv'})) {
129         local ($q1, $q2) = split(/=/, $q);
130         if ($hasmod{$q2}) {
131                 @rv = grep { $_ ne $q1 } @rv;
132                 }
133         }
134 return &unique(@rv);
135 }
136
137 # include_style(&pam)
138 # Returns 1 if includes are done with pam_stack.so, 2 if done with include
139 # lines, 3 if done with @include, 0 if not supported
140 sub include_style
141 {
142 local ($pam) = @_;
143 local @allmods = map { @{$_->{'mods'}} } @$pam;
144 local ($atinc) = grep { $_->{'include'} } @allmods;
145 local ($inc) = grep { $_->{'control'} eq 'include' } @allmods;
146 local ($stack) = grep { $_ eq "pam_stack.so" } &list_modules();
147 return $atinc ? 3 : $inc ? 2 : $stack ? 1 : 0;
148 }
149
150 1;
151