/*
* setpwnam.c --
* edit an entry in a password database.
*
* (c) 1994 Salvatore Valente <svalente@mit.edu>
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This file is distributed with no warranty.
*
* Usage:
* 1) get a struct passwd * from getpwnam().
* You should assume a struct passwd has an infinite number of fields,
* so you should not try to create one from scratch.
* 2) edit the fields you want to edit.
* 3) call setpwnam() with the edited struct passwd.
*
* You should never directly read from or write to /etc/passwd.
* All user database queries should be directed through
* getpwnam() and setpwnam().
*
* Thanks to "two guys named Ian".
*/
/* $Author: faith $
* $Revision: 1.5 $
* $Date: 1995/10/12 14:46:36 $
*/
#undef DEBUG
/* because I use getpwent(), putpwent(), etc... */
#define _SVID_SOURCE
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pwd.h>
#include <errno.h>
#include <signal.h>
#include <sys/resource.h>
#ifdef BSD43
#include <sys/file.h>
#endif
#include <paths.h>
#include "pathnames.h"
extern int errno;
typedef int boolean;
#define false 0
#define true 1
#ifndef DEBUG
#define PASSWD_FILE _PATH_PASSWD
#define PTMP_FILE _PATH_PTMP
#define PTMPTMP_FILE _PATH_PTMPTMP
#else
#define PASSWD_FILE "/tmp/passwd"
#define PTMP_FILE "/tmp/ptmp"
#define PTMPTMP_FILE "/tmp/ptmptmp"
#endif
static int copy_pwd (struct passwd *src, struct passwd *dest);
static char *xstrdup (char *str);
static void pw_init(void);
/*
* setpwnam () --
* takes a struct passwd in which every field is filled in and valid.
* If the given username exists in the passwd file, his entry is
* replaced with the given entry.
*/
int setpwnam (struct passwd *pwd)
{
FILE *fp;
int x, save_errno, fd, ret;
struct passwd *entry;
boolean found;
struct passwd spwd;
int oldumask;
/* getpwent() returns a pointer to a static buffer.
* "pwd" might have some from getpwent(), so we have to copy it to
* some other buffer before calling getpwent().
*/
if (copy_pwd (pwd, &spwd) < 0)
return (-1);
oldumask = umask(0); /* Create with exact permissions */
pw_init();
/* sanity check */
for (x = 0; x < 3; x++) {
if (x > 0) sleep (1);
fd = open (PTMPTMP_FILE, O_WRONLY|O_CREAT, 0644);
if(fd == -1) {
perror(PTMPTMP_FILE);
umask(oldumask);
return (-1);
}
ret = link(PTMPTMP_FILE, PTMP_FILE);
unlink(PTMPTMP_FILE);
if(ret == -1)
close(fd);
else
break;
}
umask(oldumask);
if (ret == -1) return (-1);
/* ptmp should be owned by root.root or root.wheel */
if (chown (PTMP_FILE, (uid_t) 0, (gid_t) 0) < 0)
perror ("chown");
/* open ptmp for writing and passwd for reading */
fp = fdopen (fd, "w");
if (! fp) goto fail;
setpwent ();
/* parse the passwd file */
found = false;
while ((entry = getpwent ()) != NULL) {
if (! strcmp (spwd.pw_name, entry->pw_name)) {
entry = &spwd;
found = true;
}
if (putpwent (entry, fp) < 0) goto fail;
}
if (fclose (fp) < 0) goto fail;
close (fd);
endpwent ();
if (! found) {
errno = ENOENT; /* give me something better */
goto fail;
}
/* we don't care if we can't remove the backup file */
unlink (PASSWD_FILE".OLD");
/* we don't care if we can't create the backup file */
link (PASSWD_FILE, PASSWD_FILE".OLD");
/* we DO care if we can't rename to the passwd file */
if (rename (PTMP_FILE, PASSWD_FILE) < 0)
goto fail;
/* finally: success */
return 0;
fail:
save_errno = errno;
if (fp) fclose (fp);
if (fd >= 0) close (fd);
endpwent ();
unlink (PTMP_FILE);
errno = save_errno;
return (-1);
}
#define memzero(ptr, size) memset((char *) ptr, 0, size)
static int failed;
static int copy_pwd (struct passwd *src, struct passwd *dest)
{
/* this routine destroys abstraction barriers. it's not portable
* across systems, or even across different versions of the C library
* on a given system. it's dangerous and evil and wrong and I dispise
* getpwent() for forcing me to write this.
*/
failed = 0;
memzero (dest, sizeof (struct passwd));
dest->pw_name = xstrdup (src->pw_name);
dest->pw_passwd = xstrdup (src->pw_passwd);
dest->pw_uid = src->pw_uid;
dest->pw_gid = src->pw_gid;
dest->pw_gecos = xstrdup (src->pw_gecos);
dest->pw_dir = xstrdup (src->pw_dir);
dest->pw_shell = xstrdup (src->pw_shell);
return (failed);
}
static char *xstrdup (char *str)
{
char *dup;
if (! str)
return NULL;
dup = (char *) malloc (strlen (str) + 1);
if (! dup) {
failed = -1;
return NULL;
}
strcpy (dup, str);
return dup;
}
#ifdef NO_PUTPWENT
int putpwent (const struct passwd *p, FILE *stream)
{
if (p == NULL || stream == NULL) {
errno = EINVAL;
return (-1);
}
if (fprintf (stream, "%s:%s:%u:%u:%s:%s:%s\n",
p->pw_name, p->pw_passwd, p->pw_uid, p->pw_gid,
p->pw_gecos, p->pw_dir, p->pw_shell) < 0)
return (-1);
return(0);
}
#endif
static void
pw_init()
{
struct rlimit rlim;
/* Unlimited resource limits. */
rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
(void)setrlimit(RLIMIT_CPU, &rlim);
(void)setrlimit(RLIMIT_FSIZE, &rlim);
(void)setrlimit(RLIMIT_STACK, &rlim);
(void)setrlimit(RLIMIT_DATA, &rlim);
(void)setrlimit(RLIMIT_RSS, &rlim);
/* Don't drop core (not really necessary, but GP's). */
rlim.rlim_cur = rlim.rlim_max = 0;
(void)setrlimit(RLIMIT_CORE, &rlim);
/* Turn off signals. */
(void)signal(SIGALRM, SIG_IGN);
(void)signal(SIGHUP, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGPIPE, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGTERM, SIG_IGN);
(void)signal(SIGTSTP, SIG_IGN);
(void)signal(SIGTTOU, SIG_IGN);
/* Create with exact permissions. */
(void)umask(0);
}