]> git.ipfire.org Git - thirdparty/util-linux.git/blame - login-utils/setpwnam.c
Merge branch 'fix-exit-codes' of https://github.com/rudimeier/util-linux
[thirdparty/util-linux.git] / login-utils / setpwnam.c
CommitLineData
6dbe3af9 1/*
7ff9c2aa 2 * setpwnam.c -- edit an entry in a password database.
6dbe3af9
KZ
3 *
4 * (c) 1994 Salvatore Valente <svalente@mit.edu>
5 * This file is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
fd6b7a7f 10 * Edited 11/10/96 (DD/MM/YY ;-) by Nicolai Langfeldt (janl@math.uio.no)
7ff9c2aa 11 * to read /etc/passwd directly so that passwd, chsh and chfn can work on
123ddced
BS
12 * machines that run NIS (previously YP). Changes will not be made to
13 * usernames starting with +.
7ff9c2aa 14 *
6dbe3af9
KZ
15 * This file is distributed with no warranty.
16 *
17 * Usage:
18 * 1) get a struct passwd * from getpwnam().
7ff9c2aa
SK
19 * You should assume a struct passwd has an infinite number of fields, so
20 * you should not try to create one from scratch.
6dbe3af9
KZ
21 * 2) edit the fields you want to edit.
22 * 3) call setpwnam() with the edited struct passwd.
23 *
7ff9c2aa
SK
24 * A _normal user_ program should never directly manipulate etc/passwd but
25 * /use getpwnam() and (family, as well as) setpwnam().
fd6b7a7f 26 *
7ff9c2aa
SK
27 * But, setpwnam was made to _edit_ the password file. For use by chfn,
28 * chsh and passwd. _I_ _HAVE_ to read and write /etc/passwd directly. Let
29 * those who say nay be forever silent and think about how getpwnam (and
30 * family) works on a machine running YP.
fd6b7a7f 31 *
7ff9c2aa
SK
32 * Added checks for failure of malloc() and removed error reporting to
33 * stderr, this is a library function and should not print on the screen,
34 * but return appropriate error codes.
fd6b7a7f 35 * 27-Jan-97 - poe@daimi.aau.dk
6dbe3af9
KZ
36 *
37 * Thanks to "two guys named Ian".
fd6b7a7f
KZ
38 *
39 * $Author: poer $
40 * $Revision: 1.13 $
41 * $Date: 1997/06/23 08:26:29 $
6dbe3af9
KZ
42 */
43
726f69e2 44#undef DEBUG
6dbe3af9 45
7ff9c2aa 46#include <errno.h>
6dbe3af9 47#include <fcntl.h>
7ff9c2aa 48#include <paths.h>
6dbe3af9 49#include <pwd.h>
decd9632 50#include <shadow.h>
726f69e2 51#include <signal.h>
7ff9c2aa
SK
52#include <stdbool.h>
53#include <stdio.h>
54#include <stdlib.h>
55#include <string.h>
726f69e2 56#include <sys/resource.h>
fd6b7a7f 57#include <sys/stat.h>
7ff9c2aa
SK
58#include <sys/types.h>
59#include <unistd.h>
726f69e2 60
d73fd391 61#include "c.h"
1b1af0c1 62#include "fileutils.h"
439cdf1e 63#include "closestream.h"
7ff9c2aa 64#include "setpwnam.h"
6dbe3af9 65
726f69e2 66static void pw_init(void);
6dbe3af9
KZ
67
68/*
69 * setpwnam () --
70 * takes a struct passwd in which every field is filled in and valid.
fd6b7a7f 71 * If the given username exists in the passwd file, the entry is
6dbe3af9
KZ
72 * replaced with the given entry.
73 */
bde91c85 74int setpwnam(struct passwd *pwd, const char *prefix)
6dbe3af9 75{
7ff9c2aa 76 FILE *fp = NULL, *pwf = NULL;
decd9632 77 int save_errno;
7ff9c2aa 78 int found;
7ff9c2aa
SK
79 int namelen;
80 int buflen = 256;
78711782 81 int contlen, rc;
7ff9c2aa 82 char *linebuf = NULL;
decd9632 83 char *tmpname = NULL;
7ff9c2aa
SK
84
85 pw_init();
86
bde91c85 87 if ((fp = xfmkstemp(&tmpname, "/etc", prefix)) == NULL)
7ff9c2aa
SK
88 return -1;
89
90 /* ptmp should be owned by root.root or root.wheel */
decd9632 91 if (fchown(fileno(fp), (uid_t) 0, (gid_t) 0) < 0)
7ff9c2aa
SK
92 goto fail;
93
decd9632
SK
94 /* acquire exclusive lock */
95 if (lckpwdf() < 0)
96 goto fail;
7ff9c2aa
SK
97 pwf = fopen(PASSWD_FILE, "r");
98 if (!pwf)
99 goto fail;
100
101 namelen = strlen(pwd->pw_name);
102
103 linebuf = malloc(buflen);
104 if (!linebuf)
105 goto fail;
106
107 /* parse the passwd file */
108 found = false;
109
110 /* Do you wonder why I don't use getpwent? Read comments at top of
111 * file */
112 while (fgets(linebuf, buflen, pwf) != NULL) {
113 contlen = strlen(linebuf);
114 while (linebuf[contlen - 1] != '\n' && !feof(pwf)) {
115 char *tmp;
116 /* Extend input buffer if it failed getting the whole line,
117 * so now we double the buffer size */
118 buflen *= 2;
119 tmp = realloc(linebuf, buflen);
120 if (tmp == NULL)
121 goto fail;
122 linebuf = tmp;
123 /* And fill the rest of the buffer */
124 if (fgets(&linebuf[contlen], buflen / 2, pwf) == NULL)
125 break;
126 contlen = strlen(linebuf);
127 /* That was a lot of work for nothing. Gimme perl! */
128 }
129
130 /* Is this the username we were sent to change? */
131 if (!found && linebuf[namelen] == ':' &&
132 !strncmp(linebuf, pwd->pw_name, namelen)) {
133 /* Yes! So go forth in the name of the Lord and
134 * change it! */
135 if (putpwent(pwd, fp) < 0)
136 goto fail;
137 found = true;
138 continue;
139 }
140 /* Nothing in particular happened, copy input to output */
141 fputs(linebuf, fp);
fd6b7a7f
KZ
142 }
143
7961acce 144 /* xfmkstemp is too restrictive by default for passwd file */
decd9632
SK
145 if (fchmod(fileno(fp), 0644) < 0)
146 goto fail;
1b1af0c1
KZ
147 rc = close_stream(fp);
148 fp = NULL;
149 if (rc != 0)
78711782
KZ
150 goto fail;
151
7ff9c2aa
SK
152 fclose(pwf); /* I don't think I want to know if this failed */
153 pwf = NULL;
154
155 if (!found) {
156 errno = ENOENT; /* give me something better */
157 goto fail;
fd6b7a7f 158 }
7ff9c2aa
SK
159
160 /* we don't care if we can't remove the backup file */
161 unlink(PASSWD_FILE ".OLD");
162 /* we don't care if we can't create the backup file */
163 ignore_result(link(PASSWD_FILE, PASSWD_FILE ".OLD"));
164 /* we DO care if we can't rename to the passwd file */
decd9632 165 if (rename(tmpname, PASSWD_FILE) < 0)
7ff9c2aa
SK
166 goto fail;
167 /* finally: success */
decd9632 168 ulckpwdf();
31af559e 169 free(linebuf);
7ff9c2aa
SK
170 return 0;
171
172 fail:
173 save_errno = errno;
decd9632 174 ulckpwdf();
7ff9c2aa
SK
175 if (fp != NULL)
176 fclose(fp);
decd9632
SK
177 if (tmpname != NULL)
178 unlink(tmpname);
179 free(tmpname);
7ff9c2aa
SK
180 if (pwf != NULL)
181 fclose(pwf);
7ff9c2aa 182 free(linebuf);
7ff9c2aa
SK
183 errno = save_errno;
184 return -1;
6dbe3af9
KZ
185}
186
fd6b7a7f 187/* Set up the limits so that we're not foiled */
0d132731 188static void pw_init(void)
6dbe3af9 189{
7ff9c2aa 190 struct rlimit rlim;
6dbe3af9 191
7ff9c2aa
SK
192 /* Unlimited resource limits. */
193 rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
194 setrlimit(RLIMIT_CPU, &rlim);
195 setrlimit(RLIMIT_FSIZE, &rlim);
196 setrlimit(RLIMIT_STACK, &rlim);
197 setrlimit(RLIMIT_DATA, &rlim);
198 setrlimit(RLIMIT_RSS, &rlim);
6dbe3af9 199
fd6b7a7f 200#ifndef DEBUG
7ff9c2aa
SK
201 /* Don't drop core (not really necessary, but GP's). */
202 rlim.rlim_cur = rlim.rlim_max = 0;
203 setrlimit(RLIMIT_CORE, &rlim);
6dbe3af9 204#endif
726f69e2 205
7ff9c2aa
SK
206 /* Turn off signals. */
207 signal(SIGALRM, SIG_IGN);
208 signal(SIGHUP, SIG_IGN);
209 signal(SIGINT, SIG_IGN);
210 signal(SIGPIPE, SIG_IGN);
211 signal(SIGQUIT, SIG_IGN);
212 signal(SIGTERM, SIG_IGN);
213 signal(SIGTSTP, SIG_IGN);
214 signal(SIGTTOU, SIG_IGN);
215
216 /* Create with exact permissions. */
217 umask(0);
726f69e2 218}