*: unify concurrent-safe update of /etc/{passwd,group,[g]shadow}
[platform/upstream/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 tarball for details.
10  *
11  */
12 #include "libbb.h"
13
14 static void xgroup_study(struct group *g)
15 {
16         /* Make sure gr_name is unused */
17         if (getgrnam(g->gr_name)) {
18                 goto error;
19         }
20
21         /* Check if the desired gid is free
22          * or find the first free one */
23         while (1) {
24                 if (!getgrgid(g->gr_gid)) {
25                         return; /* found free group: return */
26                 }
27                 if (option_mask32) {
28                         /* -g N, cannot pick gid other than N: error */
29                         g->gr_name = itoa(g->gr_gid);
30                         goto error;
31                 }
32                 g->gr_gid++;
33                 if (g->gr_gid <= 0) {
34                         /* overflowed: error */
35                         bb_error_msg_and_die("no gids left");
36                 }
37         }
38
39  error:
40         /* exit */
41         bb_error_msg_and_die("group %s already exists", g->gr_name);
42 }
43
44 /* append a new user to the passwd file */
45 static void new_group(char *group, gid_t gid)
46 {
47         struct group gr;
48         char *p;
49
50         /* make sure gid and group haven't already been allocated */
51         gr.gr_gid = gid;
52         gr.gr_name = group;
53         xgroup_study(&gr);
54
55         /* add entry to group */
56         p = xasprintf("x:%u:", gr.gr_gid);
57         if (update_passwd(bb_path_group_file, group, p, NULL) < 0)
58                 exit(EXIT_FAILURE);
59         if (ENABLE_FEATURE_CLEAN_UP)
60                 free(p);
61 #if ENABLE_FEATURE_SHADOWPASSWDS
62         /* Ignore errors: if file is missing we suppose admin doesn't want it */
63         update_passwd(bb_path_gshadow_file, group, "!::", NULL);
64 #endif
65 }
66
67 /*
68  * addgroup will take a login_name as its first parameter.
69  *
70  * gid can be customized via command-line parameters.
71  * If called with two non-option arguments, addgroup
72  * will add an existing user to an existing group.
73  */
74 int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
75 int addgroup_main(int argc UNUSED_PARAM, char **argv)
76 {
77         char *group;
78         gid_t gid = 0;
79
80         /* need to be root */
81         if (geteuid()) {
82                 bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
83         }
84
85         /* Syntax:
86          *  addgroup group
87          *  addgroup -g num group
88          *  addgroup user group
89          * Check for min, max and missing args */
90         opt_complementary = "-1:?2";
91         if (getopt32(argv, "g:", &group)) {
92                 gid = xatoul_range(group, 0, ((unsigned long)(gid_t)ULONG_MAX) >> 1);
93         }
94         /* move past the commandline options */
95         argv += optind;
96         //argc -= optind;
97
98 #if ENABLE_FEATURE_ADDUSER_TO_GROUP
99         if (argv[1]) {
100                 struct group *gr;
101
102                 if (option_mask32) {
103                         /* -g was there, but "addgroup -g num user group"
104                          * is a no-no */
105                         bb_show_usage();
106                 }
107
108                 /* check if group and user exist */
109                 xuname2uid(argv[0]); /* unknown user: exit */
110                 gr = xgetgrnam(argv[1]); /* unknown group: exit */
111                 /* check if user is already in this group */
112                 for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) {
113                         if (!strcmp(argv[0], *(gr->gr_mem))) {
114                                 /* user is already in group: do nothing */
115                                 return EXIT_SUCCESS;
116                         }
117                 }
118                 if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) {
119                         return EXIT_FAILURE;
120                 }
121 # if ENABLE_FEATURE_SHADOWPASSWDS
122                 update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]);
123 # endif
124         } else
125 #endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */
126         {
127                 die_if_bad_username(argv[0]);
128                 new_group(argv[0], gid);
129
130         }
131         /* Reached only on success */
132         return EXIT_SUCCESS;
133 }