randomconfig fixes
[oweals/busybox.git] / loginutils / addgroup.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * addgroup - add groups to /etc/group and /etc/gshadow
4  *
5  * Copyright (C) 1999 by Lineo, inc. and John Beppu
6  * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7  * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
8  *
9  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10  */
11 //config:config ADDGROUP
12 //config:       bool "addgroup (8.6 kb)"
13 //config:       default y
14 //config:       select LONG_OPTS
15 //config:       help
16 //config:       Utility for creating a new group account.
17 //config:
18 //config:config FEATURE_ADDUSER_TO_GROUP
19 //config:       bool "Support adding users to groups"
20 //config:       default y
21 //config:       depends on ADDGROUP
22 //config:       help
23 //config:       If called with two non-option arguments,
24 //config:       addgroup will add an existing user to an
25 //config:       existing group.
26
27 //applet:IF_ADDGROUP(APPLET_NOEXEC(addgroup, addgroup, BB_DIR_USR_SBIN, BB_SUID_DROP, addgroup))
28
29 //kbuild:lib-$(CONFIG_ADDGROUP) += addgroup.o
30
31 //usage:#define addgroup_trivial_usage
32 //usage:       "[-g GID] [-S] " IF_FEATURE_ADDUSER_TO_GROUP("[USER] ") "GROUP"
33 //usage:#define addgroup_full_usage "\n\n"
34 //usage:       "Add a group" IF_FEATURE_ADDUSER_TO_GROUP(" or add a user to a group") "\n"
35 //usage:     "\n        -g GID  Group id"
36 //usage:     "\n        -S      Create a system group"
37
38 #include "libbb.h"
39
40 #if CONFIG_LAST_SYSTEM_ID < CONFIG_FIRST_SYSTEM_ID
41 #error Bad LAST_SYSTEM_ID or FIRST_SYSTEM_ID in .config
42 #endif
43 #if CONFIG_LAST_ID < CONFIG_LAST_SYSTEM_ID
44 #error Bad LAST_ID or LAST_SYSTEM_ID in .config
45 #endif
46
47 #define OPT_GID                       (1 << 0)
48 #define OPT_SYSTEM_ACCOUNT            (1 << 1)
49
50 static void xgroup_study(struct group *g)
51 {
52         unsigned max = CONFIG_LAST_ID;
53
54         /* Make sure gr_name is unused */
55         if (getgrnam(g->gr_name)) {
56                 bb_error_msg_and_die("%s '%s' in use", "group", g->gr_name);
57                 /* these format strings are reused in adduser and addgroup */
58         }
59
60         /* if a specific gid is requested, the --system switch and */
61         /* min and max values are overridden, and the range of valid */
62         /* gid values is set to [0, INT_MAX] */
63         if (!(option_mask32 & OPT_GID)) {
64                 if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
65                         g->gr_gid = CONFIG_FIRST_SYSTEM_ID;
66                         max = CONFIG_LAST_SYSTEM_ID;
67                 } else {
68                         g->gr_gid = CONFIG_LAST_SYSTEM_ID + 1;
69                 }
70         }
71         /* Check if the desired gid is free
72          * or find the first free one */
73         while (1) {
74                 if (!getgrgid(g->gr_gid)) {
75                         return; /* found free group: return */
76                 }
77                 if (option_mask32 & OPT_GID) {
78                         /* -g N, cannot pick gid other than N: error */
79                         bb_error_msg_and_die("%s '%s' in use", "gid", itoa(g->gr_gid));
80                         /* this format strings is reused in adduser and addgroup */
81                 }
82                 if (g->gr_gid == max) {
83                         /* overflowed: error */
84                         bb_error_msg_and_die("no %cids left", 'g');
85                         /* this format string is reused in adduser and addgroup */
86                 }
87                 g->gr_gid++;
88         }
89 }
90
91 /* append a new user to the passwd file */
92 static void new_group(char *group, gid_t gid)
93 {
94         struct group gr;
95         char *p;
96
97         /* make sure gid and group haven't already been allocated */
98         gr.gr_gid = gid;
99         gr.gr_name = group;
100         xgroup_study(&gr);
101
102         /* add entry to group */
103         p = xasprintf("x:%u:", (unsigned) gr.gr_gid);
104         if (update_passwd(bb_path_group_file, group, p, NULL) < 0)
105                 exit(EXIT_FAILURE);
106         if (ENABLE_FEATURE_CLEAN_UP)
107                 free(p);
108 #if ENABLE_FEATURE_SHADOWPASSWDS
109         /* /etc/gshadow fields:
110          * 1. Group name.
111          * 2. Encrypted password.
112          *    If set, non-members of the group can join the group
113          *    by typing the password for that group using the newgrp command.
114          *    If the value is of this field ! then no user is allowed
115          *    to access the group using the newgrp command. A value of !!
116          *    is treated the same as a value of ! only it indicates
117          *    that a password has never been set before. If the value is null,
118          *    only group members can log into the group.
119          * 3. Group administrators (comma delimited list).
120          *    Group members listed here can add or remove group members
121          *    using the gpasswd command.
122          * 4. Group members (comma delimited list).
123          */
124         /* Ignore errors: if file is missing we assume admin doesn't want it */
125         update_passwd(bb_path_gshadow_file, group, "!::", NULL);
126 #endif
127 }
128
129 //FIXME: upstream addgroup has no short options! NOT COMPATIBLE!
130 static const char addgroup_longopts[] ALIGN1 =
131                 "gid\0"                 Required_argument "g"
132                 "system\0"              No_argument       "S"
133                 ;
134
135 /*
136  * addgroup will take a login_name as its first parameter.
137  *
138  * gid can be customized via command-line parameters.
139  * If called with two non-option arguments, addgroup
140  * will add an existing user to an existing group.
141  */
142 int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
143 int addgroup_main(int argc UNUSED_PARAM, char **argv)
144 {
145 #if ENABLE_FEATURE_ADDUSER_TO_GROUP
146         unsigned opts;
147 #endif
148         const char *gid = "0";
149
150         /* need to be root */
151         if (geteuid()) {
152                 bb_simple_error_msg_and_die(bb_msg_perm_denied_are_you_root);
153         }
154         /* Syntax:
155          *  addgroup group
156          *  addgroup --gid num group
157          *  addgroup user group
158          * Check for min, max and missing args */
159 #if ENABLE_FEATURE_ADDUSER_TO_GROUP
160         opts =
161 #endif
162         getopt32long(argv, "^" "g:S" "\0" "-1:?2", addgroup_longopts,
163                                 &gid
164         );
165         /* move past the commandline options */
166         argv += optind;
167         //argc -= optind;
168
169 #if ENABLE_FEATURE_ADDUSER_TO_GROUP
170         if (argv[1]) {
171                 struct group *gr;
172
173                 if (opts & OPT_GID) {
174                         /* -g was there, but "addgroup -g num user group"
175                          * is a no-no */
176                         bb_show_usage();
177                 }
178
179                 /* check if group and user exist */
180                 xuname2uid(argv[0]); /* unknown user: exit */
181                 gr = xgetgrnam(argv[1]); /* unknown group: exit */
182                 /* check if user is already in this group */
183                 for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) {
184                         if (strcmp(argv[0], *(gr->gr_mem)) == 0) {
185                                 /* user is already in group: do nothing */
186                                 return EXIT_SUCCESS;
187                         }
188                 }
189                 if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) {
190                         return EXIT_FAILURE;
191                 }
192 # if ENABLE_FEATURE_SHADOWPASSWDS
193                 update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]);
194 # endif
195         } else
196 #endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */
197         {
198                 die_if_bad_username(argv[0]);
199                 new_group(argv[0], xatou_range(gid, 0, CONFIG_LAST_ID));
200         }
201         /* Reached only on success */
202         return EXIT_SUCCESS;
203 }