summaryrefslogtreecommitdiffstats
path: root/chpass/pw_yp.c
diff options
context:
space:
mode:
Diffstat (limited to 'chpass/pw_yp.c')
-rw-r--r--chpass/pw_yp.c328
1 files changed, 238 insertions, 90 deletions
diff --git a/chpass/pw_yp.c b/chpass/pw_yp.c
index 2c1c138..9493db8 100644
--- a/chpass/pw_yp.c
+++ b/chpass/pw_yp.c
@@ -20,7 +20,7 @@
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
@@ -35,7 +35,7 @@
* Center for Telecommunications Research
* Columbia University, New York City
*
- * $Id: pw_yp.c,v 1.1 1995/08/13 16:12:27 wpaul Exp $
+ * $Id: pw_yp.c,v 1.9 1996/02/23 04:20:19 wpaul Exp $
*/
#ifdef YP
@@ -45,6 +45,7 @@
#include <netdb.h>
#include <time.h>
#include <sys/types.h>
+#include <sys/stat.h>
#include <pwd.h>
#include <errno.h>
#include <err.h>
@@ -57,14 +58,18 @@
#include <sys/param.h>
#include <limits.h>
#include <rpc/rpc.h>
-#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
#include <rpcsvc/ypclnt.h>
#include <rpcsvc/yppasswd.h>
#include <pw_util.h>
#include "pw_yp.h"
+#include "ypxfr_extern.h"
+#include "yppasswd_comm.h"
+#include "yppasswd_private.h"
#define PERM_SECURE (S_IRUSR|S_IWUSR)
-HASHINFO openinfo = {
+static HASHINFO openinfo = {
4096, /* bsize */
32, /* ffactor */
256, /* nelem */
@@ -73,22 +78,29 @@ HASHINFO openinfo = {
0, /* lorder */
};
+int force_old = 0;
int _use_yp = 0;
+int suser_override = 0;
+char *yp_domain = NULL;
+char *yp_server = NULL;
+
+extern char *tempname;
/* Save the local and NIS password information */
struct passwd local_password;
struct passwd yp_password;
-
void copy_yp_pass(p, x, m)
char *p;
int x, m;
{
register char *t, *s = p;
+ static char *buf;
yp_password.pw_fields = 0;
- t = (char *)malloc(m + 10);
+ buf = (char *)realloc(buf, m + 10);
+ bzero(buf, m + 10);
/* Turn all colons into NULLs */
while (strchr(s, ':')) {
@@ -96,7 +108,8 @@ int x, m;
*(s - 1)= '\0';
}
-#define EXPAND(e) e = t; while (*t++ = *p++);
+ t = buf;
+#define EXPAND(e) e = t; while ((*t++ = *p++));
EXPAND(yp_password.pw_name);
yp_password.pw_fields |= _PWF_NAME;
EXPAND(yp_password.pw_passwd);
@@ -115,7 +128,7 @@ int x, m;
yp_password.pw_fields |= _PWF_CHANGE;
yp_password.pw_expire = atol(p);
p += (strlen(p) + 1);
- yp_password.pw_expire |= _PWF_EXPIRE;
+ yp_password.pw_fields |= _PWF_EXPIRE;
}
EXPAND(yp_password.pw_gecos);
yp_password.pw_fields |= _PWF_GECOS;
@@ -132,9 +145,12 @@ char *p;
int m;
{
register char *t;
+ static char *buf;
- t = (char *)malloc(m + 10);
+ buf = (char *)realloc(buf, m + 10);
+ bzero(buf, m + 10);
+ t = buf;
EXPAND(local_password.pw_name);
EXPAND(local_password.pw_passwd);
bcopy(p, (char *)&local_password.pw_uid, sizeof(int));
@@ -156,60 +172,141 @@ int m;
}
/*
+ * It is not mandatory that an NIS master server also be a client.
+ * However, if the NIS master is not configured as a client, then the
+ * domain name will not be set and ypbind will not be running, so we
+ * will be unable to use the ypclnt routines inside libc. We therefore
+ * need our own magic version of yp_match() which we can use in any
+ * environment.
+ */
+static int my_yp_match(server, domain, map, key, keylen, result, resultlen)
+ char *server;
+ char *domain;
+ char *map;
+ char *key;
+ unsigned long keylen;
+ char **result;
+ unsigned long *resultlen;
+{
+ ypreq_key ypkey;
+ ypresp_val *ypval;
+ CLIENT *clnt;
+ static char buf[YPMAXRECORD + 2];
+
+ bzero((char *)buf, sizeof(buf));
+
+ if ((clnt = clnt_create(server, YPPROG,YPVERS,"udp")) == NULL) {
+ warnx("%s", clnt_spcreateerror("failed to create handle"));
+ pw_error(tempname, 0, 1);
+ }
+
+ ypkey.domain = domain;
+ ypkey.map = map;
+ ypkey.key.keydat_len = keylen;
+ ypkey.key.keydat_val = key;
+
+ if ((ypval = ypproc_match_2(&ypkey, clnt)) == NULL) {
+ clnt_destroy(clnt);
+ warnx("%s",clnt_sperror(clnt,"YPPROC_MATCH failed"));
+ pw_error(tempname, 0, 1);
+ }
+
+ clnt_destroy(clnt);
+
+ if (ypval->stat != YP_TRUE) {
+ int stat = ypval->stat;
+ xdr_free(xdr_ypresp_val, (char *)ypval);
+ if (stat == YP_NOMAP && strstr(map, "master.passwd"))
+ return(1);
+ if (stat == YP_NOKEY)
+ return(1);
+ warnx("ypmatch failed: %s", yperr_string(ypprot_err(stat)));
+ pw_error(tempname, 0, 1);
+ }
+
+
+ strncpy((char *)&buf, ypval->val.valdat_val, ypval->val.valdat_len);
+
+ *result = (char *)&buf;
+ *resultlen = ypval->val.valdat_len;
+
+ xdr_free(xdr_ypresp_val, (char *)ypval);
+
+ return(0);
+}
+
+/*
* Check if the user we're working with is local or in NIS.
*/
-int use_yp (user)
-char *user;
+int use_yp (user, uid, which)
+ char *user;
+ uid_t uid;
+ int which; /* 0 = use username, 1 = use uid */
{
int user_local = 0, user_yp = 0, user_exists = 0;
DB *dbp;
DBT key,data;
char bf[UT_NAMESIZE + 2];
- char *domain;
char *result;
- int resultlen, rval;
+ char *server;
+ int resultlen;
+ char ubuf[UT_NAMESIZE + 2];
+ if (which) {
+ snprintf(ubuf, sizeof(ubuf), "%lu", uid);
+ user = (char *)&ubuf;
+ }
- /* Is the user anywhere */
- if (getpwnam(user) != NULL)
- user_exists = 1;
+ /* Grope around for the user in the usual way */
+ if (which) {
+ if (getpwuid(uid) != NULL)
+ user_exists = 1;
+ } else {
+ if (getpwnam(user) != NULL)
+ user_exists = 1;
+ }
+ /* Now grope directly through the user database */
if ((dbp = dbopen(_PATH_SMP_DB, O_RDONLY, PERM_SECURE,
- DB_HASH, &openinfo)) == NULL)
- errx(1, "error opening database: %s.", _PATH_MP_DB);
+ DB_HASH, &openinfo)) == NULL) {
+ warn("error opening database: %s.", _PATH_MP_DB);
+ pw_error(tempname, 0, 1);
+ }
/* Is NIS turned on */
bf[0] = _PW_KEYYPENABLED;
key.data = (u_char *)bf;
key.size = 1;
- if (!(dbp->get)(dbp,&key,&data,0)) {
- if ((rval = yp_get_default_domain(&domain))) {
- warnx("can't get local NIS domain name: %s",yperr_string(rval));
- pw_error(NULL, 0, 1);
- }
+ if (!(dbp->get)(dbp,&key,&data,0) || (yp_domain && yp_server)) {
+ server = get_yp_master(0);
/* Is the user in the NIS passwd map */
- if (!yp_match(domain, "passwd.byname", user, strlen(user),
+ if (!my_yp_match(server, yp_domain, which ? "passwd.byuid" :
+ "passwd.byname", user, strlen(user),
&result, &resultlen)) {
- user_yp = 1;
+ user_yp = user_exists = 1;
+ *(char *)(result + resultlen) = '\0';
copy_yp_pass(result, 0, resultlen);
- free(result);
}
- /* Is the user in the NIS passwd map */
- if (user_yp && !yp_match(domain, "master.passwd.byname",
+ /* Is the user in the NIS master.passwd map */
+ if (user_yp && !my_yp_match(server, yp_domain, which ?
+ "master.passwd.byuid" : "master.passwd.byname",
user, strlen(user),
&result, &resultlen)) {
+ *(char *)(result + resultlen) = '\0';
copy_yp_pass(result, 1, resultlen);
}
- free(result);
}
/* Is the user in the local password database */
- bf[0] = _PW_KEYBYNAME;
- bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
+ bf[0] = which ? _PW_KEYBYUID : _PW_KEYBYNAME;
+ if (which)
+ bcopy((char *)&uid, bf + 1, sizeof(uid));
+ else
+ bcopy((char *)user, bf + 1, MIN(strlen(user), UT_NAMESIZE));
key.data = (u_char *)bf;
- key.size = strlen(user) + 1;
+ key.size = which ? sizeof(uid) + 1 : strlen(user) + 1;
if (!(dbp->get)(dbp,&key,&data,0)) {
user_local = 1;
copy_local_pass(data.data, data.size);
@@ -232,31 +329,37 @@ char *user;
* Find the name of the NIS master server for this domain
* and make sure it's running yppasswdd.
*/
-static char *get_yp_master(void)
+char *get_yp_master(getserver)
+ int getserver;
{
- char *domain, *mastername;
- int rval;
+ char *mastername;
+ int rval, localport;
+ struct stat st;
/* Get default NIS domain. */
- if ((rval = yp_get_default_domain(&domain))) {
+ if (yp_domain == NULL && (rval = yp_get_default_domain(&yp_domain))) {
warnx("can't get local NIS domain name: %s",yperr_string(rval));
- pw_error(NULL, 0, 1);
+ pw_error(tempname, 0, 1);
}
/* Get master server of passwd map. */
- if ((rval = yp_master(domain, "passwd.byname", &mastername))) {
- warnx("can't get master NIS server: %s", yperr_string(rval));
- pw_error(NULL, 0, 1);
+ if ((mastername = ypxfr_get_master(yp_domain, "passwd.byname",
+ yp_server, yp_server ? 0 : 1)) == NULL) {
+ warnx("can't get name of master NIS server");
+ pw_error(tempname, 0, 1);
}
+ if (!getserver)
+ return(mastername);
+
/* Check if yppasswdd is out there. */
if ((rval = getrpcport(mastername, YPPASSWDPROG, YPPASSWDPROC_UPDATE,
IPPROTO_UDP)) == 0) {
- warnx("yppasswdd not running on NIS master server");
- pw_error(NULL, 0, 1);
+ warnx("rpc.yppasswdd is not running on the NIS master server");
+ pw_error(tempname, 0, 1);
}
/*
@@ -265,91 +368,136 @@ static char *get_yp_master(void)
*/
if (rval >= IPPORT_RESERVED) {
- warnx("yppasswdd server not running on reserved port");
- pw_error(NULL, 0, 1);
+ warnx("rpc.yppasswdd server not running on reserved port");
+ pw_error(tempname, 0, 1);
+ }
+
+ /* See if _we_ are the master server. */
+ if (!force_old && !getuid() && (localport = getrpcport("localhost",
+ YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP)) != 0) {
+ if (localport == rval && stat(sockname, &st) != -1) {
+ suser_override = 1;
+ mastername = "localhost";
+ }
}
/* Everything checks out: return the name of the server. */
return (mastername);
}
+
/*
* Ask the user for his NIS password and submit the new information
- * to yppasswdd. Note that yppasswdd requires password authentication
+ * to yppasswdd. Note that rpc.yppasswdd requires password authentication
* and only allows changes to existing records rather than the addition
* of new records. (To do actual updates we would need something like
- * secure RPC and ypupdated, which FreeBSD doesn't have yet.) This means
- * that the superuser cannot use chpass(1) to add new users records to
- * the NIS password database.
+ * secure RPC and ypupdated, which FreeBSD doesn't have yet.) The FreeBSD
+ * rpc.yppasswdd has some special hooks to allow the superuser update
+ * information without specifying a password, however this only works
+ * for the superuser on the NIS master server.
*/
void yp_submit(pw)
-struct passwd *pw;
+ struct passwd *pw;
{
struct yppasswd yppasswd;
+ struct master_yppasswd master_yppasswd;
CLIENT *clnt;
- char *master, *password, *encpass;
- int rval, status = 0;
- struct timeval tv;
+ char *master, *password;
+ int *status = NULL;
+ struct rpc_err err;
- /* Populate the yppasswd structure that gets handed to yppasswdd. */
- /*
- * XXX This is done first to work around what looks like a very
- * strange memory corruption bug: the text fields pointed to
- * by the members of the 'pw' structure appear to be clobbered
- * after get_yp_master() returns (in particular, it happens
- * during getrpcport()). I don't know exactly where the problem
- * lies: I traced it all the way to gethostbyname(), then gave
- * up.
- */
- yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
- yppasswd.newpw.pw_name = strdup(pw->pw_name);
- yppasswd.newpw.pw_uid = pw->pw_uid;
- yppasswd.newpw.pw_gid = pw->pw_gid;
- yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
- yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
- yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+ _use_yp = 1;
/* Get NIS master server name */
- master = get_yp_master();
+ master = get_yp_master(1);
+
+ /* Populate the yppasswd structure that gets handed to yppasswdd. */
+
+ if (suser_override) {
+ master_yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+ master_yppasswd.newpw.pw_name = strdup(pw->pw_name);
+ master_yppasswd.newpw.pw_uid = pw->pw_uid;
+ master_yppasswd.newpw.pw_gid = pw->pw_gid;
+ master_yppasswd.newpw.pw_expire = pw->pw_expire;
+ master_yppasswd.newpw.pw_change = pw->pw_change;
+ master_yppasswd.newpw.pw_fields = pw->pw_fields;
+ master_yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+ master_yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+ master_yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+ master_yppasswd.newpw.pw_class = strdup(pw->pw_class);
+ master_yppasswd.oldpass = ""; /* not really needed */
+ master_yppasswd.domain = yp_domain;
+ } else {
+ yppasswd.newpw.pw_passwd = strdup(pw->pw_passwd);
+ yppasswd.newpw.pw_name = strdup(pw->pw_name);
+ yppasswd.newpw.pw_uid = pw->pw_uid;
+ yppasswd.newpw.pw_gid = pw->pw_gid;
+ yppasswd.newpw.pw_gecos = strdup(pw->pw_gecos);
+ yppasswd.newpw.pw_dir = strdup(pw->pw_dir);
+ yppasswd.newpw.pw_shell = strdup(pw->pw_shell);
+ yppasswd.oldpass = "";
+ }
/* Get the user's password for authentication purposes. */
printf ("Changing NIS information for %s on %s\n",
- yppasswd.newpw.pw_name, master);
- encpass = (getpwnam(yppasswd.newpw.pw_name))->pw_passwd;
- password = getpass("Please enter password: ");
- if (strncmp(crypt(password, encpass), encpass, strlen(encpass))) {
- warnx("Password incorrect.");
- pw_error(NULL, 0, 1);
+ pw->pw_name, master);
+
+ if (pw->pw_passwd[0] && !suser_override) {
+ password = getpass("Please enter password: ");
+ if (strncmp(crypt(password,pw->pw_passwd),
+ pw->pw_passwd,strlen(pw->pw_passwd))) {
+ warnx("Password incorrect.");
+ pw_error(tempname, 0, 1);
+ }
+ yppasswd.oldpass = password; /* XXX */
}
- yppasswd.oldpass = password; /* XXX */
+ if (suser_override) {
+ /* Talk to server via AF_UNIX socket. */
+ if (senddat(&master_yppasswd)) {
+ warnx("failed to contact local rpc.yppasswdd");
+ pw_error(tempname, 0, 1);
+ }
+ /* Get return code. */
+ status = getresp();
+ } else {
+ /* Create a handle to yppasswdd. */
+
+ if ((clnt = clnt_create(master, YPPASSWDPROG,
+ YPPASSWDVERS, "udp")) == NULL) {
+ warnx("failed to contact rpc.yppasswdd on %s: %s",
+ master, clnt_spcreateerror(""));
+ pw_error(tempname, 0, 1);
+ }
- /* Create a handle to yppasswdd. */
+ clnt->cl_auth = authunix_create_default();
- clnt = clnt_create(master, YPPASSWDPROG, YPPASSWDVERS, "udp");
- clnt->cl_auth = authunix_create_default();
+ status = yppasswdproc_update_1(&yppasswd, clnt);
- /* Set a timeout and make the RPC call. */
+ clnt_geterr(clnt, &err);
- tv.tv_sec = 20;
- tv.tv_usec = 0;
- rval = clnt_call(clnt, YPPASSWDPROC_UPDATE, xdr_yppasswd,
- (char *)&yppasswd, xdr_int, (char *)&status, &tv);
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ }
/* Call failed: signal the error. */
- if (rval) {
- warnx("NIS update failed: %s", clnt_sperrno(rval));
+ if ((!suser_override && err.re_status) != RPC_SUCCESS || status == NULL || *status) {
+ warnx("NIS update failed: %s", (err.re_status != RPC_SUCCESS &&
+ !suser_override) ? clnt_sperrno(err.re_status) :
+ "rpc.yppasswdd returned error status");
pw_error(NULL, 0, 1);
}
/* Success. */
- auth_destroy(clnt->cl_auth);
- clnt_destroy(clnt);
- warnx("NIS information changed on host %s", master);
+ if (suser_override)
+ warnx("NIS information changed on host %s, domain %s",
+ master, yp_domain);
+ else
+ warnx("NIS information changed on host %s", master);
return;
}