xref: /illumos-gate/usr/src/lib/pam_modules/authtok_get/authtok_get.c (revision cbea7aca3fd7787405cbdbd93752998f03dfc25f)
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