d8459783db452dca16f298d261a6c2cdaea6eaf3
[pve-access-control.git] / PVE / Auth / PAM.pm
1 package PVE::Auth::PAM;
2
3 use strict;
4 use warnings;
5
6 use PVE::Tools qw(run_command);
7 use PVE::Auth::Plugin;
8 use Authen::PAM qw(:constants);
9
10 use base qw(PVE::Auth::Plugin);
11
12 sub type {
13     return 'pam';
14 }
15
16 sub options {
17     return {
18         default => { optional => 1 },
19         comment => { optional => 1 },
20         tfa => { optional => 1 },
21     };
22 }
23
24 sub authenticate_user {
25     my ($class, $config, $realm, $username, $password) = @_;
26
27     # user (www-data) need to be able to read /etc/passwd /etc/shadow
28     die "no password\n" if !$password;
29
30     my $pamh = new Authen::PAM('common-auth', $username, sub {
31         my @res;
32         while(@_) {
33             my $msg_type = shift;
34             my $msg = shift;
35             push @res, (0, $password);
36         }
37         push @res, 0;
38         return @res;
39     });
40
41     if (!ref ($pamh)) {
42         my $err = $pamh->pam_strerror($pamh);
43         die "error during PAM init: $err";
44     }
45
46     my $res;
47
48     if (($res = $pamh->pam_authenticate(0)) != PAM_SUCCESS) {
49         my $err = $pamh->pam_strerror($res);
50         die "$err\n";
51     }
52
53     if (($res = $pamh->pam_acct_mgmt (0)) != PAM_SUCCESS) {
54         my $err = $pamh->pam_strerror($res);
55         die "$err\n";
56     }
57
58     $pamh = 0; # call destructor
59
60     return 1;
61 }
62
63
64 sub store_password {
65     my ($class, $config, $realm, $username, $password) = @_;
66
67     my $cmd = ['usermod'];
68
69     my $epw = PVE::Auth::Plugin::encrypt_pw($password);
70
71     push @$cmd, '-p', $epw, $username;
72
73     run_command($cmd, errmsg => 'change password failed');
74 }
75
76 1;