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
30 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 #include <sys/types.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <sys/stat.h>
38 #include <grp.h>
39 #include <unistd.h>
40 #include <userdefs.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include "users.h"
44 #include "messages.h"
45
46 #define MYBUFSIZE (LINE_MAX)
47 /* Corresponds to MYBUFSIZE in grpck.c, BUFCONST in nss_dbdefs.c */
48
49 int
edit_group(char * login,char * new_login,gid_t gids[],int overwrite)50 edit_group(char *login, char *new_login, gid_t gids[], int overwrite)
51 {
52 char **memptr;
53 char t_name[] = "/etc/gtmp.XXXXXX";
54 int fd;
55 FILE *e_fptr, *t_fptr;
56 struct group *g_ptr; /* group structure from fgetgrent */
57 int i;
58 int modified = 0;
59 struct stat sbuf;
60
61 int bufsize, g_length, sav_errno;
62 long g_curr = 0L;
63 char *g_string, *new_g_string, *gstr_off;
64
65 if ((e_fptr = fopen(GROUP, "r")) == NULL)
66 return (EX_UPDATE);
67
68 if (fstat(fileno(e_fptr), &sbuf) != 0) {
69 (void) fclose(e_fptr);
70 return (EX_UPDATE);
71 }
72
73 if ((fd = mkstemp(t_name)) == -1) {
74 (void) fclose(e_fptr);
75 return (EX_UPDATE);
76 }
77
78 if ((t_fptr = fdopen(fd, "w")) == NULL) {
79 (void) close(fd);
80 (void) unlink(t_name);
81 (void) fclose(e_fptr);
82 return (EX_UPDATE);
83 }
84
85 /*
86 * Get ownership and permissions correct
87 */
88
89 if (fchmod(fd, sbuf.st_mode) != 0 ||
90 fchown(fd, sbuf.st_uid, sbuf.st_gid) != 0) {
91 (void) fclose(t_fptr);
92 (void) fclose(e_fptr);
93 (void) unlink(t_name);
94 return (EX_UPDATE);
95 }
96
97 g_curr = ftell(e_fptr);
98
99 /* Make TMP file look like we want GROUP file to look */
100
101 bufsize = MYBUFSIZE;
102 if ((g_string = malloc(bufsize)) == NULL) {
103 (void) fclose(t_fptr);
104 (void) fclose(e_fptr);
105 (void) unlink(t_name);
106 return (EX_UPDATE);
107 }
108 /*
109 * bufsize contains the size of the currently allocated buffer
110 * buffer size, which is initially MYBUFSIZE but when a line
111 * greater than MYBUFSIZE is encountered then bufsize gets increased
112 * by MYBUFSIZE.
113 * g_string always points to the beginning of the buffer (even after
114 * realloc()).
115 * gstr_off = g_string + MYBUFSIZE * (n), where n >= 0.
116 */
117 while (!feof(e_fptr) && !ferror(e_fptr)) {
118 g_length = 0;
119 gstr_off = g_string;
120 while (fgets(gstr_off, (bufsize - g_length), e_fptr) != NULL) {
121 g_length += strlen(gstr_off);
122 if (g_string[g_length - 1] == '\n' || feof(e_fptr))
123 break;
124 new_g_string = realloc(g_string, (bufsize + MYBUFSIZE));
125 if (new_g_string == NULL) {
126 free(g_string);
127 (void) fclose(t_fptr);
128 (void) fclose(e_fptr);
129 (void) unlink(t_name);
130 return (EX_UPDATE);
131 }
132 bufsize += MYBUFSIZE;
133 g_string = new_g_string;
134 gstr_off = g_string + g_length;
135 }
136 if (g_length == 0) {
137 continue;
138 }
139
140 /* While there is another group string */
141
142 (void) fseek(e_fptr, g_curr, SEEK_SET);
143 errno = 0;
144 g_ptr = fgetgrent(e_fptr);
145 sav_errno = errno;
146 g_curr = ftell(e_fptr);
147
148 if (g_ptr == NULL) {
149 /* tried to parse a group string over MYBUFSIZ char */
150 if (sav_errno == ERANGE)
151 errmsg(M_GROUP_ENTRY_OVF);
152 else
153 errmsg(M_READ_ERROR);
154
155 modified = 0; /* bad group file: cannot rebuild */
156 break;
157 }
158
159 /* first delete the login from the group, if it's there */
160 if (overwrite || !gids) {
161 if (g_ptr->gr_mem != NULL) {
162 for (memptr = g_ptr->gr_mem; *memptr;
163 memptr++) {
164 if (strcmp(*memptr, login) == 0) {
165 /* Delete this one */
166 char **from = memptr + 1;
167
168 g_length -= (strlen(*memptr)+1);
169
170 do {
171 *(from - 1) = *from;
172 } while (*from++);
173
174 modified++;
175 break;
176 }
177 }
178 }
179 }
180
181 /* now check to see if group is one to add to */
182 if (gids) {
183 for (i = 0; gids[i] != -1; i++) {
184 if (g_ptr->gr_gid == gids[i]) {
185 /* Find end */
186 for (memptr = g_ptr->gr_mem; *memptr;
187 memptr++)
188 ;
189 g_length += strlen(new_login ?
190 new_login : login)+1;
191
192 *memptr++ = new_login ?
193 new_login : login;
194 *memptr = NULL;
195
196 modified++;
197 }
198 }
199 }
200 putgrent(g_ptr, t_fptr);
201 }
202 free(g_string);
203
204 (void) fclose(e_fptr);
205
206 if (fclose(t_fptr) != 0) {
207 (void) unlink(t_name);
208 return (EX_UPDATE);
209 }
210
211 /* Now, update GROUP file, if it was modified */
212 if (modified) {
213 if (rename(t_name, GROUP) != 0) {
214 (void) unlink(t_name);
215 return (EX_UPDATE);
216 }
217 return (EX_SUCCESS);
218 } else {
219 (void) unlink(t_name);
220 return (EX_SUCCESS);
221 }
222 }
223