xref: /illumos-gate/usr/src/lib/pam_modules/authtok_get/authtok_get.c (revision e7cbe64f7a72dae5cb44f100db60ca88f3313c65)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/varargs.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <syslog.h>
33 
34 #include <security/pam_appl.h>
35 #include <security/pam_modules.h>
36 #include <security/pam_impl.h>
37 
38 #include <sys/note.h>
39 
40 #include <libintl.h>
41 
42 #include <passwdutil.h>
43 
44 /*PRINTFLIKE2*/
45 void
46 error(pam_handle_t *pamh, char *fmt, ...)
47 {
48 	va_list ap;
49 	char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
50 
51 	va_start(ap, fmt);
52 	(void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
53 	(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL);
54 	va_end(ap);
55 }
56 
57 int
58 read_authtok(pam_handle_t *pamh, int debug)
59 {
60 	int res;
61 	char *authtok;
62 	char *pwd;
63 
64 	/*
65 	 * We are about to read the new AUTHTOK. Store the AUTHTOK that
66 	 * the user used to authenticate in OLDAUTHTOK, so it is available
67 	 * to future modules. If OLDAUTHTOK is already set, we leave it alone
68 	 */
69 
70 	res = pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&authtok);
71 	if (res != PAM_SUCCESS)
72 		return (res);
73 
74 	if (authtok == NULL) {
75 		res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&authtok);
76 		if (res != PAM_SUCCESS)
77 			return (res);
78 		if (authtok != NULL) {
79 			res = pam_set_item(pamh, PAM_OLDAUTHTOK,
80 						(void *)authtok);
81 			if (res == PAM_SUCCESS)
82 				res = pam_set_item(pamh, PAM_AUTHTOK, NULL);
83 
84 			if (debug)
85 				syslog(LOG_DEBUG, "read_authtok: Copied "
86 				    "AUTHTOK to 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, (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 		char *service;
122 		if ((pam_get_item(pamh, PAM_SERVICE, (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
149 verify_authtok(pam_handle_t *pamh, int debug)
150 {
151 	int res;
152 	char *authtok;
153 	char *pwd;
154 
155 	if (debug)
156 		syslog(LOG_DEBUG, "pam_authtok_get: verifying authtok");
157 
158 	/*
159 	 * All we need to do, is make sure that the user re-enters
160 	 * the password correctly.
161 	 */
162 
163 	res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&authtok);
164 	if (res != PAM_SUCCESS || authtok == NULL)
165 		return (PAM_AUTHTOK_ERR);
166 
167 	res = __pam_get_authtok(pamh, PAM_PROMPT, 0, dgettext(TEXT_DOMAIN,
168 	    "Re-enter new Password: "), &pwd);
169 
170 	if (res != PAM_SUCCESS)
171 		return (res);
172 
173 	if (strcmp(authtok, pwd) != 0) {
174 		char *service;
175 
176 		if ((pam_get_item(pamh, PAM_SERVICE, (void **)&service) ==
177 		    PAM_SUCCESS) && service != NULL) {
178 			error(pamh, dgettext(TEXT_DOMAIN,
179 			    "%s: They don't match."), service);
180 		}
181 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
182 		(void) memset(pwd, 0, strlen(pwd));
183 		free(pwd);
184 		return (PAM_AUTHTOK_ERR);
185 	}
186 
187 	if (debug)
188 		syslog(LOG_DEBUG, "pam_authtok_get: new password verified");
189 
190 	(void) memset(pwd, 0, strlen(pwd));
191 	free(pwd);
192 	return (PAM_IGNORE);
193 }
194 
195 int
196 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
197 {
198 	int i;
199 	int debug = 0;
200 	int res;
201 
202 	for (i = 0; i < argc; i++)
203 		if (strcmp(argv[i], "debug") == 0)
204 			debug = 1;
205 
206 	if ((flags & PAM_PRELIM_CHECK) == PAM_PRELIM_CHECK)
207 		res = read_authtok(pamh, debug);
208 	else
209 		res = verify_authtok(pamh, debug);
210 
211 	return (res);
212 }
213 
214 /*
215  * int pam_sm_authenticate(pamh, flags, argc, argv)
216  *
217  * Read authentication token from user.
218  */
219 int
220 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
221 {
222 
223 	char	*user;
224 	char	*password;
225 	int	i;
226 	int	debug = 0;
227 	int	res;
228 	int	fail = 0;
229 
230 	attrlist al[1];
231 	pam_repository_t *auth_rep = NULL;
232 	pwu_repository_t *pwu_rep  = NULL;
233 
234 	for (i = 0; i < argc; i++)
235 		if (strcmp(argv[i], "debug") == 0)
236 			debug = 1;
237 
238 	if (debug)
239 		syslog(LOG_DEBUG, "pam_authtok_get:pam_sm_authenticate: "
240 		    "flags = %d", flags);
241 
242 	if ((res = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) {
243 		if (debug)
244 			syslog(LOG_DEBUG,
245 			    "pam_authtok_get: get user failed: %s",
246 			    pam_strerror(pamh, res));
247 		return (res);
248 	}
249 
250 	if (user == NULL || *user == '\0') {
251 		syslog(LOG_ERR, "pam_authtok_get: pam_sm_authenticate: "
252 				"PAM_USER NULL or empty");
253 		return (PAM_SYSTEM_ERR);
254 	}
255 
256 	res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&password);
257 	if (res != PAM_SUCCESS)
258 		return (res);
259 
260 	if (password != NULL)
261 		return (PAM_IGNORE);
262 
263 	/*
264 	 * No password has been entered yet. Check to see if we need
265 	 * to obtain a password
266 	 */
267 
268 	res = pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep);
269 	if (res != PAM_SUCCESS) {
270 		syslog(LOG_ERR, "pam_authtok_get: error getting repository");
271 		return (PAM_SYSTEM_ERR);
272 	}
273 
274 	if (auth_rep == NULL) {
275 		pwu_rep = PWU_DEFAULT_REP;
276 	} else {
277 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
278 			return (PAM_BUF_ERR);
279 		pwu_rep->type = auth_rep->type;
280 		pwu_rep->scope = auth_rep->scope;
281 		pwu_rep->scope_len = auth_rep->scope_len;
282 	}
283 
284 	(void) memset(&al, 0, sizeof (al));
285 	al[0].type = ATTR_PASSWD;
286 	al[0].next = NULL;
287 
288 	res = __get_authtoken_attr(user, pwu_rep, al);
289 
290 	if (pwu_rep != PWU_DEFAULT_REP)
291 		free(pwu_rep);
292 
293 	if (res == PWU_SUCCESS &&
294 	    (al[0].data.val_s == NULL || al[0].data.val_s[0] == '\0')) {
295 		/*
296 		 * if PAM_DIASALLOW_NULL_AUTHTOK has not been set, we
297 		 * simply return IGNORE
298 		 */
299 		if ((flags & PAM_DISALLOW_NULL_AUTHTOK) == 0)
300 			return (PAM_IGNORE);
301 
302 		/*
303 		 * NULL authtoks are not allowed, so we need to
304 		 * fail. We will ask for a password to mask the
305 		 * failure however.
306 		 */
307 
308 		fail = 1;
309 	}
310 	if (al[0].data.val_s != NULL) {
311 		(void) memset(al[0].data.val_s, 0, strlen(al[0].data.val_s));
312 		free(al[0].data.val_s);
313 	}
314 
315 	res = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
316 	    dgettext(TEXT_DOMAIN, "Password: "), &password);
317 	if (res != PAM_SUCCESS)
318 		return (res);
319 
320 	if (password != NULL) {
321 		(void) pam_set_item(pamh, PAM_AUTHTOK, (void *)password);
322 		(void) memset(password, 0, strlen(password));
323 		free(password);
324 	} else if (debug) {
325 		syslog(LOG_DEBUG, "pam_authtok_get: pam_sm_authenticate: "
326 				"got NULL password from get_authtok()");
327 	}
328 
329 	if (fail) {
330 		syslog(LOG_DEBUG, "pam_authtok_get:pam_sm_authenticate: "
331 		    "failing because NULL authtok not allowed");
332 		return (PAM_AUTH_ERR);
333 	} else
334 		return (PAM_IGNORE);
335 }
336 
337 /*ARGSUSED*/
338 int
339 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
340 {
341 	return (PAM_IGNORE);
342 }
343