1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <sys/stat.h>
35 #include <grp.h>
36 #include <unistd.h>
37 #include <userdefs.h>
38 #include <errno.h>
39 #include <limits.h>
40 #include "users.h"
41 #include "messages.h"
42
43 #define MYBUFSIZE (LINE_MAX)
44 /* Corresponds to MYBUFSIZE in grpck.c, BUFCONST in nss_dbdefs.c */
45
46 int
edit_group(char * login,char * new_login,gid_t gids[],int overwrite)47 edit_group(char *login, char *new_login, gid_t gids[], int overwrite)
48 {
49 char **memptr;
50 char t_name[] = "/etc/gtmp.XXXXXX";
51 int fd;
52 FILE *e_fptr, *t_fptr;
53 struct group *g_ptr; /* group structure from fgetgrent */
54 int i;
55 int modified = 0;
56 struct stat sbuf;
57
58 int bufsize, g_length, sav_errno;
59 long g_curr = 0L;
60 char *g_string, *new_g_string, *gstr_off;
61
62 if ((e_fptr = fopen(GROUP, "r")) == NULL)
63 return (EX_UPDATE);
64
65 if (fstat(fileno(e_fptr), &sbuf) != 0) {
66 (void) fclose(e_fptr);
67 return (EX_UPDATE);
68 }
69
70 if ((fd = mkstemp(t_name)) == -1) {
71 (void) fclose(e_fptr);
72 return (EX_UPDATE);
73 }
74
75 if ((t_fptr = fdopen(fd, "w")) == NULL) {
76 (void) close(fd);
77 (void) unlink(t_name);
78 (void) fclose(e_fptr);
79 return (EX_UPDATE);
80 }
81
82 /*
83 * Get ownership and permissions correct
84 */
85
86 if (fchmod(fd, sbuf.st_mode) != 0 ||
87 fchown(fd, sbuf.st_uid, sbuf.st_gid) != 0) {
88 (void) fclose(t_fptr);
89 (void) fclose(e_fptr);
90 (void) unlink(t_name);
91 return (EX_UPDATE);
92 }
93
94 g_curr = ftell(e_fptr);
95
96 /* Make TMP file look like we want GROUP file to look */
97
98 bufsize = MYBUFSIZE;
99 if ((g_string = malloc(bufsize)) == NULL) {
100 (void) fclose(t_fptr);
101 (void) fclose(e_fptr);
102 (void) unlink(t_name);
103 return (EX_UPDATE);
104 }
105 /*
106 * bufsize contains the size of the currently allocated buffer
107 * buffer size, which is initially MYBUFSIZE but when a line
108 * greater than MYBUFSIZE is encountered then bufsize gets increased
109 * by MYBUFSIZE.
110 * g_string always points to the beginning of the buffer (even after
111 * realloc()).
112 * gstr_off = g_string + MYBUFSIZE * (n), where n >= 0.
113 */
114 while (!feof(e_fptr) && !ferror(e_fptr)) {
115 g_length = 0;
116 gstr_off = g_string;
117 while (fgets(gstr_off, (bufsize - g_length), e_fptr) != NULL) {
118 g_length += strlen(gstr_off);
119 if (g_string[g_length - 1] == '\n' || feof(e_fptr))
120 break;
121 new_g_string = realloc(g_string, (bufsize + MYBUFSIZE));
122 if (new_g_string == NULL) {
123 free(g_string);
124 (void) fclose(t_fptr);
125 (void) fclose(e_fptr);
126 (void) unlink(t_name);
127 return (EX_UPDATE);
128 }
129 bufsize += MYBUFSIZE;
130 g_string = new_g_string;
131 gstr_off = g_string + g_length;
132 }
133 if (g_length == 0) {
134 continue;
135 }
136
137 /* While there is another group string */
138
139 (void) fseek(e_fptr, g_curr, SEEK_SET);
140 errno = 0;
141 g_ptr = fgetgrent(e_fptr);
142 sav_errno = errno;
143 g_curr = ftell(e_fptr);
144
145 if (g_ptr == NULL) {
146 /* tried to parse a group string over MYBUFSIZ char */
147 if (sav_errno == ERANGE)
148 errmsg(M_GROUP_ENTRY_OVF);
149 else
150 errmsg(M_READ_ERROR);
151
152 modified = 0; /* bad group file: cannot rebuild */
153 break;
154 }
155
156 /* first delete the login from the group, if it's there */
157 if (overwrite || !gids) {
158 if (g_ptr->gr_mem != NULL) {
159 for (memptr = g_ptr->gr_mem; *memptr;
160 memptr++) {
161 if (strcmp(*memptr, login) == 0) {
162 /* Delete this one */
163 char **from = memptr + 1;
164
165 g_length -= (strlen(*memptr)+1);
166
167 do {
168 *(from - 1) = *from;
169 } while (*from++);
170
171 modified++;
172 break;
173 }
174 }
175 }
176 }
177
178 /* now check to see if group is one to add to */
179 if (gids) {
180 for (i = 0; gids[i] != -1; i++) {
181 if (g_ptr->gr_gid == gids[i]) {
182 /* Find end */
183 for (memptr = g_ptr->gr_mem; *memptr;
184 memptr++)
185 ;
186 g_length += strlen(new_login ?
187 new_login : login)+1;
188
189 *memptr++ = new_login ?
190 new_login : login;
191 *memptr = NULL;
192
193 modified++;
194 }
195 }
196 }
197 putgrent(g_ptr, t_fptr);
198 }
199 free(g_string);
200
201 (void) fclose(e_fptr);
202
203 if (fclose(t_fptr) != 0) {
204 (void) unlink(t_name);
205 return (EX_UPDATE);
206 }
207
208 /* Now, update GROUP file, if it was modified */
209 if (modified) {
210 if (rename(t_name, GROUP) != 0) {
211 (void) unlink(t_name);
212 return (EX_UPDATE);
213 }
214 return (EX_SUCCESS);
215 } else {
216 (void) unlink(t_name);
217 return (EX_SUCCESS);
218 }
219 }
220