nisplus_attr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <shadow.h>
#include <syslog.h>
#include <rpc/key_prot.h>
#include <rpcsvc/nispasswd.h>
#include <limits.h>
#include <nss_dbdefs.h>
#include <rpcsvc/nis_dhext.h>
#include "passwdutil.h"
#include "utils.h"
#include "npd_clnt.h"
/*
* nis+ definition
*/
#define PKTABLE "cred.org_dir"
#define PKTABLELEN 12
#define PASSTABLE "passwd.org_dir"
#define PASSTABLELEN 14
#define PKMAP "publickey.byname"
/*
* NIS+ columns
*/
#define COL_NAME 0
#define COL_PASSWD 1
#define COL_UID 2
#define COL_GID 3
#define COL_GECOS 4
#define COL_HOMEDIR 5
#define COL_SHELL 6
#define COL_SHADOW 7
/*
* undocumented NIS+ interface
*/
extern bool_t __nis_isadmin(char *, char *, char *);
void **buf);
char **auth_user, int *privileged);
/*
* nisplus function pointer table, used by passwdutil_init to initialize
* the global Repository-OPerations table "rops"
*/
struct repops nisplus_repops = {
NULL, /* checkhistory */
NULL, /* lock */
NULL /* unlock */
};
#define PWU_NO_PROTO 0
#define PWU_OLD_PROTO 1
#define PWU_NEW_PROTO 2
/*
*/
struct statebuf {
char *domain;
int proto; /* which protocol to use for the update */
int hash_pword; /* password is plaintext, hash before storing */
};
/*
* These messages match the nispasswd_code values in <rpcsvc/nispasswd.h>
*/
char *npd_errmsg[] = {
"Password update daemon is not running with NIS+ master server",
"User has no NIS+ password entry",
"NIS+ identifier invalid",
"User has no NIS+ password entry",
"No shadow password information",
"Shadow information corrupt",
"NIS+ password has not aged enough",
"Couldn't generate a common DES key",
"Invalid verifier",
"NIS+ password invalid",
"NIS+ server failed to encrypt verifier",
"NIS+ server failed to decrypt password",
"NIS+ keys updated",
"NIS+ server could not re-encrypt key",
"Permission denied",
"NIS+ server not responding",
"NIS+ error",
"NIS+ system error",
"NIS+ buffer too small",
"Invalid arguments"
};
/*
* nisplus_handle(name, domain, access_type)
*
* Create a handle used to talk to the NIS+ server
*
* 'access_type' flag is used to check whether we are doing a lookup or
* an update. If it's update, we will use MASTER_ONLY flag in the
* call to nis_list(). If it's lookup, it's okay to go and search
* in replica's database when master is down.
*
*/
{
return (NULL);
domain);
if (access_type == NISPLUS_LOOKUP)
else
return (NULL);
return (handle);
}
/*
* determine the name of the domain this user's account resides in.
*/
{
return (nis_local_directory());
return (pwd_domain);
}
int
{
/*
* In contrast to what we'd like, we really need to set the effective
* UID here, in order for nis_local_principal() to return an answer
* based on *who* we are instead of *what privileges* we have.
*
* This makes this module thread-unsafe!
*/
}
/*
* nisplus_user_to_authenticate(user, rep, auth_user, privileged)
*/
int
char **auth_user, int *privileged)
{
int res;
/*
* special case: don't bother to get root from NIS+
*/
return (PWU_NOT_FOUND);
if (res != PWU_SUCCESS)
return (res);
res = PWU_SUCCESS;
*privileged = 0;
goto out;
} else {
char pwd_buf[NSS_BUFLEN_PASSWD];
*privileged = 1;
goto out;
}
(int)uid);
} else {
}
}
out:
if (buf)
return (res);
}
/*
* nisplus_getattr(name, items, rep)
*
* retrieve attributes specified in "items"
*/
int
{
attrlist *p;
int res;
if (res != PWU_SUCCESS)
return (res);
switch (p->type) {
case ATTR_NAME:
break;
case ATTR_GECOS:
break;
case ATTR_HOMEDIR:
break;
case ATTR_SHELL:
break;
case ATTR_PASSWD:
break;
case ATTR_REP_NAME:
break;
case ATTR_UID:
break;
case ATTR_GID:
break;
case ATTR_LSTCHG:
break;
case ATTR_MIN:
break;
case ATTR_MAX:
break;
case ATTR_WARN:
break;
case ATTR_INACT:
break;
case ATTR_EXPIRE:
break;
case ATTR_FLAG:
break;
default:
break;
}
}
out:
if (buf)
return (res);
}
/*
* nisplus_getpwnam(name, items, rep, buf)
*
* Get all account info on user "name".
*/
/*ARGSUSED*/
int
void **buf)
{
int res;
return (PWU_NOMEM);
if (res != PWU_SUCCESS) {
return (res);
}
if (res != PWU_SUCCESS) {
return (res);
}
else
/*
* The protocol to use will be determined in nisplus_update()
*/
statebuf->hash_pword = 0;
return (PWU_SUCCESS);
}
/*
* max_present(list)
*
* returns '1' if a ATTR_MAX with value != -1 is present. (in other words:
* if password aging is to be turned on).
*/
static int
{
return (1);
else
return (0);
}
/*
* nisplus_update(items, rep, buf)
*
* Update the information in "buf" to reflect the attributes specified
* in items
*/
/*ARGSUSED*/
int
{
attrlist *p;
char *pword;
int len;
/*
* There are two different protocols that can be used to
* update the NIS+ server. The "new" protocol can be used
* for "passwd", "gecos" and "shell" info. All other info
* needs to be changed using the "old" protocol.
*
* Since nisplus_putpwnam() does not know what attributes
* have been changed, we keep track of which protocol to use
* in here.
*/
switch (p->type) {
/*
* We don't update NAME, UID, GID
*/
case ATTR_NAME:
case ATTR_UID:
case ATTR_GID:
break;
/*
* AGE and COMMENT are not supported by NIS+
*/
case ATTR_AGE:
case ATTR_COMMENT:
break;
/*
* Nothing special needs to be done for
* server policy
*/
case ATTR_PASSWD:
/*
* Note that we don't encrypt the new password.
* This encryption is done by the NPD client
* routines
*/
return (PWU_NOMEM);
/*
* We don't set col_flags since we use the new
* protocol to update the password
*/
/*
* In case we need to fall-back on the old protocol,
* we set the age fields here. Normally the NPD
* will update this field for us, but using the old
* update protocol, we need to do it ourselves.
*/
/* Forced password change. Disable aging */
}
break;
case ATTR_LOCK_ACCOUNT:
statebuf->hash_pword = 0;
sizeof (LOCKSTRING)-1) != 0) {
return (PWU_NOMEM);
}
statebuf->hash_pword = 0;
}
break;
case ATTR_UNLOCK_ACCOUNT:
sizeof (LOCKSTRING)-1) == 0) {
statebuf->hash_pword = 0;
}
break;
case ATTR_NOLOGIN_ACCOUNT:
}
return (PWU_NOMEM);
}
statebuf->hash_pword = 0;
break;
case ATTR_EXPIRE_PASSWORD:
break;
case ATTR_GECOS:
return (PWU_NOMEM);
break;
case ATTR_HOMEDIR:
return (PWU_NOMEM);
break;
case ATTR_SHELL:
return (PWU_NOMEM);
break;
case ATTR_LSTCHG:
break;
case ATTR_MIN:
max_present(p->next) == 0)
return (PWU_AGING_DISABLED);
break;
case ATTR_MAX:
/* Turn off aging. Reset min and warn too */
} else {
/* Turn account aging on */
/*
* minage was not set with command-
* line option: set to zero
*/
}
/*
* If aging was turned off, we update lstchg.
* We take care not to update lstchg if the
* user has no password, otherwise the user
* Might not be required to provide a password
* the next time [s]he logs in.
*/
}
}
break;
case ATTR_WARN:
return (PWU_AGING_DISABLED);
break;
case ATTR_INACT:
break;
case ATTR_EXPIRE:
break;
case ATTR_FLAG:
break;
default:
break;
}
}
return (PWU_SUCCESS);
}
/*
* nisplus_update_cred()
*
* Update the user's credentials. This routine is called if the
* old password is different from the old rpc password. In that case,
* the update though the npd will have changed the password, but not
* the credentials.
*/
int
{
char *domain;
char *newpw;
char mname[NIS_MAXNAMELEN];
int reencrypt_tries = 0;
int reencrypt_success = 0;
int mod_entry = 0;
int res;
int i;
return (PWU_BAD_CREDPASS);
}
else
/*
* a privileged user won't be able to update the user's credentials
*/
return (PWU_NO_PRIV_CRED_UPDATE);
}
return (PWU_NOT_FOUND);
}
return (PWU_RECOVERY_ERR);
if (res != PWU_SUCCESS) {
return (PWU_SYSTEM_ERROR);
}
return (PWU_SUCCESS);
}
char *oldcryptsecret;
char *newcryptsecret;
char *authtype;
continue;
}
if (newcryptsecret == NULL)
continue;
/* update cred at server */
return (PWU_RECOVERY_ERR);
}
return (PWU_RECOVERY_ERR);
}
return (PWU_RECOVERY_ERR);
}
mod_entry++;
/* set column stuff to NULL to that we can free eobj */
(void) nis_destroy_object(eobj);
(void) nis_freeresult(mres);
}
if (reencrypt_tries > 0 && mod_entry == 0) {
return (PWU_RECOVERY_ERR);
}
if (mod_entry < reencrypt_tries) {
return (PWU_UPDATED_SOME_CREDS);
}
return (PWU_SUCCESS);
}
/*
* nisplus_new_proto(name, oldpw, oldrpcpw, rep, buf)
*
* Implement NIS+ attribute updates using the NIS+ Password Daemon (NPD)
* This routine is used to update passwd, gecos and shell attributes
*/
int
{
/* pointers to possibly updated fields */
char *newpass;
char *gecos;
char *shell;
/* NIS+ server key material */
char *srv_pubkey = NULL;
/* User key material */
uint32_t ident = 0;
int error = 0;
int retval;
int npd_res;
"Couldn't make a client handle to NIS+ password daemon");
goto out;
}
goto out;
}
goto out;
}
/* Generate a key-pair for this user */
srv_keyalgtype, oldpw) == 0) {
goto out;
}
/*
* Get the common DES key(s) from the server's pubkey and the
* user's secret key
*/
goto out;
}
/*
* Must preserve password length here since NPD decrypts the login
* password as part of the authentication.
*/
if (npd_res == NPD_FAILED) {
if (error >= 0 &&
else
if (error == NPD_INVALIDARGS) {
} else if (error == NPD_PASSINVALID) {
retval = PWU_DENIED;
} else {
/*
* we need to tell the old protocol to update
* the password and the age fields.
*/
}
goto out;
} else if (npd_res == NPD_TRYAGAIN) {
/*
* Since the the non-privileged user's password has already
* been verified, we only get here if the privileged user
* entered a wrong NIS+ administrator password.
* The origional passwd requested administrator
* password again. We bail-out instead.
*/
goto out;
}
else
else
else
if (npd_res == NPD_FAILED) {
} else if (npd_res == NPD_PARTIALSUCCESS) {
/*
* This can only indicate that the server
* failed to update the credentials (SECRETKEY).
* We therefore try to update the credentials directly.
*/
} else {
}
} else {
}
out:
if (clnt) {
}
return (retval);
}
/*
* nisplus_old_proto(name, oldpw, oldrpcpw, rep, buf)
*
* Update account attributes using the nis_tables(3NSL) interface
*/
int
{
int *col_flags;
char key[NIS_MAXNAMELEN];
int pw_changed = 0; /* indicates whether we should update creds */
int retval;
return (PWU_RECOVERY_ERR);
if (col_flags[COL_PASSWD]) {
if (statebuf->hash_pword) {
char *salt;
return (PWU_NOMEM);
else {
/* algorithm problem? */
"passwdutil: crypt_gensalt "
"%m");
return (PWU_UPDATE_FAILED);
}
}
pw_changed = 1;
} else {
}
}
}
if (col_flags[COL_HOMEDIR]) {
}
}
if (col_flags[COL_SHADOW]) {
"%d:%d:%d:%d:%d::%u",
} else {
"%d:%d:%d:%d:%d:%d:%u",
}
}
return (PWU_RECOVERY_ERR);
}
goto out;
}
/*
* It is possible that we have permission to modify the
* encrypted password but not the shadow column in the
* NIS+ table. In this case, we should try updating only
* the password field and not the aging stuff (lstchg).
* With the current NIS+ passwd table format, this would
* be the case most of the times.
*/
goto again;
} else {
}
(void) nis_destroy_object(eobj);
(void) nis_freeresult(result);
out:
return (retval);
}
/*
* nis_putpwnam(name, oldpw, oldrpcpw, rep, buf)
*
* update the NIS+ server using the appropriate protocol(s) for the
* attributes that have changed.
*/
int
{
int result = PWU_SUCCESS;
return (PWU_NOT_FOUND);
if (oldpw) {
}
if (oldrpcpw) {
}
}
if (getuid() != 0)
}
return (result);
}
/*
* Given a cred table entry, return the secret key, auth type, key length
* of secret key, and algorithm type of secret key.
*
* The type of key must be listed in the NIS+ security cf.
*
* Return TRUE on success and FALSE on failure.
*/
int
char **seckey,
char **authtype,
{
return (0);
}
/* Don't need the "local" unix system cred. */
return (0);
}
sizeof (mechalias))) {
"can't convert authtype '%s' to mechanism alias",
*authtype);
return (0);
}
/* Make sure the mech is in the NIS+ security cf. */
"can't convert mechanism alias '%s' to keylen and algtype",
return (0);
}
return (0);
}
return (1);
}
int
{
int namelen;
struct nis_result *local_res;
char *local_cname;
"[auth_name=%d,auth_type=LOCAL],%s.%s",
return (PWU_SYSTEM_ERROR);
}
if (local_res)
return (PWU_SUCCESS);
}
if (local_cname == NULL) {
return (PWU_CRED_ERROR);
}
"[cname=%s],%s.%s", /* get all entries for user */
return (PWU_SYSTEM_ERROR);
}
return (PWU_SUCCESS);
}
int
{
if (uid == 0)
else
}
/*
* oldpw was truncated in pam_dhkeys' pam_sm_chauthtok.
*/
int
{
int res;
int i;
int success_cnt;
char *domain;
return (PWU_SUCCESS);
/* We need the user's uid */
return (PWU_NOT_FOUND);
else
/* privileged user; don't ask for old RPC password */
return (PWU_SUCCESS);
}
return (PWU_CRED_ERROR);
if (res != PWU_SUCCESS)
return (PWU_SYSTEM_ERROR);
return (PWU_CRED_ERROR);
/*
* Decrypt each of the credentials found with the oldpw.
* count the number of succesfull decrypts.
*/
success_cnt = 0;
char *auth;
char *key;
char *tmpkey;
continue;
"Can't get netname");
continue;
}
return (PWU_NOMEM);
success_cnt++;
}
if (success_cnt)
return (PWU_SUCCESS);
else
return (PWU_CRED_ERROR);
}
char *
{
char *newsec;
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (newsec);
}