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