]>
Commit | Line | Data |
---|---|---|
1360e6f0 DM |
1 | package PMG::AccessControl; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use Authen::PAM; | |
6 | ||
1a8170cf | 7 | use PVE::Tools; |
1360e6f0 | 8 | use PVE::JSONSchema qw(get_standard_option); |
5232f267 | 9 | use PVE::Exception qw(raise raise_perm_exc); |
1360e6f0 | 10 | |
62ebb4bc | 11 | use PMG::UserConfig; |
6c54c10a DM |
12 | use PMG::LDAPConfig; |
13 | use PMG::LDAPSet; | |
1360e6f0 DM |
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 | ||
1360e6f0 DM |
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 | ||
62ebb4bc | 40 | ($username, $ruid, $realm) = PMG::Utils::verify_username($username); |
1360e6f0 DM |
41 | |
42 | if ($realm eq 'pam') { | |
62ebb4bc DM |
43 | die "invalid pam user (only root allowed)\n" if $ruid ne 'root'; |
44 | authenticate_pam_user($ruid, $password); | |
1360e6f0 | 45 | return $username; |
6c54c10a | 46 | } elsif ($realm eq 'pmg') { |
62ebb4bc | 47 | my $usercfg = PMG::UserConfig->new(); |
be06bc52 | 48 | $usercfg->authenticate_user($username, $password); |
62ebb4bc | 49 | return $username; |
6c54c10a DM |
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 | } | |
62ebb4bc | 61 | |
1360e6f0 DM |
62 | die "no such realm '$realm'\n"; |
63 | } | |
64 | ||
be06bc52 DM |
65 | sub set_user_password { |
66 | my ($username, $password) = @_; | |
1360e6f0 | 67 | |
be06bc52 DM |
68 | my ($ruid, $realm); |
69 | ||
70 | ($username, $ruid, $realm) = PMG::Utils::verify_username($username); | |
1360e6f0 | 71 | |
62ebb4bc DM |
72 | if ($realm eq 'pam') { |
73 | die "invalid pam user (only root allowed)\n" if $ruid ne 'root'; | |
1360e6f0 | 74 | |
62ebb4bc | 75 | my $cmd = ['usermod']; |
1360e6f0 | 76 | |
1a8170cf | 77 | my $epw = PVE::Tools::encrypt_pw($password); |
1360e6f0 | 78 | |
62ebb4bc | 79 | push @$cmd, '-p', $epw, $ruid; |
1360e6f0 | 80 | |
5437c8d1 | 81 | PVE::Tools::run_command($cmd, errmsg => "change password for '$ruid' failed"); |
1360e6f0 | 82 | |
62ebb4bc | 83 | } elsif ($realm eq 'pmg') { |
be06bc52 | 84 | PMG::UserConfig->set_user_password($username, $password); |
62ebb4bc DM |
85 | } else { |
86 | die "no such realm '$realm'\n"; | |
87 | } | |
1360e6f0 DM |
88 | } |
89 | ||
62ebb4bc | 90 | # test if user exists and is enabled |
d3d13358 | 91 | # returns: role |
1360e6f0 | 92 | sub check_user_enabled { |
27ca2dae | 93 | my ($usercfg, $username, $noerr) = @_; |
1360e6f0 | 94 | |
be06bc52 DM |
95 | my ($ruid, $realm); |
96 | ||
97 | ($username, $ruid, $realm) = PMG::Utils::verify_username($username, 1); | |
1360e6f0 | 98 | |
62ebb4bc DM |
99 | if ($realm && $ruid) { |
100 | if ($realm eq 'pam') { | |
d3d13358 | 101 | return 'root' if $ruid eq 'root'; |
62ebb4bc DM |
102 | } elsif ($realm eq 'pmg') { |
103 | my $usercfg = PMG::UserConfig->new(); | |
be06bc52 | 104 | my $data = $usercfg->lookup_user_data($username, $noerr); |
d3d13358 | 105 | return $data->{role} if $data && $data->{enable}; |
6c54c10a DM |
106 | } elsif ($realm eq 'quarantine') { |
107 | return 'quser'; | |
62ebb4bc DM |
108 | } |
109 | } | |
1360e6f0 | 110 | |
5232f267 | 111 | raise_perm_exc("user '$username' is disabled") if !$noerr; |
1360e6f0 DM |
112 | |
113 | return undef; | |
114 | } | |
115 | ||
62ebb4bc | 116 | sub authenticate_pam_user { |
1360e6f0 DM |
117 | my ($username, $password) = @_; |
118 | ||
62ebb4bc | 119 | # user need to be able to read /etc/passwd /etc/shadow |
1360e6f0 DM |
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 | ||
9d7f54a3 | 144 | if (($res = $pamh->pam_acct_mgmt(0)) != PAM_SUCCESS) { |
1360e6f0 DM |
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 | ||
1360e6f0 | 154 | 1; |