9 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
10 |
* |
* |
11 |
*/ |
*/ |
|
|
|
12 |
#include "libbb.h" |
#include "libbb.h" |
13 |
|
|
14 |
|
#if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID |
15 |
|
#error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config |
16 |
|
#endif |
17 |
|
|
18 |
|
#define OPT_GID (1 << 0) |
19 |
|
#define OPT_SYSTEM_ACCOUNT (1 << 1) |
20 |
|
|
21 |
|
/* We assume GID_T_MAX == INT_MAX */ |
22 |
static void xgroup_study(struct group *g) |
static void xgroup_study(struct group *g) |
23 |
{ |
{ |
24 |
|
unsigned max = INT_MAX; |
25 |
|
|
26 |
/* Make sure gr_name is unused */ |
/* Make sure gr_name is unused */ |
27 |
if (getgrnam(g->gr_name)) { |
if (getgrnam(g->gr_name)) { |
28 |
goto error; |
bb_error_msg_and_die("%s '%s' in use", "group", g->gr_name); |
29 |
|
/* these format strings are reused in adduser and addgroup */ |
30 |
} |
} |
31 |
|
|
32 |
|
/* if a specific gid is requested, the --system switch and */ |
33 |
|
/* min and max values are overridden, and the range of valid */ |
34 |
|
/* gid values is set to [0, INT_MAX] */ |
35 |
|
if (!(option_mask32 & OPT_GID)) { |
36 |
|
if (option_mask32 & OPT_SYSTEM_ACCOUNT) { |
37 |
|
g->gr_gid = CONFIG_FIRST_SYSTEM_ID; |
38 |
|
max = CONFIG_LAST_SYSTEM_ID; |
39 |
|
} else { |
40 |
|
g->gr_gid = CONFIG_LAST_SYSTEM_ID + 1; |
41 |
|
max = 64999; |
42 |
|
} |
43 |
|
} |
44 |
/* Check if the desired gid is free |
/* Check if the desired gid is free |
45 |
* or find the first free one */ |
* or find the first free one */ |
46 |
while (1) { |
while (1) { |
47 |
if (!getgrgid(g->gr_gid)) { |
if (!getgrgid(g->gr_gid)) { |
48 |
return; /* found free group: return */ |
return; /* found free group: return */ |
49 |
} |
} |
50 |
if (option_mask32) { |
if (option_mask32 & OPT_GID) { |
51 |
/* -g N, cannot pick gid other than N: error */ |
/* -g N, cannot pick gid other than N: error */ |
52 |
g->gr_name = itoa(g->gr_gid); |
bb_error_msg_and_die("%s '%s' in use", "gid", itoa(g->gr_gid)); |
53 |
goto error; |
/* this format strings is reused in adduser and addgroup */ |
54 |
} |
} |
55 |
g->gr_gid++; |
if (g->gr_gid == max) { |
|
if (g->gr_gid <= 0) { |
|
56 |
/* overflowed: error */ |
/* overflowed: error */ |
57 |
bb_error_msg_and_die("no gids left"); |
bb_error_msg_and_die("no %cids left", 'g'); |
58 |
|
/* this format string is reused in adduser and addgroup */ |
59 |
} |
} |
60 |
|
g->gr_gid++; |
61 |
} |
} |
|
|
|
|
error: |
|
|
/* exit */ |
|
|
bb_error_msg_and_die("group %s already exists", g->gr_name); |
|
62 |
} |
} |
63 |
|
|
64 |
/* append a new user to the passwd file */ |
/* append a new user to the passwd file */ |
65 |
static void new_group(char *group, gid_t gid) |
static void new_group(char *group, gid_t gid) |
66 |
{ |
{ |
|
FILE *file; |
|
67 |
struct group gr; |
struct group gr; |
68 |
|
char *p; |
69 |
|
|
70 |
/* make sure gid and group haven't already been allocated */ |
/* make sure gid and group haven't already been allocated */ |
71 |
gr.gr_gid = gid; |
gr.gr_gid = gid; |
73 |
xgroup_study(&gr); |
xgroup_study(&gr); |
74 |
|
|
75 |
/* add entry to group */ |
/* add entry to group */ |
76 |
file = xfopen(bb_path_group_file, "a"); |
p = xasprintf("x:%u:", (unsigned) gr.gr_gid); |
77 |
/* group:passwd:gid:userlist */ |
if (update_passwd(bb_path_group_file, group, p, NULL) < 0) |
78 |
fprintf(file, "%s:x:%u:\n", group, (unsigned)gr.gr_gid); |
exit(EXIT_FAILURE); |
79 |
if (ENABLE_FEATURE_CLEAN_UP) |
if (ENABLE_FEATURE_CLEAN_UP) |
80 |
fclose(file); |
free(p); |
81 |
#if ENABLE_FEATURE_SHADOWPASSWDS |
#if ENABLE_FEATURE_SHADOWPASSWDS |
82 |
file = fopen_or_warn(bb_path_gshadow_file, "a"); |
/* /etc/gshadow fields: |
83 |
if (file) { |
* 1. Group name. |
84 |
fprintf(file, "%s:!::\n", group); |
* 2. Encrypted password. |
85 |
if (ENABLE_FEATURE_CLEAN_UP) |
* If set, non-members of the group can join the group |
86 |
fclose(file); |
* by typing the password for that group using the newgrp command. |
87 |
} |
* If the value is of this field ! then no user is allowed |
88 |
|
* to access the group using the newgrp command. A value of !! |
89 |
|
* is treated the same as a value of ! only it indicates |
90 |
|
* that a password has never been set before. If the value is null, |
91 |
|
* only group members can log into the group. |
92 |
|
* 3. Group administrators (comma delimited list). |
93 |
|
* Group members listed here can add or remove group members |
94 |
|
* using the gpasswd command. |
95 |
|
* 4. Group members (comma delimited list). |
96 |
|
*/ |
97 |
|
/* Ignore errors: if file is missing we assume admin doesn't want it */ |
98 |
|
update_passwd(bb_path_gshadow_file, group, "!::", NULL); |
99 |
#endif |
#endif |
100 |
} |
} |
101 |
|
|
102 |
#if ENABLE_FEATURE_ADDUSER_TO_GROUP |
#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS |
103 |
static void add_user_to_group(char **args, |
static const char addgroup_longopts[] ALIGN1 = |
104 |
const char *path, |
"gid\0" Required_argument "g" |
105 |
FILE* FAST_FUNC (*fopen_func)(const char *fileName, const char *mode)) |
"system\0" No_argument "S" |
106 |
{ |
; |
|
char *line; |
|
|
int len = strlen(args[1]); |
|
|
llist_t *plist = NULL; |
|
|
FILE *group_file; |
|
|
|
|
|
group_file = fopen_func(path, "r"); |
|
|
|
|
|
if (!group_file) return; |
|
|
|
|
|
while ((line = xmalloc_fgetline(group_file)) != NULL) { |
|
|
/* Find the group */ |
|
|
if (!strncmp(line, args[1], len) |
|
|
&& line[len] == ':' |
|
|
) { |
|
|
/* Add the new user */ |
|
|
line = xasprintf("%s%s%s", line, |
|
|
last_char_is(line, ':') ? "" : ",", |
|
|
args[0]); |
|
|
} |
|
|
llist_add_to_end(&plist, line); |
|
|
} |
|
|
|
|
|
if (ENABLE_FEATURE_CLEAN_UP) { |
|
|
fclose(group_file); |
|
|
group_file = fopen_func(path, "w"); |
|
|
while ((line = llist_pop(&plist))) { |
|
|
if (group_file) |
|
|
fprintf(group_file, "%s\n", line); |
|
|
free(line); |
|
|
} |
|
|
if (group_file) |
|
|
fclose(group_file); |
|
|
} else { |
|
|
group_file = fopen_func(path, "w"); |
|
|
if (group_file) |
|
|
while ((line = llist_pop(&plist))) |
|
|
fprintf(group_file, "%s\n", line); |
|
|
} |
|
|
} |
|
107 |
#endif |
#endif |
108 |
|
|
109 |
/* |
/* |
116 |
int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
117 |
int addgroup_main(int argc UNUSED_PARAM, char **argv) |
int addgroup_main(int argc UNUSED_PARAM, char **argv) |
118 |
{ |
{ |
119 |
char *group; |
unsigned opts; |
120 |
gid_t gid = 0; |
unsigned gid = 0; |
121 |
|
|
122 |
/* need to be root */ |
/* need to be root */ |
123 |
if (geteuid()) { |
if (geteuid()) { |
124 |
bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); |
125 |
} |
} |
126 |
|
#if ENABLE_FEATURE_ADDGROUP_LONG_OPTIONS |
127 |
|
applet_long_options = addgroup_longopts; |
128 |
|
#endif |
129 |
/* Syntax: |
/* Syntax: |
130 |
* addgroup group |
* addgroup group |
131 |
* addgroup -g num group |
* addgroup -g num group |
132 |
* addgroup user group |
* addgroup user group |
133 |
* Check for min, max and missing args */ |
* Check for min, max and missing args */ |
134 |
opt_complementary = "-1:?2"; |
opt_complementary = "-1:?2:g+"; |
135 |
if (getopt32(argv, "g:", &group)) { |
opts = getopt32(argv, "g:S", &gid); |
|
gid = xatoul_range(group, 0, ((unsigned long)(gid_t)ULONG_MAX) >> 1); |
|
|
} |
|
136 |
/* move past the commandline options */ |
/* move past the commandline options */ |
137 |
argv += optind; |
argv += optind; |
138 |
//argc -= optind; |
//argc -= optind; |
141 |
if (argv[1]) { |
if (argv[1]) { |
142 |
struct group *gr; |
struct group *gr; |
143 |
|
|
144 |
if (option_mask32) { |
if (opts & OPT_GID) { |
145 |
/* -g was there, but "addgroup -g num user group" |
/* -g was there, but "addgroup -g num user group" |
146 |
* is a no-no */ |
* is a no-no */ |
147 |
bb_show_usage(); |
bb_show_usage(); |
149 |
|
|
150 |
/* check if group and user exist */ |
/* check if group and user exist */ |
151 |
xuname2uid(argv[0]); /* unknown user: exit */ |
xuname2uid(argv[0]); /* unknown user: exit */ |
152 |
xgroup2gid(argv[1]); /* unknown group: exit */ |
gr = xgetgrnam(argv[1]); /* unknown group: exit */ |
153 |
/* check if user is already in this group */ |
/* check if user is already in this group */ |
|
gr = getgrnam(argv[1]); |
|
154 |
for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) { |
for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) { |
155 |
if (!strcmp(argv[0], *(gr->gr_mem))) { |
if (!strcmp(argv[0], *(gr->gr_mem))) { |
156 |
/* user is already in group: do nothing */ |
/* user is already in group: do nothing */ |
157 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |
158 |
} |
} |
159 |
} |
} |
160 |
add_user_to_group(argv, bb_path_group_file, xfopen); |
if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) { |
161 |
#if ENABLE_FEATURE_SHADOWPASSWDS |
return EXIT_FAILURE; |
162 |
add_user_to_group(argv, bb_path_gshadow_file, fopen_or_warn); |
} |
163 |
#endif |
# if ENABLE_FEATURE_SHADOWPASSWDS |
164 |
|
update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]); |
165 |
|
# endif |
166 |
} else |
} else |
167 |
#endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */ |
#endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */ |
168 |
{ |
{ |