1 |
|
|
/* $OpenBSD: user.c,v 1.120 2017/05/24 09:18:15 mestre Exp $ */ |
2 |
|
|
/* $NetBSD: user.c,v 1.69 2003/04/14 17:40:07 agc Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1999 Alistair G. Crooks. All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* 3. The name of the author may not be used to endorse or promote |
16 |
|
|
* products derived from this software without specific prior written |
17 |
|
|
* permission. |
18 |
|
|
* |
19 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
20 |
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
21 |
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
22 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
23 |
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
24 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
25 |
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 |
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
27 |
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
28 |
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
29 |
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 |
|
|
*/ |
31 |
|
|
|
32 |
|
|
#include <sys/types.h> |
33 |
|
|
#include <sys/stat.h> |
34 |
|
|
|
35 |
|
|
#include <ctype.h> |
36 |
|
|
#include <dirent.h> |
37 |
|
|
#include <err.h> |
38 |
|
|
#include <errno.h> |
39 |
|
|
#include <fcntl.h> |
40 |
|
|
#include <grp.h> |
41 |
|
|
#include <limits.h> |
42 |
|
|
#include <login_cap.h> |
43 |
|
|
#include <paths.h> |
44 |
|
|
#include <pwd.h> |
45 |
|
|
#include <stdarg.h> |
46 |
|
|
#include <stdio.h> |
47 |
|
|
#include <stdlib.h> |
48 |
|
|
#include <string.h> |
49 |
|
|
#include <syslog.h> |
50 |
|
|
#include <time.h> |
51 |
|
|
#include <unistd.h> |
52 |
|
|
#include <util.h> |
53 |
|
|
|
54 |
|
|
#include "usermgmt.h" |
55 |
|
|
|
56 |
|
|
|
57 |
|
|
/* this struct describes a uid range */ |
58 |
|
|
typedef struct range_t { |
59 |
|
|
uid_t r_from; /* low uid */ |
60 |
|
|
uid_t r_to; /* high uid */ |
61 |
|
|
} range_t; |
62 |
|
|
|
63 |
|
|
/* this struct encapsulates the user information */ |
64 |
|
|
typedef struct user_t { |
65 |
|
|
int u_flags; /* see below */ |
66 |
|
|
uid_t u_uid; /* uid of user */ |
67 |
|
|
char *u_password; /* encrypted password */ |
68 |
|
|
char *u_comment; /* comment field */ |
69 |
|
|
char *u_home; /* home directory */ |
70 |
|
|
char *u_primgrp; /* primary group */ |
71 |
|
|
int u_groupc; /* # of secondary groups */ |
72 |
|
|
const char *u_groupv[NGROUPS_MAX]; /* secondary groups */ |
73 |
|
|
char *u_shell; /* user's shell */ |
74 |
|
|
char *u_basedir; /* base directory for home */ |
75 |
|
|
char *u_expire; /* when account will expire */ |
76 |
|
|
char *u_inactive; /* when password will expire */ |
77 |
|
|
char *u_skeldir; /* directory for startup files */ |
78 |
|
|
char *u_class; /* login class */ |
79 |
|
|
unsigned int u_rsize; /* size of range array */ |
80 |
|
|
unsigned int u_rc; /* # of ranges */ |
81 |
|
|
range_t *u_rv; /* the ranges */ |
82 |
|
|
unsigned int u_defrc; /* # of ranges in defaults */ |
83 |
|
|
int u_preserve; /* preserve uids on deletion */ |
84 |
|
|
} user_t; |
85 |
|
|
|
86 |
|
|
/* flags for which fields of the user_t replace the passwd entry */ |
87 |
|
|
enum { |
88 |
|
|
F_COMMENT = 0x0001, |
89 |
|
|
F_DUPUID = 0x0002, |
90 |
|
|
F_EXPIRE = 0x0004, |
91 |
|
|
F_GROUP = 0x0008, |
92 |
|
|
F_HOMEDIR = 0x0010, |
93 |
|
|
F_MKDIR = 0x0020, |
94 |
|
|
F_INACTIVE = 0x0040, |
95 |
|
|
F_PASSWORD = 0x0080, |
96 |
|
|
F_SECGROUP = 0x0100, |
97 |
|
|
F_SHELL = 0x0200, |
98 |
|
|
F_UID = 0x0400, |
99 |
|
|
F_USERNAME = 0x0800, |
100 |
|
|
F_CLASS = 0x1000, |
101 |
|
|
F_SETSECGROUP = 0x4000, |
102 |
|
|
F_ACCTLOCK = 0x8000, |
103 |
|
|
F_ACCTUNLOCK = 0x10000 |
104 |
|
|
}; |
105 |
|
|
|
106 |
|
|
#define CONFFILE "/etc/usermgmt.conf" |
107 |
|
|
#define _PATH_NONEXISTENT "/nonexistent" |
108 |
|
|
|
109 |
|
|
#ifndef DEF_GROUP |
110 |
|
|
#define DEF_GROUP "=uid" |
111 |
|
|
#endif |
112 |
|
|
|
113 |
|
|
#ifndef DEF_BASEDIR |
114 |
|
|
#define DEF_BASEDIR "/home" |
115 |
|
|
#endif |
116 |
|
|
|
117 |
|
|
#ifndef DEF_SKELDIR |
118 |
|
|
#define DEF_SKELDIR "/etc/skel" |
119 |
|
|
#endif |
120 |
|
|
|
121 |
|
|
#ifndef DEF_SHELL |
122 |
|
|
#define DEF_SHELL _PATH_KSHELL |
123 |
|
|
#endif |
124 |
|
|
|
125 |
|
|
#ifndef DEF_COMMENT |
126 |
|
|
#define DEF_COMMENT "" |
127 |
|
|
#endif |
128 |
|
|
|
129 |
|
|
#ifndef DEF_LOWUID |
130 |
|
|
#define DEF_LOWUID 1000 |
131 |
|
|
#endif |
132 |
|
|
|
133 |
|
|
#ifndef DEF_HIGHUID |
134 |
|
|
#define DEF_HIGHUID 60000 |
135 |
|
|
#endif |
136 |
|
|
|
137 |
|
|
#ifndef DEF_INACTIVE |
138 |
|
|
#define DEF_INACTIVE 0 |
139 |
|
|
#endif |
140 |
|
|
|
141 |
|
|
#ifndef DEF_EXPIRE |
142 |
|
|
#define DEF_EXPIRE NULL |
143 |
|
|
#endif |
144 |
|
|
|
145 |
|
|
#ifndef DEF_CLASS |
146 |
|
|
#define DEF_CLASS "" |
147 |
|
|
#endif |
148 |
|
|
|
149 |
|
|
#ifndef WAITSECS |
150 |
|
|
#define WAITSECS 10 |
151 |
|
|
#endif |
152 |
|
|
|
153 |
|
|
#ifndef NOBODY_UID |
154 |
|
|
#define NOBODY_UID 32767 |
155 |
|
|
#endif |
156 |
|
|
|
157 |
|
|
/* some useful constants */ |
158 |
|
|
enum { |
159 |
|
|
MaxShellNameLen = 256, |
160 |
|
|
MaxFileNameLen = PATH_MAX, |
161 |
|
|
MaxUserNameLen = _PW_NAME_LEN, |
162 |
|
|
MaxCommandLen = 2048, |
163 |
|
|
PasswordLength = _PASSWORD_LEN, |
164 |
|
|
LowGid = DEF_LOWUID, |
165 |
|
|
HighGid = DEF_HIGHUID |
166 |
|
|
}; |
167 |
|
|
|
168 |
|
|
/* Full paths of programs used here */ |
169 |
|
|
#define CHMOD "/bin/chmod" |
170 |
|
|
#define CHOWN "/sbin/chown" |
171 |
|
|
#define MKDIR "/bin/mkdir" |
172 |
|
|
#define MV "/bin/mv" |
173 |
|
|
#define NOLOGIN "/sbin/nologin" |
174 |
|
|
#define PAX "/bin/pax" |
175 |
|
|
#define RM "/bin/rm" |
176 |
|
|
|
177 |
|
|
#define UNSET_INACTIVE "Null (unset)" |
178 |
|
|
#define UNSET_EXPIRY "Null (unset)" |
179 |
|
|
|
180 |
|
|
static int adduser(char *, user_t *); |
181 |
|
|
static int append_group(char *, int, const char **); |
182 |
|
|
static int asystem(const char *, ...) |
183 |
|
|
__attribute__((__format__(__printf__, 1, 2))); |
184 |
|
|
static int copydotfiles(char *, char *); |
185 |
|
|
static int creategid(char *, gid_t, const char *); |
186 |
|
|
static int getnextgid(uid_t *, uid_t, uid_t); |
187 |
|
|
static int getnextuid(int, uid_t *, uid_t, uid_t); |
188 |
|
|
static int is_local(char *, const char *); |
189 |
|
|
static int modify_gid(char *, char *); |
190 |
|
|
static int moduser(char *, char *, user_t *); |
191 |
|
|
static int removehomedir(const char *, uid_t, const char *); |
192 |
|
|
static int rm_user_from_groups(char *); |
193 |
|
|
static int save_range(user_t *, char *); |
194 |
|
|
static int scantime(time_t *, char *); |
195 |
|
|
static int setdefaults(user_t *); |
196 |
|
|
static int valid_class(char *); |
197 |
|
|
static int valid_group(char *); |
198 |
|
|
static int valid_login(char *); |
199 |
|
|
static size_t expand_len(const char *, const char *); |
200 |
|
|
static struct group *find_group_info(const char *); |
201 |
|
|
static struct passwd *find_user_info(const char *); |
202 |
|
|
static void checkeuid(void); |
203 |
|
|
static void memsave(char **, const char *, size_t); |
204 |
|
|
static void read_defaults(user_t *); |
205 |
|
|
|
206 |
|
|
static int verbose; |
207 |
|
|
|
208 |
|
|
/* if *cpp is non-null, free it, then assign `n' chars of `s' to it */ |
209 |
|
|
static void |
210 |
|
|
memsave(char **cpp, const char *s, size_t n) |
211 |
|
|
{ |
212 |
|
|
free(*cpp); |
213 |
|
|
if ((*cpp = calloc (n + 1, sizeof(char))) == NULL) |
214 |
|
|
err(1, NULL); |
215 |
|
|
memcpy(*cpp, s, n); |
216 |
|
|
(*cpp)[n] = '\0'; |
217 |
|
|
} |
218 |
|
|
|
219 |
|
|
/* a replacement for system(3) */ |
220 |
|
|
static int |
221 |
|
|
asystem(const char *fmt, ...) |
222 |
|
|
{ |
223 |
|
|
va_list vp; |
224 |
|
|
char buf[MaxCommandLen]; |
225 |
|
|
int ret; |
226 |
|
|
|
227 |
|
|
va_start(vp, fmt); |
228 |
|
|
(void) vsnprintf(buf, sizeof(buf), fmt, vp); |
229 |
|
|
va_end(vp); |
230 |
|
|
if (verbose) { |
231 |
|
|
printf("Command: %s\n", buf); |
232 |
|
|
} |
233 |
|
|
if ((ret = system(buf)) != 0) { |
234 |
|
|
warnx("[Warning] can't system `%s'", buf); |
235 |
|
|
} |
236 |
|
|
return ret; |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
/* remove a users home directory, returning 1 for success (ie, no problems encountered) */ |
240 |
|
|
static int |
241 |
|
|
removehomedir(const char *user, uid_t uid, const char *dir) |
242 |
|
|
{ |
243 |
|
|
struct stat st; |
244 |
|
|
|
245 |
|
|
/* userid not root? */ |
246 |
|
|
if (uid == 0) { |
247 |
|
|
warnx("Not deleting home directory `%s'; userid is 0", dir); |
248 |
|
|
return 0; |
249 |
|
|
} |
250 |
|
|
|
251 |
|
|
/* directory exists (and is a directory!) */ |
252 |
|
|
if (stat(dir, &st) < 0) { |
253 |
|
|
warnx("Home directory `%s' doesn't exist", dir); |
254 |
|
|
return 0; |
255 |
|
|
} |
256 |
|
|
if (!S_ISDIR(st.st_mode)) { |
257 |
|
|
warnx("Home directory `%s' is not a directory", dir); |
258 |
|
|
return 0; |
259 |
|
|
} |
260 |
|
|
|
261 |
|
|
/* userid matches directory owner? */ |
262 |
|
|
if (st.st_uid != uid) { |
263 |
|
|
warnx("User `%s' doesn't own directory `%s', not removed", |
264 |
|
|
user, dir); |
265 |
|
|
return 0; |
266 |
|
|
} |
267 |
|
|
|
268 |
|
|
(void) seteuid(uid); |
269 |
|
|
/* we add the "|| true" to keep asystem() quiet if there is a non-zero exit status. */ |
270 |
|
|
(void) asystem("%s -rf %s > /dev/null 2>&1 || true", RM, dir); |
271 |
|
|
(void) seteuid(0); |
272 |
|
|
if (rmdir(dir) < 0) { |
273 |
|
|
warnx("Unable to remove all files in `%s'", dir); |
274 |
|
|
return 0; |
275 |
|
|
} |
276 |
|
|
return 1; |
277 |
|
|
} |
278 |
|
|
|
279 |
|
|
/* |
280 |
|
|
* check that the effective uid is 0 - called from funcs which will |
281 |
|
|
* modify data and config files. |
282 |
|
|
*/ |
283 |
|
|
static void |
284 |
|
|
checkeuid(void) |
285 |
|
|
{ |
286 |
|
|
if (geteuid() != 0) { |
287 |
|
|
errx(EXIT_FAILURE, "Program must be run as root"); |
288 |
|
|
} |
289 |
|
|
} |
290 |
|
|
|
291 |
|
|
/* copy any dot files into the user's home directory */ |
292 |
|
|
static int |
293 |
|
|
copydotfiles(char *skeldir, char *dir) |
294 |
|
|
{ |
295 |
|
|
struct dirent *dp; |
296 |
|
|
DIR *dirp; |
297 |
|
|
int n; |
298 |
|
|
|
299 |
|
|
if (*skeldir == '\0') |
300 |
|
|
return 0; |
301 |
|
|
if ((dirp = opendir(skeldir)) == NULL) { |
302 |
|
|
warn("can't open source . files dir `%s'", skeldir); |
303 |
|
|
return 0; |
304 |
|
|
} |
305 |
|
|
for (n = 0; (dp = readdir(dirp)) != NULL && n == 0 ; ) { |
306 |
|
|
if (strcmp(dp->d_name, ".") == 0 || |
307 |
|
|
strcmp(dp->d_name, "..") == 0) { |
308 |
|
|
continue; |
309 |
|
|
} |
310 |
|
|
n = 1; |
311 |
|
|
} |
312 |
|
|
(void) closedir(dirp); |
313 |
|
|
if (n == 0) { |
314 |
|
|
warnx("No \"dot\" initialisation files found"); |
315 |
|
|
} else { |
316 |
|
|
(void) asystem("cd %s && %s -rw -pe %s . %s", |
317 |
|
|
skeldir, PAX, (verbose) ? "-v" : "", dir); |
318 |
|
|
} |
319 |
|
|
return n; |
320 |
|
|
} |
321 |
|
|
|
322 |
|
|
/* create a group entry with gid `gid' */ |
323 |
|
|
static int |
324 |
|
|
creategid(char *group, gid_t gid, const char *name) |
325 |
|
|
{ |
326 |
|
|
struct stat st; |
327 |
|
|
FILE *from; |
328 |
|
|
FILE *to; |
329 |
|
|
char *buf; |
330 |
|
|
char f[MaxFileNameLen]; |
331 |
|
|
int fd, ret; |
332 |
|
|
int wroteit = 0; |
333 |
|
|
size_t len; |
334 |
|
|
|
335 |
|
|
if (getgrnam(group) != NULL) { |
336 |
|
|
warnx("group `%s' already exists", group); |
337 |
|
|
return 0; |
338 |
|
|
} |
339 |
|
|
if ((from = fopen(_PATH_GROUP, "r")) == NULL) { |
340 |
|
|
warn("can't create gid for `%s': can't open `%s'", group, |
341 |
|
|
_PATH_GROUP); |
342 |
|
|
return 0; |
343 |
|
|
} |
344 |
|
|
if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { |
345 |
|
|
warn("can't lock `%s'", _PATH_GROUP); |
346 |
|
|
} |
347 |
|
|
(void) fstat(fileno(from), &st); |
348 |
|
|
(void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); |
349 |
|
|
if ((fd = mkstemp(f)) < 0) { |
350 |
|
|
warn("can't create gid: mkstemp failed"); |
351 |
|
|
fclose(from); |
352 |
|
|
return 0; |
353 |
|
|
} |
354 |
|
|
if ((to = fdopen(fd, "w")) == NULL) { |
355 |
|
|
warn("can't create gid: fdopen `%s' failed", f); |
356 |
|
|
fclose(from); |
357 |
|
|
close(fd); |
358 |
|
|
unlink(f); |
359 |
|
|
return 0; |
360 |
|
|
} |
361 |
|
|
while ((buf = fgetln(from, &len)) != NULL && len > 0) { |
362 |
|
|
ret = 0; |
363 |
|
|
if (buf[0] == '+' && wroteit == 0) { |
364 |
|
|
ret = fprintf(to, "%s:*:%u:%s\n", group, gid, name); |
365 |
|
|
wroteit = 1; |
366 |
|
|
} |
367 |
|
|
if (ret == -1 || |
368 |
|
|
fprintf(to, "%*.*s", (int)len, (int)len, buf) != len) { |
369 |
|
|
warn("can't create gid: short write to `%s'", f); |
370 |
|
|
fclose(from); |
371 |
|
|
fclose(to); |
372 |
|
|
unlink(f); |
373 |
|
|
return 0; |
374 |
|
|
} |
375 |
|
|
} |
376 |
|
|
ret = 0; |
377 |
|
|
if (wroteit == 0) |
378 |
|
|
ret = fprintf(to, "%s:*:%u:%s\n", group, gid, name); |
379 |
|
|
fclose(from); |
380 |
|
|
if (fclose(to) == EOF || ret == -1) { |
381 |
|
|
warn("can't create gid: short write to `%s'", f); |
382 |
|
|
unlink(f); |
383 |
|
|
return 0; |
384 |
|
|
} |
385 |
|
|
if (rename(f, _PATH_GROUP) < 0) { |
386 |
|
|
warn("can't create gid: can't rename `%s' to `%s'", f, |
387 |
|
|
_PATH_GROUP); |
388 |
|
|
unlink(f); |
389 |
|
|
return 0; |
390 |
|
|
} |
391 |
|
|
(void) chmod(_PATH_GROUP, st.st_mode & 0777); |
392 |
|
|
syslog(LOG_INFO, "new group added: name=%s, gid=%u", group, gid); |
393 |
|
|
return 1; |
394 |
|
|
} |
395 |
|
|
|
396 |
|
|
/* modify the group entry with name `group' to be newent */ |
397 |
|
|
static int |
398 |
|
|
modify_gid(char *group, char *newent) |
399 |
|
|
{ |
400 |
|
|
struct stat st; |
401 |
|
|
FILE *from; |
402 |
|
|
FILE *to; |
403 |
|
|
char buf[LINE_MAX]; |
404 |
|
|
char f[MaxFileNameLen]; |
405 |
|
|
char *colon; |
406 |
|
|
int groupc; |
407 |
|
|
int entc; |
408 |
|
|
int fd; |
409 |
|
|
int cc; |
410 |
|
|
|
411 |
|
|
if ((from = fopen(_PATH_GROUP, "r")) == NULL) { |
412 |
|
|
warn("can't modify gid for `%s': can't open `%s'", group, |
413 |
|
|
_PATH_GROUP); |
414 |
|
|
return 0; |
415 |
|
|
} |
416 |
|
|
if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { |
417 |
|
|
warn("can't lock `%s'", _PATH_GROUP); |
418 |
|
|
} |
419 |
|
|
(void) fstat(fileno(from), &st); |
420 |
|
|
(void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); |
421 |
|
|
if ((fd = mkstemp(f)) < 0) { |
422 |
|
|
warn("can't modify gid: mkstemp failed"); |
423 |
|
|
fclose(from); |
424 |
|
|
return 0; |
425 |
|
|
} |
426 |
|
|
if ((to = fdopen(fd, "w")) == NULL) { |
427 |
|
|
warn("can't modify gid: fdopen `%s' failed", f); |
428 |
|
|
fclose(from); |
429 |
|
|
close(fd); |
430 |
|
|
unlink(f); |
431 |
|
|
return 0; |
432 |
|
|
} |
433 |
|
|
groupc = strlen(group); |
434 |
|
|
while (fgets(buf, sizeof(buf), from) != NULL) { |
435 |
|
|
cc = strlen(buf); |
436 |
|
|
if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { |
437 |
|
|
while (fgetc(from) != '\n' && !feof(from)) |
438 |
|
|
cc++; |
439 |
|
|
warnx("%s: line `%s' too long (%d bytes), skipping", |
440 |
|
|
_PATH_GROUP, buf, cc); |
441 |
|
|
continue; |
442 |
|
|
} |
443 |
|
|
if ((colon = strchr(buf, ':')) == NULL) { |
444 |
|
|
/* |
445 |
|
|
* The only valid entry with no column is the all-YP |
446 |
|
|
* line. |
447 |
|
|
*/ |
448 |
|
|
if (strcmp(buf, "+\n") != 0) { |
449 |
|
|
warnx("badly formed entry `%.*s'", cc - 1, buf); |
450 |
|
|
continue; |
451 |
|
|
} |
452 |
|
|
} else { |
453 |
|
|
entc = (int)(colon - buf); |
454 |
|
|
if (entc == groupc && strncmp(group, buf, entc) == 0) { |
455 |
|
|
if (newent == NULL) { |
456 |
|
|
continue; |
457 |
|
|
} else { |
458 |
|
|
cc = strlcpy(buf, newent, sizeof(buf)); |
459 |
|
|
if (cc >= sizeof(buf)) { |
460 |
|
|
warnx("group `%s' entry too long", |
461 |
|
|
newent); |
462 |
|
|
fclose(from); |
463 |
|
|
fclose(to); |
464 |
|
|
unlink(f); |
465 |
|
|
return (0); |
466 |
|
|
} |
467 |
|
|
} |
468 |
|
|
} |
469 |
|
|
} |
470 |
|
|
if (fwrite(buf, cc, 1, to) != 1) { |
471 |
|
|
warn("can't modify gid: short write to `%s'", f); |
472 |
|
|
fclose(from); |
473 |
|
|
fclose(to); |
474 |
|
|
unlink(f); |
475 |
|
|
return 0; |
476 |
|
|
} |
477 |
|
|
} |
478 |
|
|
fclose(from); |
479 |
|
|
if (fclose(to) == EOF) { |
480 |
|
|
warn("can't modify gid: short write to `%s'", f); |
481 |
|
|
unlink(f); |
482 |
|
|
return 0; |
483 |
|
|
} |
484 |
|
|
if (rename(f, _PATH_GROUP) < 0) { |
485 |
|
|
warn("can't modify gid: can't rename `%s' to `%s'", f, _PATH_GROUP); |
486 |
|
|
unlink(f); |
487 |
|
|
return 0; |
488 |
|
|
} |
489 |
|
|
(void) chmod(_PATH_GROUP, st.st_mode & 0777); |
490 |
|
|
if (newent == NULL) { |
491 |
|
|
syslog(LOG_INFO, "group deleted: name=%s", group); |
492 |
|
|
} else { |
493 |
|
|
syslog(LOG_INFO, "group information modified: name=%s", group); |
494 |
|
|
} |
495 |
|
|
return 1; |
496 |
|
|
} |
497 |
|
|
|
498 |
|
|
/* modify the group entries for all `groups', by adding `user' */ |
499 |
|
|
static int |
500 |
|
|
append_group(char *user, int ngroups, const char **groups) |
501 |
|
|
{ |
502 |
|
|
struct group *grp; |
503 |
|
|
struct passwd *pwp; |
504 |
|
|
struct stat st; |
505 |
|
|
FILE *from; |
506 |
|
|
FILE *to; |
507 |
|
|
char buf[LINE_MAX]; |
508 |
|
|
char f[MaxFileNameLen]; |
509 |
|
|
char *colon; |
510 |
|
|
char *ugid = NULL; |
511 |
|
|
int fd; |
512 |
|
|
int cc; |
513 |
|
|
int i; |
514 |
|
|
int j; |
515 |
|
|
|
516 |
|
|
if ((pwp = getpwnam(user))) { |
517 |
|
|
if ((ugid = group_from_gid(pwp->pw_gid, 1)) == NULL) { |
518 |
|
|
warnx("can't get primary group for user `%s'", user); |
519 |
|
|
return 0; |
520 |
|
|
} |
521 |
|
|
} |
522 |
|
|
|
523 |
|
|
for (i = 0 ; i < ngroups ; i++) { |
524 |
|
|
if ((grp = getgrnam(groups[i])) == NULL) { |
525 |
|
|
warnx("can't append group `%s' for user `%s'", |
526 |
|
|
groups[i], user); |
527 |
|
|
} else { |
528 |
|
|
for (j = 0 ; grp->gr_mem[j] ; j++) { |
529 |
|
|
if (strcmp(user, grp->gr_mem[j]) == 0) { |
530 |
|
|
/* already in it */ |
531 |
|
|
groups[i] = ""; |
532 |
|
|
} |
533 |
|
|
} |
534 |
|
|
} |
535 |
|
|
} |
536 |
|
|
if ((from = fopen(_PATH_GROUP, "r")) == NULL) { |
537 |
|
|
warn("can't append group for `%s': can't open `%s'", user, |
538 |
|
|
_PATH_GROUP); |
539 |
|
|
return 0; |
540 |
|
|
} |
541 |
|
|
if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { |
542 |
|
|
warn("can't lock `%s'", _PATH_GROUP); |
543 |
|
|
} |
544 |
|
|
(void) fstat(fileno(from), &st); |
545 |
|
|
(void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); |
546 |
|
|
if ((fd = mkstemp(f)) < 0) { |
547 |
|
|
warn("can't append group: mkstemp failed"); |
548 |
|
|
fclose(from); |
549 |
|
|
return 0; |
550 |
|
|
} |
551 |
|
|
if ((to = fdopen(fd, "w")) == NULL) { |
552 |
|
|
warn("can't append group: fdopen `%s' failed", f); |
553 |
|
|
fclose(from); |
554 |
|
|
close(fd); |
555 |
|
|
unlink(f); |
556 |
|
|
return 0; |
557 |
|
|
} |
558 |
|
|
while (fgets(buf, sizeof(buf), from) != NULL) { |
559 |
|
|
cc = strlen(buf); |
560 |
|
|
if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { |
561 |
|
|
while (fgetc(from) != '\n' && !feof(from)) |
562 |
|
|
cc++; |
563 |
|
|
warnx("%s: line `%s' too long (%d bytes), skipping", |
564 |
|
|
_PATH_GROUP, buf, cc); |
565 |
|
|
continue; |
566 |
|
|
} |
567 |
|
|
if ((colon = strchr(buf, ':')) == NULL) { |
568 |
|
|
warnx("badly formed entry `%s'", buf); |
569 |
|
|
continue; |
570 |
|
|
} |
571 |
|
|
for (i = 0 ; i < ngroups ; i++) { |
572 |
|
|
j = (int)(colon - buf); |
573 |
|
|
if (ugid) { |
574 |
|
|
if (strcmp(ugid, groups[i]) == 0) { |
575 |
|
|
/* user's primary group, no need to append */ |
576 |
|
|
groups[i] = ""; |
577 |
|
|
} |
578 |
|
|
} |
579 |
|
|
if (strncmp(groups[i], buf, j) == 0 && |
580 |
|
|
groups[i][j] == '\0') { |
581 |
|
|
while (isspace((unsigned char)buf[cc - 1])) |
582 |
|
|
cc--; |
583 |
|
|
buf[(j = cc)] = '\0'; |
584 |
|
|
if (buf[strlen(buf) - 1] != ':') |
585 |
|
|
strlcat(buf, ",", sizeof(buf)); |
586 |
|
|
cc = strlcat(buf, user, sizeof(buf)) + 1; |
587 |
|
|
if (cc >= sizeof(buf)) { |
588 |
|
|
warnx("Warning: group `%s' would " |
589 |
|
|
"become too long, not modifying", |
590 |
|
|
groups[i]); |
591 |
|
|
cc = j + 1; |
592 |
|
|
} |
593 |
|
|
buf[cc - 1] = '\n'; |
594 |
|
|
buf[cc] = '\0'; |
595 |
|
|
} |
596 |
|
|
} |
597 |
|
|
if (fwrite(buf, cc, 1, to) != 1) { |
598 |
|
|
warn("can't append group: short write to `%s'", f); |
599 |
|
|
fclose(from); |
600 |
|
|
fclose(to); |
601 |
|
|
unlink(f); |
602 |
|
|
return 0; |
603 |
|
|
} |
604 |
|
|
} |
605 |
|
|
fclose(from); |
606 |
|
|
if (fclose(to) == EOF) { |
607 |
|
|
warn("can't append group: short write to `%s'", f); |
608 |
|
|
unlink(f); |
609 |
|
|
return 0; |
610 |
|
|
} |
611 |
|
|
if (rename(f, _PATH_GROUP) < 0) { |
612 |
|
|
warn("can't append group: can't rename `%s' to `%s'", f, _PATH_GROUP); |
613 |
|
|
unlink(f); |
614 |
|
|
return 0; |
615 |
|
|
} |
616 |
|
|
(void) chmod(_PATH_GROUP, st.st_mode & 0777); |
617 |
|
|
return 1; |
618 |
|
|
} |
619 |
|
|
|
620 |
|
|
/* return 1 if `login' is a valid login name */ |
621 |
|
|
static int |
622 |
|
|
valid_login(char *login_name) |
623 |
|
|
{ |
624 |
|
|
unsigned char *cp; |
625 |
|
|
|
626 |
|
|
/* The first character cannot be a hyphen */ |
627 |
|
|
if (*login_name == '-') |
628 |
|
|
return 0; |
629 |
|
|
|
630 |
|
|
for (cp = login_name ; *cp ; cp++) { |
631 |
|
|
/* We allow '$' as the last character for samba */ |
632 |
|
|
if (!isalnum((unsigned char)*cp) && *cp != '.' && |
633 |
|
|
*cp != '_' && *cp != '-' && |
634 |
|
|
!(*cp == '$' && *(cp + 1) == '\0')) { |
635 |
|
|
return 0; |
636 |
|
|
} |
637 |
|
|
} |
638 |
|
|
if ((char *)cp - login_name > MaxUserNameLen) |
639 |
|
|
return 0; |
640 |
|
|
return 1; |
641 |
|
|
} |
642 |
|
|
|
643 |
|
|
/* return 1 if `group' is a valid group name */ |
644 |
|
|
static int |
645 |
|
|
valid_group(char *group) |
646 |
|
|
{ |
647 |
|
|
unsigned char *cp; |
648 |
|
|
|
649 |
|
|
for (cp = group ; *cp ; cp++) { |
650 |
|
|
if (!isalnum((unsigned char)*cp) && *cp != '.' && |
651 |
|
|
*cp != '_' && *cp != '-') { |
652 |
|
|
return 0; |
653 |
|
|
} |
654 |
|
|
} |
655 |
|
|
if ((char *)cp - group > MaxUserNameLen) |
656 |
|
|
return 0; |
657 |
|
|
return 1; |
658 |
|
|
} |
659 |
|
|
|
660 |
|
|
/* return 1 if `class' exists */ |
661 |
|
|
static int |
662 |
|
|
valid_class(char *class) |
663 |
|
|
{ |
664 |
|
|
login_cap_t *lc; |
665 |
|
|
|
666 |
|
|
if ((lc = login_getclass(class)) != NULL) |
667 |
|
|
login_close(lc); |
668 |
|
|
return lc != NULL; |
669 |
|
|
} |
670 |
|
|
|
671 |
|
|
/* find the next gid in the range lo .. hi */ |
672 |
|
|
static int |
673 |
|
|
getnextgid(uid_t *gidp, uid_t lo, uid_t hi) |
674 |
|
|
{ |
675 |
|
|
for (*gidp = lo ; *gidp < hi ; *gidp += 1) { |
676 |
|
|
if (getgrgid((gid_t)*gidp) == NULL) { |
677 |
|
|
return 1; |
678 |
|
|
} |
679 |
|
|
} |
680 |
|
|
return 0; |
681 |
|
|
} |
682 |
|
|
|
683 |
|
|
/* save a range of uids */ |
684 |
|
|
static int |
685 |
|
|
save_range(user_t *up, char *cp) |
686 |
|
|
{ |
687 |
|
|
uid_t from; |
688 |
|
|
uid_t to; |
689 |
|
|
int i; |
690 |
|
|
|
691 |
|
|
if (up->u_rc == up->u_rsize) { |
692 |
|
|
up->u_rsize *= 2; |
693 |
|
|
if ((up->u_rv = reallocarray(up->u_rv, up->u_rsize, |
694 |
|
|
sizeof(range_t))) == NULL) { |
695 |
|
|
warn(NULL); |
696 |
|
|
return 0; |
697 |
|
|
} |
698 |
|
|
} |
699 |
|
|
if (up->u_rv && sscanf(cp, "%u..%u", &from, &to) == 2) { |
700 |
|
|
for (i = up->u_defrc ; i < up->u_rc ; i++) { |
701 |
|
|
if (up->u_rv[i].r_from == from && up->u_rv[i].r_to == to) { |
702 |
|
|
break; |
703 |
|
|
} |
704 |
|
|
} |
705 |
|
|
if (i == up->u_rc) { |
706 |
|
|
up->u_rv[up->u_rc].r_from = from; |
707 |
|
|
up->u_rv[up->u_rc].r_to = to; |
708 |
|
|
up->u_rc += 1; |
709 |
|
|
} |
710 |
|
|
} else { |
711 |
|
|
warnx("Bad range `%s'", cp); |
712 |
|
|
return 0; |
713 |
|
|
} |
714 |
|
|
return 1; |
715 |
|
|
} |
716 |
|
|
|
717 |
|
|
/* set the defaults in the defaults file */ |
718 |
|
|
static int |
719 |
|
|
setdefaults(user_t *up) |
720 |
|
|
{ |
721 |
|
|
char template[MaxFileNameLen]; |
722 |
|
|
FILE *fp; |
723 |
|
|
int ret; |
724 |
|
|
int fd; |
725 |
|
|
int i; |
726 |
|
|
|
727 |
|
|
(void) snprintf(template, sizeof(template), "%s.XXXXXXXX", CONFFILE); |
728 |
|
|
if ((fd = mkstemp(template)) < 0) { |
729 |
|
|
warnx("can't mkstemp `%s' for writing", CONFFILE); |
730 |
|
|
return 0; |
731 |
|
|
} |
732 |
|
|
if ((fp = fdopen(fd, "w")) == NULL) { |
733 |
|
|
warn("can't fdopen `%s' for writing", CONFFILE); |
734 |
|
|
return 0; |
735 |
|
|
} |
736 |
|
|
ret = 1; |
737 |
|
|
if (fprintf(fp, "group\t\t%s\n", up->u_primgrp) <= 0 || |
738 |
|
|
fprintf(fp, "base_dir\t%s\n", up->u_basedir) <= 0 || |
739 |
|
|
fprintf(fp, "skel_dir\t%s\n", up->u_skeldir) <= 0 || |
740 |
|
|
fprintf(fp, "shell\t\t%s\n", up->u_shell) <= 0 || |
741 |
|
|
fprintf(fp, "class\t\t%s\n", up->u_class) <= 0 || |
742 |
|
|
fprintf(fp, "inactive\t%s\n", (up->u_inactive == NULL) ? UNSET_INACTIVE : up->u_inactive) <= 0 || |
743 |
|
|
fprintf(fp, "expire\t\t%s\n", (up->u_expire == NULL) ? UNSET_EXPIRY : up->u_expire) <= 0 || |
744 |
|
|
fprintf(fp, "preserve\t%s\n", (up->u_preserve == 0) ? "false" : "true") <= 0) { |
745 |
|
|
warn("can't write to `%s'", CONFFILE); |
746 |
|
|
ret = 0; |
747 |
|
|
} |
748 |
|
|
for (i = (up->u_defrc != up->u_rc) ? up->u_defrc : 0 ; i < up->u_rc ; i++) { |
749 |
|
|
if (fprintf(fp, "range\t\t%u..%u\n", up->u_rv[i].r_from, up->u_rv[i].r_to) <= 0) { |
750 |
|
|
warn("can't write to `%s'", CONFFILE); |
751 |
|
|
ret = 0; |
752 |
|
|
} |
753 |
|
|
} |
754 |
|
|
if (fclose(fp) == EOF) { |
755 |
|
|
warn("can't write to `%s'", CONFFILE); |
756 |
|
|
ret = 0; |
757 |
|
|
} |
758 |
|
|
if (ret) { |
759 |
|
|
ret = ((rename(template, CONFFILE) == 0) && (chmod(CONFFILE, 0644) == 0)); |
760 |
|
|
} |
761 |
|
|
return ret; |
762 |
|
|
} |
763 |
|
|
|
764 |
|
|
/* read the defaults file */ |
765 |
|
|
static void |
766 |
|
|
read_defaults(user_t *up) |
767 |
|
|
{ |
768 |
|
|
struct stat st; |
769 |
|
|
size_t lineno; |
770 |
|
|
size_t len; |
771 |
|
|
FILE *fp; |
772 |
|
|
unsigned char *cp; |
773 |
|
|
unsigned char *s; |
774 |
|
|
|
775 |
|
|
memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP)); |
776 |
|
|
memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR)); |
777 |
|
|
memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR)); |
778 |
|
|
memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL)); |
779 |
|
|
memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT)); |
780 |
|
|
memsave(&up->u_class, DEF_CLASS, strlen(DEF_CLASS)); |
781 |
|
|
up->u_rsize = 16; |
782 |
|
|
up->u_defrc = 0; |
783 |
|
|
if ((up->u_rv = calloc(up->u_rsize, sizeof(range_t))) == NULL) |
784 |
|
|
err(1, NULL); |
785 |
|
|
up->u_inactive = DEF_INACTIVE; |
786 |
|
|
up->u_expire = DEF_EXPIRE; |
787 |
|
|
if ((fp = fopen(CONFFILE, "r")) == NULL) { |
788 |
|
|
if (stat(CONFFILE, &st) < 0 && !setdefaults(up)) { |
789 |
|
|
warn("can't create `%s' defaults file", CONFFILE); |
790 |
|
|
} |
791 |
|
|
fp = fopen(CONFFILE, "r"); |
792 |
|
|
} |
793 |
|
|
if (fp != NULL) { |
794 |
|
|
while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { |
795 |
|
|
if (strncmp(s, "group", 5) == 0) { |
796 |
|
|
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
797 |
|
|
} |
798 |
|
|
memsave(&up->u_primgrp, cp, strlen(cp)); |
799 |
|
|
} else if (strncmp(s, "base_dir", 8) == 0) { |
800 |
|
|
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
801 |
|
|
} |
802 |
|
|
memsave(&up->u_basedir, cp, strlen(cp)); |
803 |
|
|
} else if (strncmp(s, "skel_dir", 8) == 0) { |
804 |
|
|
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
805 |
|
|
} |
806 |
|
|
memsave(&up->u_skeldir, cp, strlen(cp)); |
807 |
|
|
} else if (strncmp(s, "shell", 5) == 0) { |
808 |
|
|
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
809 |
|
|
} |
810 |
|
|
memsave(&up->u_shell, cp, strlen(cp)); |
811 |
|
|
} else if (strncmp(s, "password", 8) == 0) { |
812 |
|
|
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
813 |
|
|
} |
814 |
|
|
memsave(&up->u_password, cp, strlen(cp)); |
815 |
|
|
} else if (strncmp(s, "class", 5) == 0) { |
816 |
|
|
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
817 |
|
|
} |
818 |
|
|
memsave(&up->u_class, cp, strlen(cp)); |
819 |
|
|
} else if (strncmp(s, "inactive", 8) == 0) { |
820 |
|
|
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
821 |
|
|
} |
822 |
|
|
if (strcmp(cp, UNSET_INACTIVE) == 0) { |
823 |
|
|
free(up->u_inactive); |
824 |
|
|
up->u_inactive = NULL; |
825 |
|
|
} else { |
826 |
|
|
memsave(&up->u_inactive, cp, strlen(cp)); |
827 |
|
|
} |
828 |
|
|
} else if (strncmp(s, "range", 5) == 0) { |
829 |
|
|
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
830 |
|
|
} |
831 |
|
|
(void) save_range(up, cp); |
832 |
|
|
} else if (strncmp(s, "preserve", 8) == 0) { |
833 |
|
|
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
834 |
|
|
} |
835 |
|
|
up->u_preserve = (strncmp(cp, "true", 4) == 0) ? 1 : |
836 |
|
|
(strncmp(cp, "yes", 3) == 0) ? 1 : |
837 |
|
|
strtonum(cp, INT_MIN, INT_MAX, NULL); |
838 |
|
|
} else if (strncmp(s, "expire", 6) == 0) { |
839 |
|
|
for (cp = s + 6 ; isspace((unsigned char)*cp); cp++) { |
840 |
|
|
} |
841 |
|
|
if (strcmp(cp, UNSET_EXPIRY) == 0) { |
842 |
|
|
free(up->u_expire); |
843 |
|
|
up->u_expire = NULL; |
844 |
|
|
} else { |
845 |
|
|
memsave(&up->u_expire, cp, strlen(cp)); |
846 |
|
|
} |
847 |
|
|
} |
848 |
|
|
free(s); |
849 |
|
|
} |
850 |
|
|
fclose(fp); |
851 |
|
|
} |
852 |
|
|
if (up->u_rc == 0) { |
853 |
|
|
up->u_rv[up->u_rc].r_from = DEF_LOWUID; |
854 |
|
|
up->u_rv[up->u_rc].r_to = DEF_HIGHUID; |
855 |
|
|
up->u_rc += 1; |
856 |
|
|
} |
857 |
|
|
up->u_defrc = up->u_rc; |
858 |
|
|
} |
859 |
|
|
|
860 |
|
|
/* return the next valid unused uid */ |
861 |
|
|
static int |
862 |
|
|
getnextuid(int sync_uid_gid, uid_t *uid, uid_t low_uid, uid_t high_uid) |
863 |
|
|
{ |
864 |
|
|
for (*uid = low_uid ; *uid <= high_uid ; (*uid)++) { |
865 |
|
|
if (getpwuid((uid_t)(*uid)) == NULL && *uid != NOBODY_UID) { |
866 |
|
|
if (sync_uid_gid) { |
867 |
|
|
if (getgrgid((gid_t)(*uid)) == NULL) { |
868 |
|
|
return 1; |
869 |
|
|
} |
870 |
|
|
} else { |
871 |
|
|
return 1; |
872 |
|
|
} |
873 |
|
|
} |
874 |
|
|
} |
875 |
|
|
return 0; |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
/* look for a valid time, return 0 if it was specified but bad */ |
879 |
|
|
static int |
880 |
|
|
scantime(time_t *tp, char *s) |
881 |
|
|
{ |
882 |
|
|
struct tm tm; |
883 |
|
|
|
884 |
|
|
*tp = 0; |
885 |
|
|
if (s != NULL) { |
886 |
|
|
memset(&tm, 0, sizeof(tm)); |
887 |
|
|
tm.tm_isdst = -1; |
888 |
|
|
if (strptime(s, "%c", &tm) != NULL) { |
889 |
|
|
*tp = mktime(&tm); |
890 |
|
|
} else if (strptime(s, "%B %d %Y", &tm) != NULL) { |
891 |
|
|
*tp = mktime(&tm); |
892 |
|
|
} else if (isdigit((unsigned char) s[0]) != 0) { |
893 |
|
|
*tp = (time_t)atoll(s); |
894 |
|
|
} else { |
895 |
|
|
return 0; |
896 |
|
|
} |
897 |
|
|
} |
898 |
|
|
return 1; |
899 |
|
|
} |
900 |
|
|
|
901 |
|
|
/* compute the extra length '&' expansion consumes */ |
902 |
|
|
static size_t |
903 |
|
|
expand_len(const char *p, const char *username) |
904 |
|
|
{ |
905 |
|
|
size_t alen; |
906 |
|
|
size_t ulen; |
907 |
|
|
|
908 |
|
|
ulen = strlen(username); |
909 |
|
|
for (alen = 0; *p != '\0'; p++) |
910 |
|
|
if (*p == '&') |
911 |
|
|
alen += ulen - 1; |
912 |
|
|
return alen; |
913 |
|
|
} |
914 |
|
|
|
915 |
|
|
/* see if we can find out the user struct */ |
916 |
|
|
static struct passwd * |
917 |
|
|
find_user_info(const char *name) |
918 |
|
|
{ |
919 |
|
|
struct passwd *pwp; |
920 |
|
|
const char *errstr; |
921 |
|
|
uid_t uid; |
922 |
|
|
|
923 |
|
|
if ((pwp = getpwnam(name)) == NULL) { |
924 |
|
|
uid = strtonum(name, -1, UID_MAX, &errstr); |
925 |
|
|
if (errstr == NULL) |
926 |
|
|
pwp = getpwuid(uid); |
927 |
|
|
} |
928 |
|
|
return pwp; |
929 |
|
|
} |
930 |
|
|
|
931 |
|
|
/* see if we can find out the group struct */ |
932 |
|
|
static struct group * |
933 |
|
|
find_group_info(const char *name) |
934 |
|
|
{ |
935 |
|
|
struct group *grp; |
936 |
|
|
const char *errstr; |
937 |
|
|
gid_t gid; |
938 |
|
|
|
939 |
|
|
if ((grp = getgrnam(name)) == NULL) { |
940 |
|
|
gid = strtonum(name, -1, GID_MAX, &errstr); |
941 |
|
|
if (errstr == NULL) |
942 |
|
|
grp = getgrgid(gid); |
943 |
|
|
} |
944 |
|
|
return grp; |
945 |
|
|
} |
946 |
|
|
|
947 |
|
|
/* add a user */ |
948 |
|
|
static int |
949 |
|
|
adduser(char *login_name, user_t *up) |
950 |
|
|
{ |
951 |
|
|
struct group *grp; |
952 |
|
|
struct stat st; |
953 |
|
|
time_t expire; |
954 |
|
|
time_t inactive; |
955 |
|
|
char password[PasswordLength + 1]; |
956 |
|
|
char home[MaxFileNameLen]; |
957 |
|
|
char buf[LINE_MAX]; |
958 |
|
|
int sync_uid_gid; |
959 |
|
|
int masterfd; |
960 |
|
|
int ptmpfd; |
961 |
|
|
gid_t gid; |
962 |
|
|
int cc; |
963 |
|
|
int i, yp = 0; |
964 |
|
|
FILE *fp; |
965 |
|
|
|
966 |
|
|
if (!valid_login(login_name)) { |
967 |
|
|
errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); |
968 |
|
|
} |
969 |
|
|
if (!valid_class(up->u_class)) { |
970 |
|
|
errx(EXIT_FAILURE, "No such login class `%s'", up->u_class); |
971 |
|
|
} |
972 |
|
|
if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { |
973 |
|
|
err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); |
974 |
|
|
} |
975 |
|
|
if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { |
976 |
|
|
err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); |
977 |
|
|
} |
978 |
|
|
pw_init(); |
979 |
|
|
if ((ptmpfd = pw_lock(WAITSECS)) < 0) { |
980 |
|
|
int saved_errno = errno; |
981 |
|
|
close(masterfd); |
982 |
|
|
errc(EXIT_FAILURE, saved_errno, "can't obtain pw_lock"); |
983 |
|
|
} |
984 |
|
|
if ((fp = fdopen(masterfd, "r")) == NULL) { |
985 |
|
|
int saved_errno = errno; |
986 |
|
|
close(masterfd); |
987 |
|
|
close(ptmpfd); |
988 |
|
|
pw_abort(); |
989 |
|
|
errc(EXIT_FAILURE, saved_errno, |
990 |
|
|
"can't fdopen `%s' for reading", _PATH_MASTERPASSWD); |
991 |
|
|
} |
992 |
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) { |
993 |
|
|
cc = strlen(buf); |
994 |
|
|
/* |
995 |
|
|
* Stop copying the file at the yp entry; we want to |
996 |
|
|
* put the new user before it, and preserve entries |
997 |
|
|
* after the yp entry. |
998 |
|
|
*/ |
999 |
|
|
if (cc > 1 && buf[0] == '+' && buf[1] == ':') { |
1000 |
|
|
yp = 1; |
1001 |
|
|
break; |
1002 |
|
|
} |
1003 |
|
|
if (write(ptmpfd, buf, (size_t)(cc)) != cc) { |
1004 |
|
|
int saved_errno = errno; |
1005 |
|
|
fclose(fp); |
1006 |
|
|
close(ptmpfd); |
1007 |
|
|
pw_abort(); |
1008 |
|
|
errc(EXIT_FAILURE, saved_errno, |
1009 |
|
|
"short write to /etc/ptmp (not %d chars)", cc); |
1010 |
|
|
} |
1011 |
|
|
} |
1012 |
|
|
if (ferror(fp)) { |
1013 |
|
|
int saved_errno = errno; |
1014 |
|
|
fclose(fp); |
1015 |
|
|
close(ptmpfd); |
1016 |
|
|
pw_abort(); |
1017 |
|
|
errc(EXIT_FAILURE, saved_errno, "read error on %s", |
1018 |
|
|
_PATH_MASTERPASSWD); |
1019 |
|
|
} |
1020 |
|
|
/* if no uid was specified, get next one in [low_uid..high_uid] range */ |
1021 |
|
|
sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); |
1022 |
|
|
if (up->u_uid == UID_MAX) { |
1023 |
|
|
int got_id = 0; |
1024 |
|
|
|
1025 |
|
|
/* |
1026 |
|
|
* Look for a free UID in the command line ranges (if any). |
1027 |
|
|
* These start after the ranges specified in the config file. |
1028 |
|
|
*/ |
1029 |
|
|
for (i = up->u_defrc; got_id == 0 && i < up->u_rc ; i++) { |
1030 |
|
|
got_id = getnextuid(sync_uid_gid, &up->u_uid, |
1031 |
|
|
up->u_rv[i].r_from, up->u_rv[i].r_to); |
1032 |
|
|
} |
1033 |
|
|
/* |
1034 |
|
|
* If there were no free UIDs in the command line ranges, |
1035 |
|
|
* try the ranges from the config file (there will always |
1036 |
|
|
* be at least one default). |
1037 |
|
|
*/ |
1038 |
|
|
if (got_id == 0) { |
1039 |
|
|
for (i = 0; got_id == 0 && i < up->u_defrc; i++) { |
1040 |
|
|
got_id = getnextuid(sync_uid_gid, &up->u_uid, |
1041 |
|
|
up->u_rv[i].r_from, up->u_rv[i].r_to); |
1042 |
|
|
} |
1043 |
|
|
} |
1044 |
|
|
if (got_id == 0) { |
1045 |
|
|
close(ptmpfd); |
1046 |
|
|
pw_abort(); |
1047 |
|
|
errx(EXIT_FAILURE, "can't get next uid for %u", up->u_uid); |
1048 |
|
|
} |
1049 |
|
|
} |
1050 |
|
|
/* check uid isn't already allocated */ |
1051 |
|
|
if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { |
1052 |
|
|
close(ptmpfd); |
1053 |
|
|
pw_abort(); |
1054 |
|
|
errx(EXIT_FAILURE, "uid %u is already in use", up->u_uid); |
1055 |
|
|
} |
1056 |
|
|
/* if -g=uid was specified, check gid is unused */ |
1057 |
|
|
if (sync_uid_gid) { |
1058 |
|
|
if (getgrgid((gid_t)(up->u_uid)) != NULL) { |
1059 |
|
|
close(ptmpfd); |
1060 |
|
|
pw_abort(); |
1061 |
|
|
errx(EXIT_FAILURE, "gid %u is already in use", up->u_uid); |
1062 |
|
|
} |
1063 |
|
|
gid = up->u_uid; |
1064 |
|
|
} else { |
1065 |
|
|
if ((grp = find_group_info(up->u_primgrp)) == NULL) { |
1066 |
|
|
close(ptmpfd); |
1067 |
|
|
pw_abort(); |
1068 |
|
|
errx(EXIT_FAILURE, "group %s not found", up->u_primgrp); |
1069 |
|
|
} |
1070 |
|
|
gid = grp->gr_gid; |
1071 |
|
|
} |
1072 |
|
|
/* check name isn't already in use */ |
1073 |
|
|
if (!(up->u_flags & F_DUPUID) && getpwnam(login_name) != NULL) { |
1074 |
|
|
close(ptmpfd); |
1075 |
|
|
pw_abort(); |
1076 |
|
|
errx(EXIT_FAILURE, "already a `%s' user", login_name); |
1077 |
|
|
} |
1078 |
|
|
if (up->u_flags & F_HOMEDIR) { |
1079 |
|
|
if (strlcpy(home, up->u_home, sizeof(home)) >= sizeof(home)) { |
1080 |
|
|
close(ptmpfd); |
1081 |
|
|
pw_abort(); |
1082 |
|
|
errx(EXIT_FAILURE, "home directory `%s' too long", |
1083 |
|
|
up->u_home); |
1084 |
|
|
} |
1085 |
|
|
} else { |
1086 |
|
|
/* if home directory hasn't been given, make it up */ |
1087 |
|
|
if (snprintf(home, sizeof(home), "%s/%s", up->u_basedir, |
1088 |
|
|
login_name) >= sizeof(home)) { |
1089 |
|
|
close(ptmpfd); |
1090 |
|
|
pw_abort(); |
1091 |
|
|
errx(EXIT_FAILURE, "home directory `%s/%s' too long", |
1092 |
|
|
up->u_basedir, login_name); |
1093 |
|
|
} |
1094 |
|
|
} |
1095 |
|
|
if (!scantime(&inactive, up->u_inactive)) { |
1096 |
|
|
warnx("Warning: inactive time `%s' invalid, password expiry off", |
1097 |
|
|
up->u_inactive); |
1098 |
|
|
} |
1099 |
|
|
if (!scantime(&expire, up->u_expire)) { |
1100 |
|
|
warnx("Warning: expire time `%s' invalid, account expiry off", |
1101 |
|
|
up->u_expire); |
1102 |
|
|
} |
1103 |
|
|
if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR) && |
1104 |
|
|
strcmp(home, _PATH_NONEXISTENT) != 0) { |
1105 |
|
|
warnx("Warning: home directory `%s' doesn't exist, and -m was" |
1106 |
|
|
" not specified", home); |
1107 |
|
|
} |
1108 |
|
|
(void) strlcpy(password, up->u_password ? up->u_password : "*", |
1109 |
|
|
sizeof(password)); |
1110 |
|
|
cc = snprintf(buf, sizeof(buf), "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", |
1111 |
|
|
login_name, |
1112 |
|
|
password, |
1113 |
|
|
up->u_uid, |
1114 |
|
|
gid, |
1115 |
|
|
up->u_class, |
1116 |
|
|
(long long) inactive, |
1117 |
|
|
(long long) expire, |
1118 |
|
|
up->u_comment, |
1119 |
|
|
home, |
1120 |
|
|
up->u_shell); |
1121 |
|
|
if (cc >= sizeof(buf) || cc < 0 || |
1122 |
|
|
cc + expand_len(up->u_comment, login_name) >= 1023) { |
1123 |
|
|
close(ptmpfd); |
1124 |
|
|
pw_abort(); |
1125 |
|
|
errx(EXIT_FAILURE, "can't add `%s', line too long", buf); |
1126 |
|
|
} |
1127 |
|
|
if (write(ptmpfd, buf, (size_t) cc) != cc) { |
1128 |
|
|
int saved_errno = errno; |
1129 |
|
|
close(ptmpfd); |
1130 |
|
|
pw_abort(); |
1131 |
|
|
errc(EXIT_FAILURE, saved_errno, "can't add `%s'", buf); |
1132 |
|
|
} |
1133 |
|
|
if (yp) { |
1134 |
|
|
/* put back the + line */ |
1135 |
|
|
cc = snprintf(buf, sizeof(buf), "+:*::::::::\n"); |
1136 |
|
|
if (cc == -1 || cc >= sizeof(buf)) { |
1137 |
|
|
close(ptmpfd); |
1138 |
|
|
pw_abort(); |
1139 |
|
|
errx(EXIT_FAILURE, "can't add `%s', line too long", buf); |
1140 |
|
|
} |
1141 |
|
|
if (write(ptmpfd, buf, (size_t) cc) != cc) { |
1142 |
|
|
int saved_errno = errno; |
1143 |
|
|
close(ptmpfd); |
1144 |
|
|
pw_abort(); |
1145 |
|
|
errc(EXIT_FAILURE, saved_errno, "can't add `%s'", buf); |
1146 |
|
|
} |
1147 |
|
|
/* copy the entries following it, if any */ |
1148 |
|
|
while (fgets(buf, sizeof(buf), fp) != NULL) { |
1149 |
|
|
cc = strlen(buf); |
1150 |
|
|
if (write(ptmpfd, buf, (size_t)(cc)) != cc) { |
1151 |
|
|
int saved_errno = errno; |
1152 |
|
|
fclose(fp); |
1153 |
|
|
close(ptmpfd); |
1154 |
|
|
pw_abort(); |
1155 |
|
|
errc(EXIT_FAILURE, saved_errno, |
1156 |
|
|
"short write to /etc/ptmp (not %d chars)", |
1157 |
|
|
cc); |
1158 |
|
|
} |
1159 |
|
|
} |
1160 |
|
|
if (ferror(fp)) { |
1161 |
|
|
int saved_errno = errno; |
1162 |
|
|
fclose(fp); |
1163 |
|
|
close(ptmpfd); |
1164 |
|
|
pw_abort(); |
1165 |
|
|
errc(EXIT_FAILURE, saved_errno, "read error on %s", |
1166 |
|
|
_PATH_MASTERPASSWD); |
1167 |
|
|
} |
1168 |
|
|
} |
1169 |
|
|
if (up->u_flags & F_MKDIR) { |
1170 |
|
|
if (lstat(home, &st) == 0) { |
1171 |
|
|
close(ptmpfd); |
1172 |
|
|
pw_abort(); |
1173 |
|
|
errx(EXIT_FAILURE, "home directory `%s' already exists", |
1174 |
|
|
home); |
1175 |
|
|
} else { |
1176 |
|
|
if (asystem("%s -p %s", MKDIR, home) != 0) { |
1177 |
|
|
int saved_errno = errno; |
1178 |
|
|
close(ptmpfd); |
1179 |
|
|
pw_abort(); |
1180 |
|
|
errc(EXIT_FAILURE, saved_errno, |
1181 |
|
|
"can't mkdir `%s'", home); |
1182 |
|
|
} |
1183 |
|
|
(void) copydotfiles(up->u_skeldir, home); |
1184 |
|
|
(void) asystem("%s -R -P %u:%u %s", CHOWN, up->u_uid, |
1185 |
|
|
gid, home); |
1186 |
|
|
(void) asystem("%s -R u+w %s", CHMOD, home); |
1187 |
|
|
} |
1188 |
|
|
} |
1189 |
|
|
if (strcmp(up->u_primgrp, "=uid") == 0 && |
1190 |
|
|
getgrnam(login_name) == NULL && |
1191 |
|
|
!creategid(login_name, gid, "")) { |
1192 |
|
|
close(ptmpfd); |
1193 |
|
|
pw_abort(); |
1194 |
|
|
errx(EXIT_FAILURE, "can't create gid %u for login name %s", |
1195 |
|
|
gid, login_name); |
1196 |
|
|
} |
1197 |
|
|
if (up->u_groupc > 0 && !append_group(login_name, up->u_groupc, up->u_groupv)) { |
1198 |
|
|
close(ptmpfd); |
1199 |
|
|
pw_abort(); |
1200 |
|
|
errx(EXIT_FAILURE, "can't append `%s' to new groups", login_name); |
1201 |
|
|
} |
1202 |
|
|
fclose(fp); |
1203 |
|
|
close(ptmpfd); |
1204 |
|
|
if (pw_mkdb(yp ? NULL : login_name, 0) < 0) { |
1205 |
|
|
pw_abort(); |
1206 |
|
|
err(EXIT_FAILURE, "pw_mkdb failed"); |
1207 |
|
|
} |
1208 |
|
|
syslog(LOG_INFO, "new user added: name=%s, uid=%u, gid=%u, home=%s, shell=%s", |
1209 |
|
|
login_name, up->u_uid, gid, home, up->u_shell); |
1210 |
|
|
return 1; |
1211 |
|
|
} |
1212 |
|
|
|
1213 |
|
|
/* remove a user from the groups file */ |
1214 |
|
|
static int |
1215 |
|
|
rm_user_from_groups(char *login_name) |
1216 |
|
|
{ |
1217 |
|
|
struct stat st; |
1218 |
|
|
size_t login_len; |
1219 |
|
|
FILE *from; |
1220 |
|
|
FILE *to; |
1221 |
|
|
char buf[LINE_MAX]; |
1222 |
|
|
char f[MaxFileNameLen]; |
1223 |
|
|
char *cp, *ep; |
1224 |
|
|
int fd; |
1225 |
|
|
int cc; |
1226 |
|
|
|
1227 |
|
|
login_len = strlen(login_name); |
1228 |
|
|
if ((from = fopen(_PATH_GROUP, "r")) == NULL) { |
1229 |
|
|
warn("can't remove gid for `%s': can't open `%s'", |
1230 |
|
|
login_name, _PATH_GROUP); |
1231 |
|
|
return 0; |
1232 |
|
|
} |
1233 |
|
|
if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { |
1234 |
|
|
warn("can't lock `%s'", _PATH_GROUP); |
1235 |
|
|
} |
1236 |
|
|
(void) fstat(fileno(from), &st); |
1237 |
|
|
(void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); |
1238 |
|
|
if ((fd = mkstemp(f)) < 0) { |
1239 |
|
|
warn("can't remove gid for `%s': mkstemp failed", login_name); |
1240 |
|
|
fclose(from); |
1241 |
|
|
return 0; |
1242 |
|
|
} |
1243 |
|
|
if ((to = fdopen(fd, "w")) == NULL) { |
1244 |
|
|
warn("can't remove gid for `%s': fdopen `%s' failed", |
1245 |
|
|
login_name, f); |
1246 |
|
|
fclose(from); |
1247 |
|
|
close(fd); |
1248 |
|
|
unlink(f); |
1249 |
|
|
return 0; |
1250 |
|
|
} |
1251 |
|
|
while (fgets(buf, sizeof(buf), from) != NULL) { |
1252 |
|
|
cc = strlen(buf); |
1253 |
|
|
if (cc > 0 && buf[cc - 1] != '\n' && !feof(from)) { |
1254 |
|
|
while (fgetc(from) != '\n' && !feof(from)) |
1255 |
|
|
cc++; |
1256 |
|
|
warnx("%s: line `%s' too long (%d bytes), skipping", |
1257 |
|
|
_PATH_GROUP, buf, cc); |
1258 |
|
|
continue; |
1259 |
|
|
} |
1260 |
|
|
|
1261 |
|
|
/* Break out the group list. */ |
1262 |
|
|
for (cp = buf, cc = 0; *cp != '\0' && cc < 3; cp++) { |
1263 |
|
|
if (*cp == ':') |
1264 |
|
|
cc++; |
1265 |
|
|
} |
1266 |
|
|
if (cc != 3) { |
1267 |
|
|
buf[strcspn(buf, "\n")] = '\0'; |
1268 |
|
|
warnx("Malformed entry `%s'. Skipping", buf); |
1269 |
|
|
continue; |
1270 |
|
|
} |
1271 |
|
|
while ((cp = strstr(cp, login_name)) != NULL) { |
1272 |
|
|
if ((cp[-1] == ':' || cp[-1] == ',') && |
1273 |
|
|
(cp[login_len] == ',' || cp[login_len] == '\n')) { |
1274 |
|
|
ep = cp + login_len; |
1275 |
|
|
if (cp[login_len] == ',') |
1276 |
|
|
ep++; |
1277 |
|
|
else if (cp[-1] == ',') |
1278 |
|
|
cp--; |
1279 |
|
|
memmove(cp, ep, strlen(ep) + 1); |
1280 |
|
|
} else { |
1281 |
|
|
if ((cp = strchr(cp, ',')) == NULL) |
1282 |
|
|
break; |
1283 |
|
|
cp++; |
1284 |
|
|
} |
1285 |
|
|
} |
1286 |
|
|
if (fwrite(buf, strlen(buf), 1, to) != 1) { |
1287 |
|
|
warn("can't remove gid for `%s': short write to `%s'", |
1288 |
|
|
login_name, f); |
1289 |
|
|
fclose(from); |
1290 |
|
|
fclose(to); |
1291 |
|
|
unlink(f); |
1292 |
|
|
return 0; |
1293 |
|
|
} |
1294 |
|
|
} |
1295 |
|
|
(void) fchmod(fileno(to), st.st_mode & 0777); |
1296 |
|
|
fclose(from); |
1297 |
|
|
if (fclose(to) == EOF) { |
1298 |
|
|
warn("can't remove gid for `%s': short write to `%s'", |
1299 |
|
|
login_name, f); |
1300 |
|
|
unlink(f); |
1301 |
|
|
return 0; |
1302 |
|
|
} |
1303 |
|
|
if (rename(f, _PATH_GROUP) < 0) { |
1304 |
|
|
warn("can't remove gid for `%s': can't rename `%s' to `%s'", |
1305 |
|
|
login_name, f, _PATH_GROUP); |
1306 |
|
|
unlink(f); |
1307 |
|
|
return 0; |
1308 |
|
|
} |
1309 |
|
|
return 1; |
1310 |
|
|
} |
1311 |
|
|
|
1312 |
|
|
/* check that the user or group is local, not from YP/NIS */ |
1313 |
|
|
static int |
1314 |
|
|
is_local(char *name, const char *file) |
1315 |
|
|
{ |
1316 |
|
|
FILE *fp; |
1317 |
|
|
char buf[LINE_MAX]; |
1318 |
|
|
size_t len; |
1319 |
|
|
int ret; |
1320 |
|
|
int cc; |
1321 |
|
|
|
1322 |
|
|
if ((fp = fopen(file, "r")) == NULL) { |
1323 |
|
|
err(EXIT_FAILURE, "can't open `%s'", file); |
1324 |
|
|
} |
1325 |
|
|
len = strlen(name); |
1326 |
|
|
for (ret = 0 ; fgets(buf, sizeof(buf), fp) != NULL ; ) { |
1327 |
|
|
cc = strlen(buf); |
1328 |
|
|
if (cc > 0 && buf[cc - 1] != '\n' && !feof(fp)) { |
1329 |
|
|
while (fgetc(fp) != '\n' && !feof(fp)) |
1330 |
|
|
cc++; |
1331 |
|
|
warnx("%s: line `%s' too long (%d bytes), skipping", |
1332 |
|
|
file, buf, cc); |
1333 |
|
|
continue; |
1334 |
|
|
} |
1335 |
|
|
if (strncmp(buf, name, len) == 0 && buf[len] == ':') { |
1336 |
|
|
ret = 1; |
1337 |
|
|
break; |
1338 |
|
|
} |
1339 |
|
|
} |
1340 |
|
|
fclose(fp); |
1341 |
|
|
return ret; |
1342 |
|
|
} |
1343 |
|
|
|
1344 |
|
|
/* modify a user */ |
1345 |
|
|
static int |
1346 |
|
|
moduser(char *login_name, char *newlogin, user_t *up) |
1347 |
|
|
{ |
1348 |
|
|
struct passwd *pwp = NULL; |
1349 |
|
|
struct group *grp; |
1350 |
|
|
const char *homedir; |
1351 |
|
|
char buf[LINE_MAX]; |
1352 |
|
|
char acctlock_str[] = "-"; |
1353 |
|
|
char pwlock_str[] = "*"; |
1354 |
|
|
char pw_len[PasswordLength + 1]; |
1355 |
|
|
char shell_len[MaxShellNameLen]; |
1356 |
|
|
char *shell_last_char; |
1357 |
|
|
size_t colonc, loginc; |
1358 |
|
|
size_t cc; |
1359 |
|
|
size_t shell_buf; |
1360 |
|
|
FILE *master; |
1361 |
|
|
char newdir[MaxFileNameLen]; |
1362 |
|
|
char *colon; |
1363 |
|
|
char *pw_tmp = NULL; |
1364 |
|
|
char *shell_tmp = NULL; |
1365 |
|
|
int len; |
1366 |
|
|
int locked = 0; |
1367 |
|
|
int unlocked = 0; |
1368 |
|
|
int masterfd; |
1369 |
|
|
int ptmpfd; |
1370 |
|
|
int rval; |
1371 |
|
|
int i; |
1372 |
|
|
|
1373 |
|
|
if (!valid_login(newlogin)) { |
1374 |
|
|
errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); |
1375 |
|
|
} |
1376 |
|
|
if ((pwp = getpwnam_shadow(login_name)) == NULL) { |
1377 |
|
|
errx(EXIT_FAILURE, "No such user `%s'", login_name); |
1378 |
|
|
} |
1379 |
|
|
if (up != NULL) { |
1380 |
|
|
if ((*pwp->pw_passwd != '\0') && |
1381 |
|
|
(up->u_flags & F_PASSWORD) == 0) { |
1382 |
|
|
up->u_flags |= F_PASSWORD; |
1383 |
|
|
memsave(&up->u_password, pwp->pw_passwd, |
1384 |
|
|
strlen(pwp->pw_passwd)); |
1385 |
|
|
explicit_bzero(pwp->pw_passwd, strlen(pwp->pw_passwd)); |
1386 |
|
|
} |
1387 |
|
|
} |
1388 |
|
|
endpwent(); |
1389 |
|
|
|
1390 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id flock rpath cpath wpath", |
1391 |
|
|
NULL) == -1) |
1392 |
|
|
err(1, "pledge"); |
1393 |
|
|
|
1394 |
|
|
if (!is_local(login_name, _PATH_MASTERPASSWD)) { |
1395 |
|
|
errx(EXIT_FAILURE, "User `%s' must be a local user", login_name); |
1396 |
|
|
} |
1397 |
|
|
if (up != NULL) { |
1398 |
|
|
if ((up->u_flags & (F_ACCTLOCK | F_ACCTUNLOCK)) && (pwp->pw_uid < 1000)) |
1399 |
|
|
errx(EXIT_FAILURE, "(un)locking is not supported for the `%s' account", pwp->pw_name); |
1400 |
|
|
} |
1401 |
|
|
/* keep dir name in case we need it for '-m' */ |
1402 |
|
|
homedir = pwp->pw_dir; |
1403 |
|
|
|
1404 |
|
|
/* get the last char of the shell in case we need it for '-U' or '-Z' */ |
1405 |
|
|
shell_last_char = pwp->pw_shell+strlen(pwp->pw_shell) - 1; |
1406 |
|
|
|
1407 |
|
|
if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { |
1408 |
|
|
err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); |
1409 |
|
|
} |
1410 |
|
|
if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { |
1411 |
|
|
err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); |
1412 |
|
|
} |
1413 |
|
|
pw_init(); |
1414 |
|
|
if ((ptmpfd = pw_lock(WAITSECS)) < 0) { |
1415 |
|
|
int saved_errno = errno; |
1416 |
|
|
close(masterfd); |
1417 |
|
|
errc(EXIT_FAILURE, saved_errno, "can't obtain pw_lock"); |
1418 |
|
|
} |
1419 |
|
|
if ((master = fdopen(masterfd, "r")) == NULL) { |
1420 |
|
|
int saved_errno = errno; |
1421 |
|
|
close(masterfd); |
1422 |
|
|
close(ptmpfd); |
1423 |
|
|
pw_abort(); |
1424 |
|
|
errc(EXIT_FAILURE, saved_errno, "can't fdopen fd for %s", |
1425 |
|
|
_PATH_MASTERPASSWD); |
1426 |
|
|
} |
1427 |
|
|
if (up != NULL) { |
1428 |
|
|
if (up->u_flags & F_USERNAME) { |
1429 |
|
|
/* if changing name, check new name isn't already in use */ |
1430 |
|
|
if (strcmp(login_name, newlogin) != 0 && getpwnam(newlogin) != NULL) { |
1431 |
|
|
close(ptmpfd); |
1432 |
|
|
pw_abort(); |
1433 |
|
|
errx(EXIT_FAILURE, "already a `%s' user", newlogin); |
1434 |
|
|
} |
1435 |
|
|
pwp->pw_name = newlogin; |
1436 |
|
|
|
1437 |
|
|
/* |
1438 |
|
|
* Provide a new directory name in case the |
1439 |
|
|
* home directory is to be moved. |
1440 |
|
|
*/ |
1441 |
|
|
if (up->u_flags & F_MKDIR) { |
1442 |
|
|
(void) snprintf(newdir, sizeof(newdir), |
1443 |
|
|
"%s/%s", up->u_basedir, newlogin); |
1444 |
|
|
pwp->pw_dir = newdir; |
1445 |
|
|
} |
1446 |
|
|
} |
1447 |
|
|
if (up->u_flags & F_PASSWORD) { |
1448 |
|
|
if (up->u_password != NULL) |
1449 |
|
|
pwp->pw_passwd = up->u_password; |
1450 |
|
|
} |
1451 |
|
|
if (up->u_flags & F_ACCTLOCK) { |
1452 |
|
|
/* lock the account */ |
1453 |
|
|
if (*shell_last_char != *acctlock_str) { |
1454 |
|
|
shell_tmp = malloc(strlen(pwp->pw_shell) + sizeof(acctlock_str)); |
1455 |
|
|
if (shell_tmp == NULL) { |
1456 |
|
|
close(ptmpfd); |
1457 |
|
|
pw_abort(); |
1458 |
|
|
errx(EXIT_FAILURE, "account lock: cannot allocate memory"); |
1459 |
|
|
} |
1460 |
|
|
strlcpy(shell_tmp, pwp->pw_shell, sizeof(shell_len)); |
1461 |
|
|
strlcat(shell_tmp, acctlock_str, sizeof(shell_len)); |
1462 |
|
|
pwp->pw_shell = shell_tmp; |
1463 |
|
|
} else { |
1464 |
|
|
locked++; |
1465 |
|
|
} |
1466 |
|
|
/* lock the password */ |
1467 |
|
|
if (strncmp(pwp->pw_passwd, pwlock_str, sizeof(pwlock_str)-1) != 0) { |
1468 |
|
|
pw_tmp = malloc(strlen(pwp->pw_passwd) + sizeof(pwlock_str)); |
1469 |
|
|
if (pw_tmp == NULL) { |
1470 |
|
|
close(ptmpfd); |
1471 |
|
|
pw_abort(); |
1472 |
|
|
errx(EXIT_FAILURE, "password lock: cannot allocate memory"); |
1473 |
|
|
} |
1474 |
|
|
strlcpy(pw_tmp, pwlock_str, sizeof(pw_len)); |
1475 |
|
|
strlcat(pw_tmp, pwp->pw_passwd, sizeof(pw_len)); |
1476 |
|
|
pwp->pw_passwd = pw_tmp; |
1477 |
|
|
} else { |
1478 |
|
|
locked++; |
1479 |
|
|
} |
1480 |
|
|
|
1481 |
|
|
if (locked > 1) |
1482 |
|
|
warnx("account `%s' is already locked", pwp->pw_name); |
1483 |
|
|
} |
1484 |
|
|
if (up->u_flags & F_ACCTUNLOCK) { |
1485 |
|
|
/* unlock the password */ |
1486 |
|
|
if (strcmp(pwp->pw_passwd, pwlock_str) != 0 && |
1487 |
|
|
strcmp(pwp->pw_passwd, "*************") != 0) { |
1488 |
|
|
if (strncmp(pwp->pw_passwd, pwlock_str, sizeof(pwlock_str)-1) == 0) { |
1489 |
|
|
pwp->pw_passwd += sizeof(pwlock_str)-1; |
1490 |
|
|
} else { |
1491 |
|
|
unlocked++; |
1492 |
|
|
} |
1493 |
|
|
} else { |
1494 |
|
|
warnx("account `%s' has no password: cannot fully unlock", pwp->pw_name); |
1495 |
|
|
} |
1496 |
|
|
/* unlock the account */ |
1497 |
|
|
if (*shell_last_char == *acctlock_str) { |
1498 |
|
|
shell_buf = strlen(pwp->pw_shell) + 2 - sizeof(acctlock_str); |
1499 |
|
|
shell_tmp = malloc(shell_buf); |
1500 |
|
|
if (shell_tmp == NULL) { |
1501 |
|
|
close(ptmpfd); |
1502 |
|
|
pw_abort(); |
1503 |
|
|
errx(EXIT_FAILURE, "unlock: cannot allocate memory"); |
1504 |
|
|
} |
1505 |
|
|
strlcpy(shell_tmp, pwp->pw_shell, shell_buf); |
1506 |
|
|
pwp->pw_shell = shell_tmp; |
1507 |
|
|
} else { |
1508 |
|
|
unlocked++; |
1509 |
|
|
} |
1510 |
|
|
|
1511 |
|
|
if (unlocked > 1) |
1512 |
|
|
warnx("account `%s' is not locked", pwp->pw_name); |
1513 |
|
|
} |
1514 |
|
|
if (up->u_flags & F_UID) { |
1515 |
|
|
/* check uid isn't already allocated */ |
1516 |
|
|
if (!(up->u_flags & F_DUPUID) && getpwuid((uid_t)(up->u_uid)) != NULL) { |
1517 |
|
|
close(ptmpfd); |
1518 |
|
|
pw_abort(); |
1519 |
|
|
errx(EXIT_FAILURE, "uid %u is already in use", up->u_uid); |
1520 |
|
|
} |
1521 |
|
|
pwp->pw_uid = up->u_uid; |
1522 |
|
|
} |
1523 |
|
|
if (up->u_flags & F_GROUP) { |
1524 |
|
|
/* if -g=uid was specified, check gid is unused */ |
1525 |
|
|
if (strcmp(up->u_primgrp, "=uid") == 0) { |
1526 |
|
|
if (getgrgid((gid_t)(pwp->pw_uid)) != NULL) { |
1527 |
|
|
close(ptmpfd); |
1528 |
|
|
pw_abort(); |
1529 |
|
|
errx(EXIT_FAILURE, "gid %u is already " |
1530 |
|
|
"in use", pwp->pw_uid); |
1531 |
|
|
} |
1532 |
|
|
pwp->pw_gid = pwp->pw_uid; |
1533 |
|
|
if (!creategid(newlogin, pwp->pw_gid, "")) { |
1534 |
|
|
close(ptmpfd); |
1535 |
|
|
pw_abort(); |
1536 |
|
|
errx(EXIT_FAILURE, "could not create " |
1537 |
|
|
"group %s with gid %u", newlogin, |
1538 |
|
|
pwp->pw_gid); |
1539 |
|
|
} |
1540 |
|
|
} else { |
1541 |
|
|
if ((grp = find_group_info(up->u_primgrp)) == NULL) { |
1542 |
|
|
close(ptmpfd); |
1543 |
|
|
pw_abort(); |
1544 |
|
|
errx(EXIT_FAILURE, "group %s not found", |
1545 |
|
|
up->u_primgrp); |
1546 |
|
|
} |
1547 |
|
|
pwp->pw_gid = grp->gr_gid; |
1548 |
|
|
} |
1549 |
|
|
} |
1550 |
|
|
if (up->u_flags & F_INACTIVE) { |
1551 |
|
|
if (!scantime(&pwp->pw_change, up->u_inactive)) { |
1552 |
|
|
warnx("Warning: inactive time `%s' invalid, password expiry off", |
1553 |
|
|
up->u_inactive); |
1554 |
|
|
} |
1555 |
|
|
} |
1556 |
|
|
if (up->u_flags & F_EXPIRE) { |
1557 |
|
|
if (!scantime(&pwp->pw_expire, up->u_expire)) { |
1558 |
|
|
warnx("Warning: expire time `%s' invalid, account expiry off", |
1559 |
|
|
up->u_expire); |
1560 |
|
|
} |
1561 |
|
|
} |
1562 |
|
|
if (up->u_flags & F_COMMENT) |
1563 |
|
|
pwp->pw_gecos = up->u_comment; |
1564 |
|
|
if (up->u_flags & F_HOMEDIR) |
1565 |
|
|
pwp->pw_dir = up->u_home; |
1566 |
|
|
if (up->u_flags & F_SHELL) |
1567 |
|
|
pwp->pw_shell = up->u_shell; |
1568 |
|
|
if (up->u_flags & F_CLASS) { |
1569 |
|
|
if (!valid_class(up->u_class)) { |
1570 |
|
|
close(ptmpfd); |
1571 |
|
|
pw_abort(); |
1572 |
|
|
errx(EXIT_FAILURE, |
1573 |
|
|
"No such login class `%s'", up->u_class); |
1574 |
|
|
} |
1575 |
|
|
pwp->pw_class = up->u_class; |
1576 |
|
|
} |
1577 |
|
|
} |
1578 |
|
|
loginc = strlen(login_name); |
1579 |
|
|
while (fgets(buf, sizeof(buf), master) != NULL) { |
1580 |
|
|
if ((colon = strchr(buf, ':')) == NULL) { |
1581 |
|
|
warnx("Malformed entry `%s'. Skipping", buf); |
1582 |
|
|
continue; |
1583 |
|
|
} |
1584 |
|
|
colonc = (size_t)(colon - buf); |
1585 |
|
|
if (strncmp(login_name, buf, loginc) == 0 && loginc == colonc) { |
1586 |
|
|
if (up != NULL) { |
1587 |
|
|
if ((len = snprintf(buf, sizeof(buf), |
1588 |
|
|
"%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", |
1589 |
|
|
newlogin, |
1590 |
|
|
pwp->pw_passwd, |
1591 |
|
|
pwp->pw_uid, |
1592 |
|
|
pwp->pw_gid, |
1593 |
|
|
pwp->pw_class, |
1594 |
|
|
(long long)pwp->pw_change, |
1595 |
|
|
(long long)pwp->pw_expire, |
1596 |
|
|
pwp->pw_gecos, |
1597 |
|
|
pwp->pw_dir, |
1598 |
|
|
pwp->pw_shell)) >= sizeof(buf) || len < 0 || |
1599 |
|
|
len + expand_len(pwp->pw_gecos, newlogin) |
1600 |
|
|
>= 1023) { |
1601 |
|
|
close(ptmpfd); |
1602 |
|
|
pw_abort(); |
1603 |
|
|
errx(EXIT_FAILURE, "can't add `%s', " |
1604 |
|
|
"line too long (%zu bytes)", buf, |
1605 |
|
|
len + expand_len(pwp->pw_gecos, |
1606 |
|
|
newlogin)); |
1607 |
|
|
} |
1608 |
|
|
if (write(ptmpfd, buf, len) != len) { |
1609 |
|
|
int saved_errno = errno; |
1610 |
|
|
close(ptmpfd); |
1611 |
|
|
pw_abort(); |
1612 |
|
|
errc(EXIT_FAILURE, saved_errno, |
1613 |
|
|
"can't add `%s'", buf); |
1614 |
|
|
} |
1615 |
|
|
} |
1616 |
|
|
} else { |
1617 |
|
|
len = strlen(buf); |
1618 |
|
|
if ((cc = write(ptmpfd, buf, len)) != len) { |
1619 |
|
|
int saved_errno = errno; |
1620 |
|
|
close(masterfd); |
1621 |
|
|
close(ptmpfd); |
1622 |
|
|
pw_abort(); |
1623 |
|
|
errc(EXIT_FAILURE, saved_errno, |
1624 |
|
|
"short write to /etc/ptmp (%lld not %lld chars)", |
1625 |
|
|
(long long)cc, (long long)len); |
1626 |
|
|
} |
1627 |
|
|
} |
1628 |
|
|
} |
1629 |
|
|
if (up != NULL) { |
1630 |
|
|
if ((up->u_flags & F_MKDIR) && |
1631 |
|
|
asystem("%s %s %s", MV, homedir, pwp->pw_dir) != 0) { |
1632 |
|
|
int saved_errno = errno; |
1633 |
|
|
close(ptmpfd); |
1634 |
|
|
pw_abort(); |
1635 |
|
|
errc(EXIT_FAILURE, saved_errno, |
1636 |
|
|
"can't move `%s' to `%s'", homedir, pwp->pw_dir); |
1637 |
|
|
} |
1638 |
|
|
if (up->u_flags & F_SETSECGROUP) { |
1639 |
|
|
for (i = 0 ; i < up->u_groupc ; i++) { |
1640 |
|
|
if (getgrnam(up->u_groupv[i]) == NULL) { |
1641 |
|
|
close(ptmpfd); |
1642 |
|
|
pw_abort(); |
1643 |
|
|
errx(EXIT_FAILURE, |
1644 |
|
|
"aborting, group `%s' does not exist", |
1645 |
|
|
up->u_groupv[i]); |
1646 |
|
|
} |
1647 |
|
|
} |
1648 |
|
|
if (!rm_user_from_groups(newlogin)) { |
1649 |
|
|
close(ptmpfd); |
1650 |
|
|
pw_abort(); |
1651 |
|
|
errx(EXIT_FAILURE, |
1652 |
|
|
"can't reset groups for `%s'", newlogin); |
1653 |
|
|
} |
1654 |
|
|
} |
1655 |
|
|
if (up->u_groupc > 0) { |
1656 |
|
|
if (!append_group(newlogin, up->u_groupc, up->u_groupv)) { |
1657 |
|
|
close(ptmpfd); |
1658 |
|
|
pw_abort(); |
1659 |
|
|
errx(EXIT_FAILURE, "can't append `%s' to new groups", |
1660 |
|
|
newlogin); |
1661 |
|
|
} |
1662 |
|
|
} |
1663 |
|
|
} |
1664 |
|
|
fclose(master); |
1665 |
|
|
close(ptmpfd); |
1666 |
|
|
free(pw_tmp); |
1667 |
|
|
free(shell_tmp); |
1668 |
|
|
if (up != NULL && strcmp(login_name, newlogin) == 0) |
1669 |
|
|
rval = pw_mkdb(login_name, 0); |
1670 |
|
|
else |
1671 |
|
|
rval = pw_mkdb(NULL, 0); |
1672 |
|
|
if (rval == -1) { |
1673 |
|
|
pw_abort(); |
1674 |
|
|
err(EXIT_FAILURE, "pw_mkdb failed"); |
1675 |
|
|
} |
1676 |
|
|
if (up == NULL) { |
1677 |
|
|
syslog(LOG_INFO, "user removed: name=%s", login_name); |
1678 |
|
|
} else if (strcmp(login_name, newlogin) == 0) { |
1679 |
|
|
syslog(LOG_INFO, "user information modified: name=%s, uid=%u, gid=%u, home=%s, shell=%s", |
1680 |
|
|
login_name, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); |
1681 |
|
|
} else { |
1682 |
|
|
syslog(LOG_INFO, "user information modified: name=%s, new name=%s, uid=%u, gid=%u, home=%s, shell=%s", |
1683 |
|
|
login_name, newlogin, pwp->pw_uid, pwp->pw_gid, pwp->pw_dir, pwp->pw_shell); |
1684 |
|
|
} |
1685 |
|
|
return 1; |
1686 |
|
|
} |
1687 |
|
|
|
1688 |
|
|
/* print out usage message, and then exit */ |
1689 |
|
|
void |
1690 |
|
|
usermgmt_usage(const char *prog) |
1691 |
|
|
{ |
1692 |
|
|
if (strcmp(prog, "useradd") == 0) { |
1693 |
|
|
fprintf(stderr, "usage: %s -D [-b base-directory] " |
1694 |
|
|
"[-e expiry-time] [-f inactive-time]\n" |
1695 |
|
|
" [-g gid | name | =uid] [-k skel-directory] " |
1696 |
|
|
"[-L login-class]\n" |
1697 |
|
|
" [-r low..high] [-s shell]\n", prog); |
1698 |
|
|
fprintf(stderr, " %s [-mov] [-b base-directory] " |
1699 |
|
|
"[-c comment] [-d home-directory]\n" |
1700 |
|
|
" [-e expiry-time] [-f inactive-time]\n" |
1701 |
|
|
" [-G secondary-group[,group,...]] " |
1702 |
|
|
"[-g gid | name | =uid]\n" |
1703 |
|
|
" [-k skel-directory] [-L login-class] " |
1704 |
|
|
"[-p password] [-r low..high]\n" |
1705 |
|
|
" [-s shell] [-u uid] user\n", prog); |
1706 |
|
|
} else if (strcmp(prog, "usermod") == 0) { |
1707 |
|
|
fprintf(stderr, "usage: %s [-moUvZ] " |
1708 |
|
|
"[-c comment] [-d home-directory] [-e expiry-time]\n" |
1709 |
|
|
" [-f inactive-time] " |
1710 |
|
|
"[-G secondary-group[,group,...]]\n" |
1711 |
|
|
" [-g gid | name | =uid] [-L login-class] " |
1712 |
|
|
"[-l new-login]\n" |
1713 |
|
|
" [-p password] " |
1714 |
|
|
"[-S secondary-group[,group,...]]\n" |
1715 |
|
|
" [-s shell] [-u uid] user\n", |
1716 |
|
|
prog); |
1717 |
|
|
} else if (strcmp(prog, "userdel") == 0) { |
1718 |
|
|
fprintf(stderr, "usage: %s -D [-p preserve-value]\n", |
1719 |
|
|
prog); |
1720 |
|
|
fprintf(stderr, " %s [-rv] [-p preserve-value] " |
1721 |
|
|
"user\n", prog); |
1722 |
|
|
} else if (strcmp(prog, "userinfo") == 0) { |
1723 |
|
|
fprintf(stderr, "usage: %s [-e] user\n", prog); |
1724 |
|
|
} else if (strcmp(prog, "groupadd") == 0) { |
1725 |
|
|
fprintf(stderr, "usage: %s [-ov] [-g gid] group\n", |
1726 |
|
|
prog); |
1727 |
|
|
} else if (strcmp(prog, "groupdel") == 0) { |
1728 |
|
|
fprintf(stderr, "usage: %s [-v] group\n", prog); |
1729 |
|
|
} else if (strcmp(prog, "groupmod") == 0) { |
1730 |
|
|
fprintf(stderr, "usage: %s [-ov] [-g gid] [-n newname] " |
1731 |
|
|
"group\n", prog); |
1732 |
|
|
} else if (strcmp(prog, "user") == 0 || strcmp(prog, "group") == 0) { |
1733 |
|
|
fprintf(stderr, "usage: %s [add | del | mod" |
1734 |
|
|
" | info" |
1735 |
|
|
"] ...\n", |
1736 |
|
|
prog); |
1737 |
|
|
} else if (strcmp(prog, "groupinfo") == 0) { |
1738 |
|
|
fprintf(stderr, "usage: %s [-e] group\n", prog); |
1739 |
|
|
} else { |
1740 |
|
|
fprintf(stderr, "This program must be called as {user,group}{add,del,mod,info},\n%s is not an understood name.\n", prog); |
1741 |
|
|
} |
1742 |
|
|
exit(EXIT_FAILURE); |
1743 |
|
|
} |
1744 |
|
|
|
1745 |
|
|
int |
1746 |
|
|
useradd(int argc, char **argv) |
1747 |
|
|
{ |
1748 |
|
|
user_t u; |
1749 |
|
|
const char *errstr; |
1750 |
|
|
int defaultfield; |
1751 |
|
|
int bigD; |
1752 |
|
|
int c; |
1753 |
|
|
int i; |
1754 |
|
|
|
1755 |
|
|
memset(&u, 0, sizeof(u)); |
1756 |
|
|
read_defaults(&u); |
1757 |
|
|
u.u_uid = UID_MAX; |
1758 |
|
|
defaultfield = bigD = 0; |
1759 |
|
|
while ((c = getopt(argc, argv, "DG:L:b:c:d:e:f:g:k:mop:r:s:u:v")) != -1) { |
1760 |
|
|
switch(c) { |
1761 |
|
|
case 'D': |
1762 |
|
|
bigD = 1; |
1763 |
|
|
break; |
1764 |
|
|
case 'G': |
1765 |
|
|
while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && |
1766 |
|
|
u.u_groupc < NGROUPS_MAX - 2) { |
1767 |
|
|
if (u.u_groupv[u.u_groupc][0] != 0) { |
1768 |
|
|
u.u_groupc++; |
1769 |
|
|
} |
1770 |
|
|
} |
1771 |
|
|
if (optarg != NULL) { |
1772 |
|
|
warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); |
1773 |
|
|
} |
1774 |
|
|
break; |
1775 |
|
|
case 'b': |
1776 |
|
|
defaultfield = 1; |
1777 |
|
|
memsave(&u.u_basedir, optarg, strlen(optarg)); |
1778 |
|
|
break; |
1779 |
|
|
case 'c': |
1780 |
|
|
memsave(&u.u_comment, optarg, strlen(optarg)); |
1781 |
|
|
break; |
1782 |
|
|
case 'd': |
1783 |
|
|
memsave(&u.u_home, optarg, strlen(optarg)); |
1784 |
|
|
u.u_flags |= F_HOMEDIR; |
1785 |
|
|
break; |
1786 |
|
|
case 'e': |
1787 |
|
|
defaultfield = 1; |
1788 |
|
|
memsave(&u.u_expire, optarg, strlen(optarg)); |
1789 |
|
|
break; |
1790 |
|
|
case 'f': |
1791 |
|
|
defaultfield = 1; |
1792 |
|
|
memsave(&u.u_inactive, optarg, strlen(optarg)); |
1793 |
|
|
break; |
1794 |
|
|
case 'g': |
1795 |
|
|
defaultfield = 1; |
1796 |
|
|
memsave(&u.u_primgrp, optarg, strlen(optarg)); |
1797 |
|
|
break; |
1798 |
|
|
case 'k': |
1799 |
|
|
defaultfield = 1; |
1800 |
|
|
memsave(&u.u_skeldir, optarg, strlen(optarg)); |
1801 |
|
|
break; |
1802 |
|
|
case 'L': |
1803 |
|
|
defaultfield = 1; |
1804 |
|
|
memsave(&u.u_class, optarg, strlen(optarg)); |
1805 |
|
|
break; |
1806 |
|
|
case 'm': |
1807 |
|
|
u.u_flags |= F_MKDIR; |
1808 |
|
|
break; |
1809 |
|
|
case 'o': |
1810 |
|
|
u.u_flags |= F_DUPUID; |
1811 |
|
|
break; |
1812 |
|
|
case 'p': |
1813 |
|
|
memsave(&u.u_password, optarg, strlen(optarg)); |
1814 |
|
|
explicit_bzero(optarg, strlen(optarg)); |
1815 |
|
|
break; |
1816 |
|
|
case 'r': |
1817 |
|
|
defaultfield = 1; |
1818 |
|
|
(void) save_range(&u, optarg); |
1819 |
|
|
break; |
1820 |
|
|
case 's': |
1821 |
|
|
defaultfield = 1; |
1822 |
|
|
memsave(&u.u_shell, optarg, strlen(optarg)); |
1823 |
|
|
break; |
1824 |
|
|
case 'u': |
1825 |
|
|
u.u_uid = strtonum(optarg, -1, UID_MAX, &errstr); |
1826 |
|
|
if (errstr != NULL) { |
1827 |
|
|
errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); |
1828 |
|
|
} |
1829 |
|
|
break; |
1830 |
|
|
case 'v': |
1831 |
|
|
verbose = 1; |
1832 |
|
|
break; |
1833 |
|
|
default: |
1834 |
|
|
usermgmt_usage("useradd"); |
1835 |
|
|
} |
1836 |
|
|
} |
1837 |
|
|
|
1838 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id flock rpath cpath wpath", |
1839 |
|
|
NULL) == -1) |
1840 |
|
|
err(1, "pledge"); |
1841 |
|
|
|
1842 |
|
|
if (bigD) { |
1843 |
|
|
if (defaultfield) { |
1844 |
|
|
checkeuid(); |
1845 |
|
|
return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; |
1846 |
|
|
} |
1847 |
|
|
printf("group\t\t%s\n", u.u_primgrp); |
1848 |
|
|
printf("base_dir\t%s\n", u.u_basedir); |
1849 |
|
|
printf("skel_dir\t%s\n", u.u_skeldir); |
1850 |
|
|
printf("shell\t\t%s\n", u.u_shell); |
1851 |
|
|
printf("class\t\t%s\n", u.u_class); |
1852 |
|
|
printf("inactive\t%s\n", (u.u_inactive == NULL) ? UNSET_INACTIVE : u.u_inactive); |
1853 |
|
|
printf("expire\t\t%s\n", (u.u_expire == NULL) ? UNSET_EXPIRY : u.u_expire); |
1854 |
|
|
for (i = 0 ; i < u.u_rc ; i++) { |
1855 |
|
|
printf("range\t\t%u..%u\n", u.u_rv[i].r_from, u.u_rv[i].r_to); |
1856 |
|
|
} |
1857 |
|
|
return EXIT_SUCCESS; |
1858 |
|
|
} |
1859 |
|
|
argc -= optind; |
1860 |
|
|
argv += optind; |
1861 |
|
|
if (argc != 1) { |
1862 |
|
|
usermgmt_usage("useradd"); |
1863 |
|
|
} |
1864 |
|
|
checkeuid(); |
1865 |
|
|
openlog("useradd", LOG_PID, LOG_USER); |
1866 |
|
|
return adduser(*argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; |
1867 |
|
|
} |
1868 |
|
|
|
1869 |
|
|
int |
1870 |
|
|
usermod(int argc, char **argv) |
1871 |
|
|
{ |
1872 |
|
|
user_t u; |
1873 |
|
|
char newuser[MaxUserNameLen + 1]; |
1874 |
|
|
int c, have_new_user; |
1875 |
|
|
const char *errstr; |
1876 |
|
|
|
1877 |
|
|
memset(&u, 0, sizeof(u)); |
1878 |
|
|
memset(newuser, 0, sizeof(newuser)); |
1879 |
|
|
read_defaults(&u); |
1880 |
|
|
free(u.u_primgrp); |
1881 |
|
|
u.u_primgrp = NULL; |
1882 |
|
|
have_new_user = 0; |
1883 |
|
|
while ((c = getopt(argc, argv, "G:L:S:UZc:d:e:f:g:l:mop:s:u:v")) != -1) { |
1884 |
|
|
switch(c) { |
1885 |
|
|
case 'G': |
1886 |
|
|
while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && |
1887 |
|
|
u.u_groupc < NGROUPS_MAX - 2) { |
1888 |
|
|
if (u.u_groupv[u.u_groupc][0] != 0) { |
1889 |
|
|
u.u_groupc++; |
1890 |
|
|
} |
1891 |
|
|
} |
1892 |
|
|
if (optarg != NULL) { |
1893 |
|
|
warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); |
1894 |
|
|
} |
1895 |
|
|
u.u_flags |= F_SECGROUP; |
1896 |
|
|
break; |
1897 |
|
|
case 'S': |
1898 |
|
|
while ((u.u_groupv[u.u_groupc] = strsep(&optarg, ",")) != NULL && |
1899 |
|
|
u.u_groupc < NGROUPS_MAX - 2) { |
1900 |
|
|
if (u.u_groupv[u.u_groupc][0] != 0) { |
1901 |
|
|
u.u_groupc++; |
1902 |
|
|
} |
1903 |
|
|
} |
1904 |
|
|
if (optarg != NULL) { |
1905 |
|
|
warnx("Truncated list of secondary groups to %d entries", NGROUPS_MAX - 2); |
1906 |
|
|
} |
1907 |
|
|
u.u_flags |= F_SETSECGROUP; |
1908 |
|
|
break; |
1909 |
|
|
case 'U': |
1910 |
|
|
u.u_flags |= F_ACCTUNLOCK; |
1911 |
|
|
break; |
1912 |
|
|
case 'Z': |
1913 |
|
|
u.u_flags |= F_ACCTLOCK; |
1914 |
|
|
break; |
1915 |
|
|
case 'c': |
1916 |
|
|
memsave(&u.u_comment, optarg, strlen(optarg)); |
1917 |
|
|
u.u_flags |= F_COMMENT; |
1918 |
|
|
break; |
1919 |
|
|
case 'd': |
1920 |
|
|
memsave(&u.u_home, optarg, strlen(optarg)); |
1921 |
|
|
u.u_flags |= F_HOMEDIR; |
1922 |
|
|
break; |
1923 |
|
|
case 'e': |
1924 |
|
|
memsave(&u.u_expire, optarg, strlen(optarg)); |
1925 |
|
|
u.u_flags |= F_EXPIRE; |
1926 |
|
|
break; |
1927 |
|
|
case 'f': |
1928 |
|
|
memsave(&u.u_inactive, optarg, strlen(optarg)); |
1929 |
|
|
u.u_flags |= F_INACTIVE; |
1930 |
|
|
break; |
1931 |
|
|
case 'g': |
1932 |
|
|
memsave(&u.u_primgrp, optarg, strlen(optarg)); |
1933 |
|
|
u.u_flags |= F_GROUP; |
1934 |
|
|
break; |
1935 |
|
|
case 'l': |
1936 |
|
|
if (strlcpy(newuser, optarg, sizeof(newuser)) >= |
1937 |
|
|
sizeof(newuser)) |
1938 |
|
|
errx(EXIT_FAILURE, "username `%s' too long", |
1939 |
|
|
optarg); |
1940 |
|
|
have_new_user = 1; |
1941 |
|
|
u.u_flags |= F_USERNAME; |
1942 |
|
|
break; |
1943 |
|
|
case 'L': |
1944 |
|
|
memsave(&u.u_class, optarg, strlen(optarg)); |
1945 |
|
|
u.u_flags |= F_CLASS; |
1946 |
|
|
break; |
1947 |
|
|
case 'm': |
1948 |
|
|
u.u_flags |= F_MKDIR; |
1949 |
|
|
break; |
1950 |
|
|
case 'o': |
1951 |
|
|
u.u_flags |= F_DUPUID; |
1952 |
|
|
break; |
1953 |
|
|
case 'p': |
1954 |
|
|
memsave(&u.u_password, optarg, strlen(optarg)); |
1955 |
|
|
explicit_bzero(optarg, strlen(optarg)); |
1956 |
|
|
u.u_flags |= F_PASSWORD; |
1957 |
|
|
break; |
1958 |
|
|
case 's': |
1959 |
|
|
memsave(&u.u_shell, optarg, strlen(optarg)); |
1960 |
|
|
u.u_flags |= F_SHELL; |
1961 |
|
|
break; |
1962 |
|
|
case 'u': |
1963 |
|
|
u.u_uid = strtonum(optarg, -1, UID_MAX, &errstr); |
1964 |
|
|
u.u_flags |= F_UID; |
1965 |
|
|
if (errstr != NULL) { |
1966 |
|
|
errx(EXIT_FAILURE, "When using [-u uid], the uid must be numeric"); |
1967 |
|
|
} |
1968 |
|
|
break; |
1969 |
|
|
case 'v': |
1970 |
|
|
verbose = 1; |
1971 |
|
|
break; |
1972 |
|
|
default: |
1973 |
|
|
usermgmt_usage("usermod"); |
1974 |
|
|
} |
1975 |
|
|
} |
1976 |
|
|
|
1977 |
|
|
if ((u.u_flags & F_MKDIR) && !(u.u_flags & F_HOMEDIR) && |
1978 |
|
|
!(u.u_flags & F_USERNAME)) { |
1979 |
|
|
warnx("option 'm' useless without 'd' or 'l' -- ignored"); |
1980 |
|
|
u.u_flags &= ~F_MKDIR; |
1981 |
|
|
} |
1982 |
|
|
if ((u.u_flags & F_SECGROUP) && (u.u_flags & F_SETSECGROUP)) |
1983 |
|
|
errx(EXIT_FAILURE, "options 'G' and 'S' are mutually exclusive"); |
1984 |
|
|
if ((u.u_flags & F_ACCTLOCK) && (u.u_flags & F_ACCTUNLOCK)) |
1985 |
|
|
errx(EXIT_FAILURE, "options 'U' and 'Z' are mutually exclusive"); |
1986 |
|
|
if ((u.u_flags & F_PASSWORD) && (u.u_flags & (F_ACCTLOCK | F_ACCTUNLOCK))) |
1987 |
|
|
errx(EXIT_FAILURE, "options 'U' or 'Z' with 'p' are mutually exclusive"); |
1988 |
|
|
argc -= optind; |
1989 |
|
|
argv += optind; |
1990 |
|
|
if (argc != 1) { |
1991 |
|
|
usermgmt_usage("usermod"); |
1992 |
|
|
} |
1993 |
|
|
checkeuid(); |
1994 |
|
|
openlog("usermod", LOG_PID, LOG_USER); |
1995 |
|
|
return moduser(*argv, (have_new_user) ? newuser : *argv, &u) ? |
1996 |
|
|
EXIT_SUCCESS : EXIT_FAILURE; |
1997 |
|
|
} |
1998 |
|
|
|
1999 |
|
|
int |
2000 |
|
|
userdel(int argc, char **argv) |
2001 |
|
|
{ |
2002 |
|
|
struct passwd *pwp; |
2003 |
|
|
user_t u; |
2004 |
|
|
int defaultfield; |
2005 |
|
|
int rmhome; |
2006 |
|
|
int bigD; |
2007 |
|
|
int c; |
2008 |
|
|
|
2009 |
|
|
memset(&u, 0, sizeof(u)); |
2010 |
|
|
read_defaults(&u); |
2011 |
|
|
defaultfield = bigD = rmhome = 0; |
2012 |
|
|
while ((c = getopt(argc, argv, "Dp:rv")) != -1) { |
2013 |
|
|
switch(c) { |
2014 |
|
|
case 'D': |
2015 |
|
|
bigD = 1; |
2016 |
|
|
break; |
2017 |
|
|
case 'p': |
2018 |
|
|
defaultfield = 1; |
2019 |
|
|
u.u_preserve = (strcmp(optarg, "true") == 0) ? 1 : |
2020 |
|
|
(strcmp(optarg, "yes") == 0) ? 1 : |
2021 |
|
|
strtonum(optarg, INT_MIN, INT_MAX, NULL); |
2022 |
|
|
break; |
2023 |
|
|
case 'r': |
2024 |
|
|
rmhome = 1; |
2025 |
|
|
break; |
2026 |
|
|
case 'v': |
2027 |
|
|
verbose = 1; |
2028 |
|
|
break; |
2029 |
|
|
default: |
2030 |
|
|
usermgmt_usage("userdel"); |
2031 |
|
|
} |
2032 |
|
|
} |
2033 |
|
|
if (bigD) { |
2034 |
|
|
if (defaultfield) { |
2035 |
|
|
checkeuid(); |
2036 |
|
|
return setdefaults(&u) ? EXIT_SUCCESS : EXIT_FAILURE; |
2037 |
|
|
} |
2038 |
|
|
printf("preserve\t%s\n", (u.u_preserve) ? "true" : "false"); |
2039 |
|
|
return EXIT_SUCCESS; |
2040 |
|
|
} |
2041 |
|
|
argc -= optind; |
2042 |
|
|
argv += optind; |
2043 |
|
|
if (argc != 1) { |
2044 |
|
|
usermgmt_usage("userdel"); |
2045 |
|
|
} |
2046 |
|
|
|
2047 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id flock rpath cpath wpath", |
2048 |
|
|
NULL) == -1) |
2049 |
|
|
err(1, "pledge"); |
2050 |
|
|
|
2051 |
|
|
checkeuid(); |
2052 |
|
|
if ((pwp = getpwnam(*argv)) == NULL) { |
2053 |
|
|
warnx("No such user `%s'", *argv); |
2054 |
|
|
return EXIT_FAILURE; |
2055 |
|
|
} |
2056 |
|
|
if (rmhome) |
2057 |
|
|
(void)removehomedir(pwp->pw_name, pwp->pw_uid, pwp->pw_dir); |
2058 |
|
|
if (u.u_preserve) { |
2059 |
|
|
u.u_flags |= F_SHELL; |
2060 |
|
|
memsave(&u.u_shell, NOLOGIN, strlen(NOLOGIN)); |
2061 |
|
|
memsave(&u.u_password, "*", strlen("*")); |
2062 |
|
|
u.u_flags |= F_PASSWORD; |
2063 |
|
|
openlog("userdel", LOG_PID, LOG_USER); |
2064 |
|
|
return moduser(*argv, *argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; |
2065 |
|
|
} |
2066 |
|
|
if (!rm_user_from_groups(*argv)) { |
2067 |
|
|
return 0; |
2068 |
|
|
} |
2069 |
|
|
openlog("userdel", LOG_PID, LOG_USER); |
2070 |
|
|
return moduser(*argv, *argv, NULL) ? EXIT_SUCCESS : EXIT_FAILURE; |
2071 |
|
|
} |
2072 |
|
|
|
2073 |
|
|
/* add a group */ |
2074 |
|
|
int |
2075 |
|
|
groupadd(int argc, char **argv) |
2076 |
|
|
{ |
2077 |
|
|
int dupgid; |
2078 |
|
|
int gid; |
2079 |
|
|
int c; |
2080 |
|
|
const char *errstr; |
2081 |
|
|
|
2082 |
|
|
gid = GID_MAX; |
2083 |
|
|
dupgid = 0; |
2084 |
|
|
while ((c = getopt(argc, argv, "g:ov")) != -1) { |
2085 |
|
|
switch(c) { |
2086 |
|
|
case 'g': |
2087 |
|
|
gid = strtonum(optarg, -1, GID_MAX, &errstr); |
2088 |
|
|
if (errstr != NULL) { |
2089 |
|
|
errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); |
2090 |
|
|
} |
2091 |
|
|
break; |
2092 |
|
|
case 'o': |
2093 |
|
|
dupgid = 1; |
2094 |
|
|
break; |
2095 |
|
|
case 'v': |
2096 |
|
|
verbose = 1; |
2097 |
|
|
break; |
2098 |
|
|
default: |
2099 |
|
|
usermgmt_usage("groupadd"); |
2100 |
|
|
} |
2101 |
|
|
} |
2102 |
|
|
argc -= optind; |
2103 |
|
|
argv += optind; |
2104 |
|
|
if (argc != 1) { |
2105 |
|
|
usermgmt_usage("groupadd"); |
2106 |
|
|
} |
2107 |
|
|
|
2108 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock getpw", NULL) == -1) |
2109 |
|
|
err(1, "pledge"); |
2110 |
|
|
|
2111 |
|
|
checkeuid(); |
2112 |
|
|
if (!valid_group(*argv)) { |
2113 |
|
|
errx(EXIT_FAILURE, "invalid group name `%s'", *argv); |
2114 |
|
|
} |
2115 |
|
|
if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) { |
2116 |
|
|
errx(EXIT_FAILURE, "can't add group: can't get next gid"); |
2117 |
|
|
} |
2118 |
|
|
if (!dupgid && getgrgid((gid_t) gid) != NULL) { |
2119 |
|
|
errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid); |
2120 |
|
|
} |
2121 |
|
|
openlog("groupadd", LOG_PID, LOG_USER); |
2122 |
|
|
if (!creategid(*argv, gid, "")) { |
2123 |
|
|
errx(EXIT_FAILURE, "can't add group: problems with %s file", |
2124 |
|
|
_PATH_GROUP); |
2125 |
|
|
} |
2126 |
|
|
return EXIT_SUCCESS; |
2127 |
|
|
} |
2128 |
|
|
|
2129 |
|
|
/* remove a group */ |
2130 |
|
|
int |
2131 |
|
|
groupdel(int argc, char **argv) |
2132 |
|
|
{ |
2133 |
|
|
int c; |
2134 |
|
|
|
2135 |
|
|
while ((c = getopt(argc, argv, "v")) != -1) { |
2136 |
|
|
switch(c) { |
2137 |
|
|
case 'v': |
2138 |
|
|
verbose = 1; |
2139 |
|
|
break; |
2140 |
|
|
default: |
2141 |
|
|
usermgmt_usage("groupdel"); |
2142 |
|
|
} |
2143 |
|
|
} |
2144 |
|
|
argc -= optind; |
2145 |
|
|
argv += optind; |
2146 |
|
|
if (argc != 1) { |
2147 |
|
|
usermgmt_usage("groupdel"); |
2148 |
|
|
} |
2149 |
|
|
checkeuid(); |
2150 |
|
|
openlog("groupdel", LOG_PID, LOG_USER); |
2151 |
|
|
if (getgrnam(*argv) == NULL) { |
2152 |
|
|
warnx("No such group: `%s'", *argv); |
2153 |
|
|
return EXIT_FAILURE; |
2154 |
|
|
} |
2155 |
|
|
|
2156 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) |
2157 |
|
|
err(1, "pledge"); |
2158 |
|
|
|
2159 |
|
|
if (!modify_gid(*argv, NULL)) { |
2160 |
|
|
err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); |
2161 |
|
|
} |
2162 |
|
|
return EXIT_SUCCESS; |
2163 |
|
|
} |
2164 |
|
|
|
2165 |
|
|
/* modify a group */ |
2166 |
|
|
int |
2167 |
|
|
groupmod(int argc, char **argv) |
2168 |
|
|
{ |
2169 |
|
|
struct group *grp; |
2170 |
|
|
const char *errstr; |
2171 |
|
|
char buf[LINE_MAX]; |
2172 |
|
|
char *newname; |
2173 |
|
|
char **cpp; |
2174 |
|
|
int dupgid; |
2175 |
|
|
int gid; |
2176 |
|
|
int cc; |
2177 |
|
|
int c; |
2178 |
|
|
|
2179 |
|
|
gid = GID_MAX; |
2180 |
|
|
dupgid = 0; |
2181 |
|
|
newname = NULL; |
2182 |
|
|
while ((c = getopt(argc, argv, "g:n:ov")) != -1) { |
2183 |
|
|
switch(c) { |
2184 |
|
|
case 'g': |
2185 |
|
|
gid = strtonum(optarg, -1, GID_MAX, &errstr); |
2186 |
|
|
if (errstr != NULL) { |
2187 |
|
|
errx(EXIT_FAILURE, "When using [-g gid], the gid must be numeric"); |
2188 |
|
|
} |
2189 |
|
|
break; |
2190 |
|
|
case 'o': |
2191 |
|
|
dupgid = 1; |
2192 |
|
|
break; |
2193 |
|
|
case 'n': |
2194 |
|
|
memsave(&newname, optarg, strlen(optarg)); |
2195 |
|
|
break; |
2196 |
|
|
case 'v': |
2197 |
|
|
verbose = 1; |
2198 |
|
|
break; |
2199 |
|
|
default: |
2200 |
|
|
usermgmt_usage("groupmod"); |
2201 |
|
|
} |
2202 |
|
|
} |
2203 |
|
|
argc -= optind; |
2204 |
|
|
argv += optind; |
2205 |
|
|
if (argc != 1) { |
2206 |
|
|
usermgmt_usage("groupmod"); |
2207 |
|
|
} |
2208 |
|
|
checkeuid(); |
2209 |
|
|
if (gid < 0 && newname == NULL) { |
2210 |
|
|
errx(EXIT_FAILURE, "Nothing to change"); |
2211 |
|
|
} |
2212 |
|
|
if (dupgid && gid < 0) { |
2213 |
|
|
errx(EXIT_FAILURE, "Duplicate which gid?"); |
2214 |
|
|
} |
2215 |
|
|
if ((grp = getgrnam(*argv)) == NULL) { |
2216 |
|
|
errx(EXIT_FAILURE, "can't find group `%s' to modify", *argv); |
2217 |
|
|
} |
2218 |
|
|
|
2219 |
|
|
if (pledge("stdio rpath wpath cpath fattr flock", NULL) == -1) |
2220 |
|
|
err(1, "pledge"); |
2221 |
|
|
|
2222 |
|
|
if (!is_local(*argv, _PATH_GROUP)) { |
2223 |
|
|
errx(EXIT_FAILURE, "Group `%s' must be a local group", *argv); |
2224 |
|
|
} |
2225 |
|
|
if (newname != NULL && !valid_group(newname)) { |
2226 |
|
|
errx(EXIT_FAILURE, "invalid group name `%s'", newname); |
2227 |
|
|
} |
2228 |
|
|
if ((cc = snprintf(buf, sizeof(buf), "%s:%s:%u:", |
2229 |
|
|
(newname) ? newname : grp->gr_name, grp->gr_passwd, |
2230 |
|
|
(gid < 0) ? grp->gr_gid : gid)) >= sizeof(buf) || cc < 0) |
2231 |
|
|
errx(EXIT_FAILURE, "group `%s' entry too long", grp->gr_name); |
2232 |
|
|
|
2233 |
|
|
for (cpp = grp->gr_mem ; *cpp ; cpp++) { |
2234 |
|
|
cc = strlcat(buf, *cpp, sizeof(buf)) + 1; |
2235 |
|
|
if (cc >= sizeof(buf)) |
2236 |
|
|
errx(EXIT_FAILURE, "group `%s' entry too long", |
2237 |
|
|
grp->gr_name); |
2238 |
|
|
if (cpp[1] != NULL) { |
2239 |
|
|
buf[cc - 1] = ','; |
2240 |
|
|
buf[cc] = '\0'; |
2241 |
|
|
} |
2242 |
|
|
} |
2243 |
|
|
cc = strlcat(buf, "\n", sizeof(buf)); |
2244 |
|
|
if (cc >= sizeof(buf)) |
2245 |
|
|
errx(EXIT_FAILURE, "group `%s' entry too long", grp->gr_name); |
2246 |
|
|
|
2247 |
|
|
openlog("groupmod", LOG_PID, LOG_USER); |
2248 |
|
|
if (!modify_gid(*argv, buf)) |
2249 |
|
|
err(EXIT_FAILURE, "can't change %s file", _PATH_GROUP); |
2250 |
|
|
return EXIT_SUCCESS; |
2251 |
|
|
} |
2252 |
|
|
|
2253 |
|
|
/* display user information */ |
2254 |
|
|
int |
2255 |
|
|
userinfo(int argc, char **argv) |
2256 |
|
|
{ |
2257 |
|
|
struct passwd *pwp; |
2258 |
|
|
struct group *grp; |
2259 |
|
|
char **cpp; |
2260 |
|
|
int exists; |
2261 |
|
|
int i; |
2262 |
|
|
|
2263 |
|
|
exists = 0; |
2264 |
|
|
while ((i = getopt(argc, argv, "ev")) != -1) { |
2265 |
|
|
switch(i) { |
2266 |
|
|
case 'e': |
2267 |
|
|
exists = 1; |
2268 |
|
|
break; |
2269 |
|
|
case 'v': |
2270 |
|
|
verbose = 1; |
2271 |
|
|
break; |
2272 |
|
|
default: |
2273 |
|
|
usermgmt_usage("userinfo"); |
2274 |
|
|
} |
2275 |
|
|
} |
2276 |
|
|
argc -= optind; |
2277 |
|
|
argv += optind; |
2278 |
|
|
if (argc != 1) { |
2279 |
|
|
usermgmt_usage("userinfo"); |
2280 |
|
|
} |
2281 |
|
|
|
2282 |
|
|
if (pledge("stdio getpw flock rpath cpath wpath", NULL) == -1) |
2283 |
|
|
err(1, "pledge"); |
2284 |
|
|
|
2285 |
|
|
pwp = find_user_info(*argv); |
2286 |
|
|
if (exists) { |
2287 |
|
|
exit((pwp) ? EXIT_SUCCESS : EXIT_FAILURE); |
2288 |
|
|
} |
2289 |
|
|
if (pwp == NULL) { |
2290 |
|
|
errx(EXIT_FAILURE, "can't find user `%s'", *argv); |
2291 |
|
|
} |
2292 |
|
|
printf("login\t%s\n", pwp->pw_name); |
2293 |
|
|
printf("passwd\t%s\n", pwp->pw_passwd); |
2294 |
|
|
printf("uid\t%u\n", pwp->pw_uid); |
2295 |
|
|
if ((grp = getgrgid(pwp->pw_gid)) == NULL) |
2296 |
|
|
printf("groups\t%u", pwp->pw_gid); |
2297 |
|
|
else |
2298 |
|
|
printf("groups\t%s", grp->gr_name); |
2299 |
|
|
while ((grp = getgrent()) != NULL) { |
2300 |
|
|
for (cpp = grp->gr_mem ; *cpp ; cpp++) { |
2301 |
|
|
if (strcmp(*cpp, pwp->pw_name) == 0 && |
2302 |
|
|
grp->gr_gid != pwp->pw_gid) |
2303 |
|
|
printf(" %s", grp->gr_name); |
2304 |
|
|
} |
2305 |
|
|
} |
2306 |
|
|
fputc('\n', stdout); |
2307 |
|
|
printf("change\t%s", pwp->pw_change ? ctime(&pwp->pw_change) : "NEVER\n"); |
2308 |
|
|
printf("class\t%s\n", pwp->pw_class); |
2309 |
|
|
printf("gecos\t%s\n", pwp->pw_gecos); |
2310 |
|
|
printf("dir\t%s\n", pwp->pw_dir); |
2311 |
|
|
printf("shell\t%s\n", pwp->pw_shell); |
2312 |
|
|
printf("expire\t%s", pwp->pw_expire ? ctime(&pwp->pw_expire) : "NEVER\n"); |
2313 |
|
|
return EXIT_SUCCESS; |
2314 |
|
|
} |
2315 |
|
|
|
2316 |
|
|
/* display user information */ |
2317 |
|
|
int |
2318 |
|
|
groupinfo(int argc, char **argv) |
2319 |
|
|
{ |
2320 |
|
|
struct group *grp; |
2321 |
|
|
char **cpp; |
2322 |
|
|
int exists; |
2323 |
|
|
int i; |
2324 |
|
|
|
2325 |
|
|
exists = 0; |
2326 |
|
|
while ((i = getopt(argc, argv, "ev")) != -1) { |
2327 |
|
|
switch(i) { |
2328 |
|
|
case 'e': |
2329 |
|
|
exists = 1; |
2330 |
|
|
break; |
2331 |
|
|
case 'v': |
2332 |
|
|
verbose = 1; |
2333 |
|
|
break; |
2334 |
|
|
default: |
2335 |
|
|
usermgmt_usage("groupinfo"); |
2336 |
|
|
} |
2337 |
|
|
} |
2338 |
|
|
argc -= optind; |
2339 |
|
|
argv += optind; |
2340 |
|
|
if (argc != 1) { |
2341 |
|
|
usermgmt_usage("groupinfo"); |
2342 |
|
|
} |
2343 |
|
|
|
2344 |
|
|
if (pledge("stdio getpw flock rpath cpath wpath", NULL) == -1) |
2345 |
|
|
err(1, "pledge"); |
2346 |
|
|
|
2347 |
|
|
grp = find_group_info(*argv); |
2348 |
|
|
if (exists) { |
2349 |
|
|
exit((grp) ? EXIT_SUCCESS : EXIT_FAILURE); |
2350 |
|
|
} |
2351 |
|
|
if (grp == NULL) { |
2352 |
|
|
errx(EXIT_FAILURE, "can't find group `%s'", *argv); |
2353 |
|
|
} |
2354 |
|
|
printf("name\t%s\n", grp->gr_name); |
2355 |
|
|
printf("passwd\t%s\n", grp->gr_passwd); |
2356 |
|
|
printf("gid\t%u\n", grp->gr_gid); |
2357 |
|
|
printf("members\t"); |
2358 |
|
|
for (cpp = grp->gr_mem ; *cpp ; cpp++) { |
2359 |
|
|
printf("%s ", *cpp); |
2360 |
|
|
} |
2361 |
|
|
fputc('\n', stdout); |
2362 |
|
|
return EXIT_SUCCESS; |
2363 |
|
|
} |