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