Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : *
3 : : * Copyright © 2019 Endless Mobile, Inc.
4 : : *
5 : : * SPDX-License-Identifier: LGPL-2.1-or-later
6 : : *
7 : : * This library is free software; you can redistribute it and/or
8 : : * modify it under the terms of the GNU Lesser General Public
9 : : * License as published by the Free Software Foundation; either
10 : : * version 2.1 of the License, or (at your option) any later version.
11 : : *
12 : : * This library is distributed in the hope that it will be useful,
13 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this library; if not, write to the Free Software
19 : : * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 : : *
21 : : * Authors:
22 : : * - Philip Withnall <withnall@endlessm.com>
23 : : */
24 : :
25 : : #include "config.h"
26 : :
27 : : #define PAM_SM_ACCOUNT
28 : :
29 : : #include <glib.h>
30 : : #include <glib/gi18n-lib.h>
31 : : #include <libmalcontent/malcontent.h>
32 : : #include <pwd.h>
33 : : #include <security/pam_ext.h>
34 : : #include <security/pam_modules.h>
35 : : #include <security/pam_modutil.h>
36 : : #include <syslog.h>
37 : :
38 : :
39 : : /* Example usage:
40 : : *
41 : : * Here’s an example of a PAM file which uses `pam_malcontent.so`. Note
42 : : * that `pam_malcontent.so` must be listed before `pam_systemd.so`, and it must
43 : : * have type `account`.
44 : : *
45 : : * ```
46 : : * auth sufficient pam_unix.so nullok try_first_pass
47 : : * auth required pam_deny.so
48 : : *
49 : : * account required pam_nologin.so
50 : : * account sufficient pam_unix.so
51 : : * account required pam_permit.so
52 : : * -account required pam_malcontent.so
53 : : *
54 : : * password sufficient pam_unix.so nullok sha512 shadow try_first_pass try_authtok
55 : : * password required pam_deny.so
56 : : *
57 : : * -session optional pam_keyinit.so revoke
58 : : * -session optional pam_loginuid.so
59 : : * -session optional pam_systemd.so
60 : : * session sufficient pam_unix.so
61 : : * ```
62 : : */
63 : :
64 : : /* @pw_out is (transfer none) (out) (not optional) */
65 : : static int
66 : 0 : get_user_data (pam_handle_t *handle,
67 : : const char **username_out,
68 : : const struct passwd **pw_out)
69 : : {
70 : 0 : const char *username = NULL;
71 : 0 : struct passwd *pw = NULL;
72 : : int r;
73 : :
74 : 0 : g_return_val_if_fail (handle != NULL, PAM_AUTH_ERR);
75 : 0 : g_return_val_if_fail (username_out != NULL, PAM_AUTH_ERR);
76 : 0 : g_return_val_if_fail (pw_out != NULL, PAM_AUTH_ERR);
77 : :
78 : 0 : r = pam_get_user (handle, &username, NULL);
79 [ # # ]: 0 : if (r != PAM_SUCCESS)
80 : : {
81 : 0 : pam_syslog (handle, LOG_ERR, "Failed to get user name.");
82 : 0 : return r;
83 : : }
84 : :
85 [ # # # # ]: 0 : if (username == NULL || *username == '\0')
86 : : {
87 : 0 : pam_syslog (handle, LOG_ERR, "User name not valid.");
88 : 0 : return PAM_AUTH_ERR;
89 : : }
90 : :
91 : 0 : pw = pam_modutil_getpwnam (handle, username);
92 [ # # ]: 0 : if (pw == NULL)
93 : : {
94 : 0 : pam_syslog (handle, LOG_ERR, "Failed to get user data.");
95 : 0 : return PAM_USER_UNKNOWN;
96 : : }
97 : :
98 : 0 : *pw_out = pw;
99 : 0 : *username_out = username;
100 : :
101 : 0 : return PAM_SUCCESS;
102 : : }
103 : :
104 : : static void
105 : 0 : runtime_max_sec_free (pam_handle_t *handle,
106 : : void *data,
107 : : int error_status)
108 : : {
109 : 0 : g_return_if_fail (data != NULL);
110 : :
111 : 0 : g_free (data);
112 : : }
113 : :
114 : : PAM_EXTERN int
115 : : pam_sm_acct_mgmt (pam_handle_t *handle,
116 : : int flags,
117 : : int argc,
118 : : const char **argv)
119 : : {
120 : : int retval;
121 : 0 : const char *username = NULL;
122 : 0 : const struct passwd *pw = NULL;
123 : 0 : g_autoptr(GDBusConnection) connection = NULL;
124 : 0 : g_autoptr(MctManager) manager = NULL;
125 : 0 : g_autoptr(MctSessionLimits) limits = NULL;
126 : 0 : g_autoptr(GError) local_error = NULL;
127 : 0 : g_autofree gchar *runtime_max_sec_str = NULL;
128 : 0 : guint64 now = g_get_real_time ();
129 : 0 : guint64 time_remaining_secs = 0;
130 : 0 : gboolean time_limit_enabled = FALSE;
131 : :
132 : : /* Look up the user data from the handle. */
133 : 0 : retval = get_user_data (handle, &username, &pw);
134 [ # # ]: 0 : if (retval != PAM_SUCCESS)
135 : : {
136 : : /* The error has already been logged. */
137 : 0 : return retval;
138 : : }
139 : :
140 [ # # ]: 0 : if (pw->pw_uid == 0)
141 : : {
142 : : /* Always allow root, to avoid a situation where this PAM module prevents
143 : : * all users logging in with no way of recovery. */
144 : 0 : pam_info (handle, _("User ‘%s’ has no time limits enabled"), "root");
145 : 0 : return PAM_SUCCESS;
146 : : }
147 : :
148 : : /* Connect to the system bus. */
149 : 0 : connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &local_error);
150 [ # # ]: 0 : if (connection == NULL)
151 : : {
152 : 0 : pam_error (handle,
153 : : _("Error getting session limits for user ‘%s’: %s"),
154 : : username, local_error->message);
155 : 0 : return PAM_SERVICE_ERR;
156 : : }
157 : :
158 : : /* Get the time limits on this user’s session usage. */
159 : 0 : manager = mct_manager_new (connection);
160 : 0 : limits = mct_manager_get_session_limits (manager, pw->pw_uid,
161 : : MCT_MANAGER_GET_VALUE_FLAGS_NONE,
162 : : NULL, &local_error);
163 : :
164 [ # # ]: 0 : if (limits == NULL)
165 : : {
166 [ # # ]: 0 : if (g_error_matches (local_error, MCT_MANAGER_ERROR,
167 : : MCT_MANAGER_ERROR_DISABLED))
168 : : {
169 : 0 : return PAM_SUCCESS;
170 : : }
171 : : else
172 : : {
173 : 0 : pam_error (handle,
174 : : _("Error getting session limits for user ‘%s’: %s"),
175 : : username, local_error->message);
176 : 0 : return PAM_SERVICE_ERR;
177 : : }
178 : : }
179 : :
180 : : /* Check if there’s time left. */
181 [ # # ]: 0 : if (!mct_session_limits_check_time_remaining (limits, now, &time_remaining_secs,
182 : : &time_limit_enabled))
183 : : {
184 : 0 : pam_error (handle, _("User ‘%s’ has no time remaining"), username);
185 : 0 : return PAM_AUTH_ERR;
186 : : }
187 : :
188 [ # # ]: 0 : if (!time_limit_enabled)
189 : : {
190 : 0 : pam_info (handle, _("User ‘%s’ has no time limits enabled"), username);
191 : 0 : return PAM_SUCCESS;
192 : : }
193 : :
194 : : /* Propagate the remaining time to the `pam_systemd.so` module, which will
195 : : * end the user’s session when it runs out. */
196 : 0 : runtime_max_sec_str = g_strdup_printf ("%" G_GUINT64_FORMAT, time_remaining_secs);
197 : 0 : retval = pam_set_data (handle, "systemd.runtime_max_sec",
198 : 0 : g_steal_pointer (&runtime_max_sec_str), runtime_max_sec_free);
199 : :
200 [ # # ]: 0 : if (retval != PAM_SUCCESS)
201 : : {
202 : 0 : pam_error (handle, _("Error setting time limit on login session: %s"),
203 : : pam_strerror (handle, retval));
204 : 0 : return retval;
205 : : }
206 : :
207 : 0 : return PAM_SUCCESS;
208 : : }
|