util-linux/login-utils/setpwnam.c

210 lines
5.2 KiB
C

/*
* 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".
*/
/* faith
* 1.1.1.1
* 1995/02/22 19:09:24
*/
#define DEBUG 0
/* 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>
#ifdef BSD43
#include <sys/file.h>
#endif
extern int errno;
typedef int boolean;
#define false 0
#define true 1
#ifndef DEBUG
#define PASSWD_FILE "/etc/passwd"
#define PTMP_FILE "/etc/ptmp"
#else
#define PASSWD_FILE "/tmp/passwd"
#define PTMP_FILE "/tmp/ptmp"
#endif
static int copy_pwd (struct passwd *src, struct passwd *dest);
static char *xstrdup (char *str);
/*
* 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)
{
char *passwd = PASSWD_FILE;
char *ptmp = PTMP_FILE;
FILE *fp;
int x, save_errno, fd;
struct passwd *entry;
boolean found;
char buf[50];
struct passwd spwd;
/* 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);
/* sanity check */
for (x = 0; x < 3; x++) {
if (x > 0) sleep (1);
fd = open (ptmp, O_WRONLY|O_CREAT|O_EXCL, 00644);
if (fd >= 0) break;
}
if (fd < 0) return (-1);
/* ptmp should be owned by root.root or root.wheel */
if (chown (ptmp, (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;
}
strcpy (buf, passwd);
strcat (buf, "~");
/* we don't care if we can't remove the backup file */
remove (buf);
/* we don't care if we can't create the backup file */
link (passwd, buf);
/* we DO care if we can't erase the passwd file */
if (remove (passwd) < 0) {
/* if the file is still there, fail */
if (access (passwd, F_OK) == 0) goto fail;
}
/* if we can't link ptmp to passwd, all is lost */
if (link (ptmp, passwd) < 0) {
/* reinstall_system (); */
return (-1);
}
/* if we can't erase the ptmp file, we simply lose */
if (remove (ptmp) < 0)
return (-1);
/* finally: success */
return 0;
fail:
save_errno = errno;
if (fp) fclose (fp);
if (fd >= 0) close (fd);
endpwent ();
remove (ptmp);
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