#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <syslog.h>
#include <pwd.h>
#include <shadow.h>
#include <signal.h>
#include <crypt.h>
#include <rpc/rpc.h>
#include <rpcsvc/yppasswd.h>
#include <utmpx.h>
#include <nss_dbdefs.h>
#define STRSIZE 100
#define FINGERSIZE (4 * STRSIZE - 4)
#define SHELLSIZE (STRSIZE - 2)
#define UTUSERLEN (sizeof (((struct utmpx *)0)->ut_user))
extern bool_t validloginshell(char *sh, char *arg, int);
extern int validstr(char *str, size_t size);
extern int yplckpwdf();
extern int ypulckpwdf();
static char *
cryptoldpasswd(char *oldpass, char *salt, char *acctname)
{
char *oldpass_crypt = NULL;
if ((oldpass_crypt = crypt(oldpass, salt)) == NULL) {
if (errno == EINVAL) {
syslog(LOG_ERR,
"yppasswdd: password not changed for \"%s\" - "
"crypt module not supported on the master\n",
acctname);
} else {
syslog(LOG_ERR,
"yppasswdd: password not changed for \"%s\" - "
"%s\n", acctname, strerror(errno));
}
}
return (oldpass_crypt);
}
void
changepasswd(SVCXPRT *transp)
{
static const int cryptpwsize = CRYPT_MAXCIPHERTEXTLEN;
static const int fingersize = FINGERSIZE;
static const int shellsize = SHELLSIZE;
struct yppasswd yppwd;
struct passwd newpw, opwd;
struct spwd ospwd;
struct sigaction sa, osa1, osa2, osa3;
struct stat pwstat, spstat, adjstat;
char *oldpass_crypt = NULL;
char newpasswdfile[FILENAME_MAX];
char newshadowfile[FILENAME_MAX];
char newadjunctfile[FILENAME_MAX];
char tmppasswdfile[FILENAME_MAX];
char tmpshadowfile[FILENAME_MAX];
char tmpadjunctfile[FILENAME_MAX];
char pwbuf[NSS_LINELEN_PASSWD], spbuf[NSS_LINELEN_SHADOW];
char adjbuf[BUFSIZ+1], adjbuf_new[BUFSIZ+1], cmdbuf[BUFSIZ];
char adj_encrypt[CRYPT_MAXCIPHERTEXTLEN + 1];
char *adj_crypt_begin, *adj_crypt_end = NULL;
char name[UTUSERLEN + sizeof (":")];
char *p;
FILE *opwfp = NULL, *ospfp = NULL, *oadjfp = NULL,
*npwfp = NULL, *nspfp = NULL, *nadjfp = NULL;
int npwfd = -1, nspfd = -1, nadjfd = -1;
int i, ans, chsh, chpw, chgecos, namelen;
int gotadjunct = 0, gotshadow = 0, gotpasswd = 0;
int doneflag = 0, root_on_master = 0;
pid_t retval;
time_t now;
long pwpos = 0, sppos = 0;
extern int single, nogecos, noshell, nopw, mflag, Mstart, Argc;
extern char **Argv;
extern char passwd_file[], shadow_file[], adjunct_file[];
extern int useadjunct;
extern int useshadow;
memset(&yppwd, 0, sizeof (struct yppasswd));
if (!svc_getargs(transp, xdr_yppasswd, (caddr_t)&yppwd)) {
svcerr_decode(transp);
return;
}
if (
(!validstr(yppwd.newpw.pw_passwd, cryptpwsize)) ||
(!validstr(yppwd.newpw.pw_name, UTUSERLEN)) ||
(!validstr(yppwd.newpw.pw_gecos, fingersize)) ||
(!validstr(yppwd.newpw.pw_shell, shellsize))) {
svcerr_decode(transp);
return;
}
if (strcmp(transp->xp_netid, "ticlts") == 0) {
svc_local_cred_t cred;
if (!svc_get_local_cred(transp, &cred)) {
syslog(LOG_ERR, "yppasswdd: Couldn't get "
"local user credentials.\n");
} else if (cred.ruid == 0)
root_on_master = 1;
}
newpw = yppwd.newpw;
strcpy(name, newpw.pw_name);
strcat(name, ":");
namelen = strlen(name);
ans = 2;
chsh = chpw = chgecos = 0;
strcpy(newpasswdfile, passwd_file);
strcat(newpasswdfile, ".ptmp");
strcpy(newshadowfile, shadow_file);
strcat(newshadowfile, ".ptmp");
strcpy(newadjunctfile, adjunct_file);
strcat(newadjunctfile, ".ptmp");
memset(&sa, 0, sizeof (struct sigaction));
sa.sa_handler = SIG_IGN;
sigaction(SIGTSTP, &sa, (struct sigaction *)0);
sigaction(SIGHUP, &sa, &osa1);
sigaction(SIGINT, &sa, &osa2);
sigaction(SIGQUIT, &sa, &osa3);
if (yplckpwdf() < 0) {
syslog(LOG_ERR,
"yppasswdd: Password file(s) busy. "
"Try again later.\n");
ans = 8;
goto cleanup;
}
if ((opwfp = fopen(passwd_file, "r")) == NULL) {
syslog(LOG_ERR, "yppasswdd: Could not open %s\n", passwd_file);
goto cleanup;
}
fstat(fileno(opwfp), &pwstat);
if (useshadow) {
if ((ospfp = fopen(shadow_file, "r")) == NULL) {
syslog(LOG_ERR,
"yppasswdd: Could not open %s\n", shadow_file);
goto cleanup;
}
fstat(fileno(ospfp), &spstat);
}
if (useadjunct) {
if ((oadjfp = fopen(adjunct_file, "r")) == NULL) {
syslog(LOG_ERR,
"yppasswdd: Could not open %s\n",
adjunct_file);
goto cleanup;
}
fstat(fileno(oadjfp), &adjstat);
}
if ((npwfd = open(newpasswdfile, O_WRONLY | O_CREAT | O_EXCL,
pwstat.st_mode)) < 0) {
if (errno == EEXIST) {
syslog(LOG_WARNING,
"yppasswdd: passwd file busy - try again\n");
ans = 8;
} else {
syslog(LOG_ERR, "yppasswdd: %s: %m",
newpasswdfile);
ans = 9;
}
goto cleanup;
}
fchown(npwfd, pwstat.st_uid, pwstat.st_gid);
if ((npwfp = fdopen(npwfd, "w")) == NULL) {
syslog(LOG_ERR,
"yppasswdd: fdopen() on %s failed\n", newpasswdfile);
goto cleanup;
}
if (useshadow) {
if ((nspfd = open(newshadowfile, O_WRONLY | O_CREAT | O_EXCL,
spstat.st_mode)) < 0) {
if (errno == EEXIST) {
syslog(LOG_WARNING,
"yppasswdd: shadow file busy - try "
"again\n");
ans = 8;
} else {
syslog(LOG_ERR, "yppasswdd: %s: %m",
newshadowfile);
ans = 9;
}
goto cleanup;
}
fchown(nspfd, spstat.st_uid, spstat.st_gid);
if ((nspfp = fdopen(nspfd, "w")) == NULL) {
syslog(LOG_ERR,
"yppasswdd: fdopen() on %s failed\n",
newshadowfile);
goto cleanup;
}
}
if (useadjunct) {
if ((nadjfd = open(newadjunctfile, O_WRONLY | O_CREAT | O_EXCL,
adjstat.st_mode)) < 0) {
if (errno == EEXIST) {
syslog(LOG_WARNING,
"yppasswdd: adjunct file busy - try "
"again\n");
ans = 8;
} else {
syslog(LOG_ERR, "yppasswdd: %s: %m",
newadjunctfile);
ans = 9;
}
goto cleanup;
}
fchown(nadjfd, adjstat.st_uid, adjstat.st_gid);
if ((nadjfp = fdopen(nadjfd, "w")) == NULL) {
syslog(LOG_ERR,
"yppasswdd: fdopen() on %s failed\n",
newadjunctfile);
goto cleanup;
}
}
loop_in_files:
while (fgets(pwbuf, NSS_LINELEN_PASSWD, opwfp)) {
if (doneflag || strncmp(name, pwbuf, namelen)) {
if (fputs(pwbuf, npwfp) == EOF) {
syslog(LOG_ERR,
"yppasswdd: write to passwd file "
"failed.\n");
goto cleanup;
}
pwpos = ftell(opwfp);
continue;
}
gotpasswd = 1;
break;
}
if (!gotpasswd) {
syslog(LOG_ERR, "yppasswdd: user %s does not exist\n", name);
goto cleanup;
}
while (useshadow && fgets(spbuf, NSS_LINELEN_SHADOW, ospfp)) {
if (doneflag || strncmp(name, spbuf, namelen)) {
if (fputs(spbuf, nspfp) == EOF) {
syslog(LOG_ERR,
"yppasswdd: write to shadow file "
"failed.\n");
goto cleanup;
}
sppos = ftell(ospfp);
continue;
}
gotshadow = 1;
break;
}
while (useadjunct && fgets(adjbuf, BUFSIZ, oadjfp)) {
if (doneflag || strncmp(name, adjbuf, namelen)) {
if (fputs(adjbuf, nadjfp) == EOF) {
syslog(LOG_ERR,
"yppasswdd: write to adjunct file "
"failed.\n");
goto cleanup;
}
continue;
}
gotadjunct = 1;
break;
}
if (doneflag)
goto install_files;
if (useshadow && !gotshadow) {
syslog(LOG_ERR, "yppasswdd: no passwd in shadow for %s\n",
newpw.pw_name);
ans = 4;
goto cleanup;
}
if (useadjunct && !gotadjunct) {
syslog(LOG_ERR, "yppasswdd: no passwd in adjunct for %s\n",
newpw.pw_name);
ans = 4;
goto cleanup;
}
fseek(opwfp, pwpos, SEEK_SET);
opwd = *fgetpwent(opwfp);
if (useshadow) {
fseek(ospfp, sppos, SEEK_SET);
ospwd = *fgetspent(ospfp);
}
oldpass_crypt = cryptoldpasswd(yppwd.oldpass, newpw.pw_passwd,
newpw.pw_name);
if (oldpass_crypt == NULL) {
ans = 3;
goto cleanup;
}
p = newpw.pw_passwd;
if ((!nopw) &&
p && *p &&
!((*p++ == '#') && (*p++ == '#') &&
(strcmp(p, opwd.pw_name) == 0)) &&
(strcmp(oldpass_crypt, newpw.pw_passwd) != 0))
chpw = 1;
oldpass_crypt = NULL;
if ((!noshell) && (strcmp(opwd.pw_shell, newpw.pw_shell) != 0)) {
if (single)
chpw = 0;
chsh = 1;
}
if ((!nogecos) && (strcmp(opwd.pw_gecos, newpw.pw_gecos) != 0)) {
if (single) {
chpw = 0;
chsh = 0;
}
chgecos = 1;
}
if (!(chpw + chsh + chgecos)) {
syslog(LOG_NOTICE, "yppasswdd: no change for %s\n",
newpw.pw_name);
ans = 3;
goto cleanup;
}
if (useshadow && !root_on_master) {
oldpass_crypt = cryptoldpasswd(yppwd.oldpass, ospwd.sp_pwdp,
newpw.pw_name);
if (oldpass_crypt == NULL)
goto cleanup;
if (ospwd.sp_pwdp && *ospwd.sp_pwdp &&
(strcmp(oldpass_crypt, ospwd.sp_pwdp) != 0)) {
syslog(LOG_NOTICE, "yppasswdd: passwd incorrect\n",
newpw.pw_name);
ans = 7;
goto cleanup;
}
} else if (useadjunct) {
memset(adj_encrypt, 0, sizeof (adj_encrypt));
adj_crypt_begin = adjbuf + namelen;
adj_crypt_end = strchr(adj_crypt_begin, ':');
strncpy(adj_encrypt, adj_crypt_begin,
adj_crypt_end - adj_crypt_begin);
oldpass_crypt = cryptoldpasswd(yppwd.oldpass, adj_encrypt,
newpw.pw_name);
if (oldpass_crypt == NULL)
goto cleanup;
if (!root_on_master && *adj_encrypt &&
(strcmp(oldpass_crypt, adj_encrypt) != 0)) {
syslog(LOG_NOTICE, "yppasswdd: passwd incorrect\n",
newpw.pw_name);
ans = 7;
goto cleanup;
}
} else {
oldpass_crypt = cryptoldpasswd(yppwd.oldpass, opwd.pw_passwd,
newpw.pw_name);
if (oldpass_crypt == NULL)
goto cleanup;
if (!root_on_master && opwd.pw_passwd && *opwd.pw_passwd &&
(strcmp(oldpass_crypt, opwd.pw_passwd) != 0)) {
syslog(LOG_NOTICE, "yppasswdd: passwd incorrect\n",
newpw.pw_name);
ans = 7;
goto cleanup;
}
}
#ifdef DEBUG
printf("%d %d %d\n", chsh, chgecos, chpw);
printf("%s %s %s\n",
yppwd.newpw.pw_shell,
yppwd.newpw.pw_gecos,
yppwd.newpw.pw_passwd);
printf("%s %s %s\n",
opwd.pw_shell,
opwd.pw_gecos,
ospwd.sp_pwdp);
#endif
if (chsh &&
!validloginshell(opwd.pw_shell, newpw.pw_shell, root_on_master)) {
goto cleanup;
}
for (p = newpw.pw_name; (*p != '\0'); p++)
if ((*p == ':') || !(isprint(*p)))
*p = '$';
for (p = newpw.pw_passwd; (*p != '\0'); p++)
if ((*p == ':') || !(isprint(*p)))
*p = '$';
if (chgecos)
opwd.pw_gecos = newpw.pw_gecos;
if (chsh)
opwd.pw_shell = newpw.pw_shell;
if ((chsh || chgecos) && (useshadow || useadjunct || !chpw) &&
putpwent(&opwd, npwfp)) {
syslog(LOG_ERR, "yppasswdd: putpwent failed: %s\n",
passwd_file);
goto cleanup;
}
if (chpw) {
if (useshadow) {
ospwd.sp_pwdp = newpw.pw_passwd;
now = DAY_NOW;
if (ospwd.sp_max != -1) {
if (now < ospwd.sp_lstchg + ospwd.sp_min) {
syslog(LOG_ERR,
"yppasswdd: Sorry: < %ld days "
"since the last change.\n",
ospwd.sp_min);
goto cleanup;
}
}
ospwd.sp_lstchg = now;
if (putspent(&ospwd, nspfp)) {
syslog(LOG_ERR,
"yppasswdd: putspent failed: %s\n",
shadow_file);
goto cleanup;
}
} else if (useadjunct) {
sprintf(adjbuf_new,
"%s%s%s", name, newpw.pw_passwd, adj_crypt_end);
if (fputs(adjbuf_new, nadjfp) == EOF) {
syslog(LOG_ERR,
"yppasswdd: write to adjunct failed: %s\n",
adjunct_file);
goto cleanup;
}
} else {
opwd.pw_passwd = newpw.pw_passwd;
if (putpwent(&opwd, npwfp)) {
syslog(LOG_ERR,
"yppasswdd: putpwent failed: %s\n",
passwd_file);
goto cleanup;
}
}
}
if (!doneflag) {
doneflag = 1;
goto loop_in_files;
}
install_files:
fflush(npwfp);
if (useshadow)
fflush(nspfp);
if (useadjunct)
fflush(nadjfp);
strcpy(tmppasswdfile, passwd_file);
strcat(tmppasswdfile, "-");
if (useshadow) {
strcpy(tmpshadowfile, shadow_file);
strcat(tmpshadowfile, "-");
}
if (useadjunct) {
strcpy(tmpadjunctfile, adjunct_file);
strcat(tmpadjunctfile, "-");
}
if ((!useshadow && !useadjunct) || (chsh || chgecos)) {
if (rename(passwd_file, tmppasswdfile) < 0) {
syslog(LOG_CRIT, "yppasswdd: failed to backup "
"passwd file: %m");
goto cleanup;
} else {
if (rename(newpasswdfile, passwd_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to mv passwd: %m");
if (rename(tmppasswdfile, passwd_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore "
"backup of passwd file: %m");
}
goto cleanup;
}
}
}
if (useshadow && chpw) {
if (rename(shadow_file, tmpshadowfile) < 0) {
syslog(LOG_CRIT, "yppasswdd: failed to back up "
"shadow file: %m");
if (rename(tmppasswdfile, passwd_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore "
"backup of passwd file: %m");
}
goto cleanup;
} else {
if (rename(newshadowfile, shadow_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to mv shadow: %m");
if (rename(tmpshadowfile, shadow_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore "
"backup of shadow file: %m");
}
if (rename(tmppasswdfile, passwd_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore "
"backup of passwd file: %m");
}
goto cleanup;
}
}
} else if (useadjunct && chpw) {
if (rename(adjunct_file, tmpadjunctfile) < 0) {
syslog(LOG_CRIT, "yppasswdd: failed to back up "
"adjunct file: %m");
if (rename(tmppasswdfile, passwd_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore backup "
"of passwd: %m");
}
goto cleanup;
} else {
if (rename(newadjunctfile, adjunct_file) < 0) {
syslog(LOG_CRIT,
"yppassdd: failed to mv adjunct: %m");
if (rename(tmppasswdfile, passwd_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore "
"backup of passwd file: %m");
}
if (rename(tmpadjunctfile, adjunct_file) < 0) {
syslog(LOG_CRIT,
"yppasswdd: failed to restore "
"backup of adjunct file: %m");
}
goto cleanup;
}
}
}
if (doneflag)
ans = 0;
unlink(tmppasswdfile);
if (useshadow)
unlink(tmpshadowfile);
if (useadjunct)
unlink(tmpadjunctfile);
cleanup:
if (opwfp) {
fclose(opwfp);
if (ospfp) {
fclose(ospfp);
}
if (oadjfp) {
fclose(oadjfp);
}
unlink(newpasswdfile);
if (useshadow)
unlink(newshadowfile);
if (useadjunct)
unlink(newadjunctfile);
if (npwfp) {
fclose(npwfp);
if (nspfp) {
fclose(nspfp);
}
if (nadjfp) {
fclose(nadjfp);
}
}
}
ypulckpwdf();
if (doneflag && mflag) {
retval = fork();
if (retval < 0) {
syslog(LOG_ERR, "yppasswdd: Fork failed %m");
} else if (retval == 0) {
strcpy(cmdbuf, "/usr/ccs/bin/make");
for (i = Mstart + 1; i < Argc; i++) {
strcat(cmdbuf, " ");
strcat(cmdbuf, Argv[i]);
}
#ifdef DEBUG
syslog(LOG_ERR, "yppasswdd: about to "
"execute %s\n", cmdbuf);
#else
if (yplckpwdf() < 0) {
syslog(LOG_ERR, "yppasswdd: Couldn't get the "
"lock to update the maps");
} else {
setpgrp();
system(cmdbuf);
ypulckpwdf();
}
#endif
exit(0);
}
}
sigaction(SIGHUP, &osa1, (struct sigaction *)0);
sigaction(SIGINT, &osa2, (struct sigaction *)0);
sigaction(SIGQUIT, &osa3, (struct sigaction *)0);
if (!svc_sendreply(transp, xdr_int, (char *)&ans))
syslog(LOG_WARNING,
"yppasswdd: couldn\'t reply to RPC call\n");
}