Home | History | Annotate | Download | only in authtok_get
      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
     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
     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
    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
    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 		__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
    353 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
    354 {
    355 	return (PAM_IGNORE);
    356 }
    357