diff options
Diffstat (limited to 'login-utils/setpwnam.c')
-rw-r--r-- | login-utils/setpwnam.c | 266 |
1 files changed, 119 insertions, 147 deletions
diff --git a/login-utils/setpwnam.c b/login-utils/setpwnam.c index 8a01c6ec5..1f1067cf1 100644 --- a/login-utils/setpwnam.c +++ b/login-utils/setpwnam.c @@ -8,6 +8,11 @@ * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * + * Edited 11/10/96 (DD/MM/YY ;-) by Nicolai Langfeldt (janl@math.uio.no) + * to read /etc/passwd directly so that passwd, chsh and chfn can work + * on machines that run NIS (né YP). Changes will not be made to + * usernames starting with +. + * * This file is distributed with no warranty. * * Usage: @@ -17,15 +22,25 @@ * 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(). + * A _normal user_ program should never directly manipulate + * /etc/passwd but use getpwnam() and (family, as well as) + * setpwnam(). + * + * But, setpwnam was made to _edit_ the password file. For use by + * chfn, chsh and passwd. _I_ _HAVE_ to read and write /etc/passwd + * directly. Let those who say nay be forever silent and think about + * how getpwnam (and family) works on a machine running YP. + * + * Added checks for failure of malloc() and removed error reporting + * to stderr, this is a library function and should not print on the + * screen, but return appropriate error codes. + * 27-Jan-97 - poe@daimi.aau.dk * * Thanks to "two guys named Ian". - */ -/* $Author: faith $ - * $Revision: 1.5 $ - * $Date: 1995/10/12 14:46:36 $ + * + * $Author: poer $ + * $Revision: 1.13 $ + * $Date: 1997/06/23 08:26:29 $ */ #undef DEBUG @@ -43,207 +58,164 @@ #include <errno.h> #include <signal.h> #include <sys/resource.h> -#ifdef BSD43 -#include <sys/file.h> -#endif +#include <sys/stat.h> #include <paths.h> -#include "pathnames.h" - -extern int errno; +#include "setpwnam.h" -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 + * If the given username exists in the passwd file, the entry is * replaced with the given entry. */ -int setpwnam (struct passwd *pwd) +int +setpwnam (struct passwd *pwd) { - FILE *fp; + FILE *fp = NULL, *pwf = NULL; int x, save_errno, fd, ret; - struct passwd *entry; boolean found; - struct passwd spwd; int oldumask; + int namelen; + int buflen = 256; + int contlen; + char *linebuf = malloc(buflen); - /* 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); + if (!linebuf) 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); + if (x > 0) sleep(1); + fd = open(PTMPTMP_FILE, O_WRONLY|O_CREAT, 0644); + if (fd == -1) { umask(oldumask); - return (-1); + return -1; } ret = link(PTMPTMP_FILE, PTMP_FILE); unlink(PTMPTMP_FILE); - if(ret == -1) + if (ret == -1) close(fd); else break; } - umask(oldumask); - if (ret == -1) return (-1); + 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"); + if (chown(PTMP_FILE, (uid_t) 0, (gid_t) 0) < 0) return -1; /* open ptmp for writing and passwd for reading */ - fp = fdopen (fd, "w"); - if (! fp) goto fail; + fp = fdopen(fd, "w"); + if (!fp) goto fail; - setpwent (); + pwf = fopen(PASSWD_FILE, "r"); + if (!pwf) goto fail; + + namelen = strlen(pwd->pw_name); /* 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; + /* Do you wonder why I don't use getpwent? Read comments at top of file */ + while (fgets(linebuf, buflen, pwf) != NULL) { + contlen = strlen(linebuf); + while (linebuf[contlen-1] != '\n' && !feof(pwf)) { + /* Extend input buffer if it failed getting the whole line */ + + /* So now we double the buffer size */ + buflen *= 2; + + linebuf = realloc(linebuf, buflen); + if (linebuf == NULL) goto fail; + + /* And fill the rest of the buffer */ + if (fgets(&linebuf[contlen], buflen/2, pwf) == NULL) break; + contlen = strlen(linebuf); + + /* That was a lot of work for nothing. Gimme perl! */ + } + + /* Is this the username we were sent to change? */ + if (!found && linebuf[namelen] == ':' && + !strncmp(linebuf, pwd->pw_name, namelen)) { + /* Yes! So go forth in the name of the Lord and change it! */ + if (putpwent(pwd, fp) < 0) goto fail; + found = true; + continue; + } + /* Nothing in particular happened, copy input to output */ + fputs(linebuf, fp); } - if (fclose (fp) < 0) goto fail; + + if (fclose(fp) < 0) goto fail; + fp = NULL; close (fd); - endpwent (); + fd = -1; + fclose (pwf); /* I don't think I want to know if this failed */ + pwf = NULL; - if (! found) { + 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"); + unlink(PASSWD_FILE".OLD"); /* we don't care if we can't create the backup file */ - link (PASSWD_FILE, PASSWD_FILE".OLD"); + 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) + if(rename(PTMP_FILE, PASSWD_FILE) < 0) goto fail; /* finally: success */ return 0; - fail: +fail: save_errno = errno; - if (fp) fclose (fp); + if (fp != NULL) fclose (fp); + if (pwf != NULL) fclose(pwf); if (fd >= 0) close (fd); - endpwent (); - unlink (PTMP_FILE); + if (linebuf != NULL) free(linebuf); + unlink(PTMP_FILE); errno = save_errno; - return (-1); + 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); -} +/* Set up the limits so that we're not foiled */ -static char *xstrdup (char *str) +static void +pw_init() { - char *dup; - - if (! str) - return NULL; - dup = (char *) malloc (strlen (str) + 1); - if (! dup) { - failed = -1; - return NULL; - } - strcpy (dup, str); - return dup; -} + struct rlimit rlim; -#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); -} + /* Unlimited resource limits. */ + rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY; + setrlimit(RLIMIT_CPU, &rlim); + setrlimit(RLIMIT_FSIZE, &rlim); + setrlimit(RLIMIT_STACK, &rlim); + setrlimit(RLIMIT_DATA, &rlim); + setrlimit(RLIMIT_RSS, &rlim); +#ifndef DEBUG + /* Don't drop core (not really necessary, but GP's). */ + rlim.rlim_cur = rlim.rlim_max = 0; + setrlimit(RLIMIT_CORE, &rlim); #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); + /* Turn off signals. */ + signal(SIGALRM, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + + /* Create with exact permissions. */ + umask(0); } |