]>
Commit | Line | Data |
---|---|---|
1 | package PMG::AccessControl; | |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use Authen::PAM; | |
6 | ||
7 | use PVE::Tools; | |
8 | use PVE::JSONSchema qw(get_standard_option); | |
9 | use PVE::Exception qw(raise raise_perm_exc); | |
10 | ||
11 | use PMG::UserConfig; | |
12 | use PMG::LDAPConfig; | |
13 | use PMG::LDAPSet; | |
14 | ||
15 | sub normalize_path { | |
16 | my $path = shift; | |
17 | ||
18 | $path =~ s|/+|/|g; | |
19 | ||
20 | $path =~ s|/$||; | |
21 | ||
22 | $path = '/' if !$path; | |
23 | ||
24 | $path = "/$path" if $path !~ m|^/|; | |
25 | ||
26 | return undef if $path !~ m|^[[:alnum:]\.\-\_\/]+$|; | |
27 | ||
28 | return $path; | |
29 | } | |
30 | ||
31 | # password should be utf8 encoded | |
32 | # Note: some plugins delay/sleep if auth fails | |
33 | sub authenticate_user { | |
34 | my ($username, $password, $otp) = @_; | |
35 | ||
36 | die "no username specified\n" if !$username; | |
37 | ||
38 | my ($ruid, $realm); | |
39 | ||
40 | ($username, $ruid, $realm) = PMG::Utils::verify_username($username); | |
41 | ||
42 | if ($realm eq 'pam') { | |
43 | die "invalid pam user (only root allowed)\n" if $ruid ne 'root'; | |
44 | authenticate_pam_user($ruid, $password); | |
45 | return $username; | |
46 | } elsif ($realm eq 'pmg') { | |
47 | my $usercfg = PMG::UserConfig->new(); | |
48 | $usercfg->authenticate_user($username, $password); | |
49 | return $username; | |
50 | } elsif ($realm eq 'quarantine') { | |
51 | my $ldap_cfg = PMG::LDAPConfig->new(); | |
52 | my $ldap = PMG::LDAPSet->new_from_ldap_cfg($ldap_cfg, 1); | |
53 | ||
54 | if (my $ldapinfo = $ldap->account_info($ruid, $password)) { | |
55 | my $pmail = $ldapinfo->{pmail}; | |
56 | return $pmail . '@quarantine'; | |
57 | } else { | |
58 | die "ldap login failed\n"; | |
59 | } | |
60 | } | |
61 | ||
62 | die "no such realm '$realm'\n"; | |
63 | } | |
64 | ||
65 | sub set_user_password { | |
66 | my ($username, $password) = @_; | |
67 | ||
68 | my ($ruid, $realm); | |
69 | ||
70 | ($username, $ruid, $realm) = PMG::Utils::verify_username($username); | |
71 | ||
72 | if ($realm eq 'pam') { | |
73 | die "invalid pam user (only root allowed)\n" if $ruid ne 'root'; | |
74 | ||
75 | my $cmd = ['usermod']; | |
76 | ||
77 | my $epw = PVE::Tools::encrypt_pw($password); | |
78 | ||
79 | push @$cmd, '-p', $epw, $ruid; | |
80 | ||
81 | PVE::Tools::run_command($cmd, errmsg => "change password for '$ruid' failed"); | |
82 | ||
83 | } elsif ($realm eq 'pmg') { | |
84 | PMG::UserConfig->set_user_password($username, $password); | |
85 | } else { | |
86 | die "no such realm '$realm'\n"; | |
87 | } | |
88 | } | |
89 | ||
90 | # test if user exists and is enabled | |
91 | # returns: role | |
92 | sub check_user_enabled { | |
93 | my ($usercfg, $username, $noerr) = @_; | |
94 | ||
95 | my ($ruid, $realm); | |
96 | ||
97 | ($username, $ruid, $realm) = PMG::Utils::verify_username($username, 1); | |
98 | ||
99 | if ($realm && $ruid) { | |
100 | if ($realm eq 'pam') { | |
101 | return 'root' if $ruid eq 'root'; | |
102 | } elsif ($realm eq 'pmg') { | |
103 | my $usercfg = PMG::UserConfig->new(); | |
104 | my $data = $usercfg->lookup_user_data($username, $noerr); | |
105 | return $data->{role} if $data && $data->{enable}; | |
106 | } elsif ($realm eq 'quarantine') { | |
107 | return 'quser'; | |
108 | } | |
109 | } | |
110 | ||
111 | raise_perm_exc("user '$username' is disabled") if !$noerr; | |
112 | ||
113 | return undef; | |
114 | } | |
115 | ||
116 | sub authenticate_pam_user { | |
117 | my ($username, $password) = @_; | |
118 | ||
119 | # user need to be able to read /etc/passwd /etc/shadow | |
120 | ||
121 | my $pamh = Authen::PAM->new('common-auth', $username, sub { | |
122 | my @res; | |
123 | while(@_) { | |
124 | my $msg_type = shift; | |
125 | my $msg = shift; | |
126 | push @res, (0, $password); | |
127 | } | |
128 | push @res, 0; | |
129 | return @res; | |
130 | }); | |
131 | ||
132 | if (!ref($pamh)) { | |
133 | my $err = $pamh->pam_strerror($pamh); | |
134 | die "Error during PAM init: $err"; | |
135 | } | |
136 | ||
137 | my $res; | |
138 | ||
139 | if (($res = $pamh->pam_authenticate(0)) != PAM_SUCCESS) { | |
140 | my $err = $pamh->pam_strerror($res); | |
141 | die "auth failed: $err"; | |
142 | } | |
143 | ||
144 | if (($res = $pamh->pam_acct_mgmt(0)) != PAM_SUCCESS) { | |
145 | my $err = $pamh->pam_strerror($res); | |
146 | die "auth failed: $err"; | |
147 | } | |
148 | ||
149 | $pamh = 0; # call destructor | |
150 | ||
151 | return 1; | |
152 | } | |
153 | ||
154 | 1; |