1 /******************************************************************************
2 * Check user type based on login.defs.
3 *
4 * Copyright (c) 2020 Red Hat, Inc.
5 * Written by Pavel Březina <pbrezina@redhat.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, and the entire permission notice in its entirety,
12 * including the disclaimer of warranties.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior
18 * written permission.
19 *
20 * ALTERNATIVELY, this product may be distributed under the terms of
21 * the GNU Public License, in which case the provisions of the GPL are
22 * required INSTEAD OF the above restrictions. (This clause is
23 * necessary due to a potential bad interaction between the GPL and
24 * the restrictions contained in a BSD-style copyright.)
25 *
26 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
27 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
36 * OF THE POSSIBILITY OF SUCH DAMAGE.
37 *
38 */
39
40 #include "config.h"
41
42 #include <sys/types.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <syslog.h>
46 #include <unistd.h>
47 #include <pwd.h>
48 #include <ctype.h>
49 #include <errno.h>
50
51 #include <security/pam_modules.h>
52 #include <security/pam_modutil.h>
53 #include <security/pam_ext.h>
54
55 #define LOGIN_DEFS "/etc/login.defs"
56
57 enum pam_usertype_op {
58 OP_IS_SYSTEM,
59 OP_IS_REGULAR,
60
61 OP_SENTINEL
62 };
63
64 struct pam_usertype_opts {
65 enum pam_usertype_op op;
66 int use_uid;
67 int audit;
68 };
69
70 static int
71 pam_usertype_parse_args(struct pam_usertype_opts *opts,
72 pam_handle_t *pamh,
73 int argc,
74 const char **argv)
75 {
76 int i;
77
78 memset(opts, 0, sizeof(struct pam_usertype_opts));
79 opts->op = OP_SENTINEL;
80
81 for (i = 0; i < argc; i++) {
82 if (strcmp(argv[i], "use_uid") == 0) {
83 opts->use_uid = 1;
84 } else if (strcmp(argv[i], "audit") == 0) {
85 opts->audit = 1;
86 } else if (strcmp(argv[i], "issystem") == 0) {
87 opts->op = OP_IS_SYSTEM;
88 } else if (strcmp(argv[i], "isregular") == 0) {
89 opts->op = OP_IS_REGULAR;
90 } else {
91 pam_syslog(pamh, LOG_WARNING, "Unknown argument: %s", argv[i]);
92 /* Just continue. */
93 }
94 }
95
96 if (opts->op == OP_SENTINEL) {
97 pam_syslog(pamh, LOG_ERR, "Operation not specified");
98 return PAM_SERVICE_ERR;
99 }
100
101 return PAM_SUCCESS;
102 }
103
104 static int
105 pam_usertype_get_uid(struct pam_usertype_opts *opts,
106 pam_handle_t *pamh,
107 uid_t *_uid)
108 {
109 struct passwd *pwd;
110 const char *username;
111 int ret;
112
113 /* Get uid of user that runs the application. */
114 if (opts->use_uid) {
115 pwd = pam_modutil_getpwuid(pamh, getuid());
116 if (pwd == NULL) {
117 pam_syslog(pamh, LOG_ERR,
118 "error retrieving information about user %lu",
119 (unsigned long)getuid());
120 return PAM_USER_UNKNOWN;
121 }
122
123 *_uid = pwd->pw_uid;
124 return PAM_SUCCESS;
125 }
126
127 /* Get uid of user that is being authenticated. */
128 ret = pam_get_user(pamh, &username, NULL);
129 if (ret != PAM_SUCCESS) {
130 pam_syslog(pamh, LOG_NOTICE, "cannot determine user name: %s",
131 pam_strerror(pamh, ret));
132 return ret == PAM_CONV_AGAIN ? PAM_INCOMPLETE : ret;
133 }
134
135 pwd = pam_modutil_getpwnam(pamh, username);
136 if (pwd == NULL) {
137 if (opts->audit) {
138 pam_syslog(pamh, LOG_NOTICE,
139 "error retrieving information about user %s", username);
140 }
141
142 pam_modutil_getpwnam(pamh, "root");
143
144 return PAM_USER_UNKNOWN;
145 }
146 pam_modutil_getpwnam(pamh, "pam_usertype_non_existent:");
147
148 *_uid = pwd->pw_uid;
149
150 return PAM_SUCCESS;
151 }
152
153 #define MAX_UID_VALUE 0xFFFFFFFFUL
154
155 static uid_t
156 pam_usertype_get_id(pam_handle_t *pamh,
157 const char *key,
158 uid_t default_value)
159 {
160 unsigned long ul;
161 char *value;
162 char *ep;
163 uid_t uid;
164
165 value = pam_modutil_search_key(pamh, LOGIN_DEFS, key);
166 if (value == NULL) {
167 return default_value;
168 }
169
170 /* taken from get_lastlog_uid_max() */
171 ep = value + strlen(value);
172 while (ep > value && isspace(*(--ep))) {
173 *ep = '\0';
174 }
175
176 errno = 0;
177 ul = strtoul(value, &ep, 10);
178 if (!(ul >= MAX_UID_VALUE
179 || (uid_t)ul >= MAX_UID_VALUE
180 || (errno != 0 && ul == 0)
181 || value == ep
182 || *ep != '\0')) {
183 uid = (uid_t)ul;
184 } else {
185 uid = default_value;
186 }
187
188 free(value);
189
190 return uid;
191 }
192
193 static int
194 pam_usertype_is_system(pam_handle_t *pamh, uid_t uid)
195 {
196 uid_t uid_min;
197 uid_t sys_max;
198
199 if (uid == (uid_t)-1) {
200 pam_syslog(pamh, LOG_WARNING, "invalid uid");
201 return PAM_USER_UNKNOWN;
202 }
203
204 if (uid == PAM_USERTYPE_OVERFLOW_UID) {
205 /* nobody */
206 return PAM_SUCCESS;
207 }
208
209 uid_min = pam_usertype_get_id(pamh, "UID_MIN", PAM_USERTYPE_UIDMIN);
210 sys_max = pam_usertype_get_id(pamh, "SYS_UID_MAX", uid_min - 1);
211
212 if (uid <= sys_max && uid < uid_min) {
213 return PAM_SUCCESS;
214 }
215
216 return PAM_AUTH_ERR;
217 }
218
219 static int
220 pam_usertype_is_regular(pam_handle_t *pamh, uid_t uid)
221 {
222 int ret;
223
224 ret = pam_usertype_is_system(pamh, uid);
225 switch (ret) {
226 case PAM_SUCCESS:
227 return PAM_AUTH_ERR;
228 case PAM_USER_UNKNOWN:
229 return PAM_USER_UNKNOWN;
230 default:
231 return PAM_SUCCESS;
232 }
233 }
234
235 static int
236 pam_usertype_evaluate(struct pam_usertype_opts *opts,
237 pam_handle_t *pamh,
238 uid_t uid)
239 {
240 switch (opts->op) {
241 case OP_IS_SYSTEM:
242 return pam_usertype_is_system(pamh, uid);
243 case OP_IS_REGULAR:
244 return pam_usertype_is_regular(pamh, uid);
245 default:
246 pam_syslog(pamh, LOG_ERR, "Unknown operation: %d", opts->op);
247 return PAM_SERVICE_ERR;
248 }
249 }
250
251 /**
252 * Arguments:
253 * - issystem: uid less than SYS_UID_MAX
254 * - isregular: not issystem
255 * - use_uid: use user that runs application not that is being authenticate (same as in pam_succeed_if)
256 * - audit: log unknown users to syslog
257 */
258 int
259 pam_sm_authenticate(pam_handle_t *pamh, int flags UNUSED,
260 int argc, const char **argv)
261 {
262 struct pam_usertype_opts opts;
263 uid_t uid = -1;
264 int ret;
265
266 ret = pam_usertype_parse_args(&opts, pamh, argc, argv);
267 if (ret != PAM_SUCCESS) {
268 return ret;
269 }
270
271 ret = pam_usertype_get_uid(&opts, pamh, &uid);
272 if (ret != PAM_SUCCESS) {
273 return ret;
274 }
275
276 return pam_usertype_evaluate(&opts, pamh, uid);
277 }
278
279 int
280 pam_sm_setcred(pam_handle_t *pamh UNUSED, int flags UNUSED,
281 int argc UNUSED, const char **argv UNUSED)
282 {
283 return PAM_IGNORE;
284 }
285
286 int
287 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
288 {
289 return pam_sm_authenticate(pamh, flags, argc, argv);
290 }
291
292 int
293 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
294 {
295 return pam_sm_authenticate(pamh, flags, argc, argv);
296 }
297
298 int
299 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
300 {
301 return pam_sm_authenticate(pamh, flags, argc, argv);
302 }
303
304 int
305 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
306 {
307 return pam_sm_authenticate(pamh, flags, argc, argv);
308 }