diff options
-rw-r--r-- | usr.bin/chpass/Makefile | 37 | ||||
-rw-r--r-- | usr.bin/chpass/chpass.1 | 113 | ||||
-rw-r--r-- | usr.bin/chpass/chpass.c | 81 | ||||
-rw-r--r-- | usr.bin/chpass/edit.c | 10 | ||||
-rw-r--r-- | usr.bin/chpass/pw_yp.c | 328 | ||||
-rw-r--r-- | usr.bin/chpass/pw_yp.h | 79 | ||||
-rw-r--r-- | usr.bin/passwd/Makefile | 42 | ||||
-rw-r--r-- | usr.bin/passwd/passwd.1 | 55 | ||||
-rw-r--r-- | usr.bin/passwd/passwd.c | 49 | ||||
-rw-r--r-- | usr.bin/passwd/yp_passwd.c | 256 |
10 files changed, 755 insertions, 295 deletions
diff --git a/usr.bin/chpass/Makefile b/usr.bin/chpass/Makefile index a1e2947..0f582b9 100644 --- a/usr.bin/chpass/Makefile +++ b/usr.bin/chpass/Makefile @@ -4,7 +4,9 @@ PROG= chpass SRCS= chpass.c edit.c field.c pw_copy.c pw_scan.c pw_util.c table.c util.c BINOWN= root BINMODE=4555 -.PATH: ${.CURDIR}/../../usr.sbin/pwd_mkdb ${.CURDIR}/../../usr.sbin/vipw +.PATH: ${.CURDIR}/../../usr.sbin/pwd_mkdb ${.CURDIR}/../../usr.sbin/vipw \ + ${.CURDIR}/../../libexec/ypxfr \ + ${.CURDIR}/../../usr.sbin/rpc.yppasswdd CFLAGS+=-I${.CURDIR}/../../usr.sbin/pwd_mkdb -I${.CURDIR}/../../usr.sbin/vipw LINKS= ${BINDIR}/chpass ${BINDIR}/chfn LINKS+= ${BINDIR}/chpass ${BINDIR}/chsh @@ -12,11 +14,40 @@ LINKS+= ${BINDIR}/chpass ${BINDIR}/ypchpass LINKS+= ${BINDIR}/chpass ${BINDIR}/ypchfn LINKS+= ${BINDIR}/chpass ${BINDIR}/ypchsh MLINKS= chpass.1 chfn.1 chpass.1 chsh.1 -COPTS+= -DYP -SRCS+= pw_yp.c +COPTS+= -DYP -I. -I${.CURDIR}/../../libexec/ypxfr \ + -I${.CURDIR}/../../usr.sbin/rpc.yppasswdd -Dyp_error=warnx + +SRCS+= yppasswd_private_xdr.c yppasswd_comm.c yp_clnt.c \ + yppasswd_clnt.c pw_yp.c ypxfr_misc.c +CLEANFILES= yp_clnt.c yp.h yppasswd_clnt.c yppasswd.h \ + yppasswd_private_xdr.c yppasswd_private.h + DPADD= ${LIBRPCSVC} ${LIBCRYPT} LDADD+= -lrpcsvc -lcrypt +RPCGEN= rpcgen -C +RPCSRC= ${DESTDIR}/usr/include/rpcsvc/yp.x +RPCSRC_PW= ${DESTDIR}/usr/include/rpcsvc/yppasswd.x +RPCSRC_PRIV= ${.CURDIR}/../../usr.sbin/rpc.yppasswdd/yppasswd_private.x + +yp.h: ${RPCSRC} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} + +yp_clnt.c: ${RPCSRC} yp.h + ${RPCGEN} -l -o ${.TARGET} ${RPCSRC} + +yppasswd.h: ${RPCSRC_PW} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PW} + +yppasswd_clnt.c: ${RPCSRC_PW} yppasswd.h + ${RPCGEN} -l -o ${.TARGET} ${RPCSRC_PW} + +yppasswd_private.h: ${RPCSRC_PRIV} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PRIV} + +yppasswd_private_xdr.c: ${RPCSRC_PRIV} yppasswd_private.h + ${RPCGEN} -c -o ${.TARGET} ${RPCSRC_PRIV} + beforeinstall: [ ! -e ${DESTDIR}${BINDIR}/chpass ] || \ chflags noschg ${DESTDIR}${BINDIR}/chpass diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1 index 83cddb8..2ba9be0 100644 --- a/usr.bin/chpass/chpass.1 +++ b/usr.bin/chpass/chpass.1 @@ -212,39 +212,64 @@ apply. Currently, .Nm chpass can only make changes to the NIS passwd maps through -.Xr yppasswdd 8 , -which only permits changes to a user's password, shell and gecos -fields. It can not be used to change other user information or to -add new records to the NIS passwd maps. (Doing that would require -something such as ypupdated, which is not yet supported.) +.Xr rpc.yppasswdd 8 , +which normally only permits changes to a user's password, shell and GECOS +fields. Except when invoked by the super-user on the NIS master server, +.Nm chpass +(and, similarly, +.Xr passwd 1 ) +can not use the +.Xr rpc.yppasswdd 8 +server to change other user information or +add new records to the NIS passwd maps. Furthermore, -.Xr yppasswdd 8 +.Xr rpc.yppasswdd 8 requires password authentication before it will make any -changes, even if it receives a request from the super-user. +changes. The only user allowed to submit changes without supplying +a password is the super-user on the NIS master server; all other users, +including those with root privileges on NIS clients (and NIS slave +servers) must enter a password. +(The super-user on the NIS master is allowed to bypass these restrictions +largely for convenience: a user with root access +to the NIS master server already has the privileges required to make +updates to the NIS maps, but editing the map source files by hand can +be cumbersome. +.Pp +Note: these exceptions only apply when the NIS master server is a +FreeBSD system.) .Pp -As a result, the following restrictions apply when +Consequently, except where noted, the following restrictions apply when .Nm chpass is used with NIS: .Bl -enum -offset indent .It -.Pa Only the shell and gecos information may be changed. +.Pa Only the shell and GECOS information may be changed. All other fields are restricted, even when .Nm chpass -is invoked by the super-user, because the -.Xr yppasswdd 8 -daemon has no support for updating them. While support for +is invoked by the super-user. +While support for changing other fields could be added, this would lead to compatibility problems with other NIS-capable systems. Even though the super-user may supply data for other fields while editing an entry, the extra information (other than the password -- see below) will be silently discarded. +.Pp +Exception: the super-user on the NIS master server is permitted to +change any field. +.Pp .It .Pa Password authentication is required. .Nm Chpass will prompt for the user's NIS password before effecting any changes. If the password is invalid, all changes will be discarded. +.Pp +Exception: the super-user on the NIS master server is allowed to +submit changes without supplying a password. (The super-user may +choose to turn off this feature using the +.Fl o +flag, described below.) .It .Pa Adding new records to the local .Pa password database is discouraged. @@ -257,6 +282,18 @@ The administrator should use .Xr vipw 8 to modify the local password file when NIS is running. +.Pp +The super-user on the NIS master server is permitted to add new records +to the NIS password maps, provided the +.Xr rpc.yppasswdd 8 +server has been started with the +.Fl a +flag to permitted additions (it refuses them by default). +.Nm Chpass +tries to update the local password database by default; to update the +NIS maps instead, invoke chpass with the +.Fl y +flag. .It .Pa Password changes are not permitted. Users should use @@ -268,11 +305,15 @@ a new password (even though the ``Password:'' field does not show up in the editor template, the super-user may add it back by hand), but even the super-user must supply the user's original password otherwise -.Xr yppasswdd 8 +.Xr rpc.yppasswdd 8 will refuse to update the NIS maps. +.Pp +Exception: the super-user on the NIS master server is permitted to +change a user's NIS password with +.Nm chpass . .El .Pp -There are also two extra option flags that are available when +There are also a few extra option flags that are available when .Nm chpass is compiled with NIS support: .Bl -tag -width flag @@ -290,6 +331,50 @@ This flag has the opposite effect of This flag is largely redundant since .Nm chpass operates on NIS entries by default if NIS is enabled. +.It Fl d Ar domain +Specify a particular NIS domain. +.Nm Chpass +uses the system domain name by default, as set by the +.Xr domainname 1 +command. The +.Fl d +option can be used to override a default, or to specify a domain +when the system domain name is not set. +.It Fl h Ar host +Specify the name or address of an NIS server to query. Normally, +.Nm chpass +will communicate with the NIS master host specified in the +.Pa master.passwd +or +.Pa passwd +maps. On hosts that have not been configured as NIS clients, there is +no way for the program to determine this information unless the user +provides the hostname of a server. Note that the specified hostname need +not be that of the NIS master server; the name of any server, master or +slave, in a given NIS domain will do. +.Pp +When using the +.Fl d +option, the hostname defaults to ``localhost.'' The +.Fl h +option can be used in conjunction with the +.Fl d +option, in which case the user-specified hostname will override +the default. +.Pp +.It Fl o +Force the use of RPC-based updates when communicating with +.Xr rpc.yppasswdd 8 +(``old-mode''). +When invoked by the super-user on the NIS master server, +.Nm chpass +allows unrestricted changes to the NIS passwd maps using dedicated, +non-RPC-based mechanism (in this case, a UNIX domain socket). The +.Fl o +flag can be used to force +.Nm chpass +to use the standard update mechanism instead. This option is provided +mainly for testing purposes. .El .Pp .Sh FILES diff --git a/usr.bin/chpass/chpass.c b/usr.bin/chpass/chpass.c index 2bfdd10..17d742a 100644 --- a/usr.bin/chpass/chpass.c +++ b/usr.bin/chpass/chpass.c @@ -40,7 +40,7 @@ static char copyright[] = #ifndef lint static char sccsid[] = "From: @(#)chpass.c 8.4 (Berkeley) 4/2/94"; static char rcsid[] = - "$Id: chpass.c,v 1.5 1995/09/02 03:56:17 wpaul Exp $"; + "$Id: chpass.c,v 1.5 1996/02/23 14:33:05 wpaul Exp $"; #endif /* not lint */ #include <sys/param.h> @@ -63,13 +63,14 @@ static char rcsid[] = #include <pw_util.h> #include "pw_copy.h" #ifdef YP +#include <rpcsvc/yp.h> +int yp_errno = YP_TRUE; #include "pw_yp.h" #endif #include "chpass.h" #include "pathnames.h" -char *progname = "chpass"; char *tempname; uid_t uid; @@ -92,7 +93,7 @@ main(argc, argv) op = EDITENTRY; #ifdef YP - while ((ch = getopt(argc, argv, "a:p:s:ly")) != EOF) + while ((ch = getopt(argc, argv, "a:p:s:d:h:oly")) != EOF) #else while ((ch = getopt(argc, argv, "a:p:s:")) != EOF) #endif @@ -110,11 +111,42 @@ main(argc, argv) arg = optarg; break; #ifdef YP + case 'h': +#ifdef PARAMOID + if (getuid()) { + warnx("Only the superuser can use the -d flag"); + } else { +#endif + yp_server = optarg; +#ifdef PARANOID + } +#endif + break; + case 'd': +#ifdef PARANOID + if (getuid()) { + warnx("Only the superuser can use the -d flag"); + } else { +#endif + yp_domain = optarg; + if (yp_server == NULL) + yp_server = "localhost"; +#ifdef PARANOID + } +#endif + break; case 'l': - force_local = 1; + if (getuid()) { + warnx("Only the superuser can use the -h flag"); + } else { + force_local = 1; + } break; case 'y': - force_yp = 1; + _use_yp = force_yp = 1; + break; + case 'o': + force_old++; break; #endif case '?': @@ -128,6 +160,15 @@ main(argc, argv) if (op == EDITENTRY || op == NEWSH || op == NEWPW) switch(argc) { +#ifdef YP + case 0: + GETPWUID(uid) + get_yp_master(1); /* XXX just to set the suser flag */ + break; + case 1: + GETPWNAM(*argv) + get_yp_master(1); /* XXX just to set the suser flag */ +#else case 0: if (!(pw = getpwuid(uid))) errx(1, "unknown user: uid %u", uid); @@ -135,39 +176,13 @@ main(argc, argv) case 1: if (!(pw = getpwnam(*argv))) errx(1, "unknown user: %s", *argv); +#endif if (uid && uid != pw->pw_uid) baduser(); break; default: usage(); } - -#ifdef YP - pw->pw_name = strdup(pw->pw_name); - _use_yp = use_yp(pw->pw_name); - if (_use_yp == USER_YP_ONLY) { - if (!force_local) { - _use_yp = 1; - pw = (struct passwd *)&yp_password; - } else - errx(1, "unknown local user: %s.", pw->pw_name); - } else if (_use_yp == USER_LOCAL_ONLY) { - if (!force_yp) { - _use_yp = 0; - pw = (struct passwd *)&local_password; - } else - errx(1, "unknown NIS user: %s.", pw->pw_name); - } else if (_use_yp == USER_YP_AND_LOCAL) { - if (!force_local) { - _use_yp = 1; - pw = (struct passwd *)&yp_password; - } else { - _use_yp = 0; - pw = (struct passwd *)&local_password; - } - } -#endif /* YP */ - if (op == NEWSH) { /* protect p_shell -- it thinks NULL is /bin/sh */ if (!arg[0]) @@ -258,7 +273,7 @@ usage() (void)fprintf(stderr, #ifdef YP - "usage: chpass [-l] [-y] [-a list] [-p encpass] [-s shell] [user]\n"); + "usage: chpass [-l] [-y] [-d domain [-h host]] [-a list] [-p encpass] [-s shell] [user]\n"); #else "usage: chpass [-a list] [-p encpass] [-s shell] [user]\n"); #endif diff --git a/usr.bin/chpass/edit.c b/usr.bin/chpass/edit.c index c0f61e0..2968c84 100644 --- a/usr.bin/chpass/edit.c +++ b/usr.bin/chpass/edit.c @@ -99,7 +99,7 @@ display(fd, pw) (void)fprintf(fp, #ifdef YP "#Changing %s information for %s.\n", _use_yp ? "NIS" : "user database", pw->pw_name); - if (!uid && !_use_yp) { + if (!uid && (!_use_yp || suser_override)) { #else "#Changing user database information for %s.\n", pw->pw_name) if (!uid) { @@ -118,11 +118,19 @@ display(fd, pw) *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); } /* Only admin can change "restricted" shells. */ +#ifdef 0 else if (ok_shell(pw->pw_shell)) /* * Make shell a restricted field. Ugly with a * necklace, but there's not much else to do. */ +#else + else if ((!list[E_SHELL].restricted && ok_shell(pw->pw_shell)) || !uid) + /* + * If change not restrict (table.c) and standard shell + * OR if root, then allow editing of shell. + */ +#endif (void)fprintf(fp, "Shell: %s\n", *pw->pw_shell ? pw->pw_shell : _PATH_BSHELL); else diff --git a/usr.bin/chpass/pw_yp.c b/usr.bin/chpass/pw_yp.c index 2c1c138..9493db8 100644 --- a/usr.bin/chpass/pw_yp.c +++ b/usr.bin/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; } diff --git a/usr.bin/chpass/pw_yp.h b/usr.bin/chpass/pw_yp.h index 9d98a43..7d37560 100644 --- a/usr.bin/chpass/pw_yp.h +++ b/usr.bin/chpass/pw_yp.h @@ -35,18 +35,91 @@ * Center for Telecommunications Research * Columbia University, New York City * - * $Id: pw_yp.h,v 1.1 1995/08/13 16:12:28 wpaul Exp $ + * $Id: pw_yp.h,v 1.8 1996/02/17 18:14:23 wpaul Exp $ */ #ifdef YP +#include <sys/types.h> +#include <rpc/rpc.h> +#include <rpc/auth.h> +#include <rpc/auth_unix.h> /* Four possible return codes from use_yp() */ #define USER_UNKNOWN 0 #define USER_YP_ONLY 1 #define USER_LOCAL_ONLY 2 #define USER_YP_AND_LOCAL 3 + +extern int force_old; extern int _use_yp; +extern int suser_override; extern struct passwd local_password; extern struct passwd yp_password; -void yp_submit __P(( struct passwd * )); -int use_yp __P(( char * )); +extern void copy_yp_pass __P(( char *, int, int )); +extern char *yp_domain; +extern char *yp_server; +extern void yp_submit __P(( struct passwd * )); +extern int use_yp __P(( char * , uid_t , int )); +extern char *get_yp_master __P(( int )); + +/* + * Yucky. + */ +#define GETPWUID(X) \ + _use_yp = use_yp(NULL, X, 1); \ + \ + if (_use_yp == USER_UNKNOWN) { \ + errx(1, "unknown user: uid %u", X); \ + } \ + \ + if (_use_yp == USER_YP_ONLY) { \ + if (!force_local) { \ + _use_yp = 1; \ + pw = (struct passwd *)&yp_password; \ + } else \ + errx(1, "unknown local user: uid %u", X); \ + } else if (_use_yp == USER_LOCAL_ONLY) { \ + if (!force_yp) { \ + _use_yp = 0; \ + pw = (struct passwd *)&local_password; \ + } else \ + errx(1, "unknown NIS user: uid %u", X); \ + } else if (_use_yp == USER_YP_AND_LOCAL) { \ + if (!force_local) { \ + _use_yp = 1; \ + pw = (struct passwd *)&yp_password; \ + } else { \ + _use_yp = 0; \ + pw = (struct passwd *)&local_password; \ + } \ + } + +#define GETPWNAM(X) \ + _use_yp = use_yp(X, 0, 0); \ + \ + if (_use_yp == USER_UNKNOWN) { \ + errx(1, "unknown user: %s", X); \ + } \ + \ + if (_use_yp == USER_YP_ONLY) { \ + if (!force_local) { \ + _use_yp = 1; \ + pw = (struct passwd *)&yp_password; \ + } else \ + errx(1, "unknown local user: %s.", X); \ + } else if (_use_yp == USER_LOCAL_ONLY) { \ + if (!force_yp) { \ + _use_yp = 0; \ + pw = (struct passwd *)&local_password; \ + } else \ + errx(1, "unknown NIS user: %s.", X); \ + } else if (_use_yp == USER_YP_AND_LOCAL) { \ + if (!force_local) { \ + _use_yp = 1; \ + pw = (struct passwd *)&yp_password; \ + } else { \ + _use_yp = 0; \ + pw = (struct passwd *)&local_password; \ + } \ + } + #endif /* YP */ diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile index b7866de..b5d7a3c 100644 --- a/usr.bin/passwd/Makefile +++ b/usr.bin/passwd/Makefile @@ -1,15 +1,47 @@ # From: @(#)Makefile 8.3 (Berkeley) 4/2/94 -# $Id: Makefile,v 1.17 1995/09/03 11:40:37 markm Exp $ +# $Id: Makefile,v 1.18 1995/09/14 21:02:16 gibbs Exp $ PROG= passwd -SRCS= local_passwd.c yp_passwd.c passwd.c pw_copy.c pw_util.c pw_yp.c +SRCS= local_passwd.c yppasswd_private_xdr.c yppasswd_comm.c yp_passwd.c \ + passwd.c pw_copy.c pw_util.c pw_yp.c + DPADD= ${LIBCRYPT} ${LIBRPCSVC} LDADD= -lcrypt -lrpcsvc .PATH: ${.CURDIR}/../../usr.bin/chpass ${.CURDIR}/../../usr.sbin/vipw \ - ${.CURDIR}/../rlogin + ${.CURDIR}/../rlogin ${.CURDIR}/../../libexec/ypxfr \ + ${.CURDIR}/../../usr.sbin/rpc.yppasswdd + +CFLAGS+= -DCRYPT -DYP -I. -I${.CURDIR} -I${.CURDIR}/../../usr.sbin/vipw \ + -I${.CURDIR}/../../usr.bin/chpass -I${.CURDIR}/../../libexec/ypxfr \ + -I${.CURDIR}/../../usr.sbin/rpc.yppasswdd -Dyp_error=warnx + +SRCS+= ypxfr_misc.c yp_clnt.c yppasswd_clnt.c + +CLEANFILES= yp.h yp_clnt.c yppasswd.h yppasswd_clnt.c \ + yppasswd_private.h yppasswd_private_xdr.c + +RPCGEN= rpcgen -C +RPCSRC= ${DESTDIR}/usr/include/rpcsvc/yp.x +RPCSRC_PW= ${DESTDIR}/usr/include/rpcsvc/yppasswd.x +RPCSRC_PRIV= ${.CURDIR}/../../usr.sbin/rpc.yppasswdd/yppasswd_private.x + +yp.h: ${RPCSRC} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC} + +yp_clnt.c: ${RPCSRC} yp.h + ${RPCGEN} -l -o ${.TARGET} ${RPCSRC} + +yppasswd.h: ${RPCSRC_PW} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PW} + +yppasswd_clnt.c: ${RPCSRC_PW} yppasswd.h + ${RPCGEN} -l -o ${.TARGET} ${RPCSRC_PW} + +yppasswd_private.h: ${RPCSRC_PRIV} + ${RPCGEN} -h -o ${.TARGET} ${RPCSRC_PRIV} -CFLAGS+=-DCRYPT -DYP -I${.CURDIR} -I${.CURDIR}/../../usr.sbin/vipw \ - -I${.CURDIR}/../../usr.bin/chpass +yppasswd_private_xdr.c: ${RPCSRC_PRIV} yppasswd_private.h + ${RPCGEN} -c -o ${.TARGET} ${RPCSRC_PRIV} BINOWN= root BINMODE=4555 diff --git a/usr.bin/passwd/passwd.1 b/usr.bin/passwd/passwd.1 index 47a337a..533cd5e 100644 --- a/usr.bin/passwd/passwd.1 +++ b/usr.bin/passwd/passwd.1 @@ -87,6 +87,19 @@ user does not exist in either the local password database of the NIS password maps, .Nm passwd returns an error. +.Pp +When changing an NIS password, unprivileged users are required to provide +their old password for authentication (the +.Xr rpc.yppasswdd 8 +daemon requires the original password before +it will allow any changes to the NIS password maps). +This restriction applies even to the +super-user, with one important exception: the password authentication is +bypassed for the super-user on the NIS master server. This means that +the super-user on the NIS master server can make unrestricted changes to +anyone's NIS password. The super-user on NIS client systems and NIS slave +servers still needs to provide a password before the update will be processed. +.Pp The following additional options are supported for use with NIS: .Bl -tag -width flag .It Fl y @@ -114,17 +127,39 @@ default, will try to change the NIS password. The .Fl l flag can be used to change the local password instead. -.El +.It Fl d Ar domain +Specify what domain to use when changing an NIS password. By default, +.Nm passwd +assumes that the system default domain should be used. This flag is +primarily for use by the superuser on the NIS master server: a single +NIS server can support multiple domains. It is also possible that the +domainname on the NIS master may not be set (it is not necessary for +an NIS server to also be a client) in which case the +.Nm passwd +command needs to be told what domain to operate on. +.It Fl s Ar host +Specify the name of an NIS server. This option, in conjunction +with the +.Fl d +option, can be used to change an NIS password on a non-local NIS +server. When a domain is specified with the +.Fl d +option and +.Nm passwd +is unable to determine the name of the NIS master server (possibly because +the local domainname isn't set), the name of the NIS master is assumed to +be ``localhost''. This can be overriden with the +.Fl s +flag. The specified hostname need not be the name of an NIS master: the +name of the NIS master for a given map can be determined by querying any +NIS server (master or slave) in a domain, so specifying the name of a +slave server will work equally well. .Pp -When changing an NIS password, the user is required to provide -the old password for authentication (the -.Xr yppasswdd 8 -daemon requires the original password before -it will allow any changes to the NIS password maps). -This restriction applies even to the -super-user: the only way for an administrator to override a -user's NIS password is by modifying the NIS password maps on -the master NIS server. +.It Fl o +Do not automatically override the password authentication checks for the +super-user on the NIS master server; assume 'old' mode instead. This +flag is of limited practical use but is useful for testing. +.El .Sh FILES .Bl -tag -width /etc/master.passwd -compact .It Pa /etc/master.passwd diff --git a/usr.bin/passwd/passwd.c b/usr.bin/passwd/passwd.c index 92dd895..a7e2623 100644 --- a/usr.bin/passwd/passwd.c +++ b/usr.bin/passwd/passwd.c @@ -43,7 +43,7 @@ static const char copyright[] = #ifndef lint static const char sccsid[] = "From: @(#)passwd.c 8.3 (Berkeley) 4/2/94"; static const char rcsid[] = - "$Id$"; + "$Id: passwd.c,v 1.7 1995/12/16 09:45:15 markm Exp $"; #endif /* not lint */ #include <err.h> @@ -56,9 +56,10 @@ static const char rcsid[] = #ifdef YP #include <pwd.h> #include <pw_yp.h> -char *prog_name; +#include <rpcsvc/yp.h> int __use_yp = 0; -int yp_passwd(char *user); +int yp_errno = YP_TRUE; +extern int yp_passwd __P(( char * )); #endif #ifdef KERBEROS @@ -83,9 +84,9 @@ main(argc, argv) #ifdef YP #ifdef KERBEROS char realm[REALM_SZ]; -#define OPTIONS "lysfi:r:u:" +#define OPTIONS "d:h:lysfoi:r:u:" #else -#define OPTIONS "lysf" +#define OPTIONS "d:h:lysfo" #endif #else #ifdef KERBEROS @@ -99,7 +100,7 @@ main(argc, argv) #ifdef YP int res = 0; - if (strstr(argv[0], (prog_name = "yppasswd"))) __use_yp = 1; + if (strstr(argv[0], "yppasswd")) __use_yp = 1; #endif while ((ch = getopt(argc, argv, OPTIONS)) != EOF) { @@ -122,6 +123,33 @@ main(argc, argv) case 'y': /* Change NIS password */ __use_yp = 1; break; + case 'd': /* Specify NIS domain. */ +#ifdef PARANOID + if (!getuid()) { +#endif + yp_domain = optarg; + if (yp_server == NULL) + yp_server = "localhost"; +#ifdef PARANOID + } else { + warnx("Only the super-user may use the -d flag."); + } +#endif + break; + case 'h': /* Specify NIS server. */ +#ifdef PARANOID + if (!getuid()) { +#endif + yp_server = optarg; +#ifdef PARANOID + } else { + warnx("Only the super-user may use the -h flag."); + } +#endif + break; + case 'o': + force_old++; + break; #endif default: case '?': @@ -152,8 +180,8 @@ main(argc, argv) #ifdef KERBEROS if (__use_yp || (iflag == NULL && rflag == NULL && uflag == NULL)) { #endif - res = use_yp(uname); - if (res == USER_YP_ONLY) { + res = use_yp(uname, 0, 0); + if (res == USER_YP_ONLY || __use_yp) { if (!use_local_passwd) { exit(yp_passwd(uname)); } else { @@ -198,9 +226,10 @@ usage() fprintf(stderr, "usage: passwd [-l] [-i instance] [-r realm] [-u fullname]\n"); fprintf(stderr, - " [-l] [-y] [user]\n"); + " [-l] [-y] [-o] [-d domain [-h host]] [user]\n"); #else - (void)fprintf(stderr, "usage: passwd [-l] [-y] [user] \n"); + (void)fprintf(stderr, "usage: passwd [-l] [-y] [-o] [-d domain \ +[-h host]] [user] \n"); #endif #else #ifdef KERBEROS diff --git a/usr.bin/passwd/yp_passwd.c b/usr.bin/passwd/yp_passwd.c index fc6b0e4..b83e9f1 100644 --- a/usr.bin/passwd/yp_passwd.c +++ b/usr.bin/passwd/yp_passwd.c @@ -45,137 +45,141 @@ #include <rpcsvc/ypclnt.h> #include <rpcsvc/yppasswd.h> #include <pw_yp.h> - -extern char *prog_name; -uid_t uid; +#include "yppasswd_comm.h" extern char *getnewpasswd __P(( struct passwd * , int )); -char * -getserver( void ) -{ - char *domainname, *master; - int port, err; - int getrpcport(); - - if ((err = yp_get_default_domain(&domainname)) != 0) { - fprintf(stderr, "%s: can't get local yp domain: %s\n", - prog_name, yperr_string(err)); - return NULL; - } - - if ((err = yp_master(domainname, "passwd.byname", &master)) != 0) { - fprintf(stderr, "%s: can't find the master ypserver: %s\n", - prog_name, yperr_string(err)); - return NULL; - } - port = getrpcport(master, YPPASSWDPROG, YPPASSWDPROC_UPDATE, IPPROTO_UDP); - if (port==0) { - fprintf (stderr, "%s: yppasswdd not running on NIS master host\n", - prog_name); - return NULL; - } - if (port >= IPPORT_RESERVED) { - fprintf (stderr, "%s: yppasswd daemon running on illegal port.\n", - prog_name); - return NULL; - } - return master; -} - int yp_passwd(char *user) { - struct timeval timeout; - struct yppasswd yppasswd; - struct passwd *pw; - CLIENT *clnt; - char *master; - int err, status; - char *s; - - if ((master = getserver()) == NULL) { - exit(1); - } - - /* Obtain the passwd struct for the user whose password is to be changed. - */ - uid = getuid(); - if (user == NULL) { - if ((pw = getpwuid(uid)) == NULL) { - fprintf ( stderr, "%s: unknown user (uid=%d).\n", - prog_name, (int)uid ); - exit(1); - } - } else { - if ((pw = getpwnam(user)) == NULL) { - fprintf ( stderr, "%s: unknown user: %s.\n", prog_name, user ); - exit(1); - } - if (pw->pw_uid != uid && uid != 0) { - fprintf ( stderr, "%s: Only root may change account information " - "for others\n", prog_name ); - exit(1); - } - } - - /* Use the correct password */ - pw = (struct passwd *)&yp_password; - - /* Initialize password information */ - yppasswd.newpw.pw_passwd = pw->pw_passwd; - yppasswd.newpw.pw_name = pw->pw_name; - yppasswd.newpw.pw_uid = pw->pw_uid; - yppasswd.newpw.pw_gid = pw->pw_gid; - yppasswd.newpw.pw_gecos = pw->pw_gecos; - yppasswd.newpw.pw_dir = pw->pw_dir; - yppasswd.newpw.pw_shell = pw->pw_shell; - yppasswd.oldpass = NULL; - - printf("Changing NIS password for %s on %s.\n", pw->pw_name, master); - - /* Get old password */ - if(pw->pw_passwd[0]) { - - s = getpass ("Old password: "); - if( strcmp(crypt(s, pw->pw_passwd), pw->pw_passwd)) { - fprintf(stderr, "Sorry.\n"); - exit (1); - } - yppasswd.oldpass = strdup(s); - } else - yppasswd.oldpass = ""; - - if ((s = getnewpasswd(pw, 1)) == NULL) - exit (1); - yppasswd.newpw.pw_passwd = s; - - /* The yppasswd.x file said `unix authentication required', - * so I added it. This is the only reason it is in here. - * My yppasswdd doesn't use it, but maybe some others out there - * do. --okir - */ - clnt = clnt_create( master, YPPASSWDPROG, YPPASSWDVERS, "udp" ); - clnt->cl_auth = authunix_create_default(); - bzero( (char*)&status, sizeof(status) ); - timeout.tv_sec = 25; timeout.tv_usec = 0; - err = clnt_call( clnt, YPPASSWDPROC_UPDATE, - xdr_yppasswd, (char*)&yppasswd, - xdr_int, (char*)&status, - &timeout ); - - if (err) { - clnt_perrno(err); - fprintf( stderr, "\n" ); - } else if (status) { - fprintf( stderr, "Error while changing NIS password.\n"); - } - - printf("\nNIS password has%s been changed on %s.\n", - (err || status)? " not" : "", master); - - auth_destroy( clnt->cl_auth ); - clnt_destroy( clnt ); - exit ((err || status) != 0); + struct timeval timeout; + struct yppasswd yppasswd; + struct master_yppasswd master_yppasswd; + struct passwd *pw; + CLIENT *clnt; + struct rpc_err err; + char *master; + int *status = NULL; + uid_t uid; + + _use_yp = 1; + + uid = getuid(); + + if ((master = get_yp_master(1)) == NULL) { + warnx("failed to find NIS master server"); + return(1); + } + + /* + * It is presumed that by the time we get here, use_yp() + * has been called and that we have verified that the user + * actually exists. This being the case, the yp_password + * stucture has already been filled in for us. + */ + + /* Use the correct password */ + pw = (struct passwd *)&yp_password; + + if (pw->pw_uid != uid && uid != 0) { + warnx("Only the super-user may change account information \ +for other users"); + return(1); + } + + /* Initialize password information */ + 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 = ""; + 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 = ""; + } + + if (suser_override) + printf("Changing NIS password for %s on %s in domain %s.\n", + pw->pw_name, master, yp_domain); + else + printf("Changing NIS password for %s on %s.\n", + pw->pw_name, master); + + /* Get old password */ + + if(pw->pw_passwd[0] && !suser_override) { + yppasswd.oldpass = strdup(getpass("Old Password: ")); + if (strcmp(crypt(yppasswd.oldpass, pw->pw_passwd), + pw->pw_passwd)) { + errx(1, "Sorry."); + } + + } + + if (suser_override) { + if ((master_yppasswd.newpw.pw_passwd = getnewpasswd(pw, 1)) == NULL) + return(1); + } else { + if ((yppasswd.newpw.pw_passwd = getnewpasswd(pw, 1)) == NULL) + return(1); + } + + if (suser_override) { + if (senddat(&master_yppasswd)) { + warnx("failed to send request to rpc.yppasswdd"); + return(1); + } + status = getresp(); + } else { + if ((clnt = clnt_create(master, YPPASSWDPROG, + YPPASSWDVERS, "udp")) == NULL) { + warnx("failed to contact rpc.yppasswdd on host %s: %s", + master, clnt_spcreateerror("")); + return(1); + } + /* + * The yppasswd.x file said `unix authentication required', + * so I added it. This is the only reason it is in here. + * My yppasswdd doesn't use it, but maybe some others out there + * do. --okir + */ + clnt->cl_auth = authunix_create_default(); + + status = yppasswdproc_update_1(&yppasswd, clnt); + clnt_geterr(clnt, &err); + + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + } + + if ((!suser_override && err.re_status != RPC_SUCCESS) || + status == NULL || *status) { + errx(1, "Failed to change NIS password: %s", + (err.re_status != RPC_SUCCESS && !suser_override) ? + clnt_sperrno(err.re_status) : + "rpc.yppasswdd returned error status"); + } + + printf("\nNIS password has%s been changed on %s.\n", + ((err.re_status != RPC_SUCCESS && !suser_override) + || status == NULL || *status) ? + " not" : "", master); + + return ((err.re_status || status == NULL || *status)); } #endif /* YP */ |