| /* |
| * 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. |
| * |
| * 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: |
| * 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. |
| * |
| * 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: poer $ |
| * $Revision: 1.13 $ |
| * $Date: 1997/06/23 08:26:29 $ |
| * |
| */ |
| |
| #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> |
| #include <sys/stat.h> |
| #include <paths.h> |
| |
| #include "setpwnam.h" |
| |
| #define false 0 |
| #define true 1 |
| |
| typedef int boolean; |
| |
| 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, the entry is |
| * replaced with the given entry. |
| */ |
| int |
| setpwnam (struct passwd *pwd) |
| { |
| FILE *fp = NULL, *pwf = NULL; |
| int x, save_errno, fd, ret; |
| boolean found; |
| int oldumask; |
| int namelen; |
| int buflen = 256; |
| int contlen; |
| char *linebuf = malloc(buflen); |
| |
| 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|O_EXCL, 0644); |
| if (fd == -1) { |
| 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) return -1; |
| |
| /* open ptmp for writing and passwd for reading */ |
| fp = fdopen(fd, "w"); |
| if (!fp) goto fail; |
| |
| pwf = fopen(PASSWD_FILE, "r"); |
| if (!pwf) goto fail; |
| |
| namelen = strlen(pwd->pw_name); |
| |
| /* parse the passwd file */ |
| found = false; |
| /* 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; |
| fp = NULL; |
| close (fd); |
| fd = -1; |
| fclose (pwf); /* I don't think I want to know if this failed */ |
| pwf = NULL; |
| |
| 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 != NULL) fclose (fp); |
| if (pwf != NULL) fclose(pwf); |
| if (fd >= 0) close (fd); |
| free(linebuf); |
| unlink(PTMP_FILE); |
| errno = save_errno; |
| return -1; |
| } |
| |
| /* Set up the limits so that we're not foiled */ |
| |
| static void |
| pw_init() |
| { |
| struct rlimit rlim; |
| |
| /* 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 |
| |
| /* 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); |
| } |