Home | History | Annotate | Download | only in dhkeys
      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 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #include <stdlib.h>
     27 #include <syslog.h>
     28 #include <errno.h>
     29 #include <string.h>
     30 #include <rpc/rpc.h>
     31 #include <unistd.h>
     32 #include <assert.h>
     33 #include <stdarg.h>
     34 #include <sys/types.h>
     35 #include <sys/wait.h>
     36 #include <limits.h>
     37 #include <signal.h>
     38 #include <pthread.h>
     39 #include <synch.h>
     40 
     41 #include <rpcsvc/nis.h>
     42 #include <rpcsvc/yppasswd.h>
     43 #include <rpcsvc/ypclnt.h>
     44 #include <rpc/key_prot.h>
     45 #include <rpc/rpc.h>
     46 #include <nfs/nfs.h>
     47 #include <nfs/nfssys.h>
     48 #include <nss_dbdefs.h>
     49 #include <nsswitch.h>
     50 #include <rpcsvc/nis_dhext.h>
     51 
     52 #include <security/pam_appl.h>
     53 #include <security/pam_modules.h>
     54 #include <security/pam_impl.h>
     55 
     56 #include <libintl.h>
     57 
     58 #include <sys/mman.h>
     59 
     60 #include <passwdutil.h>
     61 
     62 #include "key_call_uid.h"
     63 #include <shadow.h>
     64 
     65 extern	int	_nfssys(int, void *);
     66 
     67 /*
     68  * int msg(pamh, ...)
     69  *
     70  * display message to the user
     71  */
     72 /*PRINTFLIKE2*/
     73 static int
     74 msg(pam_handle_t *pamh, char *fmt, ...)
     75 {
     76 	va_list	ap;
     77 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
     78 
     79 	va_start(ap, fmt);
     80 	(void) vsnprintf(messages[0], sizeof (messages[0]), fmt, ap);
     81 	va_end(ap);
     82 
     83 	return (__pam_display_msg(pamh, PAM_ERROR_MSG, 1, messages, NULL));
     84 }
     85 
     86 
     87 /*
     88  * Get the secret key for the given netname, key length, and algorithm
     89  * type and send it to keyserv if the given pw decrypts it.  Update the
     90  * following counter args as necessary: get_seckey_cnt, good_pw_cnt, and
     91  * set_seckey_cnt.
     92  *
     93  * Returns 0 on malloc failure, else 1.
     94  */
     95 static int
     96 get_and_set_seckey(
     97 	pam_handle_t	*pamh,			/* in */
     98 	const char	*netname,		/* in */
     99 	keylen_t	keylen,			/* in */
    100 	algtype_t	algtype,		/* in */
    101 	const char	*pw,			/* in */
    102 	uid_t		uid,			/* in */
    103 	gid_t		gid,			/* in */
    104 	int		*get_seckey_cnt,	/* out */
    105 	int		*good_pw_cnt,		/* out */
    106 	int		*set_seckey_cnt,	/* out */
    107 	int		flags,			/* in */
    108 	int		debug)			/* in */
    109 {
    110 	char	*skey;
    111 	int	skeylen;
    112 	char	messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
    113 
    114 	skeylen = BITS2NIBBLES(keylen) + 1;
    115 
    116 	if ((skey = malloc(skeylen)) == NULL) {
    117 		return (0);
    118 	}
    119 
    120 	if (getsecretkey_g(netname, keylen, algtype, skey, skeylen, pw)) {
    121 		(*get_seckey_cnt)++;
    122 
    123 		if (skey[0]) {
    124 			/* password does decrypt secret key */
    125 			(*good_pw_cnt)++;
    126 			if (key_setnet_g_uid(netname, skey, keylen, NULL, 0,
    127 			    algtype, uid, gid) >= 0) {
    128 				(*set_seckey_cnt)++;
    129 			} else {
    130 				if (debug)
    131 					syslog(LOG_DEBUG, "pam_dhkeys: "
    132 					    "get_and_set_seckey: could not "
    133 					    "set secret key for keytype "
    134 					    "%d-%d", keylen, algtype);
    135 			}
    136 		} else {
    137 			if (pamh && !(flags & PAM_SILENT)) {
    138 				(void) snprintf(messages[0],
    139 				    sizeof (messages[0]),
    140 				    dgettext(TEXT_DOMAIN,
    141 				    "Password does not "
    142 				    "decrypt secret key (type = %d-%d) "
    143 				    "for '%s'."), keylen, algtype, netname);
    144 				(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 1,
    145 				    messages, NULL);
    146 			}
    147 		}
    148 	} else {
    149 		if (debug)
    150 			syslog(LOG_DEBUG, "pam_dhkeys: get_and_set_seckey: "
    151 			    "could not get secret key for keytype %d-%d",
    152 			    keylen, algtype);
    153 	}
    154 
    155 	free(skey);
    156 
    157 	return (1);
    158 }
    159 
    160 /*
    161  * int establish_key(pamh, flags, debug, netname)
    162  *
    163  * This routine establishes the Secure RPC Credentials for the
    164  * user specified in PAM_USER, using the password in PAM_AUTHTOK.
    165  *
    166  * Establishing RPC credentials is considered a "helper" function for the PAM
    167  * stack so we should only return failures or PAM_IGNORE. Returning PAM_SUCCESS
    168  * may short circuit the stack and circumvent later critical checks.
    169  *
    170  * we are called from pam_sm_setcred:
    171  *	1. if we are root (uid == 0), we do nothing and return
    172  *	   PAM_IGNORE.
    173  *	2. else, we try to establish credentials.
    174  *
    175  * We return framework errors as appropriate such as PAM_USER_UNKNOWN,
    176  * PAM_BUF_ERR, PAM_PERM_DENIED.
    177  *
    178  * If we succeed in establishing credentials we return PAM_IGNORE.
    179  *
    180  * If we fail to establish credentials then we return:
    181  *    - PAM_SERVICE_ERR (credentials needed) or PAM_SYSTEM_ERR
    182  *      (credentials not needed) if netname could not be created;
    183  *    - PAM_AUTH_ERR (credentials needed) or PAM_IGNORE (credentials
    184  *      not needed) if no credentials were retrieved;
    185  *    - PAM_AUTH_ERR if the password didn't decrypt the cred;
    186  *    - PAM_SYSTEM_ERR if the cred's could not be stored.
    187  *
    188  * This routine returns the user's netname in "netname".
    189  *
    190  * All tools--but the PAM stack--currently use getpass() to obtain
    191  * the user's secure RPC password. We must make sure we don't use more than
    192  * the first des_block (eight) characters of whatever is handed down to us.
    193  * Therefore, we use a local variable "short_pass" to hold those 8 char's.
    194  */
    195 static int
    196 establish_key(pam_handle_t *pamh, int flags, int debug, char *netname)
    197 {
    198 	char	*user;
    199 	char	*passwd;
    200 	char	short_pass[sizeof (des_block)+1], *short_passp;
    201 	int	result;
    202 	uid_t	uid;
    203 	gid_t	gid;
    204 	int	err;
    205 
    206 	struct passwd pw;	/* Needed to obtain uid */
    207 	char	*scratch;
    208 	int	scratchlen;
    209 
    210 	mechanism_t	**mechs;
    211 	mechanism_t	**mpp;
    212 	int	get_seckey_cnt = 0;
    213 	int	set_seckey_cnt = 0;
    214 	int	good_pw_cnt = 0;
    215 	int	valid_mech_cnt = 0;
    216 
    217 	(void) pam_get_item(pamh, PAM_USER, (void **)&user);
    218 
    219 	if (user == NULL || *user == '\0') {
    220 		if (debug)
    221 			syslog(LOG_DEBUG, "pam_dhkeys: user NULL or empty");
    222 		return (PAM_USER_UNKNOWN);
    223 	}
    224 
    225 	(void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&passwd);
    226 
    227 	scratchlen = sysconf(_SC_GETPW_R_SIZE_MAX);
    228 	if ((scratch = malloc(scratchlen)) == NULL)
    229 		return (PAM_BUF_ERR);
    230 
    231 	if (getpwnam_r(user, &pw, scratch, scratchlen) == NULL) {
    232 		result = PAM_USER_UNKNOWN;
    233 		goto out;
    234 	}
    235 
    236 	uid = pw.pw_uid;
    237 	gid = pw.pw_gid;
    238 
    239 	/*
    240 	 * We don't set credentials when root logs in.
    241 	 */
    242 	if (uid == 0) {
    243 		result = PAM_IGNORE;
    244 		goto out;
    245 	}
    246 
    247 	err = user2netname(netname, uid, NULL);
    248 
    249 	if (err != 1) {
    250 		if (debug)
    251 			syslog(LOG_DEBUG, "pam_dhkeys: user2netname failed");
    252 			result = PAM_SYSTEM_ERR;
    253 		goto out;
    254 	}
    255 
    256 	/* passwd can be NULL (no passwd or su as root) */
    257 	if (passwd) {
    258 		(void) strlcpy(short_pass, passwd, sizeof (short_pass));
    259 		short_passp = short_pass;
    260 	} else
    261 		short_passp = NULL;
    262 
    263 	if (mechs = __nis_get_mechanisms(FALSE)) {
    264 
    265 		for (mpp = mechs; *mpp; mpp++) {
    266 			mechanism_t *mp = *mpp;
    267 
    268 			if (AUTH_DES_COMPAT_CHK(mp))
    269 				break;	/* fall through to AUTH_DES below */
    270 
    271 			if (!VALID_MECH_ENTRY(mp))
    272 				continue;
    273 
    274 			if (debug)
    275 				syslog(LOG_DEBUG, "pam_dhkeys: trying "
    276 				    "key type = %d-%d", mp->keylen,
    277 				    mp->algtype);
    278 			valid_mech_cnt++;
    279 			if (!get_and_set_seckey(pamh, netname, mp->keylen,
    280 			    mp->algtype, short_passp, uid, gid,
    281 			    &get_seckey_cnt, &good_pw_cnt, &set_seckey_cnt,
    282 			    flags, debug)) {
    283 				result = PAM_BUF_ERR;
    284 				goto out;
    285 			}
    286 		}
    287 		__nis_release_mechanisms(mechs);
    288 		/* fall through to AUTH_DES below */
    289 	} else {
    290 		/*
    291 		 * No usable mechs found in security congifuration file thus
    292 		 * fallback to AUTH_DES compat.
    293 		 */
    294 		if (debug)
    295 			syslog(LOG_DEBUG, "pam_dhkeys: no valid mechs "
    296 			    "found. Trying AUTH_DES.");
    297 	}
    298 
    299 	/*
    300 	 * We always perform AUTH_DES for the benefit of services like NFS
    301 	 * that may depend on the classic des 192bit key being set.
    302 	 */
    303 	if (!get_and_set_seckey(pamh, netname, AUTH_DES_KEYLEN,
    304 	    AUTH_DES_ALGTYPE, short_passp, uid, gid, &get_seckey_cnt,
    305 	    &good_pw_cnt, &set_seckey_cnt, flags, debug)) {
    306 		result = PAM_BUF_ERR;
    307 		goto out;
    308 	}
    309 
    310 	if (debug) {
    311 		syslog(LOG_DEBUG, "pam_dhkeys: mech key totals:\n");
    312 		syslog(LOG_DEBUG, "pam_dhkeys: %d valid mechanism(s)",
    313 		    valid_mech_cnt);
    314 		syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) retrieved",
    315 		    get_seckey_cnt);
    316 		syslog(LOG_DEBUG, "pam_dhkeys: %d passwd decrypt successes",
    317 		    good_pw_cnt);
    318 		syslog(LOG_DEBUG, "pam_dhkeys: %d secret key(s) set",
    319 		    set_seckey_cnt);
    320 	}
    321 
    322 	if (get_seckey_cnt == 0) {		/* No credentials */
    323 		result = PAM_IGNORE;
    324 		goto out;
    325 	}
    326 
    327 	if (good_pw_cnt == 0) {			/* wrong password */
    328 		result = PAM_AUTH_ERR;
    329 		goto out;
    330 	}
    331 
    332 	if (set_seckey_cnt == 0) {
    333 		result = PAM_SYSTEM_ERR;
    334 		goto out;
    335 	}
    336 	/* Credentials have been successfully established, return PAM_IGNORE */
    337 	result = PAM_IGNORE;
    338 out:
    339 	/*
    340 	 * If we are authenticating we attempt to establish credentials
    341 	 * where appropriate. Failure to do so is only an error if we
    342 	 * definitely needed them. Thus always return PAM_IGNORE
    343 	 * if we are authenticating and credentials were not needed.
    344 	 */
    345 	free(scratch);
    346 
    347 	(void) memset(short_pass, '\0', sizeof (short_pass));
    348 
    349 	return (result);
    350 }
    351 
    352 /*ARGSUSED*/
    353 int
    354 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
    355 {
    356 	return (PAM_IGNORE);
    357 }
    358 
    359 
    360 typedef struct argres {
    361 	uid_t uid;
    362 	int result;
    363 } argres_t;
    364 
    365 /*
    366  * Revoke NFS DES credentials.
    367  * NFS may not be installed so we need to deal with SIGSYS
    368  * when we call _nfssys(); we thus call _nfssys() in a seperate thread that
    369  * is created specifically for this call. The thread specific signalmask
    370  * is set to ignore SIGSYS. After the call to _nfssys(), the thread
    371  * ceases to exist.
    372  */
    373 static void *
    374 revoke_nfs_cred(void *ap)
    375 {
    376 	struct nfs_revauth_args nra;
    377 	sigset_t isigset;
    378 	argres_t *argres = (argres_t *)ap;
    379 
    380 	nra.authtype = AUTH_DES;
    381 	nra.uid = argres->uid;
    382 
    383 	(void) sigemptyset(&isigset);
    384 	(void) sigaddset(&isigset, SIGSYS);
    385 
    386 	if (pthread_sigmask(SIG_BLOCK, &isigset, NULL) == 0) {
    387 		argres->result = _nfssys(NFS_REVAUTH, &nra);
    388 		if (argres->result < 0 && errno == ENOSYS) {
    389 			argres->result = 0;
    390 		}
    391 	} else {
    392 		argres->result = -1;
    393 	}
    394 	return (NULL);
    395 }
    396 
    397 static int
    398 remove_key(pam_handle_t *pamh, int flags, int debug)
    399 {
    400 	int result;
    401 	char *uname;
    402 	attrlist attr_pw[2];
    403 	struct pam_repository *auth_rep = NULL;
    404 	pwu_repository_t *pwu_rep;
    405 	uid_t uid;
    406 	gid_t gid;
    407 	argres_t argres;
    408 	pthread_t tid;
    409 
    410 	(void) pam_get_item(pamh, PAM_USER, (void **)&uname);
    411 	if (uname == NULL || *uname == NULL) {
    412 		if (debug)
    413 			syslog(LOG_DEBUG,
    414 			    "pam_dhkeys: user NULL or empty in remove_key()");
    415 		return (PAM_USER_UNKNOWN);
    416 	}
    417 
    418 	if (strcmp(uname, "root") == 0) {
    419 		if ((flags & PAM_SILENT) == 0) {
    420 			char msg[3][PAM_MAX_MSG_SIZE];
    421 			(void) snprintf(msg[0], sizeof (msg[0]),
    422 			    dgettext(TEXT_DOMAIN,
    423 			    "removing root credentials would"
    424 			    " break the rpc services that"));
    425 			(void) snprintf(msg[1], sizeof (msg[1]),
    426 			    dgettext(TEXT_DOMAIN,
    427 			    "use secure rpc on this host!"));
    428 			(void) snprintf(msg[2], sizeof (msg[2]),
    429 			    dgettext(TEXT_DOMAIN,
    430 			    "root may use keylogout -f to do"
    431 			    " this (at your own risk)!"));
    432 			(void) __pam_display_msg(pamh, PAM_ERROR_MSG, 3,
    433 			    msg, NULL);
    434 		}
    435 		return (PAM_PERM_DENIED);
    436 	}
    437 
    438 	(void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&auth_rep);
    439 	if (auth_rep != NULL) {
    440 		if ((pwu_rep = calloc(1, sizeof (*pwu_rep))) == NULL)
    441 			return (PAM_BUF_ERR);
    442 		pwu_rep->type = auth_rep->type;
    443 		pwu_rep->scope = auth_rep->scope;
    444 		pwu_rep->scope_len = auth_rep->scope_len;
    445 	} else
    446 		pwu_rep = PWU_DEFAULT_REP;
    447 
    448 	/* Retrieve user's uid/gid from the password repository */
    449 	attr_pw[0].type = ATTR_UID; attr_pw[0].next = &attr_pw[1];
    450 	attr_pw[1].type = ATTR_GID; attr_pw[1].next = NULL;
    451 
    452 	result = __get_authtoken_attr(uname, pwu_rep, attr_pw);
    453 
    454 	if (pwu_rep != PWU_DEFAULT_REP)
    455 		free(pwu_rep);
    456 
    457 	if (result == PWU_NOT_FOUND)
    458 		return (PAM_USER_UNKNOWN);
    459 	if (result == PWU_DENIED)
    460 		return (PAM_PERM_DENIED);
    461 	if (result != PWU_SUCCESS)
    462 		return (PAM_SYSTEM_ERR);
    463 
    464 	uid = (uid_t)attr_pw[0].data.val_i;
    465 	gid = (gid_t)attr_pw[1].data.val_i;
    466 
    467 	(void) key_removesecret_g_uid(uid, gid);
    468 
    469 	argres.uid = uid;
    470 	argres.result = -1;
    471 
    472 	if (pthread_create(&tid, NULL, revoke_nfs_cred, (void *)&argres) == 0)
    473 		(void) pthread_join(tid, NULL);
    474 
    475 	if (argres.result < 0) {
    476 		if ((flags & PAM_SILENT) == 0) {
    477 			(void) msg(pamh, dgettext(TEXT_DOMAIN,
    478 			    "Warning: NFS credentials not destroyed"));
    479 		}
    480 		return (PAM_AUTH_ERR);
    481 	}
    482 
    483 	return (PAM_IGNORE);
    484 }
    485 
    486 int
    487 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
    488 {
    489 	int	i;
    490 	int	debug = 0;
    491 	int	result;
    492 	char	netname[MAXNETNAMELEN + 1];
    493 
    494 	for (i = 0; i < argc; i++) {
    495 		if (strcmp(argv[i], "debug") == 0)
    496 			debug = 1;
    497 		else if (strcmp(argv[i], "nowarn") == 0)
    498 			flags |= PAM_SILENT;
    499 	}
    500 
    501 	/* Check for invalid flags */
    502 	if (flags && (flags & PAM_ESTABLISH_CRED) == 0 &&
    503 	    (flags & PAM_REINITIALIZE_CRED) == 0 &&
    504 	    (flags & PAM_REFRESH_CRED) == 0 &&
    505 	    (flags & PAM_DELETE_CRED) == 0 &&
    506 	    (flags & PAM_SILENT) == 0) {
    507 		syslog(LOG_ERR, "pam_dhkeys: pam_setcred: illegal flags %d",
    508 		    flags);
    509 		return (PAM_SYSTEM_ERR);
    510 	}
    511 
    512 
    513 	if ((flags & PAM_REINITIALIZE_CRED) || (flags & PAM_REFRESH_CRED)) {
    514 		/* doesn't apply to UNIX */
    515 		if (debug)
    516 			syslog(LOG_DEBUG, "pam_dhkeys: cred reinit/refresh "
    517 			    "ignored\n");
    518 		return (PAM_IGNORE);
    519 	}
    520 
    521 	if (flags & PAM_DELETE_CRED) {
    522 		if (debug)
    523 			syslog(LOG_DEBUG, "pam_dhkeys: removing creds\n");
    524 		result = remove_key(pamh, flags, debug);
    525 	} else {
    526 		result = establish_key(pamh, flags, debug, netname);
    527 		/* Some diagnostics */
    528 		if ((flags & PAM_SILENT) == 0) {
    529 			if (result == PAM_AUTH_ERR)
    530 				(void) msg(pamh, dgettext(TEXT_DOMAIN,
    531 				    "Password does not decrypt any secret "
    532 				    "keys for %s."), netname);
    533 			else if (result == PAM_SYSTEM_ERR && netname[0])
    534 				(void) msg(pamh, dgettext(TEXT_DOMAIN,
    535 				    "Could not set secret key(s) for %s. "
    536 				    "The key server may be down."), netname);
    537 		}
    538 
    539 		/* Not having credentials set is not an error... */
    540 		result = PAM_IGNORE;
    541 	}
    542 
    543 	return (result);
    544 }
    545 
    546 /*ARGSUSED*/
    547 void
    548 rpc_cleanup(pam_handle_t *pamh, void *data, int pam_status)
    549 {
    550 	if (data) {
    551 		(void) memset(data, 0, strlen(data));
    552 		free(data);
    553 	}
    554 }
    555 
    556 /*ARGSUSED*/
    557 int
    558 pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
    559 {
    560 	return (PAM_IGNORE);
    561 }
    562