dhkeys.c revision 730bc27851cd1c735ae12ea4660481c1fffd3ff6
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <stdarg.h>
#include <limits.h>
#include <signal.h>
#include <pthread.h>
#include <synch.h>
#include <rpcsvc/nispasswd.h>
#include <rpcsvc/yppasswd.h>
#include <rpc/key_prot.h>
#include <nss_dbdefs.h>
#include <nsswitch.h>
#include <rpcsvc/nis_dhext.h>
#include <security/pam_appl.h>
#include <security/pam_modules.h>
#include <security/pam_impl.h>
#include <libintl.h>
#include <passwdutil.h>
#include "key_call_uid.h"
/* to keep track of codepath */
#define CODEPATH_PAM_SM_AUTHENTICATE 0
#define CODEPATH_PAM_SM_SETCRED 1
#define SUNW_OLDRPCPASS "SUNW-OLD-RPC-PASSWORD"
extern int _nfssys(int, void *);
/*
* int msg(pamh, ...)
*
* display message to the user
*/
/*PRINTFLIKE2*/
static int
{
}
/*
* Get the secret key for the given netname, key length, and algorithm
* type and send it to keyserv if the given pw decrypts it. Update the
* following counter args as necessary: get_seckey_cnt, good_pw_cnt, and
* set_seckey_cnt.
*
* Returns 0 on malloc failure, else 1.
*/
static int
const char *netname, /* in */
const char *pw, /* in */
int *get_seckey_cnt, /* out */
int *good_pw_cnt, /* out */
int *set_seckey_cnt, /* out */
int flags, /* in */
int debug) /* in */
{
char *skey;
int skeylen;
return (0);
}
(*get_seckey_cnt)++;
if (skey[0]) {
/* password does decrypt secret key */
(*good_pw_cnt)++;
(*set_seckey_cnt)++;
} else {
if (debug)
"get_and_set_seckey: could not "
"set secret key for keytype "
}
} else {
sizeof (messages[0]),
"Password does not "
"decrypt secret key (type = %d-%d) "
}
}
} else {
if (debug)
"could not get secret key for keytype %d-%d",
}
return (1);
}
/*
* int establish_key(pamh, flags, debug, netname)
*
* This routine establishes the Secure RPC Credentials for the
* user specified in PAM_USER, using the password in PAM_AUTHTOK.
*
* Establishing RPC credentials is considered a "helper" function for the PAM
* stack so we should only return failures or PAM_IGNORE. Returning PAM_SUCCESS
* may short circuit the stack and circumvent later critical checks.
*
* Because this routine is used for both pam_authenticate *and*
* pam_setcred, we have to be somewhat careful:
*
* - if called from pam_sm_authenticate:
* 1. if no NIS+, we don't set credentials and return PAM_IGNORE.
* 2. else, we try to establish credentials;
*
* - if called from pam_sm_setcred:
* 1. if we are root (uid == 0), we do nothing and return
* PAM_IGNORE.
* 2. else, we try to establish credentials.
*
* We return framework errors as appropriate such as PAM_USER_UNKNOWN,
* PAM_BUF_ERR, PAM_PERM_DENIED.
*
* If we succeed in establishing credentials we return PAM_IGNORE.
*
* If we fail to establish credentials then we return:
* - PAM_IGNORE if we are called from pam_sm_authenticate and we
* don't need credentials;
* - PAM_SERVICE_ERR (credentials needed) or PAM_SYSTEM_ERR
* (credentials not needed) if netname could not be created;
* - PAM_AUTH_ERR (credentials needed) or PAM_IGNORE (credentials
* not needed) if no credentials were retrieved;
* - PAM_AUTH_ERR if the password didn't decrypt the cred;
* - PAM_SYSTEM_ERR if the cred's could not be stored.
*
* This routine returns the user's netname in "netname".
*
* All tools--but the PAM stack--currently use getpass() to obtain
* the user's secure RPC password. We must make sure we don't use more than
* the first des_block (eight) characters of whatever is handed down to us.
* Therefore, we use a local variable "short_pass" to hold those 8 char's.
*/
static int
char *netname)
{
char *user;
char *passwd;
int result;
int err;
char *scratch;
int scratchlen;
/*
* Default is that credentials are needed until we explicitly
* check they are. This means all failure codes are returned
* until then.
*/
int need_cred = -1;
int auth_cred_flags;
/*
* no_warn if creds not needed and
* authenticating
*/
struct pam_repository *auth_rep;
mechanism_t **mechs;
mechanism_t **mpp;
int get_seckey_cnt = 0;
int set_seckey_cnt = 0;
int good_pw_cnt = 0;
int valid_mech_cnt = 0;
if (debug)
return (PAM_USER_UNKNOWN);
}
return (PAM_BUF_ERR);
goto out;
}
/*
* We don't set credentials when root logs in.
* We do, however, need to set the credentials if the NIS+ permissions
* require so. Thus, we only bail out if we're root and we're
* called from pam_setcred.
*/
result = PAM_IGNORE;
goto out;
}
/*
* Check to see if we REALLY need to set the credentials, i.e.
* whether not being able to do so is an error or whether we
* can ignore it.
* We need to get the password from the repository that we're
* currently authenticating against. If this is the auth_path
* and the repository isn't NIS+ we can skip establishing credentials.
* Otherwise, we will try to establish credentials but it's only
* critical iff the password is "*NP*" and the repository is NIS+.
*/
return (PAM_BUF_ERR);
} else
if (pwu_rep != PWU_DEFAULT_REP)
if (result == PWU_NOT_FOUND) {
if (debug)
user);
goto out;
} else if (result != PWU_SUCCESS) {
goto out;
}
result = PAM_IGNORE;
goto out;
}
if (auth_path) {
} else {
}
if (uid == 0) /* "root", need to create a host-netname */
else
if (err != 1) {
if (debug)
if (need_cred) {
"Secure RPC Credentials to login.", user);
} else
goto out;
}
/* passwd can be NULL (no passwd or su as root) */
if (passwd) {
} else
short_passp = NULL;
if (AUTH_DES_COMPAT_CHK(mp))
break; /* fall through to AUTH_DES below */
if (!VALID_MECH_ENTRY(mp))
continue;
if (debug)
auth_cred_flags, debug)) {
goto out;
}
}
/* fall through to AUTH_DES below */
} else {
/*
* No usable mechs found in NIS+ security cf thus
* fallback to AUTH_DES compat.
*/
if (debug)
"found. Trying AUTH_DES.");
}
/*
* We always perform AUTH_DES for the benefit of non-NIS+
* services (e.g. NFS) that may depend on the classic des
* 192bit key being set.
*/
goto out;
}
if (debug) {
}
if (get_seckey_cnt == 0) { /* No credentials */
goto out;
}
if (good_pw_cnt == 0) { /* wrong password */
goto out;
}
if (set_seckey_cnt == 0) {
goto out;
}
/* Credentials have been successfully establish, return PAM_IGNORE. */
result = PAM_IGNORE;
out:
/*
* If we are authenticating we attempt to establish credentials
* where appropriate. Failure to do so is only an error if we
* definitely needed them. Thus always return PAM_IGNORE
* if we are authenticating and credentials were not needed.
*/
result = PAM_IGNORE;
if (repository_name)
if (repository_pass)
return (result);
}
int
{
int i;
int debug = 0;
int result;
for (i = 0; i < argc; i++) {
debug = 1;
flags |= PAM_SILENT;
}
netname);
return (result);
}
typedef struct argres {
int result;
} argres_t;
/*
* Revoke NFS DES credentials.
* NFS may not be installed so we need to deal with SIGSYS
* when we call _nfssys(); we thus call _nfssys() in a seperate thread that
* is created specifically for this call. The thread specific signalmask
* is set to ignore SIGSYS. After the call to _nfssys(), the thread
* ceases to exist.
*/
static void *
revoke_nfs_cred(void *ap)
{
struct nfs_revauth_args nra;
(void) sigemptyset(&isigset);
}
} else {
}
return (NULL);
}
static int
{
int result;
char *uname;
if (debug)
"pam_dhkeys: user NULL or empty in remove_key()");
return (PAM_USER_UNKNOWN);
}
if ((flags & PAM_SILENT) == 0) {
"removing root credentials would"
" break the rpc services that"));
"use secure rpc on this host!"));
"root may use keylogout -f to do"
" this (at your own risk)!"));
}
return (PAM_PERM_DENIED);
}
return (PAM_BUF_ERR);
} else
if (pwu_rep != PWU_DEFAULT_REP)
if (result == PWU_NOT_FOUND)
return (PAM_USER_UNKNOWN);
if (result == PWU_DENIED)
return (PAM_PERM_DENIED);
if (result != PWU_SUCCESS)
return (PAM_SYSTEM_ERR);
if ((flags & PAM_SILENT) == 0) {
"Warning: NFS credentials not destroyed"));
}
return (PAM_AUTH_ERR);
}
return (PAM_IGNORE);
}
int
{
int i;
int debug = 0;
int result;
for (i = 0; i < argc; i++) {
debug = 1;
flags |= PAM_SILENT;
}
/* Check for invalid flags */
(flags & PAM_REINITIALIZE_CRED) == 0 &&
(flags & PAM_REFRESH_CRED) == 0 &&
(flags & PAM_DELETE_CRED) == 0 &&
(flags & PAM_SILENT) == 0) {
flags);
return (PAM_SYSTEM_ERR);
}
/* doesn't apply to UNIX */
if (debug)
"ignored\n");
return (PAM_IGNORE);
}
if (flags & PAM_DELETE_CRED) {
if (debug)
} else {
/* Some diagnostics */
if ((flags & PAM_SILENT) == 0) {
if (result == PAM_AUTH_ERR)
"Password does not decrypt any secret "
"keys for %s."), netname);
"Could not set secret key(s) for %s. "
"The key server may be down."), netname);
}
/* Not having credentials set is not an error... */
result = PAM_IGNORE;
}
return (result);
}
/*ARGSUSED*/
void
{
if (data) {
}
}
int
{
int i;
int debug = 0;
int res;
char *oldpw;
char *user;
int tries;
int oldpw_ok;
char *oldrpcpw;
char *oldrpcpass;
char *data;
/* password truncated at 8 chars, see comment at establish_key() */
for (i = 0; i < argc; i++)
debug = 1;
if (debug)
if ((flags & PAM_PRELIM_CHECK) == 0)
return (PAM_IGNORE);
/*
* See if the old secure-rpc password has already been set
*/
if (res == PAM_SUCCESS) {
if (debug)
"pam_dhkeys: OLDRPCPASS already set");
return (PAM_IGNORE);
}
if (debug)
return (PAM_USER_UNKNOWN);
}
/* oldpw can be NULL (eg. root changing someone's passwd) */
if (oldpw) {
} else
short_passp = NULL;
/*
* For NIS+ we need to check whether the old password equals
* the RPC password. If it doesn't, we won't be able to update
* the secure RPC credentials later on in the process.
*/
else {
return (PAM_BUF_ERR);
}
case PWU_SUCCESS:
/* oldpw matches RPC password, or no RPC password needed */
if (pwu_rep != PWU_DEFAULT_REP)
if (short_passp) {
sizeof (short_pass));
return (PAM_BUF_ERR);
}
} else
return (PAM_IGNORE);
case PWU_NOT_FOUND:
if (pwu_rep != PWU_DEFAULT_REP)
return (PAM_USER_UNKNOWN);
case PWU_BAD_CREDPASS:
/* The old password does not decrypt any credentials */
break;
case PWU_CRED_ERROR:
/*
* Indicates that the user's credentials could not be
* retrieved or removed. This could occur when a NIS+
* user is in transition to another account authority.
*/
if (pwu_rep != PWU_DEFAULT_REP)
return (PAM_AUTHTOK_ERR);
default:
if (pwu_rep != PWU_DEFAULT_REP)
return (PAM_SYSTEM_ERR);
}
/*
* We got here because the OLDAUTHTOK doesn't match the Secure RPC
* password. In compliance with the old behavior, we give the
* user two chances to get the password right. If that succeeds
* all is well; if it doesn't, we'll return an error.
*/
"This password differs from your secure RPC password."));
tries = 0;
oldpw_ok = 0;
if (tries > 1)
"This password does not decrypt your "
"secure RPC password."));
"Please enter your old Secure RPC password: "), &oldpw);
if (res != PAM_SUCCESS) {
if (pwu_rep != PWU_DEFAULT_REP)
return (res);
}
oldpw_ok = 1;
}
if (pwu_rep != PWU_DEFAULT_REP)
if (oldpw_ok == 0) {
return (PAM_AUTHTOK_ERR);
}
/*
* Since the PAM framework only provides space for two different
* password (one old and one current), there is officially no
* place to put additional passwords (like our old rpc password).
* We have no choice but to stuff it in a data item, and hope it
* will be picked up by the password-update routines.
*/
return (PAM_BUF_ERR);
return (res);
}