GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
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 |
32 |
free(*cpp); |
|
213 |
✗✓ | 16 |
if ((*cpp = calloc (n + 1, sizeof(char))) == NULL) |
214 |
err(1, NULL); |
||
215 |
16 |
memcpy(*cpp, s, n); |
|
216 |
16 |
(*cpp)[n] = '\0'; |
|
217 |
16 |
} |
|
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 |
✗✓ | 4 |
if (geteuid() != 0) { |
287 |
errx(EXIT_FAILURE, "Program must be run as root"); |
||
288 |
} |
||
289 |
2 |
} |
|
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 |
2 |
struct stat st; |
|
327 |
FILE *from; |
||
328 |
FILE *to; |
||
329 |
char *buf; |
||
330 |
1 |
char f[MaxFileNameLen]; |
|
331 |
int fd, ret; |
||
332 |
int wroteit = 0; |
||
333 |
1 |
size_t len; |
|
334 |
|||
335 |
✗✓ | 1 |
if (getgrnam(group) != NULL) { |
336 |
warnx("group `%s' already exists", group); |
||
337 |
return 0; |
||
338 |
} |
||
339 |
✗✓ | 1 |
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 |
✓✗✗✓ |
3 |
if (flock(fileno(from), LOCK_EX | LOCK_NB) < 0) { |
345 |
warn("can't lock `%s'", _PATH_GROUP); |
||
346 |
} |
||
347 |
✓✗ | 3 |
(void) fstat(fileno(from), &st); |
348 |
1 |
(void) snprintf(f, sizeof(f), "%s.XXXXXXXX", _PATH_GROUP); |
|
349 |
✗✓ | 1 |
if ((fd = mkstemp(f)) < 0) { |
350 |
warn("can't create gid: mkstemp failed"); |
||
351 |
fclose(from); |
||
352 |
return 0; |
||
353 |
} |
||
354 |
✗✓ | 1 |
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 |
✓✓ | 86 |
while ((buf = fgetln(from, &len)) != NULL && len > 0) { |
362 |
ret = 0; |
||
363 |
✗✓ | 85 |
if (buf[0] == '+' && wroteit == 0) { |
364 |
ret = fprintf(to, "%s:*:%u:%s\n", group, gid, name); |
||
365 |
wroteit = 1; |
||
366 |
} |
||
367 |
✓✗✓✗ |
170 |
if (ret == -1 || |
368 |
85 |
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 |
✓✗ | 1 |
if (wroteit == 0) |
378 |
1 |
ret = fprintf(to, "%s:*:%u:%s\n", group, gid, name); |
|
379 |
1 |
fclose(from); |
|
380 |
✗✓ | 1 |
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 |
✗✓ | 1 |
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 |
1 |
(void) chmod(_PATH_GROUP, st.st_mode & 0777); |
|
392 |
1 |
syslog(LOG_INFO, "new group added: name=%s, gid=%u", group, gid); |
|
393 |
1 |
return 1; |
|
394 |
1 |
} |
|
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 |
✗✓ | 2 |
if (*login_name == '-') |
628 |
return 0; |
||
629 |
|||
630 |
✓✓ | 18 |
for (cp = login_name ; *cp ; cp++) { |
631 |
/* We allow '$' as the last character for samba */ |
||
632 |
✓✓✓✗ ✗✗ |
9 |
if (!isalnum((unsigned char)*cp) && *cp != '.' && |
633 |
✗✓✗✗ |
1 |
*cp != '_' && *cp != '-' && |
634 |
!(*cp == '$' && *(cp + 1) == '\0')) { |
||
635 |
return 0; |
||
636 |
} |
||
637 |
} |
||
638 |
✗✓ | 1 |
if ((char *)cp - login_name > MaxUserNameLen) |
639 |
return 0; |
||
640 |
1 |
return 1; |
|
641 |
1 |
} |
|
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 |
✓✓ | 19 |
for (cp = group ; *cp ; cp++) { |
650 |
✓✓✓✗ ✗✗ |
9 |
if (!isalnum((unsigned char)*cp) && *cp != '.' && |
651 |
✗✓ | 1 |
*cp != '_' && *cp != '-') { |
652 |
return 0; |
||
653 |
} |
||
654 |
} |
||
655 |
✗✓ | 1 |
if ((char *)cp - group > MaxUserNameLen) |
656 |
return 0; |
||
657 |
1 |
return 1; |
|
658 |
1 |
} |
|
659 |
|||
660 |
/* return 1 if `class' exists */ |
||
661 |
static int |
||
662 |
valid_class(char *class) |
||
663 |
{ |
||
664 |
login_cap_t *lc; |
||
665 |
|||
666 |
✓✗ | 2 |
if ((lc = login_getclass(class)) != NULL) |
667 |
1 |
login_close(lc); |
|
668 |
1 |
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 |
2 |
struct stat st; |
|
769 |
1 |
size_t lineno; |
|
770 |
1 |
size_t len; |
|
771 |
FILE *fp; |
||
772 |
unsigned char *cp; |
||
773 |
unsigned char *s; |
||
774 |
|||
775 |
1 |
memsave(&up->u_primgrp, DEF_GROUP, strlen(DEF_GROUP)); |
|
776 |
1 |
memsave(&up->u_basedir, DEF_BASEDIR, strlen(DEF_BASEDIR)); |
|
777 |
1 |
memsave(&up->u_skeldir, DEF_SKELDIR, strlen(DEF_SKELDIR)); |
|
778 |
1 |
memsave(&up->u_shell, DEF_SHELL, strlen(DEF_SHELL)); |
|
779 |
1 |
memsave(&up->u_comment, DEF_COMMENT, strlen(DEF_COMMENT)); |
|
780 |
1 |
memsave(&up->u_class, DEF_CLASS, strlen(DEF_CLASS)); |
|
781 |
1 |
up->u_rsize = 16; |
|
782 |
1 |
up->u_defrc = 0; |
|
783 |
✗✓ | 1 |
if ((up->u_rv = calloc(up->u_rsize, sizeof(range_t))) == NULL) |
784 |
err(1, NULL); |
||
785 |
1 |
up->u_inactive = DEF_INACTIVE; |
|
786 |
1 |
up->u_expire = DEF_EXPIRE; |
|
787 |
✗✓ | 1 |
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 |
✓✗ | 1 |
if (fp != NULL) { |
794 |
✓✓ | 17 |
while ((s = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { |
795 |
✓✓ | 8 |
if (strncmp(s, "group", 5) == 0) { |
796 |
✓✓ | 6 |
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
797 |
} |
||
798 |
1 |
memsave(&up->u_primgrp, cp, strlen(cp)); |
|
799 |
✓✓ | 8 |
} else if (strncmp(s, "base_dir", 8) == 0) { |
800 |
✓✓ | 4 |
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
801 |
} |
||
802 |
1 |
memsave(&up->u_basedir, cp, strlen(cp)); |
|
803 |
✓✓ | 7 |
} else if (strncmp(s, "skel_dir", 8) == 0) { |
804 |
✓✓ | 4 |
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
805 |
} |
||
806 |
1 |
memsave(&up->u_skeldir, cp, strlen(cp)); |
|
807 |
✓✓ | 6 |
} else if (strncmp(s, "shell", 5) == 0) { |
808 |
✓✓ | 6 |
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
809 |
} |
||
810 |
1 |
memsave(&up->u_shell, cp, strlen(cp)); |
|
811 |
✗✓ | 5 |
} 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 |
✓✓ | 4 |
} else if (strncmp(s, "class", 5) == 0) { |
816 |
✓✓ | 6 |
for (cp = s + 5 ; isspace((unsigned char)*cp); cp++) { |
817 |
} |
||
818 |
1 |
memsave(&up->u_class, cp, strlen(cp)); |
|
819 |
✓✓ | 4 |
} else if (strncmp(s, "inactive", 8) == 0) { |
820 |
✓✓ | 4 |
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
821 |
} |
||
822 |
✓✗ | 1 |
if (strcmp(cp, UNSET_INACTIVE) == 0) { |
823 |
1 |
free(up->u_inactive); |
|
824 |
1 |
up->u_inactive = NULL; |
|
825 |
1 |
} else { |
|
826 |
memsave(&up->u_inactive, cp, strlen(cp)); |
||
827 |
} |
||
828 |
✗✓ | 2 |
} 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 |
✓✓ | 2 |
} else if (strncmp(s, "preserve", 8) == 0) { |
833 |
✓✓ | 4 |
for (cp = s + 8 ; isspace((unsigned char)*cp); cp++) { |
834 |
} |
||
835 |
✓✗ | 2 |
up->u_preserve = (strncmp(cp, "true", 4) == 0) ? 1 : |
836 |
✓✗ | 2 |
(strncmp(cp, "yes", 3) == 0) ? 1 : |
837 |
1 |
strtonum(cp, INT_MIN, INT_MAX, NULL); |
|
838 |
✓✗ | 2 |
} else if (strncmp(s, "expire", 6) == 0) { |
839 |
✓✓ | 6 |
for (cp = s + 6 ; isspace((unsigned char)*cp); cp++) { |
840 |
} |
||
841 |
✓✗ | 1 |
if (strcmp(cp, UNSET_EXPIRY) == 0) { |
842 |
1 |
free(up->u_expire); |
|
843 |
1 |
up->u_expire = NULL; |
|
844 |
1 |
} else { |
|
845 |
memsave(&up->u_expire, cp, strlen(cp)); |
||
846 |
} |
||
847 |
} |
||
848 |
8 |
free(s); |
|
849 |
} |
||
850 |
1 |
fclose(fp); |
|
851 |
1 |
} |
|
852 |
✓✗ | 1 |
if (up->u_rc == 0) { |
853 |
1 |
up->u_rv[up->u_rc].r_from = DEF_LOWUID; |
|
854 |
1 |
up->u_rv[up->u_rc].r_to = DEF_HIGHUID; |
|
855 |
1 |
up->u_rc += 1; |
|
856 |
1 |
} |
|
857 |
1 |
up->u_defrc = up->u_rc; |
|
858 |
1 |
} |
|
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 |
4 |
struct tm tm; |
|
883 |
|||
884 |
2 |
*tp = 0; |
|
885 |
✗✓ | 2 |
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 |
2 |
return 1; |
|
899 |
2 |
} |
|
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 |
2 |
ulen = strlen(username); |
|
909 |
✓✓ | 28 |
for (alen = 0; *p != '\0'; p++) |
910 |
✗✓ | 13 |
if (*p == '&') |
911 |
alen += ulen - 1; |
||
912 |
1 |
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 |
2 |
const char *errstr; |
|
937 |
gid_t gid; |
||
938 |
|||
939 |
✗✓ | 1 |
if ((grp = getgrnam(name)) == NULL) { |
940 |
gid = strtonum(name, -1, GID_MAX, &errstr); |
||
941 |
if (errstr == NULL) |
||
942 |
grp = getgrgid(gid); |
||
943 |
} |
||
944 |
1 |
return grp; |
|
945 |
1 |
} |
|
946 |
|||
947 |
/* add a user */ |
||
948 |
static int |
||
949 |
adduser(char *login_name, user_t *up) |
||
950 |
{ |
||
951 |
struct group *grp; |
||
952 |
2 |
struct stat st; |
|
953 |
1 |
time_t expire; |
|
954 |
1 |
time_t inactive; |
|
955 |
1 |
char password[PasswordLength + 1]; |
|
956 |
1 |
char home[MaxFileNameLen]; |
|
957 |
1 |
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 |
✗✓ | 1 |
if (!valid_login(login_name)) { |
967 |
errx(EXIT_FAILURE, "`%s' is not a valid login name", login_name); |
||
968 |
} |
||
969 |
✗✓ | 1 |
if (!valid_class(up->u_class)) { |
970 |
errx(EXIT_FAILURE, "No such login class `%s'", up->u_class); |
||
971 |
} |
||
972 |
✗✓ | 1 |
if ((masterfd = open(_PATH_MASTERPASSWD, O_RDONLY)) < 0) { |
973 |
err(EXIT_FAILURE, "can't open `%s'", _PATH_MASTERPASSWD); |
||
974 |
} |
||
975 |
✗✓ | 1 |
if (flock(masterfd, LOCK_EX | LOCK_NB) < 0) { |
976 |
err(EXIT_FAILURE, "can't lock `%s'", _PATH_MASTERPASSWD); |
||
977 |
} |
||
978 |
1 |
pw_init(); |
|
979 |
✗✓ | 1 |
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 |
✗✓ | 1 |
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 |
✓✓ | 67 |
while (fgets(buf, sizeof(buf), fp) != NULL) { |
993 |
66 |
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 |
✓✗✗✓ ✗✗ |
132 |
if (cc > 1 && buf[0] == '+' && buf[1] == ':') { |
1000 |
yp = 1; |
||
1001 |
break; |
||
1002 |
} |
||
1003 |
✓✗ | 66 |
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 |
✓✗✗✓ ✗✗ |
2 |
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 |
1 |
sync_uid_gid = (strcmp(up->u_primgrp, "=uid") == 0); |
|
1022 |
✗✓ | 1 |
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 |
✓✗✗✓ |
2 |
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 |
✗✓ | 1 |
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 |
✗✓ | 1 |
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 |
1 |
gid = grp->gr_gid; |
|
1071 |
} |
||
1072 |
/* check name isn't already in use */ |
||
1073 |
✓✗✗✓ |
2 |
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 |
✓✗ | 1 |
if (up->u_flags & F_HOMEDIR) { |
1079 |
✗✓ | 1 |
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 |
✗✓ | 1 |
if (!scantime(&inactive, up->u_inactive)) { |
1096 |
warnx("Warning: inactive time `%s' invalid, password expiry off", |
||
1097 |
up->u_inactive); |
||
1098 |
} |
||
1099 |
✗✓ | 1 |
if (!scantime(&expire, up->u_expire)) { |
1100 |
warnx("Warning: expire time `%s' invalid, account expiry off", |
||
1101 |
up->u_expire); |
||
1102 |
} |
||
1103 |
✓✗✓✗ ✗✓ |
3 |
if (lstat(home, &st) < 0 && !(up->u_flags & F_MKDIR) && |
1104 |
1 |
strcmp(home, _PATH_NONEXISTENT) != 0) { |
|
1105 |
warnx("Warning: home directory `%s' doesn't exist, and -m was" |
||
1106 |
" not specified", home); |
||
1107 |
} |
||
1108 |
✗✓ | 2 |
(void) strlcpy(password, up->u_password ? up->u_password : "*", |
1109 |
sizeof(password)); |
||
1110 |
1 |
cc = snprintf(buf, sizeof(buf), "%s:%s:%u:%u:%s:%lld:%lld:%s:%s:%s\n", |
|
1111 |
login_name, |
||
1112 |
password, |
||
1113 |
1 |
up->u_uid, |
|
1114 |
gid, |
||
1115 |
1 |
up->u_class, |
|
1116 |
1 |
(long long) inactive, |
|
1117 |
1 |
(long long) expire, |
|
1118 |
1 |
up->u_comment, |
|
1119 |
home, |
||
1120 |
1 |
up->u_shell); |
|
1121 |
✓✗✗✓ |
2 |
if (cc >= sizeof(buf) || cc < 0 || |
1122 |
1 |
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 |
✗✓ | 1 |
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 |
✗✓ | 1 |
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 |
✗✓ | 1 |
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 |
✗✓✗✗ |
1 |
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 |
✗✓✗✗ |
1 |
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 |
1 |
fclose(fp); |
|
1203 |
1 |
close(ptmpfd); |
|
1204 |
✗✓ | 1 |
if (pw_mkdb(yp ? NULL : login_name, 0) < 0) { |
1205 |
pw_abort(); |
||
1206 |
err(EXIT_FAILURE, "pw_mkdb failed"); |
||
1207 |
} |
||
1208 |
1 |
syslog(LOG_INFO, "new user added: name=%s, uid=%u, gid=%u, home=%s, shell=%s", |
|
1209 |
1 |
login_name, up->u_uid, gid, home, up->u_shell); |
|
1210 |
1 |
return 1; |
|
1211 |
1 |
} |
|
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", |
||
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 |
2 |
user_t u; |
|
1749 |
1 |
const char *errstr; |
|
1750 |
int defaultfield; |
||
1751 |
int bigD; |
||
1752 |
int c; |
||
1753 |
int i; |
||
1754 |
|||
1755 |
1 |
memset(&u, 0, sizeof(u)); |
|
1756 |
1 |
read_defaults(&u); |
|
1757 |
1 |
u.u_uid = UID_MAX; |
|
1758 |
defaultfield = bigD = 0; |
||
1759 |
✓✓ | 8 |
while ((c = getopt(argc, argv, "DG:L:b:c:d:e:f:g:k:mop:r:s:u:v")) != -1) { |
1760 |
✗✗✗✓ ✓✗✗✓ ✗✓✗✗ ✗✗✓✓ ✗✗ |
6 |
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 |
1 |
memsave(&u.u_comment, optarg, strlen(optarg)); |
|
1781 |
1 |
break; |
|
1782 |
case 'd': |
||
1783 |
1 |
memsave(&u.u_home, optarg, strlen(optarg)); |
|
1784 |
1 |
u.u_flags |= F_HOMEDIR; |
|
1785 |
1 |
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 |
1 |
memsave(&u.u_primgrp, optarg, strlen(optarg)); |
|
1797 |
1 |
break; |
|
1798 |
case 'k': |
||
1799 |
defaultfield = 1; |
||
1800 |
memsave(&u.u_skeldir, optarg, strlen(optarg)); |
||
1801 |
break; |
||
1802 |
case 'L': |
||
1803 |
defaultfield = 1; |
||
1804 |
1 |
memsave(&u.u_class, optarg, strlen(optarg)); |
|
1805 |
1 |
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 |
1 |
memsave(&u.u_shell, optarg, strlen(optarg)); |
|
1823 |
1 |
break; |
|
1824 |
case 'u': |
||
1825 |
1 |
u.u_uid = strtonum(optarg, -1, UID_MAX, &errstr); |
|
1826 |
✓✗ | 1 |
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 |
✗✓ | 2 |
if (pledge("stdio rpath wpath cpath fattr flock proc exec getpw id", |
1839 |
1 |
NULL) == -1) |
|
1840 |
err(1, "pledge"); |
||
1841 |
|||
1842 |
✗✓ | 1 |
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 |
1 |
argc -= optind; |
|
1860 |
1 |
argv += optind; |
|
1861 |
✗✓ | 1 |
if (argc != 1) { |
1862 |
usermgmt_usage("useradd"); |
||
1863 |
} |
||
1864 |
1 |
checkeuid(); |
|
1865 |
1 |
openlog("useradd", LOG_PID, LOG_USER); |
|
1866 |
1 |
return adduser(*argv, &u) ? EXIT_SUCCESS : EXIT_FAILURE; |
|
1867 |
1 |
} |
|
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", |
||
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 |
2 |
int gid; |
|
2079 |
int c; |
||
2080 |
1 |
const char *errstr; |
|
2081 |
|||
2082 |
1 |
gid = GID_MAX; |
|
2083 |
dupgid = 0; |
||
2084 |
✓✓ | 3 |
while ((c = getopt(argc, argv, "g:ov")) != -1) { |
2085 |
✓✗✗✗ |
1 |
switch(c) { |
2086 |
case 'g': |
||
2087 |
1 |
gid = strtonum(optarg, -1, GID_MAX, &errstr); |
|
2088 |
✓✗ | 1 |
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 |
1 |
argc -= optind; |
|
2103 |
1 |
argv += optind; |
|
2104 |
✗✓ | 1 |
if (argc != 1) { |
2105 |
usermgmt_usage("groupadd"); |
||
2106 |
} |
||
2107 |
|||
2108 |
✗✓ | 1 |
if (pledge("stdio rpath wpath cpath fattr flock getpw", NULL) == -1) |
2109 |
err(1, "pledge"); |
||
2110 |
|||
2111 |
1 |
checkeuid(); |
|
2112 |
✗✓ | 1 |
if (!valid_group(*argv)) { |
2113 |
errx(EXIT_FAILURE, "invalid group name `%s'", *argv); |
||
2114 |
} |
||
2115 |
✗✓✗✗ |
1 |
if (gid < 0 && !getnextgid(&gid, LowGid, HighGid)) { |
2116 |
errx(EXIT_FAILURE, "can't add group: can't get next gid"); |
||
2117 |
} |
||
2118 |
✓✗✗✓ |
2 |
if (!dupgid && getgrgid((gid_t) gid) != NULL) { |
2119 |
errx(EXIT_FAILURE, "can't add group: gid %d is a duplicate", gid); |
||
2120 |
} |
||
2121 |
1 |
openlog("groupadd", LOG_PID, LOG_USER); |
|
2122 |
✗✓ | 1 |
if (!creategid(*argv, gid, "")) { |
2123 |
errx(EXIT_FAILURE, "can't add group: problems with %s file", |
||
2124 |
_PATH_GROUP); |
||
2125 |
} |
||
2126 |
1 |
return EXIT_SUCCESS; |
|
2127 |
1 |
} |
|
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 |
} |
Generated by: GCOVR (Version 3.3) |