1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 *
25 * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
26 */
27
28 #include <sys/varargs.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <syslog.h>
32
33 #include <security/pam_appl.h>
34 #include <security/pam_modules.h>
35 #include <security/pam_impl.h>
36
37 #include <sys/note.h>
38
39 #include <libintl.h>
40
41 #include <passwdutil.h>
42
43 /*PRINTFLIKE2*/
44 void
error(pam_handle_t * pamh,char * fmt,...)45 error(pam_handle_t *pamh, char *fmt, ...)
46 {
47 va_list ap;
48 char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
49
50 va_start(ap, fmt);
51 (void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
52 (void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL);
53 va_end(ap);
54 }
55
56 int
read_authtok(pam_handle_t * pamh,int debug)57 read_authtok(pam_handle_t *pamh, int debug)
58 {
59 int res;
60 const char *authtok;
61 char *pwd;
62
63 /*
64 * We are about to read the new AUTHTOK. Store the AUTHTOK that
65 * the user used to authenticate in OLDAUTHTOK, so it is available
66 * to future modules. If OLDAUTHTOK is already set, we leave it alone
67 */
68
69 res = pam_get_item(pamh, PAM_OLDAUTHTOK, (const void **)&authtok);
70 if (res != PAM_SUCCESS)
71 return (res);
72
73 if (authtok == NULL) {
74 res = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&authtok);
75 if (res != PAM_SUCCESS)
76 return (res);
77 if (authtok != NULL) {
78 res = pam_set_item(pamh, PAM_OLDAUTHTOK,
79 (void *)authtok);
80 if (res == PAM_SUCCESS)
81 res = pam_set_item(pamh, PAM_AUTHTOK, NULL);
82
83 if (debug)
84 __pam_log(LOG_AUTH | LOG_DEBUG,
85 "read_authtok: Copied AUTHTOK to "
86 "OLDAUTHTOK");
87
88 if (res != PAM_SUCCESS)
89 goto out;
90 }
91 } else {
92 /*
93 * OLDAUTHTOK was filled in. If AUTHTOK is also filled
94 * in, we either succeed a module that has done our
95 * work, or we're here because one of the modules
96 * that are stacked beyond us has returned PAM_TRY_AGAIN.
97 * In either case, we should *not* prompt for another
98 * password.
99 */
100 res = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&pwd);
101 if (res != PAM_SUCCESS)
102 goto out;
103 if (pwd != NULL) {
104 goto out;
105 }
106 }
107
108 /*
109 * Make sure PAM_AUTHTOK is empty, or the framework will not
110 * put the value read by __pam_get_authtok into it
111 */
112 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
113
114 res = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
115 dgettext(TEXT_DOMAIN, "New Password: "), &pwd);
116
117 if (res != PAM_SUCCESS)
118 goto out;
119
120 if (pwd == NULL) {
121 const char *service;
122 if ((pam_get_item(pamh, PAM_SERVICE, (const void **)&service) ==
123 PAM_SUCCESS) && service != NULL) {
124 error(pamh, dgettext(TEXT_DOMAIN, "%s: Sorry."),
125 service);
126 }
127 res = PAM_PERM_DENIED;
128 } else {
129 (void) memset(pwd, 0, strlen(pwd));
130 free(pwd);
131 }
132 out:
133 if (res != PAM_SUCCESS) {
134 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
135 (void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
136 } else {
137 /*
138 * Since we don't actually check the password, we should
139 * not return PAM_SUCCESS if everything went OK.
140 * We should return PAM_IGNORE instead.
141 */
142 res = PAM_IGNORE;
143 }
144
145 return (res);
146 }
147
148 int
verify_authtok(pam_handle_t * pamh,int debug)149 verify_authtok(pam_handle_t *pamh, int debug)
150 {
151 int res;
152 const char *authtok;
153 char *pwd;
154
155 if (debug)
156 __pam_log(LOG_AUTH | LOG_DEBUG,
157 "pam_authtok_get: verifying authtok");
158
159 /*
160 * All we need to do, is make sure that the user re-enters
161 * the password correctly.
162 */
163
164 res = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&authtok);
165 if (res != PAM_SUCCESS || authtok == NULL)
166 return (PAM_AUTHTOK_ERR);
167
168 res = __pam_get_authtok(pamh, PAM_PROMPT, 0, dgettext(TEXT_DOMAIN,
169 "Re-enter new Password: "), &pwd);
170
171 if (res != PAM_SUCCESS)
172 return (res);
173
174 if (strcmp(authtok, pwd) != 0) {
175 const char *service;
176
177 if ((pam_get_item(pamh, PAM_SERVICE, (const void **)&service) ==
178 PAM_SUCCESS) && service != NULL) {
179 error(pamh, dgettext(TEXT_DOMAIN,
180 "%s: They don't match."), service);
181 }
182 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
183 (void) memset(pwd, 0, strlen(pwd));
184 free(pwd);
185 return (PAM_AUTHTOK_ERR);
186 }
187
188 if (debug)
189 __pam_log(LOG_AUTH | LOG_DEBUG,
190 "pam_authtok_get: new password verified");
191
192 (void) memset(pwd, 0, strlen(pwd));
193 free(pwd);
194 return (PAM_IGNORE);
195 }
196
197 int
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)198 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
199 {
200 int i;
201 int debug = 0;
202 int res;
203
204 for (i = 0; i < argc; i++)
205 if (strcmp(argv[i], "debug") == 0)
206 debug = 1;
207
208 if ((flags & PAM_PRELIM_CHECK) == PAM_PRELIM_CHECK)
209 res = read_authtok(pamh, debug);
210 else
211 res = verify_authtok(pamh, debug);
212
213 return (res);
214 }
215
216 /*
217 * int pam_sm_authenticate(pamh, flags, argc, argv)
218 *
219 * Read authentication token from user.
220 */
221 int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)222 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
223 {
224 const char *user;
225 char *password;
226 int i;
227 int debug = 0;
228 int res;
229 int fail = 0;
230
231 attrlist al[1];
232 const pam_repository_t *auth_rep = NULL;
233 pwu_repository_t *pwu_rep = NULL;
234
235 for (i = 0; i < argc; i++)
236 if (strcmp(argv[i], "debug") == 0)
237 debug = 1;
238
239 if (debug)
240 __pam_log(LOG_AUTH | LOG_DEBUG,
241 "pam_authtok_get:pam_sm_authenticate: flags = %d", flags);
242
243 if ((res = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) {
244 if (debug)
245 __pam_log(LOG_AUTH | LOG_DEBUG,
246 "pam_authtok_get: get user failed: %s",
247 pam_strerror(pamh, res));
248 return (res);
249 }
250
251 if (user == NULL || *user == '\0') {
252 __pam_log(LOG_AUTH | LOG_ERR,
253 "pam_authtok_get: pam_sm_authenticate: PAM_USER NULL or "
254 "empty");
255 return (PAM_SYSTEM_ERR);
256 }
257
258 res = pam_get_item(pamh, PAM_AUTHTOK, (const void **)&password);
259 if (res != PAM_SUCCESS)
260 return (res);
261
262 if (password != NULL)
263 return (PAM_IGNORE);
264
265 /*
266 * No password has been entered yet. Check to see if we need
267 * to obtain a password
268 */
269
270 res = pam_get_item(pamh, PAM_REPOSITORY, (const void **)&auth_rep);
271 if (res != PAM_SUCCESS) {
272 __pam_log(LOG_AUTH | LOG_ERR,
273 "pam_authtok_get: error getting repository");
274 return (PAM_SYSTEM_ERR);
275 }
276
277 if (auth_rep == NULL) {
278 pwu_rep = PWU_DEFAULT_REP;
279 } else {
280 if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
281 return (PAM_BUF_ERR);
282 pwu_rep->type = auth_rep->type;
283 pwu_rep->scope = auth_rep->scope;
284 pwu_rep->scope_len = auth_rep->scope_len;
285 }
286
287 (void) memset(&al, 0, sizeof (al));
288 al[0].type = ATTR_PASSWD;
289 al[0].next = NULL;
290
291 res = __get_authtoken_attr(user, pwu_rep, al);
292
293 if (pwu_rep != PWU_DEFAULT_REP)
294 free(pwu_rep);
295
296 if (res == PWU_SUCCESS &&
297 (al[0].data.val_s == NULL || al[0].data.val_s[0] == '\0')) {
298 const char *service = NULL;
299 const char *rhost = NULL;
300
301 /*
302 * if PAM_DIASALLOW_NULL_AUTHTOK has not been set, we
303 * simply return IGNORE
304 */
305 if ((flags & PAM_DISALLOW_NULL_AUTHTOK) == 0)
306 return (PAM_IGNORE);
307
308 /*
309 * NULL authtoks are not allowed, so we need to fail.
310 * We will ask for a password to mask the failure however.
311 */
312 (void) pam_get_item(pamh, PAM_RHOST, (const void **)&rhost);
313 (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
314 if (service == NULL)
315 service = "unknown";
316 if (rhost == NULL || *rhost == '\0')
317 rhost = "localhost";
318 __pam_log(LOG_AUTH | LOG_NOTICE,
319 "pam_authtok_get: %s: empty password not allowed for "
320 "%s from %s.", service, user, rhost);
321 fail = 1;
322 }
323 if (al[0].data.val_s != NULL) {
324 (void) memset(al[0].data.val_s, 0, strlen(al[0].data.val_s));
325 free(al[0].data.val_s);
326 }
327
328 res = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
329 dgettext(TEXT_DOMAIN, "Password: "), &password);
330 if (res != PAM_SUCCESS)
331 return (res);
332
333 if (password != NULL) {
334 (void) pam_set_item(pamh, PAM_AUTHTOK, (const void *)password);
335 (void) memset(password, 0, strlen(password));
336 free(password);
337 } else if (debug) {
338 __pam_log(LOG_AUTH | LOG_DEBUG,
339 "pam_authtok_get: pam_sm_authenticate: "
340 "got NULL password from get_authtok()");
341 }
342
343 if (fail) {
344 __pam_log(LOG_AUTH | LOG_DEBUG,
345 "pam_authtok_get:pam_sm_authenticate: "
346 "failing because NULL authtok not allowed");
347 return (PAM_AUTH_ERR);
348 } else
349 return (PAM_IGNORE);
350 }
351
352 /*ARGSUSED*/
353 int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)354 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
355 {
356 return (PAM_IGNORE);
357 }
358