npd_svc.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-2002 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* NPD service routines
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include <shadow.h>
#include <crypt.h>
#include <stdlib.h>
#include <unistd.h>
#include <rpcsvc/yppasswd.h>
#include <rpc/key_prot.h>
#include <rpc/des_crypt.h>
#include <rpcsvc/nispasswd.h>
#include "npd_cache.h"
#include "npd_svcsubr.h"
#include <sys/byteorder.h>
#include <rpcsvc/nis_dhext.h>
extern int max_attempts;
extern int cache_time;
extern int verbose;
extern int debug;
#define _NPD_PASSMAXLEN 16
#define MAX_RETRY 3
extern char *ypfwd; /* YP domain to fwd NIS+ req */
/*
* service routine for first part of the nispasswd update
* protocol.
*/
{
int ans = 0;
char prin[NIS_MAXNAMELEN];
unsigned char xpass[_NPD_PASSMAXLEN];
int status;
char *oldpass;
unsigned long randval;
if (verbose)
/* check if I'm running on the host == master(domain) */
return (TRUE);
}
/* get caller info. from auth_handle */
if (rqstp)
else
prin[0] = '\0';
if (verbose)
prin);
/* caller == admin ? ; Y -> skip checks, N -> do aging checks */
/* authenticated user, check if they are privileged */
check_aging = FALSE;
}
/* "." + null + "." = 3 */
(size_t)NIS_MAXNAMELEN) {
return (TRUE);
}
if (debug)
"_authenticate_: 'nobody' or NUL prinicipal set to '%s'", prin);
}
/*
* The credential information may have changed for the user,
* we should refresh the cache to make sure or the user is
* a different user and the necessary checks need to be made.
*/
if (refresh)
else {
goto again;
}
/* check if there is a cached entry */
/* found an entry - check if it has expired */
/*
* check if this attempt > max_attempts.
*/
"too many failed attempts for %s",
return (TRUE);
}
if (argp->ident == 0) {
/*
* a new session and we have an entry cached
* but we have not reached max_attempts, so
* just update entry with the new pass
*/
}
} else { /* entry has expired */
(void) free_upd_item(entry);
}
} else {
if (argp->ident != 0) {
"no cache entry found for %s but the identifier is %d",
return (TRUE);
}
}
/* get passwd info for username */
return (TRUE);
}
case NIS_SUCCESS:
break;
case NIS_NOTFOUND:
(void) nis_freeresult(pass_res);
return (TRUE);
default:
"NIS+ error (%d) getting passwd entry for %s",
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* if user check if 'min' days have passed since 'lastchg' */
if (check_aging) {
ans == NPD_NOTAGED) {
"password has not aged enough for %s",
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* if ans == NPD_NOSHDWINFO then aging cannot be enforced */
}
/*
* Find out what key length and algorithm type we're dealing with.
*/
} else {
/*
* This will check if the key_type exists in the
* NIS+ security cf.
*/
&algtype) < 0) {
(void) nis_freeresult(pass_res);
return (TRUE);
}
}
/* generate CK (from P.c and S.d) */
"cannot generate common DES key for %s",
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* decrypt the passwd sent */
(void) nis_freeresult(pass_res);
return (TRUE);
}
(char *)&ivec);
else
(char *)&ivec);
if (DES_FAILED(status)) {
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* assign an ID and generate R on the first call of a session */
if (argp->ident == 0) {
if ((int)ident == -1) {
(void) nis_freeresult(pass_res);
return (TRUE);
}
(void) __npd_gen_rval(&randval);
} else {
/* second or third attempt */
ident = argp->ident;
if (entry_exp) /* gen a new random val */
(void) __npd_gen_rval(&randval);
else {
(void) nis_freeresult(pass_res);
return (TRUE);
}
} else {
/* gen a new random val */
(void) __npd_gen_rval(&randval);
} else
}
}
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* encrypt the passwd and compare with that stored in NIS+ */
if (same_user) {
(void) nis_freeresult(pass_res);
return (TRUE);
}
if (debug)
"_authenticate_: pw decrypt failed");
(void) nis_freeresult(pass_res);
/* cache relevant info */
if (ans <= 0) {
} else
} else {
/* found an entry, attempt == max_attempts */
/*
* not really a system error but we
* want the caller to think that 'cos
* they are obviously trying to break-in.
* Perhaps, we should not respond at all,
* the client side would timeout.
*/
}
}
if (verbose)
(void) __npd_print_entry(prin);
return (TRUE);
}
} else {
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* admin changing another users password */
/*
* we have no idea where this admin's
* passwd record is stored BUT we do know
* where their PK cred(s) is stored from
* the netname, so lets try to decrypt
* the secret key(s) with the passwd that
* was sent across.
*/
(void) nis_freeresult(pass_res);
/* cache relevant info */
if (ans <= 0) {
}
}
return (TRUE);
}
}
/* done with pass_res */
(void) nis_freeresult(pass_res);
/* cache relevant info */
(char *)xpass);
if (ans <= 0) {
return (TRUE);
}
} else {
return (TRUE);
}
}
}
if (verbose)
(void) __npd_print_entry(prin);
return (TRUE);
}
/*
* service routine for second part of the nispasswd update
* protocol.
*/
/* ARGSUSED2 */
{
struct update_item *entry;
char shadow[80];
int error = NPD_SUCCESS;
register int i;
char *old_pass;
/* set to success, and reset to error when warranted */
return (TRUE);
}
if (verbose) {
}
/*
* iterate thru entry list and decrypt R sent and new passwd until
* we have a rand hit (or reach end of list)
*/
/* decrypt R and new passwd */
for (i = 0; i < __NPD_MAXPASSBYTES; i++)
return (TRUE);
}
if (verbose)
break;
} else
if (verbose)
"rand miss: entry rval=%ld; continue...",
}
if (verbose)
return (TRUE);
}
/* encrypt new passwd */
return (TRUE);
}
return (TRUE);
}
case NIS_SUCCESS:
break;
case NIS_NOTFOUND:
(void) nis_freeresult(pass_res);
return (TRUE);
default:
"NIS+ error (%d) getting passwd entry for %s",
(void) nis_freeresult(pass_res);
return (TRUE);
}
/* can change passwd, shell or gecos */
/* clear out the error list */
/* if a gecos field is provided... */
"insufficient permission for %s to change the gecos",
}
}
/* if a shell field is provided... */
"insufficient permission for %s to change the shell",
/*
* If already set to partial success, that means
* that gecos field and error was provided, so
* add the next item to the error list.
*/
} else {
}
}
}
/* update lstchg field in the shadow area */
(void) nis_freeresult(pass_res);
goto end;
}
}
/* clone an entry object to update passwd entry */
goto end;
}
/* save the old values for restoring before freeing the object */
/* set column value to entry column */
/* strlen("[name=],passwd.") + null + "." = 17 */
(size_t)NIS_MAXNAMELEN) {
goto end;
}
/* put together table index info and object modification */
/* add dot "." if necessary */
/* update NIS+ passwd table */
/* if NIS+ update fails, bail now */
goto end;
}
/* NIS+ master updated; if YP-forwarding turned on, do YP */
if (ypfwd) {
int try = 0; /* retry counter for YP & NIS+ updates */
/*
* Attempt the YP passwd map update;
* on failures use exponential backoff
*/
try++;
}
/*
* On repeated failures (MAX_RETRY),
* give up & undo NIS+ table update
*/
try = 0; /* clear for undo attempts */
"unable to update NIS(YP) passwd \
/*
* The passwd tbl update failed, so set 'error'
* to an NPD failure. Here is where it gets
* tricky. The permissions on the passwd table
* must be turned off because the failover mech-
* anism will try to get NISD to make the update.
*/
/* set "new" passwd back to old password */
/* undo the NIS+ passwd table update */
do {
/* exponential backoff each try */
/*
* Update table with old passwd information,
* freeing the old 'mod_res' struct first.
* 'eobj' has already been set to 'ecol' above.
*/
/* On success, exit; otherwise increment ctr */
else try++;
/* NIS+ update repeated failures, bail now and log */
passwd update; maybe out-of-sync with YP map -- verify by hand");
}
}
}
/*
* The 'error' flag can only be changed from NPD_SUCCESS if
* YP updating was on and then, only in a failure scenario.
* In all other situations, update the credential!
*/
if (error == NPD_SUCCESS) {
/* attempt to update PK cred(s) */
if (error != NIS_SUCCESS) {
}
} else {
}
/*
* Only set the reason union member if partial success;
* otherwise may wipe out npd_err union member.
*/
}
}
/* Epilogue just consists of freeing up data */
end:
/* Restore column stuff so that we can free eobj */
if (eobj) {
(void) nis_destroy_object(eobj);
}
/*
* The code to free pobj is not necessary as in
* yppasswdproc_update_1_svc() because pobj is
* freed when pass_res is just below. Otherwise
* if it's in here, it will dump core.
*/
return (TRUE);
}
/*
* yppasswd update service routine.
* The error codes returned are from the 4.x rpc.yppasswdd.c,
* it seems that the client side only checks if the result is
* non-zero in which case it prints a generic message !
*/
/* ARGSUSED2 */
int *result;
{
/* set new password from YP passwd struct */
if (verbose)
"received yp password update request from %s",
/* fill-in NIS+ server information */
*result = -1;
return (TRUE);
}
/*
* make the nis_stats call to check if the server is running
* in compat mode and get the list of directories this server
* is serving
*/
if (status != NIS_SUCCESS) {
*result = -1;
return (1);
}
/* old server */
"NIS+ server does not support the new statistics tags");
*result = -1;
return (1);
}
/* check if server is running in NIS compat mode */
"Local NIS+ server is not running in NIS compat mode");
*result = -1;
return (1);
}
/*
* find the dir that has a passwd entry for this user
* POLICY: if user has a passwd stored in more then one
* dir then do not make an update. if user has an entry
* in only one dir, then make an update in that dir.
*/
*result = -1;
return (1);
}
*result = -1;
goto ypend;
}
/* if ans == NPD_NOSHDWINFO then aging cannot be enforced */
/* validate the old passwd */
goto ypend;
}
/* mismatch on old passwd */
*result = 7;
goto ypend;
}
/* can change passwd, gecos or shell */
}
/* need to strip org_dir part of the domain */
dom++;
} else
/* "." + null + "." = 3 */
(size_t)NIS_MAXNAMELEN) {
*result = -1;
goto ypend;
}
"insufficient permission for %s to change the gecos",
*result = 2;
goto ypend;
}
}
"insufficient permission for %s to change the shell",
*result = 2;
goto ypend;
}
}
/*
* from 4.x:
* This fixes a really bogus security hole, basically anyone can
* call the rpc passwd daemon, give them their own passwd and a
* give themselves root access. With this code it will simply make
* it impossible for them to login again, and as a bonus leave
* a cookie for the always vigilant system administrator to ferret
* them out.
*/
if ((*p == ':') || !(isprint(*p)))
*p = '$'; /* you lose ! */
if ((*p == ':') || !(isprint(*p)))
*p = '$'; /* you lose ! */
/* update lstchg field */
*result = -1;
goto ypend;
}
}
/* clone an entry object to update passwd entry */
*result = -1;
goto ypend;
}
/* save the old values to restore while freeing the object */
/* set column value to entry column */
/* strlen("[name=],passwd.") + null + "." = 17 */
(size_t)NIS_MAXNAMELEN) {
*result = -1;
goto ypend;
}
/* put together table index info and object modification */
/* add dot "." if necessary */
/* update NIS+ passwd table */
/* nisd in temp read-only mode (nisbackup(1M)) */
"could not update NIS+ passwd: %s",
/*
* 8 is the magic number from way back in 4.1.x.
* User should see
*/
*result = 8;
goto ypend;
}
/* if NIS+ update fails, bail now */
"could not update NIS+ passwd: %s",
*result = 13;
goto ypend;
}
/*
* nothing happens here because we cannot re-encrypt the credential
* because we do not have the unencrypted new password .... :^(
*/
*result = 0;
/* NIS+ master updated; if YP-forwarding turned on, do YP */
if (ypfwd) {
int try = 0; /* retry counter for YP & NIS+ updates */
/*
* Attempt the YP passwd map update;
* on failures use exponential backoff
*/
try++;
}
/*
* On repeated failures (MAX_RETRY),
* give up & undo NIS+ table update
*/
try = 0; /* clear for undo attempts */
"unable to update NIS(YP) passwd \
*result = -1;
/* set "new" passwd back to old password */
/* undo the NIS+ passwd table update */
do {
/* exponential backoff each try */
/*
* Update table with old passwd information,
* freeing the old 'mod_res' struct first.
* 'eobj' has already been set to 'ecol' above.
*/
/* On success, exit; otherwise increment ctr */
else try++;
/* NIS+ update repeated failures, bail now and log */
passwd update; maybe out-of-sync with YP map -- verify by hand");
}
}
}
/* the epilogue just consists of freeing up data */
if (eobj) {
(void) nis_destroy_object(eobj);
}
if (pobj)
(void) nis_destroy_object(pobj);
return (1);
}
/* ARGSUSED */
int
{
/*
* (void) xdr_free(xdr_result, result);
* Insert additional freeing code here, if needed
*/
return (1);
}