npd_svcsubr.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 1994-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Contains the sub-routines required by the server.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <shadow.h>
#include <crypt.h>
#include <rpc/key_prot.h>
#include <rpc/des_crypt.h>
#include <mp.h>
#include <rpcsvc/nispasswd.h>
#include <rpcsvc/nis_dhext.h>
#include <unistd.h>
#include <pwd.h>
#include <limits.h>
#include "npd_svcsubr.h"
#include "nisplus_tables.h"
extern int verbose;
extern bool_t generatekeys;
bool_t __npd_prin2netname(char *, char []);
/*
* get the password information for this user
* the result should be freed using nis_freeresult()
*/
struct nis_result *
char *user;
char *domain;
{
char buf[NIS_MAXNAMELEN];
return (NULL);
/* strlen("[name=],passwd.org_dir.") + null + "." = 25 */
return (NULL);
}
/*
* get the credential information for this user
* the result should be freed using nis_freeresult()
*/
static struct nis_result *
char *principal;
char *domain;
char *auth_type;
{
char buf[NIS_MAXNAMELEN];
return (NULL);
/* strlen("[cname=,auth_type=],cred.org_dir.") + null + "." = 35 */
return (NULL);
}
static bool_t
struct nis_object *obj;
{
long val;
/* defaults */
/* shadow information is in column 7 */
return (FALSE);
return (FALSE);
p = ageinfo;
/* format is: lstchg:min:max:warn:inact:expire:flag */
return (FALSE);
}
if (ep != p)
p = ep + 1;
return (FALSE);
}
if (ep != p)
p = ep + 1;
return (FALSE);
}
if (ep != p)
p = ep + 1;
return (FALSE);
}
if (ep != p)
p = ep + 1;
return (FALSE);
}
if (ep != p)
p = ep + 1;
return (FALSE);
}
if (ep != p)
p = ep + 1;
return (FALSE);
}
if (ep != p)
return (TRUE);
}
/*
* checks if the password has aged sufficiently
*/
struct nis_object *obj;
int *res;
{
long now;
/* password changed before or just now */
*res = NPD_NOTAGED;
return (FALSE);
} else
return (TRUE);
}
}
*res = NPD_NOSHDWINFO;
return (FALSE);
}
/*
* Authenticate the admin by decrypting any of their secret keys with
* the passwd they sent across. If any of the secret keys, of mech types
* listed in the NIS+ security cf, are decrypted, then return TRUE.
*/
{
char *d;
struct nis_result *cres;
mechanism_t **mechs;
return (FALSE);
"__authenticate_admin: cannot convert principal '%s' to netname", prin);
return (FALSE);
}
if (d == NULL)
d = nis_local_directory();
else
d++;
mechanism_t **mpp;
if (verbose)
"__authenticate_admin: trying mech '%s' for user '%s'",
if (AUTH_DES_COMPAT_CHK(mp)) {
goto try_auth_des;
}
if (! VALID_MECH_ENTRY(mp))
continue;
sizeof (auth_type)))
continue;
char *sp;
if (debug)
"__authenticate_admin: got cred entry");
mechs);
if (debug)
"__authenticate_admin: xdecrypt success");
(void) nis_freeresult(cres);
return (TRUE);
}
}
}
(void) nis_freeresult(cres);
}
} else {
/*
* No valid mechs in the NIS+ security cf file,
* so let's try AUTH_DES.
*/
char *sp;
(void) nis_freeresult(cres);
return (TRUE);
}
}
}
(void) nis_freeresult(cres);
}
return (FALSE);
}
/*
* build a netname given a nis+ principal name
*/
char *prin;
char netname[MAXNETNAMELEN];
{
char name[NIS_MAXNAMELEN];
char *domain;
return (FALSE);
return (FALSE);
/* get the domain name */
else
return (FALSE);
return (FALSE);
else
*domain++ = '\0';
/* nis_getpwdent() will fully qualify the domain */
return (FALSE);
case NIS_SUCCESS:
(void) nis_freeresult(pass_res);
/* we cannot simply call user2netname() ! */
return (FALSE);
return (TRUE);
case NIS_NOTFOUND:
/*
* assumption: hosts DO NOT have their passwords
* stored in NIS+ ==> not a user but a host
*/
return (FALSE);
(void) nis_freeresult(pass_res);
return (TRUE);
default:
(void) nis_freeresult(pass_res);
return (FALSE);
}
}
/*
* make a new DH (classic "DES" or new "DH640-0") cred entry and add it
*/
char *domain, /* domain name */
char *auth_type, /* cred table auth type name */
char *newpass) /* new pw used to encrypt secret key */
{
char *encrypted_secret = NULL;
struct nis_result *mod_res;
char buf[NIS_MAXNAMELEN];
char nisdomain[NIS_MAXNAMELEN];
char netname[MAXNETNAMELEN];
int status;
/* check args */
goto out;
}
goto out;
}
/* build netname from principal name */
goto out;
}
/* initialize object */
/* owner: r; group: rmcd */
goto out;
}
goto out;
}
/* generate key-pair */
newpass)) {
"could not generate DH key pair for %d-%d",
goto out;
}
&encrypted_secret, TRUE)) {
"could not encrypt secret key for %d-%d",
goto out;
}
/* build cred entry */
}
/* strlen("cred.org_dir.") + null = 14 */
return (FALSE);
(void) nis_print_object(&obj);
(void) nis_freeresult(mod_res);
switch (status) {
case NIS_SUCCESS:
if (verbose)
"generated Diffie-Hellman key pair (type %d-%d) for '%s'",
goto out;
case NIS_PERMISSION:
"permission denied to add a %s cred entry for %s",
goto out;
default:
"error creating %s cred for %s, NIS+ error: %s",
goto out;
}
out:
return (ret);
}
/*
* Reencrypt a DH cred tbl secret key and return a ptr to it's newly
* allocated memory on successful return. Also, if the global generatekeys
* is set, return a ptr of newly allocated memory containing a new public
* key too.
*
* Return NULL on any type of failure.
*
* Caller must free secret and public key memory on successful return.
*/
static char *
char *oldpass, /* in */
char *newpass, /* in */
char **public, /* out */
char *netname) /* in */
{
char *reencrypted_secret = NULL;
return (NULL);
"reencrypte_secret: no memory!");
return (NULL);
}
if (verbose)
"could not decrypt keytype %d-%d for '%s'",
if (generatekeys == TRUE) {
== NULL) {
return (NULL);
}
"could not generate new Diffie-Hellman key pair for type %d-%d",
return (NULL);
}
if (verbose)
"generated Diffie-Hellman key pair (type %d-%d) for '%s'",
} else {
return (NULL);
}
}
&reencrypted_secret, TRUE) == 0) {
return (NULL);
}
return (reencrypted_secret);
}
/*
* reencrypt the secret key (or generate a new key-pair)
* and update the cred table (for 1 key of type 'authtype').
*/
int
char *domain, /* domainname */
char *authtype,
char *oldpass,
char *newpass,
int *err)
{
char prin[NIS_MAXNAMELEN];
char netname[MAXNETNAMELEN];
char *oldcryptsecret = NULL;
char *newcryptsecret = NULL;
char buf[NIS_MAXNAMELEN];
int status;
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
/* "." + "." + null = 3 */
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
switch (status) {
case NIS_SUCCESS:
*err = NPD_KEYNOTREENC;
break;
}
if (oldpass) {
}
if (newpass) {
}
*err = NPD_KEYNOTREENC;
(void) nis_freeresult(cred_res);
return (NIS_SYSTEMERROR);
}
/* public key changed too */
}
/* strlen("[cname=,auth_type=],cred.") + null + "." = 27 */
*err = NPD_KEYNOTREENC;
break;
}
*err = NPD_KEYNOTREENC;
break;
}
if (status != NIS_SUCCESS)
*err = NPD_KEYNOTREENC;
else
if (verbose)
"Diffie-Hellman key (type %d-%d) reencrypted for '%s'",
/* restore ecol so that we can free eobj */
(void) nis_destroy_object(eobj);
(void) nis_freeresult(mod_res);
break;
case NIS_NOTFOUND:
if ((generatekeys == TRUE) &&
*err = NPD_KEYSUPDATED;
} else {
*err = NPD_KEYNOTREENC;
}
break;
default:
*err = NPD_KEYNOTREENC;
"NIS+ error (%d) getting cred entry for %s",
break;
}
(void) nis_freeresult(cred_res);
return (status);
}
/*
* Loop thru configed DH mechs and if the cred entry exists
* and can be decrypted with old pw, then reencrypt it. If
* it cannot be decrypted or does not exist, and the generatekeys
* option is set, then create a new DH key pair.
*
* If any of the key possibilities are reencrypted or generated successfully,
* then return NIS_SUCCESS (and set 'err' to NIS_SUCCESS).
*
* On failure, return the NIS error code and set 'err' to the NPD error code.
*/
int
char *domain, /* in */
char *oldpass, /* in */
char *newpass, /* in */
int *err) /* out */
{
int ret;
int localerr;
int key_success = 0;
mechanism_t **mechs;
mechanism_t **mpp;
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
/* "." + "." + null = 3 */
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
if (debug)
user);
/*
* Try all the mechs listed in the NIS+ security cf (or until
* AUTH_DES (192bit DH keys) entry is reached).
*/
if (AUTH_DES_COMPAT_CHK(mp)) {
goto try_auth_des;
}
if (!VALID_MECH_ENTRY(mp))
continue;
sizeof (auth_type))) {
"cannot convert mech alias '%s' to auth type",
continue;
}
if (ret == NIS_SUCCESS) {
key_success++;
} else {
"cannot reencrypt ('%s') creds for '%s'",
if (debug)
"__npd_upd_all_pk_creds: upd_cred fail; return = %d, localerr = %d",
}
}
} else {
if (debug)
"__npd_upd_all_pk_creds: no valid GSS mechs found...trying AUTH_DES...");
if (ret == NIS_SUCCESS) {
key_success++;
} else {
"cannot reencrypt ('%s') creds for '%s'",
if (debug)
"__npd_upd_all_pk_creds: upd_cred des fail; ret = %d, localerr = %d",
}
}
/*
* Return success if any of the key possibilities were
* reencrypted or generated.
*/
if (key_success > 0) {
*err = NIS_SUCCESS;
return (NIS_SUCCESS);
}
*err = NPD_KEYNOTREENC;
return (NIS_SYSTEMERROR);
}
/*
* encrypt new passwd
*/
char *
{
char *newpass;
char *salt;
return (NULL);
/*
* We can't call getpwnam_r in here because the search paths
* for the machine running rpc.nispasswdd may not actually find
* the user. It is for this reason we got the nis_result passed
* in - the output of nis_list(). We need to turn that into a
* struct passwd before passing it to crypt_gensalt(3c).
*/
}
}
return (newpass);
}
/*
* find the passwd object for this user from the list
* of dirs given. If object is found in more than one
* place return an error, otherwise clone the object.
* Note, the object should be freed using nis_destroy_object().
*/
char *user;
char *dirlist;
{
char *tmplist;
char buf[NIS_MAXNAMELEN];
return (FALSE);
return (FALSE);
}
continue; /* not an org_dir */
/* strlen("[name=],passwd." + null + "." = 17 */
(size_t)NIS_MAXNAMELEN) {
return (FALSE);
}
case NIS_NOTFOUND: /* skip */
(void) nis_freeresult(tmpres);
continue;
case NIS_SUCCESS:
/* should only have one entry */
(void) nis_freeresult(tmpres);
(void) nis_destroy_object(tmpobj);
return (FALSE);
}
/* found in more than one dir */
(void) nis_destroy_object(tmpobj);
(void) nis_freeresult(tmpres);
return (FALSE);
}
(void) nis_freeresult(tmpres);
return (FALSE);
}
(void) nis_freeresult(tmpres);
continue;
default:
/* some NIS+ error - quit processing */
break;
}
}
(void) nis_freeresult(tmpres);
return (FALSE);
return (TRUE);
}
/*
* go thru' the list of dirs and see if the host is a
* master server for any 'org_dir'.
*/
char *host;
char *dirlist;
{
char *tmplist;
nis_server **srvs;
unsigned sleep_time;
unsigned total_sleep_time;
unsigned next_log_time;
return (FALSE);
return (FALSE);
}
continue; /* not an org_dir */
/*
* Attempt to get the nis serv list
* If no success after an hour, exit
*/
sleep_time = 1;
total_sleep_time = 0;
next_log_time = 60;
if (total_sleep_time >= 3600)
break; /* give up after an hour */
/*
* Log errors at 1 minute, 10 minutes, 20 minutes,
* 30 minutes, 40 minutes, and 50 minutes
*/
if (total_sleep_time >= next_log_time) {
"cannot get a list of servers that serve '%s'. Retrying...",
curdir);
if (next_log_time == 60)
next_log_time = 600;
else
next_log_time += 600;
}
(void) sleep(sleep_time);
/*
* sleep times of 1,2,4,8,16,29 seconds (total 60)
* and once a minute thereafter
*/
if (sleep_time <= 8)
sleep_time += sleep_time;
else if (sleep_time <= 16)
sleep_time = 29;
else
sleep_time = 60;
}
"cannot get a list of servers that serve '%s'", curdir);
return (FALSE);
}
(void) nis_freeservlist(srvs);
return (TRUE);
}
(void) nis_freeservlist(srvs);
}
return (FALSE);
}
/*
* check whether this principal has permission to
*/
unsigned long right; /* access right seeked */
char *prin; /* principal seeking access */
int column; /* column being modified */
{
char buf[NIS_MAXNAMELEN];
int mod_ok;
/* strlen("passwd." + null + "." = 9 */
return (FALSE);
}
(void) nis_freeresult(res);
return (FALSE);
}
} else {
(void) nis_freeresult(res);
return (FALSE);
}
/* check the permission on the table */
(void) nis_freeresult(res);
return (TRUE);
}
/* check the permission on the obj */
(void) nis_freeresult(res);
return (TRUE);
}
/* invalid column */
(void) nis_freeresult(res);
return (FALSE);
}
/* check the permission on column being modified */
(void) nis_freeresult(res);
return (mod_ok);
}
void
unsigned long *randval;
{
int i, shift;
unsigned int seed = 0;
for (i = 0; i < 1024; i++) {
shift = i % 8 * sizeof (int);
}
}
/*
* The following code is a modified version of nis_object2ent which was
* If you make fixes here you should check if they are also valid for the
* nss_nisplus.so version.
*
* The code had to be changed since the original was written for use inside
* the nsswitch code which wasn't suitable for direct inclusion here.
*/
static struct passwd *
{
int len;
/*
* If we got more than one nis_object, we just ignore object(s)
* except the first. Although it should never have happened.
*
* ASSUMPTION: All the columns in the NIS+ tables are
* null terminated.
*/
return (NULL);
}
/*
* pw_name: user name
*/
return (NULL);
/*
* pw_uid: user id
*/
if (len < 2) {
return (NULL);
} else {
return (NULL);
}
}
/*
* pw_passwd: user passwd. Do not HAVE to get this here
* because the caller would do a getspnam() anyway.
*/
if (len < 2) {
/*
* don't return NULL pointer, lot of stupid programs
* out there.
*/
} else {
}
/*
* pw_gid: user's primary group id.
*/
if (len < 2) {
return (NULL);
} else {
return (NULL);
}
}
/*
* pw_gecos: user's real name.
*/
if (len < 2) {
/*
* don't return NULL pointer, lot of stupid programs
* out there.
*/
} else {
}
/*
* pw_dir: user's home directory
*/
if (len < 2) {
/*
* don't return NULL pointer, lot of stupid programs
* out there.
*/
} else {
}
/*
* pw_shell: user's login shell
*/
if (len < 2) {
/*
* don't return NULL pointer, lot of stupid programs
* out there.
*/
} else {
}
/*
* pw_age and pw_comment shouldn't be used anymore, but various things
* (allegedly in.ftpd) merrily do strlen() on them anyway, so we
* keep the peace by returning a zero-length string instead of a
* null pointer.
*/
if (debug)
"pw->pw_name = %s pw->pw_uid = %d pw->pw_gid = %d "
"pw->pw_gecos = %s pw->pw_dir = %s pw->pw_shell = %s ",
return (pw);
}