Initial commit for Tizen
[profile/extras/shadow-utils.git] / lib / groupio.c
1 /*
2  * Copyright (c) 1990 - 1994, Julianne Frances Haugh
3  * Copyright (c) 1996 - 2000, Marek Michałkiewicz
4  * Copyright (c) 2001       , Michał Moskal
5  * Copyright (c) 2005       , Tomasz Kłoczko
6  * Copyright (c) 2007 - 2009, Nicolas François
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. The name of the copyright holders or contributors may not be used to
18  *    endorse or promote products derived from this software without
19  *    specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24  * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT
25  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <config.h>
35
36 #ident "$Id: groupio.c 2822 2009-04-27 20:18:00Z nekral-guest $"
37
38 #include <assert.h>
39 #include <stdio.h>
40
41 #include "prototypes.h"
42 #include "defines.h"
43 #include "commonio.h"
44 #include "getdef.h"
45 #include "groupio.h"
46
47 static /*@null@*/struct commonio_entry *merge_group_entries (
48         /*@null@*/ /*@returned@*/struct commonio_entry *gr1,
49         /*@null@*/struct commonio_entry *gr2);
50 static int split_groups (unsigned int max_members);
51 static int group_open_hook (void);
52
53 static /*@null@*/ /*@only@*/void *group_dup (const void *ent)
54 {
55         const struct group *gr = ent;
56
57         return __gr_dup (gr);
58 }
59
60 static void group_free (/*@out@*/ /*@only@*/void *ent)
61 {
62         struct group *gr = ent;
63
64         gr_free (gr);
65 }
66
67 static const char *group_getname (const void *ent)
68 {
69         const struct group *gr = ent;
70
71         return gr->gr_name;
72 }
73
74 static void *group_parse (const char *line)
75 {
76         return (void *) sgetgrent (line);
77 }
78
79 static int group_put (const void *ent, FILE * file)
80 {
81         const struct group *gr = ent;
82
83         return (putgrent (gr, file) == -1) ? -1 : 0;
84 }
85
86 static int group_close_hook (void)
87 {
88         unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0);
89
90         if (0 == max_members) {
91                 return 1;
92         }
93
94         return split_groups (max_members);
95 }
96
97 static struct commonio_ops group_ops = {
98         group_dup,
99         group_free,
100         group_getname,
101         group_parse,
102         group_put,
103         fgetsx,
104         fputsx,
105         group_open_hook,
106         group_close_hook
107 };
108
109 static /*@owned@*/struct commonio_db group_db = {
110         GROUP_FILE,             /* filename */
111         &group_ops,             /* ops */
112         NULL,                   /* fp */
113 #ifdef WITH_SELINUX
114         NULL,                   /* scontext */
115 #endif
116         NULL,                   /* head */
117         NULL,                   /* tail */
118         NULL,                   /* cursor */
119         false,                  /* changed */
120         false,                  /* isopen */
121         false,                  /* locked */
122         false                   /* readonly */
123 };
124
125 int gr_setdbname (const char *filename)
126 {
127         return commonio_setname (&group_db, filename);
128 }
129
130 /*@observer@*/const char *gr_dbname (void)
131 {
132         return group_db.filename;
133 }
134
135 int gr_lock (void)
136 {
137         return commonio_lock (&group_db);
138 }
139
140 int gr_open (int mode)
141 {
142         return commonio_open (&group_db, mode);
143 }
144
145 /*@observer@*/ /*@null@*/const struct group *gr_locate (const char *name)
146 {
147         return commonio_locate (&group_db, name);
148 }
149
150 /*@observer@*/ /*@null@*/const struct group *gr_locate_gid (gid_t gid)
151 {
152         const struct group *grp;
153
154         gr_rewind ();
155         while (   ((grp = gr_next ()) != NULL)
156                && (grp->gr_gid != gid)) {
157         }
158
159         return grp;
160 }
161
162 int gr_update (const struct group *gr)
163 {
164         return commonio_update (&group_db, (const void *) gr);
165 }
166
167 int gr_remove (const char *name)
168 {
169         return commonio_remove (&group_db, name);
170 }
171
172 int gr_rewind (void)
173 {
174         return commonio_rewind (&group_db);
175 }
176
177 /*@observer@*/ /*@null@*/const struct group *gr_next (void)
178 {
179         return commonio_next (&group_db);
180 }
181
182 int gr_close (void)
183 {
184         return commonio_close (&group_db);
185 }
186
187 int gr_unlock (void)
188 {
189         return commonio_unlock (&group_db);
190 }
191
192 void __gr_set_changed (void)
193 {
194         group_db.changed = true;
195 }
196
197 /*@dependent@*/ /*@null@*/struct commonio_entry *__gr_get_head (void)
198 {
199         return group_db.head;
200 }
201
202 /*@observer@*/const struct commonio_db *__gr_get_db (void)
203 {
204         return &group_db;
205 }
206
207 void __gr_del_entry (const struct commonio_entry *ent)
208 {
209         commonio_del_entry (&group_db, ent);
210 }
211
212 static int gr_cmp (const void *p1, const void *p2)
213 {
214         gid_t u1, u2;
215
216         if ((*(struct commonio_entry **) p1)->eptr == NULL) {
217                 return 1;
218         }
219         if ((*(struct commonio_entry **) p2)->eptr == NULL) {
220                 return -1;
221         }
222
223         u1 = ((struct group *) (*(struct commonio_entry **) p1)->eptr)->gr_gid;
224         u2 = ((struct group *) (*(struct commonio_entry **) p2)->eptr)->gr_gid;
225
226         if (u1 < u2) {
227                 return -1;
228         } else if (u1 > u2) {
229                 return 1;
230         } else {
231                 return 0;
232         }
233 }
234
235 /* Sort entries by GID */
236 int gr_sort ()
237 {
238         return commonio_sort (&group_db, gr_cmp);
239 }
240
241 static int group_open_hook (void)
242 {
243         unsigned int max_members = getdef_unum("MAX_MEMBERS_PER_GROUP", 0);
244         struct commonio_entry *gr1, *gr2;
245
246         if (0 == max_members) {
247                 return 1;
248         }
249
250         for (gr1 = group_db.head; NULL != gr1; gr1 = gr1->next) {
251                 for (gr2 = gr1->next; NULL != gr2; gr2 = gr2->next) {
252                         struct group *g1 = (struct group *)gr1->eptr;
253                         struct group *g2 = (struct group *)gr2->eptr;
254                         if (NULL != g1 &&
255                             NULL != g2 &&
256                             0 == strcmp (g1->gr_name, g2->gr_name) &&
257                             0 == strcmp (g1->gr_passwd, g2->gr_passwd) &&
258                             g1->gr_gid == g2->gr_gid) {
259                                 /* Both group entries refer to the same
260                                  * group. It is a split group. Merge the
261                                  * members. */
262                                 gr1 = merge_group_entries (gr1, gr2);
263                                 if (NULL == gr1)
264                                         return 0;
265                                 /* Unlink gr2 */
266                                 if (NULL != gr2->next) {
267                                         gr2->next->prev = gr2->prev;
268                                 }
269                                 /* gr2 does not start with head */
270                                 assert (NULL != gr2->prev);
271                                 gr2->prev->next = gr2->next;
272                         }
273                 }
274                 assert (NULL != gr1);
275         }
276
277         return 1;
278 }
279
280 /*
281  * Merge the list of members of the two group entries.
282  *
283  * The commonio_entry arguments shall be group entries.
284  *
285  * You should not merge the members of two groups if they don't have the
286  * same name, password and gid.
287  *
288  * It merge the members of the second entry in the first one, and return
289  * the modified first entry on success, or NULL on failure (with errno
290  * set).
291  */
292 static /*@null@*/struct commonio_entry *merge_group_entries (
293         /*@null@*/ /*@returned@*/struct commonio_entry *gr1,
294         /*@null@*/struct commonio_entry *gr2)
295 {
296         struct group *gptr1;
297         struct group *gptr2;
298         char **new_members;
299         size_t members = 0;
300         char *new_line;
301         size_t new_line_len, i;
302         if (NULL == gr2 || NULL == gr1) {
303                 errno = EINVAL;
304                 return NULL;
305         }
306
307         gptr1 = (struct group *)gr1->eptr;
308         gptr2 = (struct group *)gr2->eptr;
309         if (NULL == gptr2 || NULL == gptr1) {
310                 errno = EINVAL;
311                 return NULL;
312         }
313
314         /* Concatenate the 2 lines */
315         new_line_len = strlen (gr1->line) + strlen (gr2->line) +1;
316         new_line = (char *)malloc ((new_line_len + 1) * sizeof(char*));
317         if (NULL == new_line) {
318                 errno = ENOMEM;
319                 return NULL;
320         }
321         snprintf(new_line, new_line_len, "%s\n%s", gr1->line, gr2->line);
322         new_line[new_line_len] = '\0';
323
324         /* Concatenate the 2 list of members */
325         for (i=0; NULL != gptr1->gr_mem[i]; i++);
326         members += i;
327         for (i=0; NULL != gptr2->gr_mem[i]; i++) {
328                 char **pmember = gptr1->gr_mem;
329                 while (NULL != *pmember) {
330                         if (0 == strcmp(*pmember, gptr2->gr_mem[i])) {
331                                 break;
332                         }
333                         pmember++;
334                 }
335                 if (NULL == *pmember) {
336                         members++;
337                 }
338         }
339         new_members = (char **)malloc ( (members+1) * sizeof(char*) );
340         if (NULL == new_members) {
341                 free (new_line);
342                 errno = ENOMEM;
343                 return NULL;
344         }
345         for (i=0; NULL != gptr1->gr_mem[i]; i++) {
346                 new_members[i] = gptr1->gr_mem[i];
347         }
348         members = i;
349         for (i=0; NULL != gptr2->gr_mem[i]; i++) {
350                 char **pmember = new_members;
351                 while (NULL != *pmember) {
352                         if (0 == strcmp(*pmember, gptr2->gr_mem[i])) {
353                                 break;
354                         }
355                         pmember++;
356                 }
357                 if (NULL == *pmember) {
358                         new_members[members] = gptr2->gr_mem[i];
359                         members++;
360                         new_members[members] = NULL;
361                 }
362         }
363
364         gr1->line = new_line;
365         gptr1->gr_mem = new_members;
366
367         return gr1;
368 }
369
370 /*
371  * Scan the group database and split the groups which have more members
372  * than specified, if this is the result from a current change.
373  *
374  * Return 0 on failure (errno set) and 1 on success.
375  */
376 static int split_groups (unsigned int max_members)
377 {
378         struct commonio_entry *gr;
379
380         for (gr = group_db.head; NULL != gr; gr = gr->next) {
381                 struct group *gptr = (struct group *)gr->eptr;
382                 struct commonio_entry *new;
383                 struct group *new_gptr;
384                 unsigned int members = 0;
385
386                 /* Check if this group must be split */
387                 if (!gr->changed)
388                         continue;
389                 if (NULL == gptr)
390                         continue;
391                 for (members = 0; NULL != gptr->gr_mem[members]; members++);
392                 if (members <= max_members)
393                         continue;
394
395                 new = (struct commonio_entry *) malloc (sizeof *new);
396                 if (NULL == new) {
397                         errno = ENOMEM;
398                         return 0;
399                 }
400                 new->eptr = group_dup(gr->eptr);
401                 if (NULL == new->eptr) {
402                         free (new);
403                         errno = ENOMEM;
404                         return 0;
405                 }
406                 new_gptr = (struct group *)new->eptr;
407                 new->line = NULL;
408                 new->changed = true;
409
410                 /* Enforce the maximum number of members on gptr */
411                 gptr->gr_mem[max_members] = NULL;
412                 /* The number of members in new_gptr will be check later */
413                 new_gptr->gr_mem = &new_gptr->gr_mem[max_members];
414
415                 /* insert the new entry in the list */
416                 new->prev = gr;
417                 new->next = gr->next;
418                 gr->next = new;
419         }
420
421         return 1;
422 }
423