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 
26 #include <sys/varargs.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <syslog.h>
30 
31 #include <security/pam_appl.h>
32 #include <security/pam_modules.h>
33 #include <security/pam_impl.h>
34 
35 #include <sys/note.h>
36 
37 #include <libintl.h>
38 
39 #include <passwdutil.h>
40 
41 /*PRINTFLIKE2*/
42 void
error(pam_handle_t * pamh,char * fmt,...)43 error(pam_handle_t *pamh, char *fmt, ...)
44 {
45 	va_list ap;
46 	char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
47 
48 	va_start(ap, fmt);
49 	(void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
50 	(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL);
51 	va_end(ap);
52 }
53 
54 int
read_authtok(pam_handle_t * pamh,int debug)55 read_authtok(pam_handle_t *pamh, int debug)
56 {
57 	int res;
58 	char *authtok;
59 	char *pwd;
60 
61 	/*
62 	 * We are about to read the new AUTHTOK. Store the AUTHTOK that
63 	 * the user used to authenticate in OLDAUTHTOK, so it is available
64 	 * to future modules. If OLDAUTHTOK is already set, we leave it alone
65 	 */
66 
67 	res = pam_get_item(pamh, PAM_OLDAUTHTOK, (void **)&authtok);
68 	if (res != PAM_SUCCESS)
69 		return (res);
70 
71 	if (authtok == NULL) {
72 		res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&authtok);
73 		if (res != PAM_SUCCESS)
74 			return (res);
75 		if (authtok != NULL) {
76 			res = pam_set_item(pamh, PAM_OLDAUTHTOK,
77 			    (void *)authtok);
78 			if (res == PAM_SUCCESS)
79 				res = pam_set_item(pamh, PAM_AUTHTOK, NULL);
80 
81 			if (debug)
82 				__pam_log(LOG_AUTH | LOG_DEBUG,
83 				    "read_authtok: Copied AUTHTOK to "
84 				    "OLDAUTHTOK");
85 
86 			if (res != PAM_SUCCESS)
87 				goto out;
88 		}
89 	} else {
90 		/*
91 		 * OLDAUTHTOK was filled in. If AUTHTOK is also filled
92 		 * in, we either succeed a module that has done our
93 		 * work, or we're here because one of the modules
94 		 * that are stacked beyond us has returned PAM_TRY_AGAIN.
95 		 * In either case, we should *not* prompt for another
96 		 * password.
97 		 */
98 		res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&pwd);
99 		if (res != PAM_SUCCESS)
100 			goto out;
101 		if (pwd != NULL) {
102 			goto out;
103 		}
104 	}
105 
106 	/*
107 	 * Make sure PAM_AUTHTOK is empty, or the framework will not
108 	 * put the value read by __pam_get_authtok into it
109 	 */
110 	(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
111 
112 	res = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
113 	    dgettext(TEXT_DOMAIN, "New Password: "), &pwd);
114 
115 	if (res != PAM_SUCCESS)
116 		goto out;
117 
118 	if (pwd == NULL) {
119 		char *service;
120 		if ((pam_get_item(pamh, PAM_SERVICE, (void **)&service) ==
121 		    PAM_SUCCESS) && service != NULL) {
122 			error(pamh, dgettext(TEXT_DOMAIN, "%s: Sorry."),
123 			    service);
124 		}
125 		res = PAM_PERM_DENIED;
126 	} else {
127 		(void) memset(pwd, 0, strlen(pwd));
128 		free(pwd);
129 	}
130 out:
131 	if (res != PAM_SUCCESS) {
132 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
133 		(void) pam_set_item(pamh, PAM_OLDAUTHTOK, NULL);
134 	} else {
135 		/*
136 		 * Since we don't actually check the password, we should
137 		 * not return PAM_SUCCESS if everything went OK.
138 		 * We should return PAM_IGNORE instead.
139 		 */
140 		res = PAM_IGNORE;
141 	}
142 
143 	return (res);
144 }
145 
146 int
verify_authtok(pam_handle_t * pamh,int debug)147 verify_authtok(pam_handle_t *pamh, int debug)
148 {
149 	int res;
150 	char *authtok;
151 	char *pwd;
152 
153 	if (debug)
154 		__pam_log(LOG_AUTH | LOG_DEBUG,
155 		    "pam_authtok_get: verifying authtok");
156 
157 	/*
158 	 * All we need to do, is make sure that the user re-enters
159 	 * the password correctly.
160 	 */
161 
162 	res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&authtok);
163 	if (res != PAM_SUCCESS || authtok == NULL)
164 		return (PAM_AUTHTOK_ERR);
165 
166 	res = __pam_get_authtok(pamh, PAM_PROMPT, 0, dgettext(TEXT_DOMAIN,
167 	    "Re-enter new Password: "), &pwd);
168 
169 	if (res != PAM_SUCCESS)
170 		return (res);
171 
172 	if (strcmp(authtok, pwd) != 0) {
173 		char *service;
174 
175 		if ((pam_get_item(pamh, PAM_SERVICE, (void **)&service) ==
176 		    PAM_SUCCESS) && service != NULL) {
177 			error(pamh, dgettext(TEXT_DOMAIN,
178 			    "%s: They don't match."), service);
179 		}
180 		(void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
181 		(void) memset(pwd, 0, strlen(pwd));
182 		free(pwd);
183 		return (PAM_AUTHTOK_ERR);
184 	}
185 
186 	if (debug)
187 		__pam_log(LOG_AUTH | LOG_DEBUG,
188 		    "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
pam_sm_chauthtok(pam_handle_t * pamh,int flags,int argc,const char ** argv)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
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)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 		__pam_log(LOG_AUTH | LOG_DEBUG,
240 		    "pam_authtok_get:pam_sm_authenticate: flags = %d", flags);
241 
242 	if ((res = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS) {
243 		if (debug)
244 			__pam_log(LOG_AUTH | 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 		__pam_log(LOG_AUTH | LOG_ERR,
252 		    "pam_authtok_get: pam_sm_authenticate: PAM_USER NULL or "
253 		    "empty");
254 		return (PAM_SYSTEM_ERR);
255 	}
256 
257 	res = pam_get_item(pamh, PAM_AUTHTOK, (void **)&password);
258 	if (res != PAM_SUCCESS)
259 		return (res);
260 
261 	if (password != NULL)
262 		return (PAM_IGNORE);
263 
264 	/*
265 	 * No password has been entered yet. Check to see if we need
266 	 * to obtain a password
267 	 */
268 
269 	res = pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep);
270 	if (res != PAM_SUCCESS) {
271 		__pam_log(LOG_AUTH | LOG_ERR,
272 		    "pam_authtok_get: error getting repository");
273 		return (PAM_SYSTEM_ERR);
274 	}
275 
276 	if (auth_rep == NULL) {
277 		pwu_rep = PWU_DEFAULT_REP;
278 	} else {
279 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
280 			return (PAM_BUF_ERR);
281 		pwu_rep->type = auth_rep->type;
282 		pwu_rep->scope = auth_rep->scope;
283 		pwu_rep->scope_len = auth_rep->scope_len;
284 	}
285 
286 	(void) memset(&al, 0, sizeof (al));
287 	al[0].type = ATTR_PASSWD;
288 	al[0].next = NULL;
289 
290 	res = __get_authtoken_attr(user, pwu_rep, al);
291 
292 	if (pwu_rep != PWU_DEFAULT_REP)
293 		free(pwu_rep);
294 
295 	if (res == PWU_SUCCESS &&
296 	    (al[0].data.val_s == NULL || al[0].data.val_s[0] == '\0')) {
297 		char *service = NULL;
298 		char *rhost = NULL;
299 
300 		/*
301 		 * if PAM_DIASALLOW_NULL_AUTHTOK has not been set, we
302 		 * simply return IGNORE
303 		 */
304 		if ((flags & PAM_DISALLOW_NULL_AUTHTOK) == 0)
305 			return (PAM_IGNORE);
306 
307 		/*
308 		 * NULL authtoks are not allowed, so we need to fail.
309 		 * We will ask for a password to mask the failure however.
310 		 */
311 		(void) pam_get_item(pamh, PAM_RHOST, (void **)&rhost);
312 		(void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
313 		if (service == NULL)
314 			service = "unknown";
315 		if (rhost == NULL || *rhost == '\0')
316 			rhost = "localhost";
317 		__pam_log(LOG_AUTH | LOG_NOTICE,
318 		    "pam_authtok_get: %s: empty password not allowed for "
319 		    "%s from %s.", service, user, rhost);
320 		fail = 1;
321 	}
322 	if (al[0].data.val_s != NULL) {
323 		(void) memset(al[0].data.val_s, 0, strlen(al[0].data.val_s));
324 		free(al[0].data.val_s);
325 	}
326 
327 	res = __pam_get_authtok(pamh, PAM_PROMPT, PAM_AUTHTOK,
328 	    dgettext(TEXT_DOMAIN, "Password: "), &password);
329 	if (res != PAM_SUCCESS)
330 		return (res);
331 
332 	if (password != NULL) {
333 		(void) pam_set_item(pamh, PAM_AUTHTOK, (void *)password);
334 		(void) memset(password, 0, strlen(password));
335 		free(password);
336 	} else if (debug) {
337 		__pam_log(LOG_AUTH | LOG_DEBUG,
338 		    "pam_authtok_get: pam_sm_authenticate: "
339 		    "got NULL password from get_authtok()");
340 	}
341 
342 	if (fail) {
343 		__pam_log(LOG_AUTH | LOG_DEBUG,
344 		    "pam_authtok_get:pam_sm_authenticate: "
345 		    "failing because NULL authtok not allowed");
346 		return (PAM_AUTH_ERR);
347 	} else
348 		return (PAM_IGNORE);
349 }
350 
351 /*ARGSUSED*/
352 int
pam_sm_setcred(pam_handle_t * pamh,int flags,int argc,const char ** argv)353 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
354 {
355 	return (PAM_IGNORE);
356 }
357