]> git.cameronkatri.com Git - pw-darwin.git/blob - pw/pw_group.c
pw(8) -- a backend utility to manage the user and group databases.
[pw-darwin.git] / pw / pw_group.c
1 /*-
2 * Copyright (c) 1996 by David L. Nugent <davidn@blaze.net.au>.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer as
10 * the first lines of this file unmodified.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by David L. Nugent.
17 * 4. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE DAVID L. NUGENT ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id$
33 */
34
35 #include <unistd.h>
36 #include <ctype.h>
37 #include <termios.h>
38
39 #include "pw.h"
40 #include "bitmap.h"
41
42
43 static int print_group(struct group * grp, int pretty);
44 static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
45
46 int
47 pw_group(struct userconf * cnf, int mode, struct cargs * args)
48 {
49 struct carg *a_name = getarg(args, 'n');
50 struct carg *a_gid = getarg(args, 'g');
51 struct carg *arg;
52 struct group *grp = NULL;
53 char *members[_UC_MAXGROUPS];
54
55 static struct group fakegroup =
56 {
57 "nogroup",
58 "*",
59 -1,
60 NULL
61 };
62
63 if (mode == M_PRINT && getarg(args, 'a')) {
64 int pretty = getarg(args, 'p') != NULL;
65
66 setgrent();
67 while ((grp = getgrent()) != NULL)
68 print_group(grp, pretty);
69 endgrent();
70 return X_ALLOK;
71 }
72 if (a_gid == NULL) {
73 if (a_name == NULL)
74 cmderr(X_CMDERR, "group name or id required\n");
75
76 if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) {
77 (a_gid = a_name)->ch = 'g';
78 a_name = NULL;
79 }
80 }
81 grp = (a_name != NULL) ? getgrnam(a_name->val) : getgrgid((gid_t) atoi(a_gid->val));
82
83 if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
84 if (a_name == NULL && grp == NULL) /* Try harder */
85 grp = getgrgid(atoi(a_gid->val));
86
87 if (grp == NULL) {
88 if (mode == M_PRINT && getarg(args, 'F')) {
89 fakegroup.gr_name = a_name ? a_name->val : "nogroup";
90 fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
91 return print_group(&fakegroup, getarg(args, 'p') != NULL);
92 }
93 cmderr(X_CMDERR, "unknown group `%s'\n", a_name ? a_name->val : a_gid->val);
94 }
95 if (a_name == NULL) /* Needed later */
96 a_name = addarg(args, 'n', grp->gr_name);
97
98 /*
99 * Handle deletions now
100 */
101 if (mode == M_DELETE) {
102 gid_t gid = grp->gr_gid;
103
104 if (delgrent(grp) == -1)
105 cmderr(X_NOUPDATE, "Error updating group file: %s\n", strerror(errno));
106 pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
107 return X_ALLOK;
108 } else if (mode == M_PRINT)
109 return print_group(grp, getarg(args, 'p') != NULL);
110
111 if (a_gid)
112 grp->gr_gid = (gid_t) atoi(a_gid->val);
113
114 if ((arg = getarg(args, 'l')) != NULL)
115 grp->gr_name = arg->val;
116 } else {
117 if (a_name == NULL) /* Required */
118 cmderr(X_CMDERR, "group name required\n");
119 else if (grp != NULL) /* Exists */
120 cmderr(X_EXISTS, "group name `%s' already exists\n", a_name->val);
121
122 memset(members, 0, sizeof members);
123 grp = &fakegroup;
124 grp->gr_name = a_name->val;
125 grp->gr_passwd = "*";
126 grp->gr_gid = gr_gidpolicy(cnf, args);
127 grp->gr_mem = members;
128 }
129
130 /*
131 * This allows us to set a group password Group passwords is an
132 * antique idea, rarely used and insecure (no secure database) Should
133 * be discouraged, but it is apparently still supported by some
134 * software.
135 */
136
137 if ((arg = getarg(args, 'h')) != NULL) {
138 if (strcmp(arg->val, "-") == 0)
139 grp->gr_passwd = "*"; /* No access */
140 else {
141 int fd = atoi(arg->val);
142 int b;
143 int istty = isatty(fd);
144 struct termios t;
145 char *p, line[256];
146
147 if (istty) {
148 if (tcgetattr(fd, &t) == -1)
149 istty = 0;
150 else {
151 struct termios n = t;
152
153 /* Disable echo */
154 n.c_lflag &= ~(ECHO);
155 tcsetattr(fd, TCSANOW, &n);
156 printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
157 fflush(stdout);
158 }
159 }
160 b = read(fd, line, sizeof(line) - 1);
161 if (istty) { /* Restore state */
162 tcsetattr(fd, TCSANOW, &t);
163 fputc('\n', stdout);
164 fflush(stdout);
165 }
166 if (b < 0) {
167 perror("-h file descriptor");
168 return X_CMDERR;
169 }
170 line[b] = '\0';
171 if ((p = strpbrk(line, " \t\r\n")) != NULL)
172 *p = '\0';
173 if (!*line)
174 cmderr(X_CMDERR, "empty password read on file descriptor %d\n", fd);
175 grp->gr_passwd = pw_pwcrypt(line);
176 }
177 }
178 if ((mode == M_ADD && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) {
179 perror("group update");
180 return X_NOUPDATE;
181 }
182 /* grp may have been invalidated */
183 if ((grp = getgrnam(a_name->val)) == NULL)
184 cmderr(X_NOTFOUND, "group disappeared during update\n");
185
186 pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
187
188 return X_ALLOK;
189 }
190
191
192 static gid_t
193 gr_gidpolicy(struct userconf * cnf, struct cargs * args)
194 {
195 struct group *grp;
196 gid_t gid = (gid_t) - 1;
197 struct carg *a_gid = getarg(args, 'g');
198
199 /*
200 * Check the given gid, if any
201 */
202 if (a_gid != NULL) {
203 gid = (gid_t) atol(a_gid->val);
204
205 if ((grp = getgrgid(gid)) != NULL && getarg(args, 'o') == NULL)
206 cmderr(X_EXISTS, "gid `%ld' has already been allocated\n", (long) grp->gr_gid);
207 } else {
208 struct bitmap bm;
209
210 /*
211 * We need to allocate the next available gid under one of
212 * two policies a) Grab the first unused gid b) Grab the
213 * highest possible unused gid
214 */
215 if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */
216 cnf->min_gid = 1000;
217 cnf->max_gid = 32000;
218 }
219 bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
220
221 /*
222 * Now, let's fill the bitmap from the password file
223 */
224 setgrent();
225 while ((grp = getgrent()) != NULL)
226 if (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) cnf->max_gid)
227 bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
228 endgrent();
229
230 /*
231 * Then apply the policy, with fallback to reuse if necessary
232 */
233 if (cnf->reuse_gids)
234 gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
235 else {
236 gid = (gid_t) (bm_lastset(&bm) + 1);
237 if (!bm_isset(&bm, gid))
238 gid += cnf->min_gid;
239 else
240 gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
241 }
242
243 /*
244 * Another sanity check
245 */
246 if (gid < cnf->min_gid || gid > cnf->max_gid)
247 cmderr(X_EXISTS, "unable to allocate a new gid - range fully used\n");
248 bm_dealloc(&bm);
249 }
250 return gid;
251 }
252
253
254 static int
255 print_group(struct group * grp, int pretty)
256 {
257 if (!pretty) {
258 char buf[_UC_MAXLINE];
259
260 fmtgrent(buf, grp);
261 fputs(buf, stdout);
262 } else {
263 int i;
264
265 printf("Group Name : %-10s #%lu\n"
266 " Members : ",
267 grp->gr_name, (long) grp->gr_gid);
268 for (i = 0; i < _UC_MAXGROUPS && grp->gr_mem[i]; i++)
269 printf("%s%s", i ? "," : "", grp->gr_mem[i]);
270 fputs("\n\n", stdout);
271 }
272 return X_ALLOK;
273 }